1

This question already has an answer here:

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?

marked as duplicate by casey, slm Oct 9 '14 at 15:40

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 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 9 '14 at 14:46
  • The problem is that I can't write the entire for loop body as -exec arguments. – MattBianco Oct 9 '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 9 '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 9 '14 at 14:58
5

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

find ${POLLDIR} -type f -mmin +1 -print0 | while IFS= read -r -d '' -r 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 \0 which is what separates each file found by find -print0 and the IFS= is needed to also work with files that contain newlines. 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).

  • This is a perfect match. I had forgotten about read -d. Thanks! – MattBianco Oct 9 '14 at 14:55

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