
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.dat
e remover o restante.
No entanto, os comandos ls
e rm
nã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+?.dat
tentará 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.datfoobarfooabr
també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-egrep
parece funcionar exatamente tão bem quanto-regextype posix-extended
.- Se a sua versão
find
nã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
.