Удалить файлы по регулярному выражению

Удалить файлы по регулярному выражению

Я хочу сохранить файлы, имена которых совпадают [0-9A-Z]{1,2}_\d{4}_\w+?\.dat, например, A1_2001_pm23aD.dat, K_1998_12.dat, и удалить остальные.

Однако команды lsи rmне поддерживают такие регулярные выражения. Как это сделать?

решение1

Использование расширенных глобусов:

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

это выведет все имена файлов/каталогов, которые не !совпадают ( ) [[:digit:][:upper:]], за которыми следует ноль или один, [[:digit:][:upper:]]затем 4 [[:digit:]]между _s и затем один или более [[:alnum:]]перед расширением .dat.
Если вы хотите выполнить рекурсивный поиск:

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

Альтернативно, с помощью gnu find(можно использовать регулярное выражение):

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

решение2

Есть много способов сделать это. Вы можете использовать скриптовый язык, который понимает регулярные выражения. Например, в Perl:

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

Будет выполнен поиск всех файлов (не подкаталоги) в текущем каталоге, соберите те, которые не соответствуют регулярному выражению, и удалите их.

Аналогичное действие можно выполнить и с помощью bash, нужно просто перевести регулярное выражение в формат POSIX ERE:

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

Обратите внимание, что в вашем регулярном выражении \w+?.datбудет предпринята попытка сопоставить наименьшую возможную буквенно-цифровую строку.любой персонажи dat. Я не понимаю, почему вы хотите использовать +?здесь, и вы, вероятно, имели в виду использовать \.dat. Я предполагаю, что вы также, вероятно, хотите убедиться, что все имя файла совпадает, так что такие вещи, как foobarfoobarfoobarA1_2001_pm23aD.datfoobarfooabrтакже удаляются. Если так, используйте один из этих вариантов:

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

или

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

Наконец, чтобы удалить каталоги, вы можете сделать следующее:

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

решение3

Вы можете сделать это с помощью find:

find . -regextype posix-extended \
            -type f ! -regex '.*/[0-9A-Z]{1,2}_[[:digit:]]{4}_[[:alnum:]_]+?\.dat' -delete
  • Конечно, вы можете поместить все это в одну строку (удалив \в конце первой строки).
  • -regextype posix-egrepпохоже, работает точно так же хорошо, как -regextype posix-extended.
  • Если ваша версия findне поддерживает -delete, используйте -exec rm -- {} +или -exec rm -- {} ';'.
  • Если вы хотите выполнить поиск только в каталоге верхнего уровня, используйте -maxdepth 1.

Связанный контент