Я работаю над сценарием, который должен выполнить действие в каждом подкаталоге определенной папки.
Какой самый эффективный способ написать это?
bash
command
directory-traversal
mikewilliamson
источник
источник
mkdir 'foo * bar'
вызоветfoo
иbar
будет итерация , даже если их не существует, и*
они будут заменены списком всех имен файлов, даже не каталогов).mkdir -p '/tmp/ /etc/passwd /'
то запустится - если кто-то запустит скрипт, следуя этой практике/tmp
, скажем, для поиска каталогов для удаления, он может в конечном итоге удалить/etc/passwd
.Ответы:
источник
find
параметры, если вы хотите рекурсивное или нерекурсивное поведение.find . -mindepth 1 -type d
mkdir 'directory name with spaces'
на четыре отдельных слова.-mindepth 1 -maxdepth 1
или это зашло слишком глубоко.Версия, которая избегает создания подпроцесса:
Или, если ваше действие - одна команда, это более кратко:
Или еще более краткая версия ( спасибо @enzotib ). Обратите внимание, что в этой версии каждое значение
D
будет иметь косую черту:источник
if
или[
с:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
или комбинацию из двух последних:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
вместо этогоfor D in *; do
.Самый простой нерекурсивный способ:
В
/
конце говорит, использовать только каталоги.Там нет необходимости
источник
shopt -s dotglob
для добавления файлов точек / точек при расширении подстановочных знаков. См. Также: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html/*
а не*/
с ,/
представляющая путь , который вы хотите использовать./*
был бы для абсолютного пути, тогда как*/
включал бы подкаталоги от текущего местоположения${d%/*}
Используйте
find
команду.В GNU
find
вы можете использовать-execdir
параметр:или с помощью
-exec
параметра:или с помощью
xargs
команды:Или используя для цикла:
Для рекурсивности попробуйте
**/
вместо этого использовать расширенную функцию globbing ( ) (включить с помощью :)shopt -s extglob
.Дополнительные примеры см .: Как перейти в каждый каталог и выполнить команду? в СО
источник
-exec {} +
указан в POSIX,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
является еще одним допустимым вариантом и вызывает меньше оболочек, чем-exec sh -c '...' {} \;
.Удобные однострочные
Вариант A подходит для папок с пробелами между ними. Кроме того, как правило, быстрее, поскольку он не печатает каждое слово в имени папки как отдельный объект.
источник
find . -type d -print0 | xargs -0 -n 1 my_command
источник
my_command
.Это создаст подоболочку (что означает, что значения переменных будут потеряны при
while
выходе из цикла):Это не будет:
Любой из них будет работать, если в именах каталогов есть пробелы.
источник
find ... -print0
иwhile IFS="" read -r -d $'\000' dir
-d ''
он меньше вводит в заблуждение относительно синтаксиса и возможностей bash, поскольку-d $'\000'
подразумевает (ложно), что$'\000'
каким-то образом отличается от''
- действительно, можно легко (и снова ошибочно) сделать вывод из то, что bash поддерживает строки в стиле Pascal (с указанием длины, может содержать литералы NUL), а не строки C (с разделителями NUL, не может содержать NUL).Вы можете попробовать:
или похожие...
Объяснение:
find . -maxdepth 1 -mindepth 1 -type d
: Найти только каталоги с максимальной рекурсивной глубиной 1 (только подкаталоги $ 1) и минимальной глубиной 1 (исключая текущую папку.
)источник
cd "$f"
. Он не работает, когда вывод изfind
строки разбит на строки, поэтому у вас будут отдельные части имени в виде отдельных значений$f
, определяющих, насколько хорошо вы делаете или не заключаете в кавычки$f
расширение.find
Выходные данные ориентированы на строку (теоретически одно имя на строку, но см. ниже) с-print
действием по умолчанию .find -print
не является безопасным способом передачи произвольных имен файлов, так как можно что-то запуститьmkdir -p $'foo\n/etc/passwd\nbar'
и получить каталог,/etc/passwd
в имени которого есть отдельная строка. Обработка имен из файлов/upload
или/tmp
каталогов без заботы - отличный способ получить атаки на повышение привилегий.принятый ответ будет разбит на пробелы, если имена каталогов имеют их, и предпочтительный синтаксис
$()
для bash / ksh. ИспользуйтеGNU find
-exec
опцию с,+;
например,find .... -exec mycommand +;
#this is same as passing to xargs
или используйте цикл while
источник
find -exec
передачей и передачейxargs
:find
игнорирует значение выхода выполняемой команды, но приxargs
ненулевом выходе завершится неудачей. Любой из них может быть правильным, в зависимости от ваших потребностей.find ... -print0 | while IFS= read -r d
безопаснее - поддерживает имена, начинающиеся или оканчивающиеся пробелом, и имена, содержащие литералы новой строки.