Как написать bash script, который проходит через каждый каталог внутри родительского_каталога, и выполняет команду в каждой директории.
Структура каталогов выглядит следующим образом:
parent_directory (имя может быть любым - не следует шаблону)
- 001 (имена каталогов следуют этому шаблону)
- 0001.txt(имена файлов следуют этому шаблону)
- 0002.txt
- 0003.txt
- 002
- 0001.txt
- 0002.txt
- 0003.txt
- 0004.txt
- 003
- 0001.txt
количество каталогов неизвестно.
Вы можете сделать следующее, когда вашим текущим каталогом является parent_directory
:
for d in [0-9][0-9][0-9]
do
( cd "$d" && your-command-here )
done
(
И )
создает подоболочку, поэтому текущий каталог не изменяется в основном скрипте.
Этот ответ, отправленный Todd, помог мне.
find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;
\( ! -name . \)
избегает выполнения команды в текущем каталоге.
find FOLDER* -maxdepth 0 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;
-exec bash -c 'cd "$0" && pwd' {} \;
поскольку некоторые каталоги содержали одинарные кавычки и некоторые двойные кавычки.
Если вы используете GNU find
, вы можете попробовать параметр -execdir
, например:
find . -type d -execdir realpath "{}" ';'
или (согласно комментарий @gniourf_gniourf):
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), но он будет разбивать имена на пробелы.
См. также:
find
в массив без использования явного цикла будет доступен в Bash 4.4 с mapfile -t -d '' dirs < <(find . -type d -print0)
. До тех пор ваш единственный вариант - использовать цикл (или отложить операцию, чтобы find
).
dirs
; Вы можете циклически обработать вывод find
(безопасно) следующим образом: find . -type d -print0 | while IFS= read -r -d '' file; do ...; done
Если папка верхнего уровня известна, вы можете просто написать что-то вроде этого:
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" в любом каталоге.
Приветствия,
ls
» - вместо этого вы можете просто сделать for dir in $YOUR_TOP_LEVEL_FOLDER/*
.
ls $dir
for dir in PARENT/*
do
test -d "$dir" || continue
# Do something with $dir...
done
В то время как один лайнер хорош для быстрого и грязного использования, я предпочитаю ниже более подробные версии для написания сценариев. Это шаблон, который я использую, который заботится о многих случаях и позволяет писать более сложный код для выполнения в папке. Вы можете написать свой bash код в функции dir_command. Ниже dir_coomand реализует пометку каждого репозитория в git в качестве примера. Остальная часть script вызывает команду 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"
Я не понимаю смысла в создании файла, так как вы хотите только итерации по папкам... Вы ищете что-то вроде этого?
cd parent
find . -type d | while read d; do
ls $d/
done
вы можете использовать
find .
для поиска всех файлов /dirs в текущей рекурсивной директории
Чем вы можете вывести на выходе команду xargs так
find . | xargs 'command here'
Вы можете добиться этого, используя трубопровод и затем используя xargs
. -I
что вам нужно использовать флаг -I
который заменит подстроку в вашей команде bash на подстроку, переданную каждым из xargs
.
ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"
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
cd
в подоболочке.
cd "$d"
было бы лучше в том смысле, что он переносится в ситуации, когда подстановочный знак совпадает с файлами, имена которых содержат пробелы и / или метасимволы оболочки.