Що розширюється до всіх файлів у поточному каталозі рекурсивно?


92

Я знаю, що **/*.extрозгортається до всіх файлів у всіх підкаталогах, що збігаються *.ext, але що таке подібне розширення, яке також включає всі такі файли в поточному каталозі?


4
Мій баш не справляється **/*.ext. Ви впевнені, що це працює для вас?
tangens

@tangens Ви повинні ввімкнути globstarопцію відповідно до відповіді Денніса.
kenorb

Відповіді:


111

Це буде працювати в Bash 4:

ls -l {,**/}*.ext

Для того, щоб глобус із подвійною зірочкою працював, globstarпотрібно встановити опцію (за замовчуванням: увімкнено):

shopt -s globstar

Від man bash:

    globstar
                  Якщо встановлено, шаблон **, що використовується у розширенні імені файлу,
                  text буде відповідати файлам і нулю або більше каталогів і
                  підкаталоги. Якщо за шаблоном слідує символ /, лише
                  каталоги та підкаталоги збігаються.

Зараз мені цікаво, чи не могла колись бути помилка в обробці globstar, тому що тепер, використовуючи просто, ls **/*.extя отримую правильні результати.

Незважаючи на це, я подивився аналіз, який kenorb робив із використанням сховища VLC, і виявив деякі проблеми з цим аналізом, і в моїй відповіді безпосередньо вище:

Порівняння з результатами роботи findкоманди є недійсними, оскільки зазначення -type fне включає інші типи файлів (зокрема каталоги), і lsкоманди, перераховані, можливо. Крім того, одна з перелічених команд ls -1 {,**/}*.*- яка, здається, базується на моїй вище, виводить лише імена, що містять крапку для тих файлів, які знаходяться в підкаталогах. Питання OP та моя відповідь містять крапку, оскільки шукають файли з певним розширенням.

Однак найголовніше полягає в тому, що існує особлива проблема з використанням lsкоманди із шаблоном globstar **. Виникає багато дублікатів, оскільки шаблон розширюється за допомогою Bash до всіх імен файлів (та назв каталогів) у дереві, яке перевіряється. Після розширення lsкоманда перераховує кожен із них та їх вміст, якщо це каталоги.

Приклад:

У нашому поточному каталозі знаходиться підкаталог Aта його вміст:

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

У цьому дереві **розширюється до "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 записів) . Якщо ви це зробите echo **, це буде точний результат, який ви отримаєте, і кожен запис представляється один раз. Однак якщо ви ls **це зробите, він видасть список кожного з цих записів. Отже, по суті, це ls Aслід ls A/AB, і т.д., тому A/ABпоказується двічі. Крім того, lsзбирається встановити вихідні дані кожного підкаталогу:

...
<blank line>
directory name:
content-item
content-item

Отже, wc -lпідрахунок підраховує всі ті порожні рядки та заголовки розділів імен каталогів, що відкидає відлік ще далі.

Це ще одна причина, чому не слід розбиратиls .

В результаті подальшого аналізу, я рекомендую не використовувати шаблон globstar ні в яких інших обставинах, окрім ітерації по дереву файлів таким чином:

for entry in **
do
    something "$entry"
done

Для останнього порівняння я використав сховище джерел Bash, яке мені було під рукою, і зробило це:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

Раніше я trміняв пробіли на нові рядки, що є дійсним лише тут, оскільки жодні імена не містять пробілів. Раніше я sedвидаляв провідну ./лінію з кожного рядка виводу з find. Я сортував висновок, findоскільки він зазвичай несортований, а розширення глобусів Баша вже відсортовано. Як бачите, єдиним результатом роботи diffбув поточний .вихідний каталог каталогу find. Коли я робив ls ** | wc -lвихід, було майже вдвічі більше рядків.


5
Я протестував Ubuntu та Cygwin, і globstarза замовчуваннямoff
Стівен Пенні

12
Найкраща відповідь! але я думаю, що цього **/*.extповинно бути достатньо. Крім того, у вас не буде прихованих файлів, якщо у вас немає shopt -s dotglob.
gniourf_gniourf

2
Для відключення globstar: shopt -u globstar.
kenorb

4
@gniourf_gniourf Питання насправді просить конкретно включити поточний каталог, тому ні, **/*.extне буде достатньо
msciwoj

2
@dotnetCarpenter: Версія Bash, яка постачається з MacOS - це 3.2, яка не підтримує globstar, як ви дізналися. Подвійна зірочка трактується так само, як і одинарна. Globstar був представлений в Bash 4.0.
Призупинено до подальшого повідомлення.

13

Це роздрукує всі файли в поточному каталозі та його підкаталогах, які закінчуються на '.ext'.

find . -name '*.ext' -print

Хоча ця відповідь не відповідає запитуваному «розширенню» ОП у найсуворішому сенсі, найімовірніше, це дасть бажаний результат.
Призупинено до подальшого повідомлення.

7

Ви можете використовувати: **/*.*для включення всіх файлів рекурсивно (увімкнути:) shopt -s globstar.

Нижче наведено тестування інших варіацій та їх поведінки.


Папка тестування з 3472 файлами у зразку папки сховища VLC :

(Всього файлів 3472 підраховувалися відповідно з : find . -type f | wc -l)

  • ls -1 **/*.* - повертає 3338
  • ls -1 {,**/}*.*- повертає 3341 (як запропонував Денніс )
  • ls -1 {,**/}* - повертає 8265
  • ls -1 **/*- повертає 7817, крім прихованих файлів (як запропонував Денніс )
  • ls -1 **/{.[^.],}*- повертає 7869 (як запропонував Денніс )
  • ls -1 {,**/}.?* - повертає 15855
  • ls -1 {,**/}.* - повертає 20321

Тому я думаю, що найближчим методом рекурсивного переліку всіх файлів є перший приклад ( **/*.*) відповідно до коментаря gniourf-gniourf (припускаючи, що файли мають відповідні розширення або використовують конкретне), оскільки другий приклад дає ще кілька дублікатів, як показано нижче :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

а інший генерує ще більше дублікатів.


Щоб включити приховані файли, використовуйте: shopt -s dotglob(відключити shopt -u dotglob). Це не рекомендується, оскільки це може впливати на команди, такі як mvабо, rmі ви можете випадково видалити неправильні файли.


На терміналі Mac і bash з увімкненим globstar я знайшов вищевказане рішення ( **/*.*) інформативним і працював найкраще. Прийнята відповідь спричинила дублікати елементів у верхньому каталозі. Моєю робочою схемою було:"${path}"**/*.*
mummybot

Було б цікаво спробувати це з іншими варіантами , як nullglob і dotglob
Уілф

4
$ find . -type f

Це перелічить усі файли в поточному каталозі. Потім ви можете виконати якусь іншу команду на виході за допомогою -exec

$find . -type f -exec grep "foo" {} \;

Це згенерує кожен файл із знахідки для рядка "foo".


Тепер, коли пройшло 11 років, може настати час, коли хтось вкаже, що find . -type fрекурсивно застосовується до кореневого каталогу поточного каталогу, а не лише до поточного каталогу.
Роджер Даль

4

Чому б просто не використовувати розширення фігурних дужок, щоб включити також поточний каталог?

./{*,**/*}.ext

Розширення фігурних дужок відбувається перед розширенням glob, тому ви можете ефективно робити те, що хочете, зі старими версіями bash, а також можете відмовитися від мавпування з globstar в нових версіях.

Крім того, вважається гарною практикою в bash включати провідні ./у ваші глобальні моделі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.