why shell for expression cannot parse xargs parameter correctly

286 views Asked by At

I have a black list to save tag id list, e.g. 1-3,7-9, actually it represents 1,2,3,7,8,9. And could expand it by below shell

for i in {1..3,7..9}; do for j in {$i}; do echo -n "$j,"; done; done
1,2,3,7,8,9

but first I should convert - to ..

echo -n "1-3,7-9" | sed 's/-/../g'
1..3,7..9

then put it into for expression as a parameter

echo -n "1-3,7-9" | sed 's/-/../g'  | xargs -I @ for i in {@}; do for j in {$i}; do echo -n "$j,"; done; done
zsh: parse error near `do'

echo -n "1-3,7-9" | sed 's/-/../g'  | xargs -I @ echo @
1..3,7..9

but for expression cannot parse it correctly, why is so?

3

There are 3 answers

1
erik258 On

Because you didn't do anything to stop the outermost shell from picking up the special keywords and characters ( do, for, $, etc ) that you mean to be run by xargs.

xargs isn't a shell built-in; it gets the command line you want it to run for each element on stdin, from its arguments. just like any other program, if you want ; or any other sequence special to be bash in an argument, you need to somehow escape it.

It seems like what you really want here, in my mind, is to invoke in a subshell a command ( your nested for loops ) for each input element.

I've come up with this; it seems to to the job:

echo -n "1-3,7-9" \
| sed 's/-/../g'  \
| xargs -I @  \
bash -c "for i in {@}; do for j in {\$i}; do echo -n \"\$j,\"; done; done;"

which gives:

{1..3},{7..9},
0
zhuguowei On

Could use below shell to achieve this

# Mac newline need special treatment
echo "1-3,7-9" | sed -e 's/-/../g' -e $'s/,/\\\n/g' | xargs -I@ echo 'for i in {@}; do echo -n "$i,"; done' | bash
1,2,3,7,8,9,%


#Linux
echo "1-3,7-9" | sed -e 's/-/../g' -e 's/,/\n/g' | xargs -I@ echo 'for i in {@}; do echo -n "$i,"; done' | bash
1,2,3,7,8,9,

but use this way is a little complicated maybe awk is more intuitive

# awk
echo "1-3,7-9,11,13-17" | awk '{n=split($0,a,","); for(i=1;i<=n;i++){m=split(a[i],a2,"-");for(j=a2[1];j<=a2[m];j++){print j}}}' | tr '\n' ','
1,2,3,7,8,9,11,13,14,15,16,17,%
1
Ole Tange On
echo -n "1-3,7-9" | perl -ne 's/-/../g;$,=",";print eval $_'