On Fri, Mar 22, 2013 at 5:11 AM, Eric Blake <[email protected]> wrote: > On 03/21/2013 08:52 PM, Juho-Pekka Kuitunen wrote: >> On Fri, Mar 22, 2013 at 3:05 AM, Bob Proulx <[email protected]> wrote: >>> Juho-Pekka Kuitunen wrote: >>>> Reproduce example; >>>> $ echo "testdir/testfile" | xargs -I '{}' echo '{}', dir: $(echo dirname >>>> '{}') = $(dirname '{}') > >>> The problem is the $(...) which is running the dirname during the >>> earlier shell command line parsing pass and passing the result off to >>> the xargs command. >> >> So if I understand correctly, subshell replacements gets executed >> before xargs fills in the replacements? I tried to rule out this >> wonkyness with the "$(echo '{}')" bit and it seemed to work in the >> correct order. The echo should produce an empty string if the subshell >> was evaluated too soon? > > If you want to see when the shell is evaluating $(), use 'set -vx': > > $ set -vx > $ echo testdir/testfile | xargs -I '{}' echo '{}', dir: $(echo dirname > '{}') = $(dirname '{}') > echo testdir/testfile | xargs -I '{}' echo '{}', dir: $(echo dirname > '{}') = $(dirname '{}') > + echo testdir/testfile > echo dirname '{}') > dirname '{}') > echo dirname '{}') > echo dirname '{}' > ++ echo dirname '{}' > dirname '{}') > dirname '{}' > ++ dirname '{}' > + xargs -I '{}' echo '{},' dir: dirname '{}' = . > testdir/testfile, dir: dirname testdir/testfile = . > $ set - > set - > + set - > > What you seem to want to do is invoke a shell instance on each file > name; that would be done as follows: > > $ echo testdir/testfile | xargs -I {} sh -c \ > 'echo "$1", dir: $(echo dirname "$1") = $(dirname "$1")' sh {} > testdir/testfile, dir: dirname testdir/testfile = testdir > > Or again with shell tracing (note there are two levels of shells - the > shell that spawns xargs, and the shell that xargs spawns, so I'm posting > two different traces): > > $ set -vx > $ echo testdir/testfile | xargs -I {} sh -c 'echo "$1", dir: $(echo > dirname "$1") = $(dirname "$1")' sh {} > echo testdir/testfile | xargs -I {} sh -c 'echo "$1", dir: $(echo > dirname "$1") = $(dirname "$1")' sh {} > + xargs -I '{}' sh -c 'echo "$1", dir: $(echo dirname "$1") = $(dirname > "$1")' sh '{}' > + echo testdir/testfile > testdir/testfile, dir: dirname testdir/testfile = testdir > $ set - > set - > + set - > $ echo testdir/testfile | xargs -I {} sh -cvx 'echo "$1", dir: $(echo > dirname "$1") = $(dirname "$1")' sh {} > echo "$1", dir: $(echo dirname "$1") = $(dirname "$1") > echo dirname "$1") > echo dirname "$1" > ++ echo dirname testdir/testfile > dirname "$1") > dirname "$1" > ++ dirname testdir/testfile > + echo testdir/testfile, dir: dirname testdir/testfile = testdir > testdir/testfile, dir: dirname testdir/testfile = testdir > > By the way, your question is mostly related to shell, and a bit with > xargs, and practically nothing to do with dirname. Your confusion on > WHEN $() is expanded would apply no matter what executable you plug in > instead of dirname. But since neither sh nor xargs belongs to > coreutils, you might get better answers by asking on a general forum on > shell programming subtleties. > >> >> Very much possibly not a bug but I'm not 100% convinced yet. I've been >> chuckling at this all evening, love a good brain teaser even if it >> turns out to be something I overlooked instead of a bug. :-) > > Hopefully shell tracing has managed to convince you. >
Yup, that did the trick. Looks like I didn't understand the way xargs works nearly well enough. Thanks for the lesson, my bad! > -- > Eric Blake eblake redhat com +1-919-301-3266 > Libvirt virtualization library http://libvirt.org > -- Juho-Pekka Kuitunen
