I've run into a really silly problem with a Linux shell script. I want to delete all files with the extension ".bz2" in a directory. In the script I call
rm "$archivedir/*.bz2"
where $archivedir is a directory path. Should be pretty simple, shouldn't it? Somehow, it manages to fail with this error:
rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory
But there is a file in that directory called test.bz2 and if I change my script to
echo rm "$archivedir/*.bz2"
and copy/paste the output of that line into a terminal window the file is removed successfully. What am I doing wrong?
Your original line
Can be re-written as
to achieve the same effect. The wildcard expansion is not taking place properly in your existing setup. By shifting the double-quote to the "front" of the file path (which is legitimate) you avoid this.
The quotes are causing the string to be interpreted as a string literal, try removing them.
Why not just
rm -rf */*.bz2
? Works for me on OSX.Just to expand on this a bit, bash has fairly complicated rules for dealing with metacharacters in quotes. In general
almost nothing is interpreted in single-quotes:
shell substitution is done inside double quotes, but file metacharacters aren't expanded:
everything inside backquotes is passed to the subshell which interprets them. A shell variable that is not exported doesn't get defined in the subshell. So, the first command echoes blank, but the second and third echo "bye":
(And getting this to print the way you want it in SO
takes several triesis apparently impossible...)TL;DR
Quote only the variable, not the whole expected path with the wildcard
Explanation
In Unix, programs generally do not interpret wildcards themselves. The shell interprets unquoted wildcards, and replaces each wildcard argument with a list of matching file names. if $archivedir might contain spaces, then
rm $archivedir/*.bz2
might not do what youYou can disable this process by quoting the wildcard character, using double or single quotes, or a backslash before it. However, that's not what you want here - you do want the wildcard expanded to the list of files that it matches.
Be careful about writing
rm $archivedir/*.bz2
(without quotes). The word splitting (i.e., breaking the command line up into arguments) happens after $archivedir is substituted. So if $archivedir contains spaces, then you'll get extra arguments that you weren't intending. Say archivedir is/var/archives/monthly/April to June
. Then you'll get the equivalent of writingrm /var/archives/monthly/April to June/*.bz2
, which tries to delete the files "/var/archives/monthly/April", "to", and all files matching "June/*.bz2", which isn't what you want.The correct solution is to write:
I've seen similar errors when calling a shell script like ./shell_script.sh from another shell script. This can be fixed by invoking it as sh shell_script.sh