1

Consider a directory with typical Microsoft Windows filenames:

New Document.txt
Foo.doc
Foo - Copy.doc

I'd like to do something on each file such as:

for sendfile in $(find ${POLLDIR} -type f -mmin +1 -print0)
do
  echo \"${sendfile}\"
  ls -l "${sendfile}"
  and-so-on
  if success_above
  then
    mv "${sendfile}" "${donedir}/."
  fi
done

Please note that I don't want to just run 1 command with "${sendfile}" as the argument. I need the loop to do error checking and other things (like moving "${sendfile}" on success and logging on failure).

What is a "correct" construct to escape/quote the filenames from find so I can use them in a for look like in the ls command above? If possible, I'd like to avoid storing the filenames in a temp file one by one.

I don't think that find -printf '"%p"\n' as suggested by triplee in the comments to question [ How do I use find when the filename contains spaces? ] would work in a for foo in $(...) do construct.

I think replacing "illegal" chars with ? would work for me in this case, but it would be very ugly. The for loop ultimately processes files in ${POLLDIR} and then moves them when done, so the chance of "Foo bar.txt" colliding with "Foo-bar.txt" is 0 (-ish).

My best attempt so far is:

for sendfile in $(find ${POLLDIR} -type f -mmin +1 -print | tr ' ' '?')
do
  ...
done

Any cleaner suggestions?

MattBianco
  • 3,704
  • You rarely find a for loop and find construction together. I think you want something like find "$POLLDIR" -type f -mmin +1 -exec sh -c 'mv -- "$@" ./target' _ {} + You can use -name flag to specify different extensions etc. No need for the loop at all. – Valentin Bajrami Oct 09 '14 at 14:46
  • The problem is that I can't write the entire for loop body as -exec arguments. – MattBianco Oct 09 '14 at 14:51
  • As I said, you don't need a for loop there. You have not explained why you'd need a for loop. Unless I'm not understanding your question. – Valentin Bajrami Oct 09 '14 at 14:53
  • @val0x00ff I tried to explain it: "I don't want to run just 1 command, I need the loop to do error checking and other things"..."and logging on failure". This is too much for -exec IMHO. – MattBianco Oct 09 '14 at 14:58

1 Answers1

10

Use find ... -print0 | while IFS="" read -d "" construct:

find "${POLLDIR}" -type f -mmin +1 -print0 | while IFS="" read -r -d "" sendfile
  do
    echo "${sendfile}"
    ls -l "${sendfile}"
    and-so-on
    if success_above
      then
        mv "${sendfile}" "${donedir}/."
    fi
done

The -d "" sets the end of line character to null (\0), which is what separates each filename found by find ... -print0 and the IFS="" is needed to also work with filenames that contain newlines - according to POSIX only slashes (/) and null (\0) are forbidden. The -r ensures that backslashes do not escape characters (so that \t for example matches an actual backslash followed by a t and not a tab).

jimmij
  • 47,140
  • 2
    This is a perfect match. I had forgotten about read -d. Thanks! – MattBianco Oct 09 '14 at 14:55
  • I regret that I have only one upvote to give! Thank you to the answerer, I was giving up hope that this was possible at all. – AJM Sep 24 '20 at 16:51