2

The following works:

ls {000/487,000/488,000/489,000/490,000/491,000/492}
...many files being listed

But this doesn't. Why ?

LIST=000/487,000/488,000/489,000/490,000/491,000/492
ls {$LIST}
ls: cannot access '{000/487,000/488,000/489,000/490,000/491,000/492}': No such file or directory
3
  • Basically, you are bucking the order of evaluation. This works: eval ls {$LIST} Commented Nov 14 at 9:31
  • just as an aside, if this is really your use case: ls {000/487,000/488,000/489,000/490,000/491,000/492} is a bit strange, you'd usually want ls 000/{487,488,489,490,491,492} for that, or even more concise, ls 000/{487..492} Commented Nov 14 at 10:02
  • Yes, that's my use case because in real use they can be different than 000. As for using eval, thanks it works but I don't really understand why. I very rarely use eval in shell scripts... Commented Nov 14 at 13:35

1 Answer 1

1

as you've probably guessed, because the brace expansion ({…,…}-> ) happens before the parameter expansion ($LIST-> string stored within). From Bash manual, "3.2 Shell Expansions":

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; filename expansion; and quote removal.

So, brace expansion happens first (by the way, that's different for zsh, where the order is history, alias, (process|parameter|command|arithmetic|brace), filename expansion, globbing).

In your example, I'd just go and temporarily set the string split character to , (I'd do that in a subshell to avoid contaminating the rest of my script):

IFS=,
ls $LIST # no brace expansion here!

But that won't help if you wanted to do something that needs brace expansion, such as LIST=a,b,c,d; ls $LIST/foo (you'd be getting ls a b c d/foo, which is not the same as ls a/foo b/foo c/foo d/foo).

A simple way out there is xargs or parallel.

LIST=000/487,000/488,000/489,000/490,000/491,000/492
echo $LIST | parallel -m -j1 -d ',' ls {}/foo

(the -m means "put as many arguments on one command line as possible within operating system limits for command lines", the -j1 means "only spawn 1 process, not one per processor core)

In zsh, you can get the brace-expansion behaviour with paramater expansions natively, using the (in my humble opininion) not trivial to remember form of ${(flags in parentheses)OPERATORvariablename}, in this case

LIST=000/487,000/488,000/489,000/490,000/491,000/492
ls ${(s:,:)^LIST}/foo

where the flags are sCHARdelimiterCHAR, "split at the string enclosed by CHAR", and the expansion operator is ^SPEC, which says "use the result of this expansion as a list in a brace expansion". (yes, it took a while to figure that out. I bet the zsh project would love clarifying documentation for man zshexpn.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.