Я знаю, що **/*.ext
розгортається до всіх файлів у всіх підкаталогах, що збігаються *.ext
, але що таке подібне розширення, яке також включає всі такі файли в поточному каталозі?
Відповіді:
Це буде працювати в 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
вихід, було майже вдвічі більше рядків.
globstar
за замовчуваннямoff
**/*.ext
повинно бути достатньо. Крім того, у вас не буде прихованих файлів, якщо у вас немає shopt -s dotglob
.
globstar
: shopt -u globstar
.
**/*.ext
не буде достатньо
Це роздрукує всі файли в поточному каталозі та його підкаталогах, які закінчуються на '.ext'.
find . -name '*.ext' -print
Ви можете використовувати: **/*.*
для включення всіх файлів рекурсивно (увімкнути:) shopt -s globstar
.
Нижче наведено тестування інших варіацій та їх поведінки.
Папка тестування з 3472 файлами у зразку папки сховища VLC :
(Всього файлів 3472 підраховувалися відповідно з : find . -type f | wc -l
)
ls -1 **/*.*
- повертає 3338ls -1 {,**/}*.*
- повертає 3341 (як запропонував Денніс )ls -1 {,**/}*
- повертає 8265ls -1 **/*
- повертає 7817, крім прихованих файлів (як запропонував Денніс )ls -1 **/{.[^.],}*
- повертає 7869 (як запропонував Денніс )ls -1 {,**/}.?*
- повертає 15855ls -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
і ви можете випадково видалити неправильні файли.
**/*.*
) інформативним і працював найкраще. Прийнята відповідь спричинила дублікати елементів у верхньому каталозі. Моєю робочою схемою було:"${path}"**/*.*
$ find . -type f
Це перелічить усі файли в поточному каталозі. Потім ви можете виконати якусь іншу команду на виході за допомогою -exec
$find . -type f -exec grep "foo" {} \;
Це згенерує кожен файл із знахідки для рядка "foo".
find . -type f
рекурсивно застосовується до кореневого каталогу поточного каталогу, а не лише до поточного каталогу.
Чому б просто не використовувати розширення фігурних дужок, щоб включити також поточний каталог?
./{*,**/*}.ext
Розширення фігурних дужок відбувається перед розширенням glob, тому ви можете ефективно робити те, що хочете, зі старими версіями bash, а також можете відмовитися від мавпування з globstar в нових версіях.
Крім того, вважається гарною практикою в bash включати провідні ./
у ваші глобальні моделі.
**/*.ext
. Ви впевнені, що це працює для вас?