Unix: cómo eliminar archivos enumerados en un archivo

92

Tengo un archivo de texto largo con una lista de máscaras de archivo que quiero eliminar

Ejemplo:

/tmp/aaa.jpg
/var/www1/*
/var/www/qwerty.php

Necesito borrarlos. Intenté rm `cat 1.txt` y dice que la lista es demasiado larga.

Encontré este comando, pero cuando reviso las carpetas de la lista, algunas de ellas todavía tienen archivos. xargs rm <1.txtLa llamada rm manual elimina archivos de dichas carpetas, por lo que no hay problemas con los permisos.

Alejandro
fuente
8
A pesar de que han pasado seis años: ¿le importaría aceptar una de las respuestas? Esto marcará la pregunta como resuelta y también ayudará a otros usuarios.
MERose

Respuestas:

114

Esto no es muy eficiente, pero funcionará si necesita patrones globales (como en / var / www / *)

for f in $(cat 1.txt) ; do 
  rm "$f"
done

Si no tiene ningún patrón y está seguro de que sus rutas en el archivo no contienen espacios en blanco u otras cosas raras, puede usar xargs así:

xargs rm < 1.txt
nos
fuente
4
¿Qué pasará con la primera solución (usando CAT) si la ruta contiene un espacio?
dibacco
58

Suponiendo que la lista de archivos está en el archivo 1.txt, haga lo siguiente:

xargs rm -r <1.txt

La -ropción provoca recursividad en cualquier directorio nombrado en 1.txt.

Si algún archivo es de solo lectura, use la -fopción para forzar la eliminación:

xargs rm -rf <1.txt

Tenga cuidado con la entrada a cualquier herramienta que realice eliminaciones programáticas. Hacen cierto que los archivos con el nombre del archivo de entrada son realmente para ser borrados. Tenga especial cuidado con los errores tipográficos aparentemente simples. Por ejemplo, si ingresa un espacio entre un archivo y su sufijo, parecerán dos nombres de archivo separados:

file .txt

es en realidad dos archivos separados: filey .txt.

Esto puede no parecer tan peligroso, pero si el error tipográfico es algo como esto:

myoldfiles *

A continuación, en lugar de eliminar todos los archivos que comienzan con myoldfiles, que va a terminar borrando myoldfilesy todos los no-punto-archivos y directorios en el directorio actual. Probablemente no sea lo que querías.

aks
fuente
1
"myoldfiles /" o "/ tmp" aún más desastroso con -rf. Tenga en cuenta el carácter de espacio junto a la "/".
Ray
23

Utilizar esta:

while IFS= read -r file ; do rm -- "$file" ; done < delete.list

Si necesita una expansión global, puede omitir las citas $file:

IFS=""
while read -r file ; do rm -- $file ; done < delete.list

Pero tenga en cuenta que los nombres de archivo pueden tener contenido "problemático" y yo usaría la versión sin comillas. Imagina este patrón en el archivo

*
*/*
*/*/*

¡Esto eliminaría bastante del directorio actual! Le animo a que prepare la lista de borrado de una manera que los patrones globales ya no sean necesarios, y luego utilice comillas como en mi primer ejemplo.

hek2mgl
fuente
3
Esta es actualmente la única respuesta que se encarga de todo /My Dir With Spaces/ /Spaces and globs/*.txt, --file-with-leading-dashesy como un bono que es POSIX y las obras en GNU / Linux, MacOS y BSD.
ese otro chico
17

Puede usar '\ n' para definir el carácter de nueva línea como delimitador.

xargs -d '\n' rm < 1.txt

Tenga cuidado con -rf porque puede eliminar lo que no desea si el 1.txt contiene rutas con espacios. Es por eso que el nuevo delimitador de línea es un poco más seguro.

En los sistemas BSD, puede usar la opción -0 para usar nuevos caracteres de línea como delimitadores como este:

xargs -0 rm < 1.txt
Rayo
fuente
1
En OS X esto me xargs: illegal option -- d
atrapa
Buen punto. Parece que no hay otra opción que -0en OS X.
Ray
2
xargs -I_ rm _también funciona en OS X :) ver stackoverflow.com/a/39335402/266309
waldyrious
Si usa BSD xargs (donde no hay -d), primero puede convertir nuevas líneas en nulos:tr '\n' '\0' < 1.txt | xargs -0 rm
OrangeDog
14

Puedes usar este one-liner:

cat 1.txt | xargs echo rm | sh

Lo que hace la expansión de shell pero se ejecuta rmel número mínimo de veces.

tgdavies
fuente
¿Siempre y cuando cualquier expansión no sea demasiado larga?
Douglas Leeder
1
Es cierto que un glob podría producir una lista de argumentos demasiado larga; puede agregar el -n <n>argumento a xargspara reducir el número de argumentos pasados ​​a cada rm, pero eso aún no lo protegerá de un solo glob que exceda el límite.
tgdavies
13

xargs -I{} sh -c 'rm {}' < 1.txtdebe hacer lo que quiera. Tenga cuidado con este comando, ya que una entrada incorrecta en ese archivo podría causar muchos problemas.

Esta respuesta se editó después de que @tdavies señalara que el original no expandía el shell.

Mark Drago
fuente
Gracias, pero no es necesario eliminar carpetas, solo archivos
Alexander
2
Los globos no se expandirán, ya que el caparazón nunca los "ve".
tgdavies
@tdavies wow, tienes razón. Este comando (aunque un poco más feo) funcionará:xargs -I{} sh -c 'rm {}' < 1.txt
Mark Drago
4

En este caso particular, debido a los peligros citados en otras respuestas,

  1. Edite, por ejemplo, en Vim y :%s/\s/\\\0/g, escapando todos los caracteres de espacio con una barra invertida.

  2. Luego :%s/^/rm -rf /, anteponiendo el comando. Con -rusted no tiene que preocuparse de tener directorios listados después de los archivos contenidos en ellos, y con -feso no se quejará debido a que faltan archivos o entradas duplicadas.

  3. Ejecute todos los comandos: $ source 1.txt

Evgeni Sergeev
fuente
Los espacios no son los únicos caracteres que necesitan escapar, también son populares en los nombres de archivo los corchetes de cualquier tipo que pueden provocar una expansión no deseada.
emk2203
4

cat 1.txt | xargs rm -f | bash Ejecutar el comando hará lo siguiente solo para archivos.

cat 1.txt | xargs rm -rf | bash Ejecutar el comando hará el siguiente comportamiento recursivo.

Uzayr
fuente
2

Solo para proporcionar otra forma, también puede simplemente usar el siguiente comando

$ cat to_remove
/tmp/file1
/tmp/file2
/tmp/file3
$ rm $( cat to_remove )
Quentin
fuente
1

Aquí hay otro ejemplo de bucle. Este también contiene una 'instrucción if' como ejemplo de verificación para ver si la entrada es un 'archivo' (o un 'directorio' por ejemplo):

for f in $(cat 1.txt); do if [ -f $f ]; then rm $f; fi; done
nthparameter
fuente
0

Aquí puede usar un conjunto de carpetas de deletelist.txt y evitar algunos patrones también

foreach f (cat deletelist.txt)
    rm -rf ls | egrep -v "needthisfile|*.cpp|*.h"
end
Chand Priyankara
fuente
0

Esto permitirá que los nombres de archivo tengan espacios (ejemplo reproducible).

# Select files of interest, here, only text files for ex.
find -type f -exec file {} \; > findresult.txt
grep ": ASCII text$" findresult.txt > textfiles.txt
# leave only the path to the file removing suffix and prefix
sed -i -e 's/:.*$//' textfiles.txt
sed -i -e 's/\.\///' textfiles.txt

#write a script that deletes the files in textfiles.txt
IFS_backup=$IFS
IFS=$(echo "\n\b")
for f in $(cat textfiles.txt); 
do 
rm "$f"; 
done
IFS=$IFS_backup

# save script as "some.sh" and run: sh some.sh
Ferroao
fuente
0

En caso de que alguien prefiera sedy eliminar sin expansión comodín :

sed -e "s/^\(.*\)$/rm -f -- \'\1\'/" deletelist.txt | /bin/sh

Recordatorio: use nombres de ruta absolutos en el archivo o asegúrese de estar en el directorio correcto.

Y para completar lo mismo con awk:

awk '{printf "rm -f -- '\''%s'\''\n",$1}' deletelist.txt | /bin/sh

La expansión de comodines funcionará si se eliminan las comillas simples, pero esto es peligroso en caso de que el nombre del archivo contenga espacios. Esto necesitaría agregar comillas alrededor de los comodines.

Marco
fuente