11

I'm trying to use xargs to run a command for each provided argument, but unfortunately the --replace/-I flag doesn't seem to work properly when conjugated with -n. It seems that {} will expand into the full list of arguments read from stdin, regardless of the -n option.

Unfortunately all of the examples on the web seem to be for commands (mv, cp, rm) which will take multiple arguments where {} is expanded.

For example, when running:

echo a b c d | xargs -n 1 -I {} echo derp {}

The output is:

derp a b c d

But I expected:

derp a
derp b
derp c
derp d

However, running it without -I {} yields the expected result:

echo a b c d | xargs -n 1 echo derp
derp a
derp b
derp c
derp d

Is there any way to achieve this with xargs? My ultimate intention is to use it to run multiple (parralel) ssh sessions, like

echo server{1..90} | xargs -n 1 -P 0 -I {} ssh {} 'echo $SOME_HOST_INFO'

I'm running xargs (GNU findutils) 4.4.2 on RHEL 6.3.

André Fernandes
  • 469
  • 1
  • 8
  • 20
  • 2
    Interestingly it works as you intend with BSD `xargs`, but not GNU `xargs`. – slhck Jul 26 '13 at 09:39
  • 1
    `man xargs` on `-I`: `Also, unquoted blanks do not terminate input items; instead the separator is the newline character. Implies -x and -L 1.` So it behaves as designed. – Daniel Beck Jul 26 '13 at 12:09
  • @André, since your intention is to run taks in parallel, have you taken a look at GNU parallel? It does the job nicely. Disclaimer: I package it for Debian. – rbrito Jul 30 '13 at 12:58
  • GNU parallel seemed like a good replacement, but unfortunately I can't install it on this particular host. – André Fernandes Aug 11 '13 at 15:31
  • @AndréFernandes Can you elaborate if you reason is not covered by http://oletange.blogspot.dk/2013/04/why-not-install-gnu-parallel.html – Ole Tange Aug 18 '13 at 11:00

2 Answers2

5

You can echo with newlines to achieve your expected result. In your case with the server expansion that would be:

$ echo -e server{1..4}"\n" | xargs -I{} echo derp {}
derp server1
derp server2
derp server3
derp server4
ahilsend
  • 250
  • 1
  • 6
  • The newline idea works around the issue (just not with your exact example): echo server{1..4} | tr ' ' '\n' | xargs -I{} echo derp {} derp server1 derp server2 derp server3 derp server4 – André Fernandes Jul 26 '13 at 12:07
  • Sorry, tested it with `zsh` ... Fixed my answer. `echo` needs the `-e` option. – ahilsend Jul 26 '13 at 12:11
3

You can make use of an extra pipe like this,

echo a b c d | xargs -n1 | xargs -I{} echo derp {}
derp a
derp b
derp c
derp d

The intermediate use of xargs 'echos' each letter 'a b c d' individually because of the '-n1' option. This puts each letter on it's own line like this,

echo a b c d | xargs -n1
a
b
c
d 

It's important to understand when using -I (string replacement), xargs implies -L, which executes the utility command (in your case echo) once per line. Also, you cannot use -n with -L as they are mutually exclusive.

synergy77
  • 31
  • 1