5

(Though word splitting has a specific definition in Bash, in this post it means to split on spaces or tabs.)

Demonstrating the question using this input to xargs,

$ cat input.txt
LineOneWithOneArg
LineTwo WithTwoArgs
LineThree WithThree Args
LineFour  With  Double  Spaces

and this Bash command to echo the arguments passed to it,

$ bash -c 'IFS=,; echo "$*"' arg0 arg1 arg2 arg3
arg1,arg2,arg3

notice how xargs -L1 word-splits each line into multiple arguments.

$ xargs <input.txt -L1 bash -c 'IFS=,; echo "$*"' arg0
LineOneWithOneArg
LineTwo,WithTwoArgs
LineThree,WithThree,Args
LineFour,With,Double,Spaces

However, xargs -I{} expands the whole line into {} as a single argument.

$ xargs <input.txt -I{} bash -c 'IFS=,; echo "$*"' arg0 {}
LineOneWithOneArg
LineTwo WithTwoArgs
LineThree WithThree Args
LineFour  With  Double  Spaces

While this is perfectly reasonable behavior for the vast majority of cases, there are times when word-splitting behavior (the first xargs example) is preferred.

And while xargs -L1 can be seen as a workaround, it can only be used to place arguments at the end of the command line, making it impossible to express

$ xargs -I{} command first-arg {} last-arg

with xargs -L1. (That is of course, unless command is able to accept arguments in a different order, as is the case with options.)

Is there any way to get xargs -I{} to word-split each line when expanding the {} placeholder?

mxxk
  • 9,514
  • 5
  • 38
  • 46

1 Answers1

5

Sort of.

echo -e "1\n2 3" | xargs sh -c 'echo a "$@" b' "$0"

Outputs:

a 1 2 3 b

ref: https://stackoverflow.com/a/35612138/1563960

Also:

echo -e "1\n2 3" | xargs -L1 sh -c 'echo a "$@" b' "$0"

Outputs:

a 1 b
a 2 3 b
webb
  • 4,180
  • 1
  • 17
  • 26
  • Ah, interesting... This works around the problem by wrapping the desired command to be executed by `xargs` in a shell, so that `xargs` to passes arguments to the shell. Then the shell script references the word-split arguments via the `$@` variable. Clever! – mxxk Nov 01 '19 at 11:12
  • Thanks! But wait, didn't you want two results in this case? `a 1 b` and `a 2 3 b`? – webb Nov 02 '19 at 12:22
  • This is actually a perfect solution, using the shell wrapper together with `$@`. Did you mean something else? – mxxk Nov 11 '19 at 23:05
  • 1
    I wasn't sure whether you wanted the output for my example to be `a 1 2 3 b` on one line or `a 1 b` and `a 2 3 b` on two lines, so I edited my answer to include both options. – webb Nov 13 '19 at 10:20
  • 1
    Ah, thanks for clarifying. In my original examples I've used either `-L1` or `-I{}` both of which take consume one line of input per command invocation. It can't hurt to have other alternatives of course. – mxxk Nov 13 '19 at 23:29