I have a variable containing a path and want to expand a glob pattern based on that path. I want to understand why my attempts don't work and what is the preferred way of doing this in bash.
EX: I want to list all text files in my home directory or only those that starts with "test". My failing attempts:
foo="~/"
echo $foo*.txt
echo ${foo}test*.txt
These results in the string outputs ~/*
and ~/test*txt
respectively. I have tried different versions with quotes etc. but I guess this enough to show my issue and level of understanding — I am a bash beginner. Is the issue related to tilde expansion vs. using $HOME?
Ultimately, I want to loop over these files but I ask this question to understand bash, not just get the result.
P.S. I am certain there are answers to this already out there but I've not managed to find any that have helped me understand this case. I tried to understand the general expansion order in bash but still don't understand how to apply it here.
In a glob, *
matches any character in a file name but it does not match /
. Thus, to get files in your home directory, try:
foo=$HOME
echo $foo/*.txt
echo ${foo}/test*.txt
In the odd case that foo
($HOME
) includes shell-active characters, it is better practice to use:
echo "$foo"/*.txt
echo "${foo}"/test*.txt
To loop over such files, use:
for fname in "$foo"/*.txt
do
# do something
done
This loop structure is safe for all filenames, even ones with spaces or other shell-active characters. This, of course, assumes that the code in your loop has $fname
inside double-quotes as appropriate.
If no files match the glob, the for loop will still run over a single item matching the unexpanded glob:
for not_exist in "$foo"/not_exists*.txt; do
if [[ "$not_exist" == "$foo/not_exists*.txt" ]]; then
# nothing matched the glob
break
fi
done
Alternatively, if bash's nullglob
option is set, the loop will only be executed if there are matching files. (Note: This is for bash
only: nullglob
is not POSIX compatible.)
foo="~/"
~/
never expands when inside quotes:
$ foo="~/"
$ echo $foo
~/
If you really want to use ~/
, don't use quotes. Unless you want to use one of the fancy features of ~
, it is usually simpler and more reliable to use $HOME
instead.