Ось рішення, сумісне з POSIX, яке поступово обробляє вихід find для видалення каталогів, які мають перерахований підкаталог. Передбачається, що в іменах каталогів немає рядків.
{ find . -type d; echo; } |
awk 'index($0,prev"/")!=1 && NR!=1 {print prev}
1 {sub(/\/$/,""); prev=$0}'
Пояснення: скрипт awk затримує друк кожного рядка, поки він не прочитає наступний рядок, і друкує лише попередній рядок, якщо він не є префіксом. Це використовує те, що find списки підкаталогів відразу після їхнього батька. Додаткова "/" щоб уникнути помилкового видалення foo коли foobar також існує. Нелегкий NR!=1 уникає друкувати початкову порожню лінію і неелантний echo; не мати настільки неефективного особливого випадку для останнього рядка. Виклик до sub видаляє кінцеву слеш з каталогу верхнього рівня, у випадку, наприклад, find ./ називався.
Як завжди, є cryptic zsh один лайнер.
echo **/.(e\''test -z $REPLY/*(/DN[1])'\':h)
Більш тривала версія:
is_leaf () { [ -z $REPLY/*(/DN[1]) ] }
echo **/.(+is_leaf:h)
Останній рядок можна спростити до echo **/(+is_leaf) якщо ви не маєте на увазі відставання /.
Резюме пояснення: речі в дужках є Глобальні класи , задокументовані в zshexpn сторінка людини. Ми фільтруємо результати глобу **/ (розширюється до поточного каталогу і всіх його підкаталогів), зберігаючи тільки ті, для яких функція is_leaf (або код між '…' ) повертає 0. Глобус коду фільтра в підкаталогах перевіряється відповідності ( $REPLY ) (насправді, [1] припиняє роботу після першого підкаталогу) і повертає статус, що вказує, чи було знайдено принаймні один підкаталог. Кваліфікатор глоба / обмежує розширення до каталогів; N означає, що розширення порожнє, якщо немає відповідності; D викликає включення файлів точок; :h - це модифікатор історії та викликає /. суфікс повинен бути позбавлений (загалом це означає dirname ).
Щоб проілюструвати можливості глобальних кваліфікаційних характеристик zsh, наведемо два інших варіанти (довше, і я думаю, що це більш неясне) з відповідним is_leaf функція:
echo **/.(e\''tmp=($REPLY/*(/DN[1])); ((!#tmp))'\':h)
echo **/.(e\''$REPLY/*(/DN[1]e:REPLY=false:)'\':h)
is_leaf () { set -- $REPLY/*(/DN[1]); ((!#)); }
is_leaf () { return $REPLY/*(/DN[1]e:REPLY=1:) }