Run a UNIX command on each file in a directroy

28 03 2007

I keep having to look it up at random times, so I though I would post it here.

First there is the basic for loop, this however has some drawbacks, namely not being able to handle files with spaces in the filename, it sees a space as a newline and treats each individual part of the filename as an individual file. For the file “Sample File.txt” the the updating timestamp output is printed fine for the one file “Updating Sample File.txt”, but the touch command will execute on multiple files “Sample” and “File.txt”.
for file in *; do echo "Updating timestamp for $file"; touch "$file"; done

Using find with xargs, this only seems to be able to handle one command at a time (unless you bother to write a script for it). It puts all the commands on one line, rather than executing the command multiple times. It also doesn’t support more than one command, so echoing the message for each individual file seems impossible and it won’t work on commands unless they accept multiple filenames.
find . -print0 | xargs -0 touch

The next one is to use find with the -exec argument and some odd parameters, this allows for multiple command and spaces :):
find . -exec echo "Updating timestamp for" {} \&\& touch {} \;

Finally if you want something thats a bit nicer for use in scripts you can use read to stop the splitting of filenames:
find . | while read FILE; do
echo "Updating timestamp for $FILE"
touch "$FILE"
done;


Actions

Information

10 responses

10 09 2007
xpt

First there is the basic for loop, it is able to handle files with spaces in
the filename, e.g., it works for files like “Sample File.txt”.

for file in *; do echo “Updating timestamp for $file”; touch “$file”; done

10 09 2007
xpt

I bet the last “find . | while ” will not be able to handle files with spaces in the filename either unless:

touch “$FILE”

10 09 2007
H3g3m0n

Thanks I missed that, updated🙂

EDIT: Seems it works without the ” under zsh which would explain why I didn’t catch it

11 09 2007
xpt

how about my 1st comment, with my command,
the basic for loop, … it works for files like “Sample File.txt”.

11 09 2007
xpt

tested before posted.🙂

12 09 2007
H3g3m0n

Its working for me too now, that was causing me massive amounts of problems before:/

I think it must have been because I was originally trying to use the for loop with `ls` or `find .` instead of * which doesn’t seem to break things up, and probably didn’t bother to try the script with the ” around $FILE when I switched from find to *, problem with * is that there doesn’t seem to be any way to do anything more advanced such pattern matching (*.txt seems to become a file called “*.txt”), but it seems handy for basic scripts. Ill probably update the article when I get time later.

24 12 2008
ignite

One more problem with loop variant: huge directories. I find one when my mailbox was massively spammed.
“find” successfully cleared it by spamc

18 05 2009
NefAledly

hm. really like it🙂

11 02 2012
Anonymous

Thanks, second command works great..!!!

1 11 2012
DW

I use this:

find . -name * | sed ‘s.^.touch “.;s.$.”.’ | su

On my Unix, sed sees “^” as the beginning of the line, and “$” as the end of the line.

Since it adds a quote before and after the filename, special characters don’t bother it. It works with more complicated commands, with parameters before and after the filename.

I like it because it works from the command line as well as from a script.

-dw




%d bloggers like this: