Скрипт Bash для удаления всех файлов и каталогов, кроме определенных

Я пытаюсь написать очень простой сценарий оболочки Bash, который будет cd в определенном каталоге, он удалит все файлы и каталоги, кроме нескольких выбранных, а затем вернется обратно в исходный каталог.

Мой код:

#!/bin/bash 
cd /path/to/desired/directory
shopt -s extglob
rm !\(filename1\|filename2\|filename3\) -rf
cd -

Я пробовал много разных способов написать символы '(' и '|', с одинарными или двойными кавычками или обратной косой чертой, но ничего не получалось. Обратите внимание, что shopt -s extglobи rm !(filename1|filename2) -rf отлично работают вне скрипта.

Вероятно, я совершаю стандартную и фундаментальную ошибку сценария bash, которую я не вижу, но опыт еще впереди...

Какие-либо предложения!? Заранее спасибо.


person mario go    schedule 05.11.2013    source источник
comment
Вы получаете какие-либо ошибки?   -  person cforbish    schedule 05.11.2013
comment
Вы уверены, что rm !(filename1|filename2) -rf нормально работает вне скрипта? Вместо этого я ожидал rm -rf !(filename1|filename2) (с опцией перед операндами).   -  person ruakh    schedule 05.11.2013
comment
@ruakh Я читал это несколько раз. Я также время от времени использую параметры rm после файлов. Может быть, это немного непоследовательно, но это всегда работало для меня. @mario вам не нужно переключаться обратно в каталог с помощью cd -, потому что при выполнении вашего скрипта используется подоболочка.   -  person EverythingRightPlace    schedule 05.11.2013
comment
@bashphil Хорошо, «cd -» - это деталь, возможно, не нужная. Спасибо, что указали.   -  person mario go    schedule 06.11.2013
comment
@ruakh Да, я уверен, что это работает. Положение опций -abc... не играет никакой роли.   -  person mario go    schedule 06.11.2013


Ответы (5)


Сразу вижу две проблемы:

  • Аргумент -rf для rm должен стоять перед именами файлов
  • Спецификаторы extglob !, (, | и ) не должны не экранироваться обратной косой чертой.

Попробуйте это вместо этого:

rm -rf !(filename1|filename2|filename3)

Если это все еще не работает, удалите аргумент -f, и вы будете получать сообщения об ошибках о том, что происходит не так, вместо того, чтобы молча их подавлять. Чтобы распечатать имя каждого удаленного файла, вы также можете добавить опцию -v.

person Adam Rosenfield    schedule 05.11.2013
comment
просто быть немного осторожным, так как -rf удалит файлы без запроса, и может быть серьезная потеря данных - person Ashish; 05.11.2013
comment
Я нашел ошибку. Я перепутал bash с sh (чью разницу я до сих пор не понимаю). rm -rf !(имя_файла1|имя_файла2|имя_файла3), а также rm !(имя_файла1|имя_файла2|имя_файла3) -rf одинаково хорошо работают вне и внутри скрипта. Почему спецификаторы не должны быть отмечены обратной косой чертой? - person mario go; 06.11.2013
comment
Я продолжаю получать bash: !: event not found с bash 4.3.11. Похоже, я забыл добавить строку shopt. - person tarabyte; 15.09.2015

Если у вас последняя версия find, используйте:

find . -not -regex "filename1|filename2|filename3" -print

Проверьте вывод и замените -print на -delete, если он печатает только те файлы, которые вы действительно хотите удалить.

Если ваша версия find не поддерживает -delete, используйте -type f -exec rm "{}" \;

Обратите внимание, что это удалит только файлы. Это важно: если вы говорите сохранить файл x, но этот файл находится в папке y, тогда он будет игнорировать x только для того, чтобы позже удалить его со всей папкой y.

Чтобы избавиться от всех пустых папок (и при этом сохранить файлы, которые вы хотите сохранить):

find . -type d -empty -exec rmdir "{}" \;

(источник: Как найти и удалить пустые каталоги и файлы в Unix )

person Aaron Digulla    schedule 05.11.2013

