Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Ideally I'd like a command like this

rm --only-if-symlink link-to-file

because I have burned myself too many times accidentally deleting the file instead of the symlink pointing to the file. This can be especially bad when sudo is involved. Now I do of course do a ls -al to make sure it's really a symlink and such but that's vulnerable to operator error (similarly named file, typo, etc) and race conditions (if somebody wanted me to delete a file for some reason). Is there some way to check if a file is a symlink and only delete it if it is in one command?

share|improve this question
1  
I don't think that the race condition should be a concern. Anybody who would be able to remove the symlink and replace it with a file would need write permission on the directory, which means they could also delete the file themselves. – Nate Eldredge Mar 21 at 3:17
up vote 14 down vote accepted
 $ rm_if_link(){ [ ! -L "$1" ] || rm -v "$1"; }

 #test
 $ touch nonlink; ln -s link
 $ rm_if_link nonlink
 $ rm_if_link link
   removed 'link'     
share|improve this answer
4  
Surely it would be more clear to remove ! and change || to && – glenn jackman Mar 20 at 22:34
5  
@glennjackman I've made it a habit to prefer the || form. && brings things down if you're in set -e mode. – PSkocik Mar 20 at 22:44
    
@PSkocik That's risky in general since it doesn't distinguish between the interesting case (the file exists and is a symlink) and other reasons for a non-zero exit code, such as an empty unquoted value ([ ! -L $1 ]), using an invalid operator ([ ! -l foo ]), or even syntax error ([ ! -q foo || echo foo) – l0b0 Mar 21 at 8:32
    
-e isn't a problem since you're using test. – Xiong Chiamiov Mar 21 at 16:55
1  
@XiongChiamiov The problem with rm_if_link(){ [ -L "$1" ] && rm -v "$1"; } is that running it on a nonlink in set -e mode will kill your shell process. You could append || : but then it becomes, IMHO, even less clearer than the ! || version, and it will prevent a rm failure from killing you in set -e, which is likely undesirable. ! || is quite concise and clear, and doesn't suffer from any of these problems. – PSkocik Mar 21 at 17:18

You can use find and its -type l test condition (which tests to see if the object found is a link or not)

For example, if you have a file called foo in the current directory, you could do this:

$ find . -type l -iname "foo" -delete

You might be able to simplify that with just:

$ find . -maxdepth 1 -type l -delete

Which would delete all symlinks in the current directory.

Warning:

The -delete option in find is really really dangerous. BE SURE TO PLACE IT AT THE END OF THE FIND COMMAND. If you misplace it, it will delete everything it finds regardless of whether the results match your conditional.

As suggested in the comments, a safer option might be to use find and rm -i (which forces you to confirm file removal) in conjuction:

$ $ find . -type l -iname "foo" | xargs rm -i

Personally I use -exec trash {} \; to temporarily delete files, because I, like yourself, have been burned by rm in the past. Double goes for a misplaced -delete flag.

http://slackermedia.ml/trashy

share|improve this answer
1  
Instead of -delete you can just print it and pass it to xargs: find . -type l -iname "foo" | xargs rm -i it's safer. – Boban P. Mar 20 at 21:34
1  
I like@BobanP.'s use of rm -i for safety, for sure. – Klaatu von Schlacker Mar 20 at 21:35
3  
"This would delete all symlinks in the current directory" ... and any point below it. – Joseph R. Mar 20 at 21:36
1  
As @JosephR. said, it will go all the way down the road. Add -maxdepth 1 in find. – Boban P. Mar 20 at 21:41
1  
Still breaks on filenames with spaces. Suggest using -print0 for find and -0 for xargs. – Wildcard Mar 20 at 21:44
zsh -c 'rm foo(@)'

@ is a glob qualifier; the pattern foo(@) matches what foo matches, but only the symbolic links.

foo(-@) would only match broken symbolic links. foo(@,L0) would match only symbolic links and empty files.

Of course, if you're running zsh in the first place, you just need to type rm foo(@). You do need to take care not to press Enter before typing (@).

share|improve this answer
1  
Zsh sounds more and more powerful when I see answers like this. – Jeff Schaller Mar 20 at 22:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.