Как перейти в каждый каталог и выполнить команду? - PullRequest
121 голосов
/ 19 сентября 2011

Как мне написать bash-скрипт, который проходит через каждый каталог в parent_directory и выполняет a команду в каждом каталоге .

Структура каталогов следующая:

parent_directory (имя может быть любым - не следует шаблону)

  • 001 (имена каталогов следуют этому шаблону)
    • 0001.txt (имена файлов следуют этому шаблону)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

количество каталогов неизвестно.

Ответы [ 10 ]

135 голосов
/ 30 ноября 2013

Этот ответ от Тодд помог мне.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

\( ! -name . \) избегает выполнения команды в текущем каталоге.

93 голосов
/ 19 сентября 2011

Вы можете сделать следующее, если текущим каталогом является parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

( и ) создают подоболочку, поэтому текущий каталог не изменяется в основном скрипте.

41 голосов
/ 22 октября 2014

Если вы используете GNU find, вы можете попробовать -execdir параметр, например ::

find . -type d -execdir realpath "{}" ';'

или (согласно @ gniourf_gniourf comment ):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

Примечание: Вы можете использовать ${0#./} вместо $0, чтобы зафиксировать ./ спереди.

или более практический пример:

find . -name .git -type d -execdir git pull -v ';'

Если вы хотите включить текущий каталог, с помощью -exec:

еще проще
find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

или используя xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Или аналогичный пример, предложенный @ gniourf_gniourf :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

Приведенные выше примеры поддерживают каталоги с пробелами в имени.


Или присваивая массиву bash:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

Измените . на свое имя папки. Если вам не нужно запускать рекурсивно, вы можете использовать: dirs=(*) вместо этого. Приведенный выше пример не поддерживает каталоги с пробелами в имени.

Таким образом, как предложено @ gniourf_gniourf , единственный правильный способ поместить вывод find в массив без использования явного цикла будет доступен в Bash 4.4 с:

mapfile -t -d '' dirs < <(find . -type d -print0)

Или не рекомендуется (который включает разбор ls):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

В приведенном выше примере будет игнорироваться текущий каталог (по запросу OP), но он будет разбиваться на имена с пробелами.

Смотри также:

19 голосов
/ 19 сентября 2011

Если папка верхнего уровня известна, вы можете просто написать что-то вроде этого:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

на $ (ИГРАЙТЕ МНОГО, ЧТО ВЫ ХОТИТЕ);Вы можете поместить столько кода, сколько хотите.

Обратите внимание, что я не "cd" ни в одном каталоге.

Cheers,

10 голосов
/ 22 января 2019

Вы можете достичь этого, используя трубопровод, а затем используя xargs. Уловка в том, что вам нужно использовать флаг -I, который заменит подстроку в вашей команде bash на подстроку, переданную каждым из xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

Вы можете заменить pwd любой командой, которую хотите выполнить в каждом каталоге.

10 голосов
/ 21 сентября 2011
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done
6 голосов
/ 02 июня 2016

В то время как один лайнер хорош для быстрого и грязного использования, я предпочитаю более подробную версию для написания скриптов.Это шаблон, который я использую, который заботится о многих крайних случаях и позволяет вам писать более сложный код для выполнения в папке.Вы можете написать свой код bash в функции dir_command.Ниже dir_coomand реализует тегирование каждого репозитория в git в качестве примера.Остальная часть скрипта вызывает dir_command для каждой папки в каталоге.Пример итерации только по заданному набору папок также включает.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"
4 голосов
/ 19 сентября 2011

вы можете использовать

find .

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

Чем вы можете передать вывод команды xargs следующим образом:

find . | xargs 'command here'
3 голосов
/ 19 сентября 2011

Я не понимаю смысла в форматировании файла, поскольку вы хотите перебирать только папки ... Вы ищете что-то подобное?

0 голосов
/ 19 сентября 2011
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...