0

Command:

$ echo 'hello' | xargs -I{} sh -c "echo 'hi'/$(echo {}| sed 's/hello/bye/g')"

Expected:

> hi/bye

Actual:

> hi/hello

Note that:

$ sh -c "echo 'hi'/$(echo hello| sed 's/hello/bye/g')"
> hi/bye

I would also highly appreciate the reason behind why the answer works, and why this does not.

EDIT:

$ set -x; echo 'hello' | xargs -I{} sh -c "echo 'hi'/$(echo {}| sed 's/hello/bye/g')"
+-zsh:126> echo hello
+-zsh:126> echo '{}'
+-zsh:126> sed s/hello/bye/g
+-zsh:126> xargs '-I{}' sh -c 'echo '\''hi'\''/{}'
hi/hello

Charles is correct, the echo is evaluated before the xargs substitution

EDIT 2:

The use case is:

find $(pwd) -path "*jpg" | head| xargs -I{} sh -c "ln -s {} $(pwd)/flat/$(echo {} | sed 's/\//_/g')"

Wanting to symlink a bunch of nested files into a flat folder with some name updates

maininformer
  • 967
  • 2
  • 17
  • 31
  • 1
    run `set -x ; echo 'hello' | xargs ...your command...`. What do you see? – KamilCuk Nov 21 '21 at 01:08
  • Note that `xargs -I{} sh -c '...{}...'` is an **extremely** bad idea from a security perspective. – Charles Duffy Nov 21 '21 at 01:11
  • What do you think is going to happen when your `{}` is substituted with `$(rm -rf ~)'$(rm -rf ~)'`? – Charles Duffy Nov 21 '21 at 01:12
  • 1
    All that said -- the big problem here is that the contents of your double quotes are expanded before `xargs` is ever run. Thus, the `sed` is complete before `xargs` starts, so it can't possibly process the _result_ of the replacement that xargs is performing. – Charles Duffy Nov 21 '21 at 01:12
  • 1
    If you gave an example more clearly representative of your real use case, we might be able to go beyond answering the literal question of "why doesn't this work?" and be in a position to the speak to the underlying question of "so what do I do instead?" – Charles Duffy Nov 21 '21 at 01:15
  • 1
    BTW, zsh is not bash; zsh questions should be tagged zsh. (bash and sh are _also_ two very different shells, and not entirely mutually compatible; even when the executables are symlinked together, bash disables many of its features when run under the sh name) – Charles Duffy Nov 21 '21 at 01:15
  • ...to speak briefly to using `xargs sh` safely -- take out the `-I{}`. Instead, let xargs add things to the shell's command line, and write your shell script to loop over them: `xargs sh -c 'for arg; do echo "hi/$(echo "$arg" | sed 's/hello/bye/g')"; done'` – Charles Duffy Nov 21 '21 at 01:18
  • (1) What's the point of the `head` there? If you didn't need it you could use `find -exec` and this whole thing would be a lot shorter. (2) Are you guaranteed GNU tools, or is compatibility with BSD/POSIX/&c required? – Charles Duffy Nov 21 '21 at 01:20
  • @KamilCuk, ...without `-print0`, `head -z`, etc. there are a bunch of bugs here surrounding behavior with surprising filenames; it'd be good to try to resolve them while we're in here advising the OP – Charles Duffy Nov 21 '21 at 01:21
  • 1
    (and `"$PWD"` is a lot faster than `$(pwd)` even if you need an absolute path) – Charles Duffy Nov 21 '21 at 01:21
  • 1
    @maininformer, ...btw, for an example of what I'm talking about wrt "behavior with surprising filenames" -- `d=$'/tmp/\n/etc/passwd\n/ '; mkdir -p -- "$d" && touch "$d/test.jpg"` is a valid command. You probably don't want your script trying to operate on `/etc/passwd`, even though that command will make `find /tmp -name '*.jpg'` show it in output. – Charles Duffy Nov 21 '21 at 01:24
  • @CharlesDuffy `head` is for testing the script. There are a lot of files. I will try find -exec, but I need the original file path for the first symlink argument and the `sed`-ed one for the unique flat filename. (2) this is a local script on my MacOS. – maininformer Nov 21 '21 at 01:24
  • 1
    Great; if the `head` is just for testing we don't need to worry about it so much. `mkdir -p flat; while IFS= read -r -d '' filename; do ln -s "$filename" "$PWD/flat/${filename//"/"/_}"; done < <(find "$PWD" -type d -name flat -prune -o -name '*.jpg' -print0)` -- note that that's for bash, not sh or zsh. – Charles Duffy Nov 21 '21 at 01:32
  • 1
    For discussion of the techniques used there, see [Using Find](https://mywiki.wooledge.org/UsingFind), including the "Actions in Bulk" and "Complex Actions" sections. – Charles Duffy Nov 21 '21 at 01:33
  • 1
    (MacOS means you may not have GNU find, or `head -z`, but that you _will_ have `-print0`) – Charles Duffy Nov 21 '21 at 01:39

0 Answers0