0
I’ve spent last hour or so trying to write a pretty simple bash script and I never felt so dumb.
So I have a list of strings (which are package selector specifications for a package manager, if that matters) which might contain asterisks. I need to build a command line preserving those asterisks and then call a program. Here is my naïve attempt:
xs="foo/bar */*"
xs_cmd=""
for x in $xs; do
xs_cmd="$xs_cmd -0 $x "
done
echo $xs_cmd
I need the echo
call to be equivalent to
echo -0 foo/bar -0 '*/*'
which outputs -0 foo/bar -0 */*
.
P. S. In reality the first line is slightly more complex: xs="$some foo/bar */* $(get_others)"
.
If you run this script in a directory containing a/b
, you’ll get -0 foo/bar -0 a/b
instead.
When I change the last line to
echo "$xs_cmd" # note the quotes
I get -0 foo/bar -0 a/b
and, first of all that’s not what I want, since now I’m invoking echo
with a single argument, and furthermore that tells me that the expansion is happening earlier, likely in the for
loop.
But I can’t change the for
line because
for x in "$xs"; do # note the quotes
will just make the loop iterate only once: -0 foo/bar */*
(there is a single -0
)
so adding those quotes is not an option for sure.
Now if I revert back to the original script and replace the first line with
xs="foo/bar '*/*'" # note additional single quotes
I get -0 foo/bar -0 '*/*'
(single quotes should’t be there).
It seems that I tried every combination of quotes and backslash-escapes of asterisks and I have literally no idea what to do now.
Errr, no, this still performs expansion in the
for
loop and outputsa/b
instead of*/*
. Another problem is that the first line is somewhat more complex in reality; I’ll update the question in a minute. – kirelagin – 2014-10-27T13:55:52.663I thought you wanted it expanded in the
for
loop. I tested it on Ubuntu 14.04 and got-0 "foo/bar" -0 "dir/file" -0 "dir/file" ...
– AFH – 2014-10-27T14:40:46.330You might want to read my question again, especially the part right before the horizontal ruler that gives the desired output: no expansion should be performed; I need all the asterisks left in place. – kirelagin – 2014-10-27T15:13:00.213
OK. I have found something that works. Make your first line
xs="foo/bar @/@"
, then after yourfor
loop add the linexs_cmd=$(echo $xs_cmd|sed "s/@/\*/g")
. If you can't find a character such as@
which is not in any of your file names, then usexs="foo/bar ///"
andxs_cmd=$(echo $xs_cmd|sed "s-///-\*/\*-g")
. I suggested using@
first as it is easier to generalise for other strings with asterisks. – AFH – 2014-10-27T16:28:26.393So, now I have
xs_cmd
which is a string with some asterisks. How do I pass it to the command (echo
in my example)?echo $xs_cmd
will perform expansion.echo "$xs_cmd"
will invokeecho
with a single argument. – kirelagin – 2014-10-27T16:37:56.490I mean, shell programming is truly insane. I could have written this program in any other language in like two minutes and make it work in just one attempt. But now I really want to know how to write this in shell! – kirelagin – 2014-10-27T16:41:36.420
Since you are fighting against file name expansion, why not disable it in this context with
set -f
and restore it afterwards withset +f
? – AFH – 2014-10-27T18:37:07.467Ah, of course! Well, I’d classify this as a workaround, not a true solution (because I’m still interested how to do this directly), but that works. Thank you! – kirelagin – 2014-10-28T06:52:45.303
I have been thinking about your comment, and I don't conclude it is a work-round. Your script fails because asterisks are being expanded when you don't want them to be, so stopping their expansion at the wrong time constitutes a solution, because it's precisely what the other proposals are trying to do. – AFH – 2014-10-28T18:06:09.717