Что рекурсивно распространяется на все файлы в текущем каталоге?
92
Я знаю, что **/*.extрасширение распространяется на все файлы во всех совпадающих подкаталогах *.ext, но что такое аналогичное расширение, которое также включает все такие файлы в текущем каталоге?
Мой bash не справляется **/*.ext. Вы уверены, что это работает для вас?
tangens
@tangens Вы должны включить globstarопцию в соответствии с ответом Денниса.
kenorb
Ответы:
111
Это будет работать в Bash 4:
ls -l {,**/}*.ext
Чтобы глобус с двойной звездочкой работал, globstarнеобходимо установить параметр (по умолчанию: включено):
shopt -s globstar
Откуда man bash:
глобус
Если установлено, шаблон **, используемый в расширении имени файла, согласуется с
текст будет соответствовать файлам и ноль или более каталогов и
подкаталоги. Если за шаблоном следует /, только
каталоги и подкаталоги совпадают.
Теперь мне интересно, могла ли когда-то быть ошибка в обработке 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будет отделен вывод каждого подкаталога:
Таким образом, использование wc -lподсчитывает все эти пустые строки и заголовки разделов с именами каталогов, что еще больше снижает счет.
Это еще одна причина, по которой вы не должны анализироватьls .
В результате этого дальнейшего анализа я рекомендую не использовать шаблон globstar ни при каких обстоятельствах, кроме повторения по дереву файлов следующим образом:
for entry in **
do
something "$entry"done
В качестве последнего сравнения я использовал репозиторий исходного кода Bash, который у меня был под рукой, и сделал следующее:
Раньше я trзаменял пробелы на новые строки, что допустимо только здесь, так как имена не содержат пробелов. Раньше я sedудалял ведущие ./из каждой строки вывода из find. Я отсортировал вывод, findтак как он обычно не отсортирован, а расширение глобусов в Bash уже отсортировано. Как вы можете видеть, единственный выход из diffбыл текущий каталог на .выход find. Когда я это сделал, ls ** | wc -lна выходе было почти вдвое больше строк.
Приостановлено до дальнейшего уведомления. источник
5
Я тестировал Ubuntu и Cygwin, и globstarпо умолчанию установленoff
Стивен Пенни
12
Лучший ответ! но я думаю, **/*.extдолжно быть достаточно. Кроме того, у вас не будет скрытых файлов, если вы shopt -s dotglob.
gniourf_gniourf 01
2
Для отключения globstar: shopt -u globstar.
kenorb
4
@gniourf_gniourf Вопрос на самом деле просит включить текущий каталог, так что нет, **/*.extэтого будет недостаточно
msciwoj
2
@dotnetCarpenter: версия Bash, которая поставляется с MacOS, - 3.2, которая, как вы узнали, не поддерживает globstar. Двойная звездочка обрабатывается так же, как и одиночная. Globstar был представлен в Bash 4.0.
Приостановлено до дальнейшего уведомления.
13
Это распечатает все файлы в текущем каталоге и его подкаталогах, которые заканчиваются на '.ext'.
Хотя этот ответ не соответствует запрошенному OP "расширению" в самом строгом смысле этого слова, он, скорее всего, приведет к желаемому результату.
Приостановлено до дальнейшего уведомления.
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 (при условии, что файлы имеют правильные расширения или используют конкретное), поскольку второй пример дает еще несколько дубликатов, как показано ниже :
Чтобы включить скрытые файлы, используйте: shopt -s dotglob(отключить с помощью shopt -u dotglob). Это не рекомендуется, потому что это может повлиять на такие команды, как mvили, rmи вы можете случайно удалить неправильные файлы.
На терминале Mac и bash с включенным globstar я нашел вышеупомянутое решение ( **/*.*) информативным и сработало лучше всего. Принятый ответ вызвал дублирование элементов в верхнем каталоге. Мой рабочий шаблон был:"${path}"**/*.*
mummybot
Было бы интересно попробовать это с другими вариантами, такими как nullglob и dotglob
Wilf
4
$ find . -type f
Это перечислит все файлы в текущем каталоге. Затем вы можете выполнить другую команду на выходе, используя -exec
$find . -type f -exec grep "foo" {} \;
Это приведет к поиску строки «foo» в каждом найденном файле.
Теперь, когда прошло 11 лет, может быть, настало время, чтобы кто-то find . -type fуказал , что применяется рекурсивно к корню в текущем каталоге, а не только к текущему каталогу.
Роджер Даль
4
Почему бы просто не использовать расширение скобок, чтобы включить и текущий каталог?
./{*,**/*}.ext
Расширение скобок происходит до расширения глобуса, поэтому вы можете эффективно делать то, что хотите, со старыми версиями bash и можете отказаться от обезьян с помощью globstar в новых версиях.
Кроме того, в bash считается хорошей практикой включать ведущие ./в свои шаблоны глобусов.
**/*.ext
. Вы уверены, что это работает для вас?globstar
опцию в соответствии с ответом Денниса.Ответы:
Это будет работать в Bash 4:
Чтобы глобус с двойной звездочкой работал,
globstar
необходимо установить параметр (по умолчанию: включено):shopt -s globstar
Откуда
man bash
:Теперь мне интересно, могла ли когда-то быть ошибка в обработке globstar, потому что теперь, используя просто,
ls **/*.ext
я получаю правильные результаты.Тем не менее , я посмотрел на анализ, который kenorb провел с использованием репозитория VLC, и обнаружил некоторые проблемы с этим анализом и в своем ответе непосредственно выше:
Сравнение с выводом
find
команды недействительно, поскольку указание-type f
не включает другие типы файлов (в частности, каталоги), аls
перечисленные команды, вероятно, включают. Кроме того, одна из перечисленных команд,ls -1 {,**/}*.*
которая, казалось бы, основана на моей выше, выводит только имена , содержащие точку для тех файлов, которые находятся в подкаталогах. Вопрос OP и мой ответ содержат точку, поскольку ищутся файлы с определенным расширением.Однако наиболее важно то, что при использовании
ls
команды с шаблоном globstar возникает особая проблема**
. Многие дубликаты возникают, поскольку Bash расширяет шаблон до всех имен файлов (и имен каталогов) в исследуемом дереве. После расширенияls
команда выводит список каждого из них и их содержимого, если они являются каталогами.Пример:
В нашем текущем каталоге находится подкаталог
A
и его содержимое:В этом дереве
**
расширяется до «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
будет отделен вывод каждого подкаталога:Таким образом, использование
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
на выходе было почти вдвое больше строк.источник
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 (при условии, что файлы имеют правильные расширения или используют конкретное), поскольку второй пример дает еще несколько дубликатов, как показано ниже :а другой генерирует еще больше дубликатов.
Чтобы включить скрытые файлы, используйте:
shopt -s dotglob
(отключить с помощьюshopt -u dotglob
). Это не рекомендуется, потому что это может повлиять на такие команды, какmv
или,rm
и вы можете случайно удалить неправильные файлы.источник
**/*.*
) информативным и сработало лучше всего. Принятый ответ вызвал дублирование элементов в верхнем каталоге. Мой рабочий шаблон был:"${path}"**/*.*
$ find . -type f
Это перечислит все файлы в текущем каталоге. Затем вы можете выполнить другую команду на выходе, используя -exec
$find . -type f -exec grep "foo" {} \;
Это приведет к поиску строки «foo» в каждом найденном файле.
источник
find . -type f
указал , что применяется рекурсивно к корню в текущем каталоге, а не только к текущему каталогу.Почему бы просто не использовать расширение скобок, чтобы включить и текущий каталог?
Расширение скобок происходит до расширения глобуса, поэтому вы можете эффективно делать то, что хотите, со старыми версиями bash и можете отказаться от обезьян с помощью globstar в новых версиях.
Кроме того, в bash считается хорошей практикой включать ведущие
./
в свои шаблоны глобусов.источник