How to assign a glob expression to a variable in a Bash script?
When the following two lines of code are executed in a bash script, "ls" complains that the files don't exist:
dirs=/content/{dev01,dev02}
ls -l $dirs
When I run the script with the -x option, it appears to be passing the variable within single quotes (which would prevent globbing):
+ dirs=/content/{dev01,dev01}
+ ls -l '/content/{dev01,dev01}'
ls: /content/{dev01,dev01}: No such file or directory
If I execute the "ls" command from my interactive shell (sans quotes), it returns the two directories.
I've been reading through the Bash Reference Manual (v 3.2) and can't see any reason for filename globbing to not take place (I'm not passing -f to the shell), or anything that I can set to ensure that globbing happens.
Solution 1:
I think it is the order of expansions:
The order of expansions is:
brace expansion
, tilde expansion, parameter,variable
and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, andpathname expansion
.
So if your variable is substituted, brace expansion doesn't take place anymore. This works for me:
eval ls $dirs
Be very careful with eval. It will execute the stuff verbatimly. So if dirs contains f{m,k}t*; some_command
, some_command will be executed after the ls finished. It will execute the string you give to eval
in the current shell. It will pass /content/dev01 /content/dev02
to ls, whether they exist or not. Putting *
after the stuff makes it a pathname-expansion, and it will omit non-existing paths:
dirs=/content/{dev01,dev02}*
I'm not 100% sure about this, but it makes sense to me.
Solution 2:
Here is an excellent discussion of what you are trying to do.
The short answer is that you want an array:
dirs=(/content/{dev01,dev01})
But what you do with the results can get more complex than what you were aiming for I think.
Solution 3:
For folks (like me) finding this through Google, @Peter and @feoh's answers are the general solution to "How to glob variables in bash script".
list_of_results=(pattern)
will save existing filenames matching pattern
into the array list_of_results
. Each element of list_of_results
will hold one filename, spaces and all.
You can access each result as "${list_of_results[<index>]}"
for <index>
starting from 0. You can get the entire list, properly quoted, as "${list_of_results[@]}"
.
Solution 4:
This isn't filename globbing, this is brace expansion. The difference is subtle, but it exists - in filename globbing you would only receive existing files as a result, while in brace expansion you can generate any kind of string.
http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion
http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion
Now, this is what worked for me:
#!/bin/sh
dirs=`echo ./{dev01,dev02}`
ls $dirs