Remover arquivos por expressão regular

4

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

No entanto, os comandos ls e rm não suportam essas expressões regulares. Como posso fazer isso?

    
por Lee 17.05.2016 / 15:28

3 respostas

5

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 corresponderem ( ! ) [[:digit:][:upper:]] seguido por zero ou um [[:digit:][:upper:]] seguido por 4 [[:digit:]] entre _ s e, em seguida, 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)

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

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

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

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

Isso irá procurar por todos os arquivos ( não subdiretórios ) no diretório atual, coletar aqueles que não corresponderem à regex e excluí-los.

Você também pode fazer algo parecido com o bash, você só precisa traduzir o regex para o 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, em sua regex, \w+?.dat tentará corresponder à menor cadeia alfanumérica possível qualquer caractere e dat . Não vejo por que você gostaria de usar +? aqui e provavelmente pretendia usar \.dat . Eu estou supondo que você provavelmente também quer certificar-se de que o nome do arquivo inteiro corresponda, de modo que coisas como foobarfoobarfoobarA1_2001_pm23aD.datfoobarfooabr também sejam removidas. Em caso afirmativo, 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

Por fim, para excluir também os diretórios, você pode fazer:

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

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-egrep parece funcionar exatamente também como -regextype posix-extended .
  • Se a sua versão de find não for compatível com -delete , use -exec rm -- {} + ou -exec rm -- {} ';' .
  • Se você quiser pesquisar apenas o diretório de nível superior, use -maxdepth 1 .
por 17.05.2016 / 23:54