Supprimer le fichier, mais uniquement s'il s'agit d'un lien symbolique

11

Idéalement, j'aimerais une commande comme celle-ci

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

car je me suis brûlé trop de fois en supprimant accidentellement le fichier au lieu du lien symbolique pointant vers le fichier. Cela peut être particulièrement mauvais lorsque sudo est impliqué. Maintenant, je fais bien sûr un ls -alpour m'assurer qu'il s'agit bien d'un lien symbolique et autres, mais cela est vulnérable aux erreurs de l'opérateur (fichier de même nom, faute de frappe, etc.) et aux conditions de concurrence (si quelqu'un voulait que je supprime un fichier pour une raison quelconque). Existe-t-il un moyen de vérifier si un fichier est un lien symbolique et de le supprimer uniquement s'il se trouve dans une seule commande?

Shelvacu
la source
1
Je ne pense pas que la condition de course devrait être une préoccupation. Quiconque serait en mesure de supprimer le lien symbolique et de le remplacer par un fichier aurait besoin d'une autorisation d'écriture sur le répertoire, ce qui signifie qu'il pourrait également supprimer le fichier lui-même.
Nate Eldredge

Réponses:

19
 $ rm_if_link(){ [ ! -L "$1" ] || rm -v "$1"; }

 #test
 $ touch nonlink; ln -s link
 $ rm_if_link nonlink
 $ rm_if_link link
   removed 'link'     
PSkocik
la source
4
Il serait sûrement plus clair de le supprimer !et de le remplacer ||par&&
glenn jackman
5
@glennjackman J'ai pris l'habitude de préférer la ||forme. &&fait baisser les choses si vous êtes en set -emode.
PSkocik
@PSkocik C'est risqué en général car il ne fait pas de distinction entre le cas intéressant (le fichier existe et est un lien symbolique) et d'autres raisons pour un code de sortie différent de zéro, comme une valeur vide entre guillemets ( [ ! -L $1 ]), en utilisant un opérateur non valide ( [ ! -l foo ]), ou même erreur de syntaxe ( [ ! -q foo || echo foo)
l0b0
2
@XiongChiamiov Le problème avec rm_if_link(){ [ -L "$1" ] && rm -v "$1"; }est que l'exécuter sur un set -e mode non- link va tuer votre processus shell. Vous pouvez ajouter || :mais cela devient, à mon humble avis, encore moins clair que la ! ||version, et cela empêchera une rmpanne de vous tuer set -e, ce qui est probablement indésirable. ! ||est assez concis et clair, et ne souffre d'aucun de ces problèmes.
PSkocik
1
Non . Autres options: a) mettez l' rmintérieur de votre test, b) utilisez une instruction if réelle, c) ne faites pas le tour en utilisant -edes shells interactifs (et testez vos scripts!). Si vous voulez discuter de la lisibilité, utiliser ifpour tester si quelque chose est un lien est clairement le gagnant.
Boycott SE pour Monica Cellio
5

Vous pouvez utiliser findet sa -type lcondition de test (qui teste pour voir si l'objet trouvé est une encre l ou non)

Par exemple, si vous avez un fichier appelé foodans le répertoire courant, vous pouvez faire ceci:

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

Vous pourriez être en mesure de simplifier cela avec juste:

$ find . -maxdepth 1 -type l -delete

Ce qui supprimerait tous les liens symboliques dans le répertoire courant.

Attention:

L' -deleteoption findest vraiment très dangereuse. ASSUREZ-VOUS DE LE PLACER À LA FIN DE LA COMMANDE FIND. Si vous le perdez, il supprimera tout ce qu'il trouvera, que les résultats correspondent ou non à votre condition.

Comme suggéré dans les commentaires, une option plus sûre peut être d'utiliser findet rm -i(ce qui vous oblige à confirmer la suppression du fichier) en conjonction:

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

Personnellement, j'utilise -exec trash {} \;pour supprimer temporairement des fichiers, parce que, comme vous, j'ai été brûlé par rmle passé. Double va pour un -deletedrapeau mal placé .

http://slackermedia.ml/trashy

Klaatu von Schlacker
la source
1
Au lieu de -delete, vous pouvez simplement l'imprimer et le passer à xargs: find . -type l -iname "foo" | xargs rm -i c'est plus sûr.
Boban P.
1
J'aime l'utilisation de @BobanP. rm -iPour la sécurité, bien sûr.
Klaatu von Schlacker
3
"Cela supprimerait tous les liens symboliques dans le répertoire courant" ... et tout point en dessous.
Joseph R.
1
Comme @JosephR. dit, il ira tout le long de la route. Ajoutez -maxdepth 1 dans find.
Boban P.
1
Casse toujours sur les noms de fichiers avec des espaces. Suggérez d'utiliser -print0pour findet -0pour xargs.
Wildcard
4
zsh -c 'rm foo(@)'

@est un qualificatif glob ; le motif foo(@)correspond à ce qui foocorrespond, mais seulement les liens symboliques.

foo(-@)ne correspondrait qu'à des liens symboliques rompus. foo(@,L0)correspondrait uniquement aux liens symboliques et aux fichiers vides.

Bien sûr, si vous utilisez zsh en premier lieu, il vous suffit de taper rm foo(@). Vous devez faire attention à ne pas appuyer Enteravant de taper (@).

Gilles 'SO- arrête d'être méchant'
la source
1
Zsh semble de plus en plus puissant quand je vois des réponses comme celle-ci.
Jeff Schaller