2

I'm trying to match backup files in a script and then delete them. The backup files are the ones that have the tilde (~) in them:

$ ls -al
drwxr-xr-x 3 jwalton jwalton 4096 Feb 18 09:00 .
drwxr-xr-x 5 jwalton jwalton 4096 Feb 18 08:54 ..
drwxr-xr-x 5 jwalton jwalton 4096 Feb 18 08:54 test-proj
-rwxr-xr-x 1 jwalton jwalton  664 Feb 18 09:00 clean.sh
-rwxr-xr-x 1 jwalton jwalton  628 Feb 18 08:59 clean.sh~

My clean script has the following test, but it looks like I'm doing something wrong:

if [ -e "*~" ]; then
  rm "*~"
fi

What is the correct test to match a backed up file?

jww
  • 97,681
  • 90
  • 411
  • 885

4 Answers4

7

You don't need to test it. rm -f (ignoring non-existing files) will do the job.

rm -f *~

Inside the quote, * matches * literally:

Example:

$ ls
*~  a  a~
$ ls *~
*~  a~
$ ls "*~"
*~
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • Thanks falsetru. The reason I am trying to match it first is to avoid the "*~ not found" error message. – jww Feb 18 '14 at 14:18
  • 2
    @noloader, If you use `-f` option, `rm` will not output error about that. – falsetru Feb 18 '14 at 14:18
  • 1
    It is because `~` is not expanded within double quotes. – fedorqui Feb 18 '14 at 14:20
  • 1
    @fedorqui, Maybe you mean `*`? `~` is expanded to home directory (only when the word starts with it) – falsetru Feb 18 '14 at 14:21
  • No (although `*` also can have this problem). I mean that `~` is not expanded when within double brackets --> http://stackoverflow.com/a/3963747/1983854 – fedorqui Feb 18 '14 at 14:26
  • 1
    @fedorqui, According to bash(1), *Tilde Expansion*: **If a word begins with an unquoted tilde character (`~`)**, all of the characters preceding the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. .... – falsetru Feb 18 '14 at 14:27
  • That's a good point! Also, note in my previous comment I wanted to say "when within double quotes", instead of "... double brackets". – fedorqui Feb 18 '14 at 14:33
5

It's your quotes that are screwing it up. It's looking for a file that is literally called *~. This works, but only if *~ expands to exactly one file::

if [ -e *~ ]; then
   rm *~
fi

Why? Because the shell expands wildcards first. So if it matches nothing, it's equivalent to:

if [ -e ]; then  // false positive!

If it matches one file x.txt~, then it expands to:

if [ -e x.txt~ ]; then // OK

If it matches more than one file, x.txt~ y.txt~, then it expands to:

if [ -e x.txt~ y.txt~ ]; then // "[: x.txt~: binary operator expected"

It's worth knowing that UNIX filenames can contain any character except / and ASCII 0. So you can do:

 $ touch "*~"
 $ ls "*~"
 *~
 $ rm "*~"

And you can end up with files with awkward names such as -r.

 $ touch -- -r  # or imagine, your own C, Java etc. program
 $ ls
 -r
 $ rm -- -r

However, the if isn't really necessary. It's only there to prevent ls from giving No such file or directory errors, and you can suppress those with:

rm -f *~
slim
  • 40,215
  • 13
  • 94
  • 127
  • 1
    The `[ -e *~ ]` will only work if the pattern expands to exactly one file; otherwise, you'll get an error about a binary operator expected. – chepner Feb 18 '14 at 14:23
1

I guess using find is best:

find ./ -name "*~" -exec rm {} \; 
dstronczak
  • 2,406
  • 4
  • 28
  • 41
1

If you want to recursively descend into subdirectories:

shopt -s globstar nullglob
rm **~
glenn jackman
  • 238,783
  • 38
  • 220
  • 352