
Я хочу сохранить файлы, имена которых совпадают [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
.