перейдите во все каталоги, выполните команду для файлов в этом каталоге и вернитесь в предыдущий текущий каталог

41

Я пытаюсь написать скрипт, который будет запускаться в заданном каталоге со многими одноуровневыми подкаталогами. Сценарий перейдет в каждый из подкаталогов, выполнит команду для файлов в каталоге и перейдет в следующий каталог. Каков наилучший способ сделать это?

Что-то джонс
источник
1
Учитывая текущий уровень предоставленной информации, я не вижу никакого отношения к youtube-dl.
HalosGhost

Ответы:

83
for d in ./*/ ; do (cd "$d" && somecommand); done
psusi
источник
12
Поэтому, так как ответчик пропустил какое-либо объяснение, я попытаюсь один. for d in ./*/запускает цикл, который хранит каждый элемент в./*/ (список файлов / папок, в данном случае) хранится в переменной $d. do (cd "$d" && somecommand);запускает тело цикла. Внутри тела, он начинает подоболочку и бежит cdи somecommandкоманду. Поскольку это дочерняя оболочка, родительская оболочка (оболочка, из которой вы запускаете эту команду) сохраняет свои CWD и другие переменные среды. doneпросто закрывает тело цикла
Qix
этот метод работает для подкаталогов каталогов:, for d in ./*/ ; do (cd "$d" && ls); doneне будет работать. но for d in ./*/ ; do (cd "$d" && for d in ./*/ ; do (cd "$d" && ls); done ); doneбудет работать. -использование ls в качестве команды в этом примере.
Майкл Диммитт
-bash: cd: ./*/: No such file or directory
С. Тарык Четин
15

Лучший способ - вообще не использовать cd:

find some/dir -type f -execdir somecommand {} \;

execdirвроде exec, но рабочий каталог другой:

   -execdir command {} [;|+]
          Like   -exec,   but  the  specified  command  is  run  from  the
          subdirectory containing the matched file, which is not  normally
          the  directory  in  which  you  started  find.  This a much more
          secure  method  for  invoking  commands,  as  it   avoids   race
          conditions  during resolution of the paths to the matched files.

Это не POSIX.

Мур
источник
Это работает с псевдонимами? У меня есть один для загрузки определенных файлов, но он не распознает его, когда я набираю find * /. Link -type f -execdir md $ (cat .link) {} \;
Что-то Джонс
@SomethingJones нет, findвыполняет эти команды, поэтому он не будет знать о псевдонимах. Что mdи является .linkкаталогом?
Муру
.link - это текстовый файл с URL-адресом, который необходимо загрузить. md - это псевдоним wget с набором флагов. Есть ли способ сделать так, чтобы он знал об псевдонимах?
Что-то Джонс
@SomethingJones Для вашего конкретного варианта использования, в bash: find . -type f -iname '*.link' -execdir ${BASH_ALIASES[md]} -i {} \;Вам не нужно делать catс этим wget, у которого есть -iфлаг для чтения в URL из файла. Кроме того, это несколько отличается от вашего первоначального вопроса (поскольку вас интересуют только файлы с именами, .linkа не любые другие файлы, которые могут присутствовать).
Муру
Вы знаете, как это сделать с Zsh? Я попробовал то, что вы мне дали, и получаю ошибку "Плохая замена". Кроме того, как я могу извлечь содержимое файла .link? Я знаю, что мне это не нужно в этом случае, но я думаю, что скоро буду.
Что-то Джонс
2
cd -P .
for dir in ./*/
do cd -P "$dir" ||continue
   printf %s\\n "$PWD" >&2
   command && cd "$OLDPWD" || 
! break; done || ! cd - >&2

Приведенная выше команда не нуждается в каких-либо подоболочках - она ​​просто отслеживает свой прогресс в текущей оболочке, чередуя $OLDPWDи $PWD. Когда вы cd -обмениваетесь значения этих двух переменных, в основном, как он меняет каталоги. Он также печатает имя для каждого каталога, так как он работает там в stderr.

Я только что взглянул на это и решил, что смогу лучше справиться с обработкой ошибок. Он пропустит каталог, в который он не может cd- и cdнапечатает сообщение о том, почему stderr - и будет breakиметь ненулевой код выхода, если ваш commandне выполняется успешно или если запуск commandкаким- либо образом влияет на его способность вернуться в исходный каталог -$OLDPWD . В этом случае он также делает cd -последний - и записывает полученное имя текущего рабочего каталога в stderr.

mikeserv
источник
1
for D in ./*; do
    if [ -d "$D" ]; then
        cd "$D"
        run_something
        cd ..
    fi
done
Кирилл
источник