Что рекурсивно распространяется на все файлы в текущем каталоге?

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

Ответ 1

Это будет работать в Bash 4:

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

Для работы globstar с двумя звездочками необходимо установить параметр globstar (по умолчанию: on):

shopt -s globstar

От man bash:

    globstar
                  If set, the pattern ** used in a filename expansion con‐
                  text will match a files and zero or more directories and
                  subdirectories.  If the pattern is followed by a /, only
                  directories and subdirectories match.

Теперь мне интересно, не было ли когда-то ошибки в обработке globstar, потому что теперь, используя просто ls **/*.ext Ext, я получаю правильные результаты.

Несмотря на это, я посмотрел на анализ, который kenorb выполнил с использованием репозитория VLC, и обнаружил некоторые проблемы с этим анализом и в своем ответе, приведенном выше:

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

Однако наиболее важно то, что существует особая проблема с использованием команды 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 поскольку он обычно не сортируется, а расширение глобусов в Bash уже отсортировано. Как видите, единственным выходом из diff был текущий каталог . вывод по find. Когда я сделал ls ** | wc -l ls ** | wc -l на выходе было почти вдвое больше строк.

Ответ 2

Этот wil печатает все файлы в текущем каталоге и его подкаталогах, которые заканчиваются на .ext.

find . -name '*.ext' -print

Ответ 3

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

Ниже приведены результаты тестирования других вариантов и того, как они себя ведут.


Тестирование папки с 3472 файлами в образце папка хранилища VLC:

(Всего файлов по 3472 подсчитано по: find . -type f | wc -l)

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

Итак, я думаю, что самый близкий способ перечислить все файлы рекурсивно - это первый пример (**/*.*) в соответствии с gniourf-gniourf comment (при условии, что файлы имеют правильные расширения или использовать конкретный), так как второй пример содержит несколько дубликатов, как показано ниже:

$ 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, и вы можете случайно удалить неправильные файлы.

Ответ 4

$ find . -type f

Это будет список всех файлов в текущем каталоге. Затем вы можете сделать еще одну команду на выходе с помощью -exec

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

Это будет grep каждый файл из find для строки "foo".

Ответ 5

Почему бы просто не использовать расширение скобки, чтобы включить текущий каталог?

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

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

Кроме того, он считал, что хорошая практика в bash включает в себя ведущие ./ в ваших шаблонах glob.