для циклу в папках із символом \ 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 і параметр -dread. Див 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.
Тара S Вольпе

5
Ви повинні цитувати змінну:rmdir "$file"
glenn jackman

1
@glennjackman Просто опублікуйте це як відповідь. Це правильне рішення, і крім коментарів, це не найкраще місце для цього. Upvote вже мається на увазі :)
Сергій Колодяжний

Відповіді:


12

У вас є лише одна команда, тому достатньо зателефонувати за findдопомогою виклику -execпрапора 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командний рядок. І BTW " але він не працюватиме з непорожніми каталогами " також стосується rmdir, тому це насправді не відрізняється від -delete. Це відмінність від rm -r.
Пітер Кордес

2
find -type d -empty -delete( -deleteмається на увазі -depth).
Кевін

10

Ви можете використовувати кульки оболонки замість find:

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

Глоб оболонки */відповідає всім папкам у поточному каталозі. Ця конструкція для циклу забезпечує автоматичне правильне розділення слів.

Зауважте, що залежно від параметрів оболонки це може ігнорувати приховані папки (назва, що починається з а.). Таку поведінку можна змінити, щоб відповідати всім файлам за поточний сеанс за допомогою команди shopt -s dotglob.

Також не забувайте завжди цитувати свої змінні.


глобус знайде лише файли / каталоги в поточному каталозі, а не рекурсивно, як findце робиться
ilkkachu

1
Ви правий @ilkkachu. Це можна змінити, включивши параметр оболонки globstar shopt -s globstarі **/*/замість цього використовуючи глобул .
Байт-командир

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якщо ви не хочете працювати рекурсивно.

Третій та блискучий спосіб IMO пояснюється в цій відповіді steeldriver :

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

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


2
findє рекурсивним, але цикл ОП не є. Щоб повторити таку поведінку, використовуйте find -maxdepth 1 -type d -exec rmdir {} +. ( -depthв цьому випадку зайве.) Акуратний трюк printfдля наслідування find -print0, я цього раніше не бачив.
Пітер Кордес

1
Ой! Я побачив цикл lsі whileцикл, я пропустив, що вони переспрямовувались із заміни процесу, findзамість того, щоб lsпереходити в цикл і просто чомусь не показували це. Це find *дійсно поховано. Так, це рекурсивно, і вийде з ладу, якщо в каталозі занадто багато записів, включаючи не-каталоги, оскільки він не використовується */. find .було б набагато краще, тому що findце швидше, statніж глобальне розширення баша.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.