views:

122

answers:

4

Here's what I'm trying to achieve.

I have a directory with over 4200 sub-files/sub-directories, all of which need to be set to a certain timestamp.

This is a problem because many of them have whitespaces and other weird characters.

First thing I tried:

touch $(find .)

Won't work because of the spaces. It will update the timestamps on all files and directories that are space-free, but those that are not, do not receive the new timestamp. In addition, as one might expected, it creates files in the root directory corresponding to the elements of filenames with spaces. So if I have a file somewhere named "One Two Three", 'touch' creates the 3 files, "One", "Two" and "Three" in the root. This by itself can be remedied by using -c with 'touch', but of course it still won't update the file with the spacey name.

The second thing I tried was:

for FILENAME in $(find .); do touch -t 201007162310.00 "$FILENAME"; done

But it's the same story.

I've found a couple of results online, but they're all trying to achieve something differently and so suggest ways that won't work for me.

So now I'm lost. Any ideas?

Is it really not possible to make the 'find' command prepend and append each line with a quotation mark or something?

EDIT: This needs to be done via shell script. Using Perl, PHP or any other environment will not help. I could easily do this with anything else than Unix shell scripting, but it has to be in a Unix shell script, preferably not a big one.

+1  A: 

A couple of options:

  • Use read, like this find . | while read x; do touch $x; done
  • Use xargs, like this find . -print0 | xargs -0 touch
Sjoerd
Change `read` to `read -r` or file names with backslashes won't work, and change `touch "$x"` or file names with spaces and globbing characters (`\[?*`) won't work. Or better, just forget the first kludgy solution and use `-print0` (and GNU only BSD) or `find -exec` (standard).
Gilles
+4  A: 

find and xargs can be made to communicate using null terminated strings, so you can do

find . -print0 | xargs -0 touch

This will not do exactly what you want, as there will be a number of invocations of touch causing differing times. Therefore you can use another file to act as a reference timestamp

touch /tmp/ref
find. -print0 | xargs -0 touch -r /tmp/ref
Beano
Actually I'm using the -t operator on 'touch' to designate the exact timing. :) That works, thanks a bunch!
Helgi Hrafn Gunnarsson
A: 

If you do

ls -b

the -b will cause the filename to be printed out with the escape character '\' in front of the space.

Update: I tried this

find . | ls -b | xargs touch

and it seemed to work for my little test case.

Chance
Doesn't really help with this particular problem, but it's a nice thing to know, +1 :)
Helgi Hrafn Gunnarsson
Helgi, don't know if you will be notified of me editing my answer, so I am adding this comment.
Chance
`ls` doesn't parse its standard input, so it won't recurse into directories. `ls -b` prints octal escapes for nonprintable characters (which `xargs` doesn't parse) and leaves `'` and `"` alone (so `xargs` will try to parse them). This is an example of why [you shouldn't parse the output of ls](http://mywiki.wooledge.org/ParsingLs).
Gilles
I stand corrected. In fact mine only works for files in the current directory. My bad.
Chance
+5  A: 
find -exec touch -t 201007162310.00 \+

This is the simplest and the most robust way to do what you want.

Roman Cheplyaka