I've written a small script made to backup downloads from TOR because occasionally TOR will try to merge its place holder file (a zero byte file with original file name) and its actual download file (original file plus a .part extension) early which will result in a loss of all data.

As an addition in this script I wanted to have the script delete the backed up .part file once the download was complete. The problem is that often times the downloaded file names contain spaces or special characters forcing me to use double quotes which works great until I have more than one file downloading, at which point find expands all the files on one line and I get no match on my test statement.

Maybe my approach to this is all wrong but if not, how can I get seperated file names for the rm command?

if [ ! -d /sdcard/Download/tordownloadbackup ]; then
mkdir /sdcard/Download/tordownloadbackup
echo 'backing-up'
find /sdcard/Download/ -maxdepth 1 -name '*.part' -print -exec cp {} /sdcard/Download/tordownloadbackup/ \;

for f in "`find /sdcard/Download/tordownloadbackup/ -type f |rev |cut -c 6-100| cut -d / -f 1 |rev`"; do

if [ -s /sdcard/Download/"$f" ]; then
    if [ -f /sdcard/Download/tordownloadbackup/"$f".part ]; then
    rm /sdcard/Download/tordownloadbackup/"$f".part
    echo "deleting $d"
sleep 300
  • Don't use for -- mywiki.wooledge.org/BashFAQ/001 – glenn jackman Jul 28 '20 at 17:33
  • I was going to post the final answer that worked but I keep getting a CAPTCHA challenge that doesn't work. Thanks to Hauke Laging for leading me to the final solution and to rexkogitans for causing me to learn a bit more about the shell. – Knightrider Jul 29 '20 at 17:18

If you are certain there are no newlines in the file names then you can just do this:

find /sdcard/Download/tordownloadbackup/ -type f -printf '%f\n' |
    awk '{ print substr($0,1,length($0)-5); }' |
    while IFS= read -r filename; do
        : ...

The general approach with whatever characters in the path is:

find . -exec bash -c 'ls -l "$@"' bash {} +

This command:

for f in "`find /sdcard/Download/tordownloadbackup/ -type f | ...

looks awkward and is error prone. It is really really discouraged to iterate over a printed list of files by using for.

The most robust way to iterate over files found via find in bash is to use a read and null terminated strings. Use < <(command) after the while to pipe the command's output into read, this is called process substitution.

while IFS= read -r -d $'\0' file; do
    # Arbitrary operations on "$file" here
done < <(find /some/path -type f -print0)

Kudos to an old answer by @SiegeX here: https://stackoverflow.com/questions/8677546/reading-null-delimited-strings-through-a-bash-loop

Also, rev |cut -c 6-100| cut -d / -f 1 |rev looks strange. I think this should print the directory base name. Please use either the bash-builtin string manipulation or dirname and basename for this.

So, you may end up rewriting this loop to (with string manipulation, faster because built-in):

while IFS= read -r -d $'\0' file; do
  # other stuff here
done < <(find /sdcard/Download/tordownloadbackup/ -type f -print0)

For more info on substring removal, please refer to The Linux Documentation Project.

Or use basename and dirname (slower because external programs):

while IFS= read -r -d $'\0' file; do
  Dirbasename="$(basename -- "$(dirname -- "$file")")"
  # other stuff here
done < <(find /sdcard/Download/tordownloadbackup/ -type f -print0)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.