![]() |
Ditto Within a For Loop
Hi all,
I'm not a Unix scripter and I am wondering why this part of my simple shell script does not work. My objective is to put the 2 most recently modified files of a directory into a disk image. I believe I first have to ditto them to a temp directory. So, this doesn't work... for i in "ls -1t | head -2" do ditto -rsrcFork "$i" /Temp/"$i" done The file names do have spaces in them. I tried putting the quotes in various places. The loop seems to pass ditto both file names at once. Any help greatly appreciated. Thanks. |
for i in `ls -1t | head -2`;
? |
Quote:
|
Tried escaping the quotes with \? edit - hmm, actually I don't think that'll work. You could use sed to mangle the variable contents though. Something along the lines of passing it through a
sed -e 's/ /\\ /g' |
Quote:
Say there are two files within "Folder One"; "File One" and "File Two"... cd ~/Desktop/"Folder One" x="$(ls -t | head -2)" ditto "$x" ~/Desktop Returns... ditto: /Users/me/Desktop/Folder One/File One.rtf File Two.rtf: No such file or directory It loses the path to the second file. ? |
Solved
This works...
cd ~/Desktop/"Folder One"; for i in "`pwd ls -t | head -2`"; do ditto "$i" ~/Desktop; done Thanks. |
Quote:
The problem is that the variable $x contains a newline: Code:
% echo $xArgument #1: File One File Two Argument #2: /Users/me/Desktop Thus 'ditto' will be looking for one file whose name is: "File One File Two" (with an embedded newline) and the fact that there is no file with that name is what the error message is saying. |
The xargs command can be a useful addition to your Unix bag of tricks. This lets you do a one-liner:
Code:
ls -t "Folder 1" | head -2 | xargs -i ditto "Folder 1/{}" "Folder 2/" |
I note that another way of doing this would sidestep the loop entirely and instead pass all of the files to be copied in one call to 'ditto' via the use the 'xargs' command.
E.g.: ls -1t | head -2 | enquote | xargs -J % ditto % ~/Desktop In the above I have used my Bash function 'enquote' (in my ~/.profile): Code:
# enquote: surround lines with quotes (useful in pipes) - from mervTormel |
Quote:
I don't see any "-i" option in the 'xargs' on my 10.4.5 system. |
Quote:
I'm used to the Solaris version of xargs, and sadly have no Macs at work. Your version of the command is the right one for OS X. Sorry for the misinformation. |
Quote:
I just did a few quick tests, and it seems the -0 (zero) option to xargs will discern between spaces within filenames and spaces between files. (I think the trailing newline after each filename is the key). So, I believe this line will work the same: ls -1t | head -2 | xargs -0 -J % ditto % ~/Desktop But I still like that "enquote" function. ;) -HI- |
Quote:
Code:
% mkdir Foo |
son-of-a-:o
|
This works (yes, on my G5 this time) :)
Code:
G5:~$ ls -1t | head -2 | xargs -I % ls "%"The -0 option tells xargs to split on a "null" character, which you will have a hard time generating using ls. It's meant to be used in conjunction with the -print0 option of the find command. |
Quote:
I was worried about the 255 character limit mentioned in the man page but it doesn't seem to affect things here. I guess I don't understand it - the 'xargs' man page isn't a model of clarity. :) I.e. ls -1t | head -100 | xargs -I % ls % works fine after doing: for ((i=0; i<100; i++)); do touch "file "$i; done |
Thanks to all for your replies. This has been very interesting. I was wondering if someone could explain the meaning of the percent signs in the following code...
ls -1t | head -2 | xargs -I % ls "%" And why is the last one quoted? Thanks. |
Quote:
Code:
-I replstrIt turns out that there is no need to quote the % in the 'ls' example. But quoting it means that the replacement strings (from the results of 'head -2') will be quoted. |
Quote:
I just assumed the quotes were needed when you had file names containing spaces. But you're right, it works without them. This must mean that xargs tokenizes the command line before doing placeholder replacement, then calls some form of exec with an argument list, rather than passing the whole command line to a sub-shell. Which makes perfect sense if you think about it. And you're right, that man page is pretty murky. This excerpt from the Cygwin man pages makes it a little clearer. I think it still applies to the OS X version. Quote:
|
Quote:
Code:
% curl -s http://www.opensource.apple.com/darwinsource/10.4.5.ppc/shell_cmds-74.1/xargs/xargs.c | grep execv |
Quote:
Quote:
at all... plus, there is still a discernible difference when used in this example. Back to the Foo directory: Code:
$ ls -1t |
xargs
Sorry to beat this horse again... but when I actually type it out,
I sometimes start to understand it better. Guess this won't be any big news, but -- while the magical xargs -I% works wonders on paths with spaces -- it still stumbles on single quotes: Code:
$ cd Foo; ls -1 Employing hayne's handy enquoter makes all go well: Code:
$ ls -1 | sed 's/^.*$/"&"/' | xargs -I% file % Code:
$ ls -1"Bartlett's Quotations" into ""Bartlett's Quotations"" which -in effect- becomes Bartlett's Quotations Let's tweak enquoter so it won't quote any double quotes: Code:
$ ls -1 | sed '/"/!s/^.*$/"&"/'Code:
$ ls -1 | sed '/"/!s/^.*$/"&"/' | xargs -I% file % Code:
$ ls -1 | sed '/"/!s/^.*$/"&"/' | xargs -I% file "%"Code:
$ ls -1 | sed '/"/!s/^.*$/"&"/' | xargs -I% file \"%\" (Umm, not really). FORGET IT. It's hopeless that way. Must resort to null chars to manage *all* possible cominations of quotes... Code:
$ find . -name "*i*" -print0 | xargs -0 -I% file %Add the -n1 option to pass them one at a time. Code:
$ find . -name "*i*" -print0 | xargs -0 -n1 -I% file %-- But: must we always use find's print0 to talk to xargs -0 ? The original ls -1t | head -2 had a particular purpose. Isn't there some way we can insert nulls after any command's list output? Let's try tr Code:
$ ls -1 | tr "\n" "\000\n" | xargs -0 -n1 fileGoing for broke... Code:
$ ls -1t | head -2 | tr "\n" "\000\n" | xargs -0 -n1 -I% cp -v % %.bak-HI- |
Using 'tr' to change newlines into null characters is a good idea.
But do you still need the newlines if you do that? I.e. wouldn't tr "\n" "\000" work just as well? |
Thanks hayne... guess I got so "caught-up-in-the-moment" that
I wasn't thinking about further code reduction. I wonder though if that newline might be missed... further down the pipeline. (?) [No, I guess you're right.] Well, we're even then: You trimmed nuller() { /usr/bin/tr "\n" "\000"; } ...and I tweaked enquoter() { /usr/bin/sed '/"/!s/^.*$/"&"/'; } ;) (Not of much use I guess, unless the entire "name" was quoted.) |
| All times are GMT -5. The time now is 05:28 PM. |
Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.