Ubuntu – rm works on command line but not in script

bashrmscripts

When I do rm *.old.* on the command line it removes correctly,
but when I do it in the following portion of my script, it does not rm all the *.old.* files.

What is wrong in my bash script:

 for i in ./*; do
    if [[ -f $i ]];  then

        if [[ $i  ==  *.old.* ]]; then
                oldfile=$i
                echo "this file is to be removed: $oldfile"
                rm $oldfile
                exec 2>errorfile
            if [ -s $errorfile ]
            then
                echo "rm failed"
            else
                echo "removed $oldfile!"
            fi
        else
            echo "file with old extension  does not exist"
        fi

        orig=$i
        dest=$i.old
        cp $orig $dest
        echo "Copied $i"

    else
        echo "${i} is not a file"
    fi 
done

Best Answer

There are various possible failure points in your script. First of all, rm *.old* will use globbing to create a list of all matching files, and that can deal with file names containing whitespace. Your script, however, assigns a variable to each result of the glob and does that without quoting. That will break if your file names contain whitespace. For example:

$ ls
'file name with spaces.old.txt'  file.old.txt
$ rm *.old.*   ## works: both files are deleted

$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'

As you can see, the loop failed for the file with spaces in its name. To do it correctly, you would need to quote the variable:

$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'

The same issue applies to pretty much every use of $i in your script. You should always quote your variables.

The next possible issue is that you seem to expect that *.old.* matches files with the extension .old. It doesn't. It matches "0 or more characters" (*), then a ., then "old", then another . and then "0 or more characters again". This means that it will not match something like file.old, but only something like `file.old.foo:

$ ls
file.old  file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo     

So, no match foe file.old. In any case, your script is far more complex than needed. Try this one instead:

#!/bin/bash

for i in *; do
    if [[ -f "$i" ]];  then
        if [[ "$i"  ==  *.old ]]; then
            rm -v "$i" || echo "rm failed for $i"
        else
            echo "$i doesn't have an .old extension"
        fi
        cp -v "$i" "$i".old
    else
        echo "$i is not a file"
    fi 
done

Note that I added -v to the rm and cpwhich does the same thing as what you were doing with yourecho` statements.

This isn't perfect since when you find, for example, file.old, that will be removed and, later on, the script will try to copy it and fail since the file no longer exists. However, you haven't explained what you script is actually attempting to do so I can't fix that for you unless you tell us what you are really trying to accomplish.

If what you want is to i) remove all files with the .old extension and ii) add the .old extension to any existing files that don't have it, all you really need is:

#!/bin/bash

for i in *.old; do
    if [[ -f "$i" ]]; then
        rm -v "$i" || echo "rm failed for $i"
    else
        echo "$i is not a file"
    fi 
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
    if [[ -f "$i" ]]; then
        ## the -v makes cp report copied files
        cp -v "$i" "$i".old
    fi
done