Я не рекомендую пытаться написать правило для исключения определенных файлов. Что я, скорее всего, сделаю, так это попытаюсь найти либо файловую маску/глобус, либо какую-то другую привязку в именах файлов, которые вы хотите удалить, которых нет в файлах, которые вы хотите сохранить. В большинстве случаев вы, вероятно, могли бы сделать это путем расширения. К сожалению, поскольку вы не упомянули имена файлов, я не могу вам помочь более конкретно.

Что-то вроде двух (или более) циклов поиска: -

find *.mp3 d.mp3 -maxdepth 1 -type f | while read mp3s
do
mv $mp3s ~/d.stuff-to-keep
done    

find *.txt d.mp3 -maxdepth 1 -type f while read textfiles
do
rm -v $textfiles
done

Если нет, поищите другую общую характеристику файлов. Если его нет, вам может потребоваться переименовать свои файлы, чтобы получить его. Я всегда помещаю множество привязок (отдельные поля имен, расширения и т. д.) в свои имена файлов, потому что таким образом у меня есть много разных способов делать с ними то, что мне нужно. Мой обычный формат имени: -

<extension>.+<author>+<year>+<work-name>+<volume-number>+<number-of-volumes>

Знаки «плюс» — это разделители полей, тире — пробелы, но без тире в качестве первого или последнего символа. Все буквы нижнего регистра и никакие управляющие символы или другие не буквенно-цифровые символы разрешены. В результате я могу вырезать их с помощью awk, cut или чего-то еще, что мне может понадобиться, и легко переименовать их. Помещение расширения в начале имени также означает, что ls нужен только один флаг (-l), чтобы получить идеально упорядоченную файловую систему.

Если вы знаете, как использовать свою структуру каталогов в качестве базы данных с помощью основных инструментов POSIX, вам иногда не нужно будет устанавливать что-то более сложное, чтобы делать то, что вы хотите.

У вас есть ограничение на количество длинных символов в именах файлов в UNIX, а также автозавершение с помощью табуляции для навигации по длинным именам. Не бойтесь свободно использовать оба из них.

person petrus4    schedule 05.11.2013

Попробуйте ниже:

for i in `ls | grep -v "file1\|file2\|file3"` ; do rm -rf $i; done

Удачи!

person Nancy    schedule 05.11.2013
comment
Обратите внимание, что это потерпит неудачу во всех отношениях, если у вас есть пробелы в именах папок или файлов. Он также не работает с подпапками. - person Aaron Digulla; 05.11.2013
comment
@Aaron Digulla, что касается пробелов, попробуйте следующее: for i in ls | grep -v '"new folder"\|file2\|file3' ; сделать rm -rf $i; Выполнено - person Nancy; 05.11.2013
comment
1. Вам также нужны кавычки вокруг $i. 2. новая папка будет разделена for на new и folder. - person Aaron Digulla; 05.11.2013

если у вас есть файлы:

drwxr-xr-x 2 root root 4096 1 фев 02:17 a

drwxr-xr-x 2 корень корень 4096 1 фев 02:17 b

drwxr-xr-x 2 корень корень 4096 1 фев 02:17 с

drwxr-xr-x 2 корень корень 4096 1 фев 02:17 д

drwxr-xr-x 2 root root 4096 1 февраля 02:17 e

drwxr-xr-x 2 root root 4096 1 фев 02:17 f

drwxr-xr-x 2 root root 4096 1 февраля 02:18 nodelete1

drwxr-xr-x 2 root root 4096 1 февраля 02:18 nodelete2

Вы можете сделать:

ls | grep -v "nodel.\+" | xargs rm -rf

использовать расширенный синтаксис регулярного выражения для исключения файлов.

Объяснение:

ls --- выводит текущий каталог (ls -d для вывода только каталогов)

grep -v --- флаг v указывает, что НЕ СООТВЕТСТВУЕТ + grep позволяет писать регулярное выражение в стиле perl, если вы укажете флаг -P

xargs --- применяет действие rm -rf к каждому элементу

person Mr. BeatMasta    schedule 31.01.2014