цикл в папках с символом \ n в именах

9

У меня есть несколько папок с \nхарактером это их имена.

например:

$ ls
''$'\n''Test'

Это относится к папке с именем теста и пустой строкой перед ее именем.

Поэтому, когда я запускаю несколько таких сценариев, в родительском каталоге:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

Это показывает:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

Потому что он запускается дважды, один раз \nи другой включенный Test, потому что имя папки состоит из двух строк.

Так как я могу решить эту проблему так, чтобы скрипт знал \nTestтолько одну папку?

Тара С Вольпе
источник
3
Вам нужно использовать -print0директиву find и -dопцию read. См stackoverflow.com/a/40189667/7552
Гленна Джекман
1
@glennjackman Пожалуйста, ответьте!
десерт
@glennjackman Спасибо за ваш ответ, но у find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; doneкоманды есть этот вывод rmdir: failed to remove 'Test': No such file or directory.
Tara S Volpe
5
Вы должны процитировать переменную:rmdir "$file"
Гленн Джекман
1
@glennjackman Просто опубликуйте это как ответ. Это правильное решение, и, кроме того, комментарии не лучшее место для этого. Упвот уже подразумевается :)
Сергей Колодяжный

Ответы:

12

У вас там только одна команда, поэтому достаточно вызвать findс -execфлагом flag rmdir:

find -depth -type d -exec rmdir {} \;

Или используйте -deleteпараметр как в find -type d -delete, но он не будет работать с непустыми каталогами. Для этого вам также понадобится -emptyфлаг. Обратите внимание также, -deleteподразумевает, -depthчто может быть пропущено. Таким образом, другая жизнеспособная альтернатива, которая сохраняет все как один процесс:

find -type d -empty -delete

Если каталог не пустой, используйте rm -rf {} \;. Чтобы изолировать только каталоги \nв имени файла, мы можем объединить цитату ANSI-C в bash $'...'с -nameопцией:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly, мы могли бы справиться с этим следующим образом:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

Стоит отметить, что если вашей целью является удаление каталогов, то этого -deleteдостаточно, однако, если вы хотите выполнить команду в каталоге, то -execэто наиболее уместно.

Смотрите также

Сергей Колодяжный
источник
2
... используйте rm -rf с осторожностью . ; P Кстати, у findвас нет -deleteопции? Он удаляет пустые каталоги и выдает ошибки для непустых, таких как rmdir- может быть дешевле.
десерт
3
@dessert Это так, и я добавил это, но -deleteне буду удалять непустые каталоги. Отсюда и бережное использованиеrm -rf
Сергей Колодяжный
6
Всегда запускайте такие findкоманды всухую без какой-либо разрушительной полезной нагрузки (т. Е. Удаляйте -deleteили -exec whatever \;), чтобы проверить, действительно ли список затронутых файлов верен и не содержит ничего, что не должно быть удалено ...
Byte Commander
2
Это askubuntu.com, поэтому мы можем принять GNU find. Используйте -exec rmdir {} +для пакетирования нескольких аргументов в одну rmdirкомандную строку. И кстати, « но он не будет работать с непустыми каталогами. » Также относится к этому rmdir, так что на самом деле это не отличие от -delete. Это разница от rm -r.
Питер Кордес
2
find -type d -empty -delete( -deleteподразумевается -depth).
Кевин
10

Вы можете использовать оболочки оболочки вместо find:

for d in */ ; do 
    rmdir "$d"
done

Глобус оболочки */соответствует всем папкам в текущем каталоге. Эта конструкция цикла for обеспечивает правильное разбиение слов автоматически.

Обратите внимание, что в зависимости от параметров вашей оболочки, это может игнорировать скрытые папки (имя начинается с.). Это поведение можно изменить, чтобы оно соответствовало всем файлам текущего сеанса с помощью команды shopt -s dotglob.

Также не забывайте всегда указывать свои переменные.

Byte Commander
источник
Глоб будет искать только файлы / каталоги в текущем каталоге, а не рекурсивно, как это findделает
ilkkachu
1
Вы правы @ilkkachu. Это можно изменить, включив параметр оболочки globstar shopt -s globstarи используя **/*/вместо него glob.
Byte Commander
6

Оба ответа, написанные до сих пор, вызывают rmdirодин раз для каждого каталога, но, как rmdirможно принять несколько аргументов, я задаюсь вопросом: не существует ли более эффективного способа?

Можно просто сделать

rmdir */

и это, безусловно, самый простой и эффективный способ, но он может вызвать ошибку в случае многих каталогов (см. Какова максимальная длина аргументов командной строки в gnome-Terminal? ). Если вы хотите, чтобы этот подход работал рекурсивно, включите параметр globstarоболочки с shopt -s globstarи используйте **/*/вместо */.

С GNU find(и если мы не хотим просто использовать -delete), мы могли бы сделать

find -depth -type d -exec rmdir {} +

который строит командную строку «почти так же, как xargsстроит свои командные строки» ( man find). Замены -depthдля , -maxdepth 1если вы не хотите работать рекурсивно.

В этом ответе SteelDriver объясняет третий и блестящий способ IMO :

printf '%s\0' */ | xargs -0 rmdir

При этом используется встроенная оболочка printfдля создания списка аргументов с нулевым разделителем, затем этот список передается по трубопроводу, xargsкоторый вызывает rmdirровно столько раз, сколько необходимо. Вы можете заставить это работать рекурсивно с shopt -s globstarи **/*/вместо */как выше.

Десерт
источник
2
findявляется рекурсивным, но цикл OP - нет. Чтобы повторить это поведение, используйте find -maxdepth 1 -type d -exec rmdir {} +. ( -depthв этом случае излишне.) Аккуратный трюк с printfподражанием find -print0, я раньше такого не видел.
Питер Кордес
1
Ой! Я видел , как lsи whileпетля, я пропустил , что они были перенаправлять от подмены процесса с findвместо трубопровода lsв петлю и просто не показывать его по какой - то причине. Это find *действительно похоронено. Да, это рекурсивно и завершится ошибкой, если в каталоге слишком много записей каталога, включая не-каталоги, потому что он не использует */. find .было бы намного лучше, потому что findэто быстрее, statчем расширение bash.
Питер Кордес