25

I tried to use the xargs to pass the arguments to the echo:

[usr@linux scripts]$ echo {0..4} | xargs -n 1 echo
0
1
2
3
4

the -n 1 insured that the xargs pass 1 arguments a time to the echo.

Then I want to use this aruments twice, however the results is not I wanted:

[usr@linux scripts]$ echo {0..4} | xargs -I@ -n 1 echo @,@
0 1 2 3 4,0 1 2 3 4

the -n 1 seems disabled when I added the -I@, and this is the result I wanted:

0,0
1,1
2,2
3,3
4,4

how can I achieve that?

--------Supply------------------ I have used the method recommanded by @123 ,however ,there are still another question:

test.sh:

#!/bin/bash
a[0]=1
a[1]=2
echo "a[0] and a[1] : "${a[0]}, ${a[1]}
echo -n {0..1} | xargs -I num -d" " echo num,${a[num]},num

and this is the output:

[usr@linux scripts]$ sh test.sh 
a[0] and a[1] : 1, 2
0,1,0
1,1,1

you can see that the array a is not returned the value I wanted :< And How can I fix this problem?

spring cc
  • 937
  • 1
  • 10
  • 19
  • 3
    `echo -n {0..4} | xargs -I@ -d' ' echo @,@` ? `printf "%s\n" {0..4} | xargs -I@ echo @,@` ? – 123 Oct 19 '16 at 08:50
  • could you have a look at this question I just updated? There still some questions when using the arguments :(@123 – spring cc Oct 19 '16 at 09:26
  • 1
    Shell expansion occurs before xargs ever see it, so it is always evaluated as `${a[num]}` aka `${a[0]}`. could do something silly like `xargs -I num -d" " bash -c 'echo $num' "${a[@]}"` but theres probably a better way of achieving whatever your end goal is. – 123 Oct 19 '16 at 09:37
  • @123 I just have some doubts about `bash -c 'echo $num' "${a[@]}"`, how about the first `echo $num` works? It's seems to be the `$num` is substituted the `@` in the `${a[@]}`? – spring cc Oct 19 '16 at 09:55
  • And I just tried a command like this `a[0]=x;a[1]=y;num=1;bash -c 'echo $num' "{a[@]}"`, the output I expected is y, however the output is just blank. – spring cc Oct 19 '16 at 09:58
  • There is no `$` before `{a[@]}`... – 123 Oct 19 '16 at 10:00
  • this is a mistake about typing, I used the command with the `$`, it's just doesn't work, does it works in your computer? – spring cc Oct 19 '16 at 10:02
  • No, it's a completely different command than what i provided though, num is not being evaluated in the new shell as it hasn't been exported, xargs is substituting it in the previous example. – 123 Oct 19 '16 at 10:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126093/discussion-between-spring-cc-and-123). – spring cc Oct 19 '16 at 10:15

1 Answers1

15

If you can't change the input format, you could set the delimiter to a space:

$ echo -n {0..4} | xargs -d " " -I@ echo @,@
0,0
1,1
2,2
3,3
4,4

Otherwise, change the input to separate the tokens with a newline:

$ printf "%s\n" {0..4} | xargs -I@ echo @,@
0,0
1,1
2,2
3,3
4,4

The reason for this syntax is explained in man xargs

-I replace-str

Replace occurrences of replace-str in the  initial-arguments  with  names  read  from
standard input.  Also, unquoted blanks do not terminate input items; instead the sep‐
arator is the newline character.  Implies -x and -L 1.

So you must set the delimiter manually to a space if you want to delimit fields.

user000001
  • 32,226
  • 12
  • 81
  • 108
  • @123: I am not entirely sure why OP's version doesn't work. Apparently `xargs` splits the input line on whitespace when the `-I` option is not used, but with `-I` it treats the line as one argument. I couldn't find a good explanation of this in the man page... – user000001 Oct 19 '16 at 09:01
  • 2
    Under `-I` > `Also, unquoted blanks do not terminate input items; instead the separator is the newline character.` – 123 Oct 19 '16 at 09:05
  • Could you have a look a the question which I just updated? There are still some question in using of the arguments :( – spring cc Oct 19 '16 at 09:24
  • @springcc: re the edit, the reason why it doesn't work is that the variables are substituted before xargs is executed, so `${a[num]}` gets the first value of array, since `var` is undefined. You could solve it using eval, but there is probably a better solution... – user000001 Oct 19 '16 at 09:31
  • @user000001 I have tried the eval as `eval echo -n {0..1} | xargs -I num -d" " echo num,${a[num]},num`, however it still not work. Do you have some method? – spring cc Oct 19 '16 at 09:37
  • 2
    @springcc You have encountered a long-known problem. You can't use `xagrs -I` replacement as array iterator. So you need to invoke some subshell via `eval`, or just using `bash -c`. So you will need to export your array, but... You can't export an array in bash :) From manpage: `Array variables may not (yet) be exported.` In fact, there are some hacks do to that, but better way is to adjust the logic of your script and give only usual variables to xargs. – Hardy Rust Oct 22 '16 at 13:55
  • Using null separation `\0` in `printf` also works if you pass `-0` to `xargs`, like this: `printf "%s\0" {0..4} | xargs -0 -I{} -n 1 echo "{},{}"`. – Gabriel Staples Jul 25 '23 at 02:31
  • See also here: [Why using dirname in find command gives dots for each match?](https://stackoverflow.com/a/11158279/4561887). In some cases, using `bash -c` ensures you get the proper substitution. – Gabriel Staples Jul 25 '23 at 07:25