Eliminar archivo, pero solo si es un enlace simbólico

11

Idealmente me gustaría un comando como este

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

porque me he quemado demasiadas veces borrando accidentalmente el archivo en lugar del enlace simbólico que apunta al archivo. Esto puede ser especialmente malo cuando sudo está involucrado. Ahora, por supuesto, hago una ls -alpara asegurarme de que realmente sea un enlace simbólico y tal, pero eso es vulnerable a errores de operador (nombre similar de archivo, error tipográfico, etc.) y condiciones de carrera (si alguien quisiera que elimine un archivo por alguna razón). ¿Hay alguna manera de verificar si un archivo es un enlace simbólico y solo eliminarlo si está en un comando?

Shelvacu
fuente
1
No creo que la condición de carrera deba ser una preocupación. Cualquiera que pueda eliminar el enlace simbólico y reemplazarlo con un archivo necesitaría permiso de escritura en el directorio, lo que significa que también podría eliminar el archivo ellos mismos.
Nate Eldredge

Respuestas:

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
fuente
44
Seguramente sería más claro eliminar !y cambiar ||a&&
Glenn Jackman
55
@glennjackman He acostumbrado a preferir la ||forma. &&derriba las cosas si estás en set -emodo.
PSkocik
@PSkocik Eso es arriesgado en general ya que no distingue entre el caso interesante (el archivo existe y es un enlace simbólico) y otras razones para un código de salida distinto de cero, como un valor vacío sin comillas ( [ ! -L $1 ]), utilizando un operador no válido ( [ ! -l foo ]), o incluso error de sintaxis ( [ ! -q foo || echo foo)
l0b0
2
@XiongChiamiov El problema rm_if_link(){ [ -L "$1" ] && rm -v "$1"; }es que ejecutarlo en un set -e modo sin enlace matará su proceso de shell. Podrías agregar || :pero luego, en mi humilde opinión, se vuelve aún menos claro que la ! ||versión, y evitará que una rmfalla te mate set -e, lo que probablemente sea indeseable. ! ||es bastante conciso y claro, y no sufre ninguno de estos problemas.
PSkocik
1
Nop . Otras opciones: a) poner rmdentro de su prueba, b) usar una declaración if real, c) no andar usando -eshells interactivos internos (¡y probar sus scripts!). Si quieres discutir sobre la legibilidad, usar ifpara probar si algo es un enlace es claramente el ganador.
Boicot SE para Monica Cellio
5

Puede utilizar findy su -type lcondición de prueba (que pone a prueba para ver si el objeto encontrado es un l tinta o no)

Por ejemplo, si tiene un archivo llamado fooen el directorio actual, puede hacer esto:

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

Es posible que pueda simplificar eso con solo:

$ find . -maxdepth 1 -type l -delete

Lo cual eliminaría todos los enlaces simbólicos en el directorio actual.

Advertencia:

La -deleteopción en findes realmente muy peligrosa. ASEGÚRESE DE COLOCARLO AL FINAL DEL MANDO DE ENCUENTRO. Si lo extravía, eliminará todo lo que encuentre, independientemente de si los resultados coinciden con su condicional.

Como se sugiere en los comentarios, una opción más segura podría ser usar findy rm -i(lo que le obliga a confirmar la eliminación del archivo) en conjunción:

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

Personalmente, uso -exec trash {} \;para eliminar archivos temporalmente, porque yo, como tú, me he quemado rmen el pasado. Doble va para una -deletebandera fuera de lugar .

http://slackermedia.ml/trashy

Klaatu von Schlacker
fuente
1
En lugar de -delete, puede imprimirlo y pasarlo a xargs: find . -type l -iname "foo" | xargs rm -i es más seguro.
Boban P.
1
Me gusta el uso de @BobanP. rm -iPor seguridad, seguro.
Klaatu von Schlacker
3
"Esto eliminaría todos los enlaces simbólicos en el directorio actual" ... y cualquier punto debajo de él.
Joseph R.
1
Como @JosephR. dijo, irá todo el camino. Agregue -maxdepth 1 en find.
Boban P.
1
Todavía se rompe en los nombres de archivo con espacios. Sugiera usar -print0para findy -0para xargs.
Comodín
4
zsh -c 'rm foo(@)'

@es un calificador global ; el patrón foo(@)coincide con lo que foocoincide, pero solo los enlaces simbólicos.

foo(-@)solo coincidiría con enlaces simbólicos rotos. foo(@,L0)solo coincidiría con enlaces simbólicos y archivos vacíos.

Por supuesto, si está ejecutando zsh en primer lugar, solo necesita escribir rm foo(@). Debe tener cuidado de no presionar Enterantes de escribir (@).

Gilles 'SO- deja de ser malvado'
fuente
1
Zsh suena cada vez más poderoso cuando veo respuestas como esta.
Jeff Schaller