Unix: como excluir arquivos listados em um arquivo

95

Eu tenho um arquivo de texto longo com uma lista de máscaras de arquivo que desejo excluir

Exemplo:

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

Eu preciso excluí-los. Tentei rm `cat 1.txt` e ele diz que a lista é muito longa.

Encontrei este comando, mas quando eu verifico as pastas da lista, algumas delas ainda têm arquivos. xargs rm <1.txtA chamada de rm manual remove os arquivos dessas pastas, portanto, não há problema com as permissões.

Alexandre
fonte
9
Mesmo que seja seis anos depois: você se importaria em aceitar uma das respostas? Isso marcará a questão como resolvida e ajudará outros usuários também.
MERose

Respostas:

122

Isso não é muito eficiente, mas funcionará se você precisar de padrões glob (como em / var / www / *)

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

Se você não tiver nenhum padrão e tiver certeza de que seus caminhos no arquivo não contêm espaços em branco ou outras coisas estranhas, você pode usar xargs como:

xargs rm < 1.txt
nos
fonte
4
O que acontecerá com a primeira solução (usando CAT) se o caminho contiver um espaço?
a.dibacco
59

Supondo que a lista de arquivos esteja no arquivo 1.txt, faça:

xargs rm -r <1.txt

A -ropção causa recursão em qualquer diretório nomeado em 1.txt.

Se algum arquivo for somente leitura, use a -fopção para forçar a exclusão:

xargs rm -rf <1.txt

Seja cauteloso com informações de qualquer ferramenta que faça exclusões programáticas. Certifique -se de que os arquivos nomeados no arquivo de entrada sejam realmente excluídos. Seja especialmente cuidadoso com erros de digitação aparentemente simples. Por exemplo, se você inserir um espaço entre um arquivo e seu sufixo, aparecerá dois nomes de arquivo separados:

file .txt

são na verdade dois arquivos separados: filee .txt.

Isso pode não parecer tão perigoso, mas se o erro de digitação for algo assim:

myoldfiles *

Então, em vez de apagar todos os arquivos que começam com myoldfiles, você vai acabar apagando myoldfilese todos os não-dot-arquivos e diretórios no diretório atual. Provavelmente não é o que você queria.

aks
fonte
1
"myoldfiles /" ou "/ tmp" ainda mais desastroso com -rf. Observe o caractere de espaço ao lado de "/".
Ray
24

Usa isto:

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

Se precisar de expansão global, você pode omitir as citações $file:

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

Mas esteja avisado que os nomes dos arquivos podem conter conteúdo "problemático" e eu usaria a versão não citada. Imagine este padrão no arquivo

*
*/*
*/*/*

Isso excluiria bastante do diretório atual! Eu o encorajaria a preparar a lista de exclusão de uma forma que os padrões glob não sejam mais necessários, e então usar aspas como no meu primeiro exemplo.

hek2mgl
fonte
3
Esta é atualmente a única resposta que cuida de tudo /My Dir With Spaces/ /Spaces and globs/*.txt, --file-with-leading-dashese como um bônus é POSIX e funciona em GNU / Linux, MacOS e BSD.
aquele outro cara de
17

Você pode usar '\ n' para definir o caractere de nova linha como delimitador.

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

Tenha cuidado com o -rf porque ele pode excluir o que você não deseja se o 1.txt contiver caminhos com espaços. É por isso que o novo delimitador de linha é um pouco mais seguro.

Em sistemas BSD, você pode usar a opção -0 para usar caracteres de nova linha como delimitador como este:

xargs -0 rm < 1.txt
Raio
fonte
1
No OS X isso me pegaxargs: illegal option -- d
waldyrious
Bom ponto. Parece que não há outra opção a não ser -0no OS X.
Ray,
2
xargs -I_ rm _também funciona no OS X :) consulte stackoverflow.com/a/39335402/266309
waldyrious
Se estiver usando BSD xargs (onde não há -d), você pode primeiro converter novas linhas em nulos:tr '\n' '\0' < 1.txt | xargs -0 rm
OrangeDog
15

Você pode usar este one-liner:

cat 1.txt | xargs echo rm | sh

Que faz a expansão do shell, mas executa rmo número mínimo de vezes.

tgdavies
fonte
Contanto que qualquer expansão não seja muito longa?
Douglas Leeder
1
É verdade que um glob pode produzir uma lista de argumentos muito longa - você pode adicionar o -n <n>argumento a xargspara reduzir o número de argumentos passados ​​para cada rm, mas isso ainda não o protegerá de um único glob que exceda o limite.
tgdavies
13

xargs -I{} sh -c 'rm {}' < 1.txtdeve fazer o que quiser. Tenha cuidado com este comando, pois uma entrada incorreta nesse arquivo pode causar muitos problemas.

Esta resposta foi editada depois que @tdavies apontou que o original não fazia expansão de shell.

Mark Drago
fonte
Obrigado, mas não há necessidade de excluir pastas, apenas arquivos
Alexander
2
Os globs não serão expandidos, pois eles nunca são "vistos" pelo shell.
tgdavies
@tdavies uau - você está certo. Este comando (embora um pouco mais feio) funcionará:xargs -I{} sh -c 'rm {}' < 1.txt
Mark Drago
4

Neste caso particular, devido aos perigos citados em outras respostas, eu

  1. Edite, por exemplo, no Vim e :%s/\s/\\\0/g, escapando todos os caracteres de espaço com uma barra invertida.

  2. Então :%s/^/rm -rf /, antes do comando. Com -rvocê não precisa se preocupar em ter diretórios listados após os arquivos neles contidos, e com -fele não reclamará por falta de arquivos ou entradas duplicadas.

  3. Execute todos os comandos: $ source 1.txt

Evgeni Sergeev
fonte
Os espaços não são os únicos caracteres que precisam de escape, populares em nomes de arquivos também são colchetes de qualquer tipo que podem levar a uma expansão indesejada.
emk2203
4

cat 1.txt | xargs rm -f | bash Executar o comando fará o seguinte apenas para arquivos.

cat 1.txt | xargs rm -rf | bash Executar o comando fará o seguinte comportamento recursivo.

Uzayr
fonte
3

Só para fornecer uma outra maneira, você também pode simplesmente usar o seguinte comando

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

Aqui está outro exemplo de loop. Este também contém uma 'instrução if' como um exemplo de verificação para ver se a entrada é um 'arquivo' (ou um 'diretório', por exemplo):

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

Aqui você pode usar um conjunto de pastas de deletelist.txt , evitando alguns padrões também

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

Isso permitirá que os nomes dos arquivos tenham espaços (exemplo reproduzível).

# 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
fonte
0

Caso alguém prefira sede remova sem expansão curinga :

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

Lembrete: use nomes de caminho absolutos no arquivo ou certifique-se de que está no diretório correto.

E para completar o mesmo com awk:

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

A expansão do curinga funcionará se as aspas simples forem removidas, mas isso é perigoso caso o nome do arquivo contenha espaços. Isso precisaria adicionar aspas ao redor dos curingas.

Marco
fonte