Remover arquivos por expressão regular

Remover arquivos por expressão regular

Quero manter os arquivos cujos nomes correspondam [0-9A-Z]{1,2}_\d{4}_\w+?\.dat, por exemplo, A1_2001_pm23aD.dat, K_1998_12.date remover o restante.

No entanto, os comandos lse rmnão suportam tais expressões regulares. Como posso fazer isso?

Responder1

Usando globs estendidos:

shopt -s extglob
printf '%s\n' !([[:digit:][:upper:]]?([[:digit:][:upper:]])_[[:digit:]][[:digit:]][[:digit:]][[:digit:]]_+([[:alnum:]]).dat)

isso imprimirá todos os nomes de arquivos/diretórios que não ( !) correspondem [[:digit:][:upper:]]seguidos de zero ou um [[:digit:][:upper:]]seguido de 4 [[:digit:]]entre _s e um ou mais [[:alnum:]]antes da extensão .dat.
Se você quiser pesquisar recursivamente:

shopt -s globstar
shopt -s extglob
printf '%s\n' **/!([[:digit:][:upper:]]?([[:digit:][:upper:]])_[[:digit:]][[:digit:]][[:digit:]][[:digit:]]_+([[:alnum:]]).dat)

Alternativamente, com gnu find(você pode usar um regex):

find . -regextype egrep ! -regex '.*/[[:digit:][:upper:]]{1,2}_[[:digit:]]{4}_[[:alnum:]]+\.dat$'

Responder2

Existem muitas maneiras de fazer isso. Você poderia usar uma linguagem de script que entendesse expressões regulares. Por exemplo, em Perl:

perl -le 'unlink(grep(!/[0-9A-Z]{1,2}_\d{4}_\w+?.dat/,@ARGV))' *

Isso procurará todos os arquivos (não subdiretórios) no diretório atual, colete aqueles que não correspondem ao regex e exclua-os.

Você também pode fazer algo semelhante com o bash, basta traduzir o regex para POSIX ERE:

for f in *; do 
    [[ "$f" =~ [0-9A-Z]{1,2}_[0-9]{4}_[a-zA-Z0-9]+.dat ]] || rm "$f"; 
done

Observe que no seu regex, \w+?.dattentará corresponder à menor sequência alfanumérica possívelqualquer personageme dat. Não vejo por que você gostaria de usar +?aqui e provavelmente pretendia usar \.dat. Suponho que você provavelmente também queira ter certeza de que todo o nome do arquivo corresponde, para que coisas como foobarfoobarfoobarA1_2001_pm23aD.datfoobarfooabrtambém sejam removidas. Nesse caso, use um destes:

perl -le 'unlink(grep(!/^[0-9A-Z]{1,2}_\d{4}_\w+\.dat$/,@ARGV))' *

ou

for f in *; do 
    [[ "$f" =~ ^[0-9A-Z]{1,2}_[0-9]{4}_[a-zA-Z0-9]+.dat$ ]] || rm "$f"; 
done

Finalmente, para excluir também diretórios, você poderia fazer:

for f in *; do 
    [[ "$f" =~ ^[0-9A-Z]{1,2}_[0-9]{4}_[a-zA-Z0-9]+.dat$ ]] || rm -rf "$f"; 
done

Responder3

Você pode fazer isso com find:

find . -regextype posix-extended \
            -type f ! -regex '.*/[0-9A-Z]{1,2}_[[:digit:]]{4}_[[:alnum:]_]+?\.dat' -delete
  • Claro que você pode colocar tudo em uma linha (removendo o \no final da primeira linha).
  • -regextype posix-egrepparece funcionar exatamente tão bem quanto -regextype posix-extended.
  • Se a sua versão findnão for compatível -delete, use -exec rm -- {} +ou -exec rm -- {} ';'.
  • Se você quiser pesquisar apenas o diretório de nível superior, use -maxdepth 1.

informação relacionada