Unix - excluir arquivos e pastas, excluindo PATTERN

10

Recentemente, fui confrontado com a tarefa de excluir todos os arquivos / pastas em um diretório, excluindo aqueles que correspondiam a um padrão específico. Então, criei um comando unix de linha única para fazer o trabalho. Deve ser apenas uma linha? Suponho que não, mas é definitivamente mais legal assim!

Embora o problema seja bastante simples, fiquei um pouco surpreso com a complexidade da minha solução. Aqui está o comando que eu usei; NOTA: esta é uma solução ruim porque não lida com nomes de arquivos contendo caracteres de feed de linha (o que não importava na minha situação).

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

Eu não usei o comando "find" porque não quero recursar em pastas correspondentes a PATTERN. Por exemplo, considere a seguinte estrutura de arquivo:

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

O uso do padrão "foo" deve remover apenas "first_dir" (e seu conteúdo, é claro) e "somefile.txt" ( não "anotherfile.txt" ou "morefiles.log").

Pergunta, existem maneiras melhores (mais elegantes e corretas) de conseguir isso?


EDIT:
Recentemente, soube que "encontrar" pode ser uma opção melhor :

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

Esta solução manipula corretamente caminhos contendo caracteres de alimentação de linha.

joxl
fonte
EDIT: alterado erro "the_dir" para corrigir "first_dir".
joxl

Respostas:

10

Os exemplos a seguir foram echoprefixados para que você possa testar os padrões antes de realmente usá-los. Remova o echopara ativar o rm -rf. Substitua rm -ripara solicitar confirmação.

O ksh tem uma extensão de correspondência negativa para o globbing:

# ksh
echo rm -rf !(*foo*)

A mesma sintaxe está disponível no bash se você definir a extglobopção:

# bash
shopt -s extglob
echo rm -rf !(*foo*)

O zsh tem sua própria sintaxe para isso:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

Também pode usar a sintaxe no estilo ksh :

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))
Chris Johnsen
fonte
Simples, elegante, lida com caracteres de avanço de linha, (quase) uma linha (eu adicionei shopt -s extglobao meu arquivo .bashrc, agora é uma linha). Perfeito! Eu prometo um voto positivo assim que minha reputação permitir.
joxl
7

Aqui está outra findsolução. Não tenho certeza se isso tem alguma vantagem real sobre a sua, mas não precisa xargse permite a rara possibilidade de que * se expanda para muitos nomes.

find . -maxdepth 1 ! -name PATTERN -type f -delete

Eu também adicionei -type fpara que ele não tentasse excluir diretórios.

Atenção: -deleteé poderoso. Dei a um dos meus arquivos de teste 0 permissões e o comando acima o excluiu sem hesitar.

garyjohn
fonte
6
Teste primeiro. Substitua -deletepor -printpara ver se está encontrando os arquivos que você deseja. Se tudo estiver bom, use o histórico de comandos (por exemplo, pressione o botão para cima) para voltar ao comando anterior para garantir que você esteja trabalhando com os mesmos filtros de localização.
Doug Harris
Leia com atenção, nota que eu faço desejo de diretórios remover. E find -deletenão removerá diretórios não vazios. Observe também que find -maxdepth 1corresponde a "." (diretório atual) que é realmente ruim. Embora eu goste da sua idéia de eliminar xargs, pode-se usar find -execalternativamente. find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
joxl
0

Você pode usar este exemplo de script rebol http://askcodegeneration.com/keep-subversion/, que é muito mais compreensível e que pode gerenciar a interação do usuário, como seleção e confirmação de pasta:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
Rebol Tutorial
fonte