如何去每个目录执行一个命令?

Posted

技术标签:

【中文标题】如何去每个目录执行一个命令?【英文标题】:How to go to each directory and execute a command? 【发布时间】:2011-11-20 04:10:49 【问题描述】:

如何编写一个 bash 脚本,该脚本遍历 parent_directory 中的每个目录并执行 每个目录中的命令

目录结构如下:

parent_directory(名称可以是任何东西 - 不遵循模式)

001(目录名称遵循此模式) 0001.txt(文件名遵循此模式) 0002.txt 0003.txt 002 0001.txt 0002.txt 0003.txt 0004.txt 003 0001.txt

目录数量未知。

【问题讨论】:

【参考方案1】:

Todd 发布的这个 answer 帮助了我。

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' \;,因为一些目录包含单引号和一些双引号。 你也可以通过添加-mindepth 1省略当前目录 如何将其更改为函数接受类似“git remote get-url origin`而不是直接pwd的命令? disd()find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c 'cd "" && "$@"' \; 不起作用。【参考方案2】:

当您的当前目录为parent_directory时,您可以执行以下操作:

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

() 创建了一个子shell,因此在主脚本中当前目录不会改变。

【讨论】:

这里没关系,因为通配符只匹配数字,但在一般情况下,您基本上总是希望将目录名称放在循环内的双引号中。 cd "$d" 会更好,因为它会转移到通配符匹配名称包含空格和/或 shell 元字符的文件的情况。 (这里的所有答案似乎都有相同的缺陷,但在投票最多的答案中最重要。) 此外,绝大多数命令实际上并不关心您在哪个目录中执行它们。 for f in foo bar; do cat "$f"/*.txt; done >output 在功能上等同于 cat foo/*.txt bar/*.txt >output。但是,ls 是一个命令,它会根据您传递参数的方式产生略微不同的输出;同样,如果您从子目录运行它,输出相对文件名的grepwc 会有所不同(但通常,您希望避免进入子目录正是出于这个原因)。【参考方案3】:

如果你使用 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)

或者不推荐的方式(涉及parsing of ls):

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

上面的例子会忽略当前的目录(根据 OP 的要求),但它会用空格中断名称。

另见:

Bash: for each directory 在 SO How to enter every directory in current path and execute script? 在 SE Ubuntu

【讨论】:

在不使用显式循环的情况下将find 的输出放入数组中的唯一正确方法将在带有mapfile -t -d '' dirs &lt; &lt;(find . -type d -print0) 的Bash 4.4 中可用。在此之前,您唯一的选择是使用循环(或将操作推迟到find)。 其实你并不需要dirs数组;你可以像这样循环find的输出(安全地):find . -type d -print0 | while IFS= read -r -d '' file; do ...; done @gniourf_gniourf 我很直观,总是试图找到/制作看起来简单的东西。例如。我有this script 并且使用 bash 数组对我来说很容易且可读(通过将逻辑分成更小的部分,而不是使用管道)。引入额外的管道,IFSread,它给人的印象是额外的复杂性。我得考虑一下,也许有更简单的方法。 如果 easy 你的意思是 broken 那么是的,有更简单的方法。现在不用担心,所有这些 IFSread(如您所说)在您习惯它们时会成为第二天性……它们是编写脚本的规范和惯用方式的一部分。 顺便说一句,你的第一个命令 find . -type d -execdir echo $(pwd)/ ';' 没有做你想做的事情($(pwd)find 甚至被执行之前被扩展)......【参考方案4】:

您可以通过管道然后使用xargs 来实现此目的。问题是您需要使用-I 标志,它将用每个xargs 传递的子字符串替换您的bash 命令中的子字符串。

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

你可能想用你想在每个目录中执行的任何命令替换pwd

【讨论】:

我不知道为什么这没有被赞成,但我找到了迄今为​​止最简单的脚本。谢谢【参考方案5】:

如果知道顶层文件夹,你可以这样写:

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”。

干杯,

【讨论】:

这里有几个"Useless Use of ls" 的实例——你可以改用for dir in $YOUR_TOP_LEVEL_FOLDER/* 好吧,也许它在一般意义上没有用,但它允许直接在 ls 中进行过滤(即所有以 .tmp 结尾的目录)。这就是我使用ls $dir 表达式的原因 for subdir in 'ls $YOUR_TOP_LEVEL_FOLDER/$dir';会像parent/child1/child1_1/child1_1_1/一样遍历嵌套的多个目录吗?或者只是父目录深处的一个目录?【参考方案6】:
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done

【讨论】:

这很容易理解!【参考方案7】:

虽然一个衬里有利于快速和肮脏的使用,但我更喜欢下面更详细的版本来编写脚本。这是我使用的模板,它处理了许多边缘情况,并允许您编写更复杂的代码以在文件夹上执行。您可以在函数 dir_command 中编写 bash 代码。下面以 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"

【讨论】:

【参考方案8】:

我不明白文件的格式,因为您只想遍历文件夹...您在寻找这样的东西吗?

cd parent
find . -type d | while read d; do
   ls $d/
done

【讨论】:

你应该在 find 中添加-maxdepth 1 -mindepth 1 否则它也会获取所有子目录!【参考方案9】:

你可以使用

find .

递归搜索当前目录中的所有文件/目录

你可以像这样通过管道输出 xargs 命令

find . | xargs 'command here'

【讨论】:

这不是被问到的。【参考方案10】:
  #!/bin.bash
for folder_to_go in $(find . -mindepth 1 -maxdepth 1 -type d \( -name "*" \) ) ; 
                                    # you can add pattern insted of * , here it goes to any folder 
                                    #-mindepth / maxdepth 1 means one folder depth   
do
cd $folder_to_go
  echo $folder_to_go "########################################## "
  
  whatever you want to do is here

cd ../ # if maxdepth/mindepath = 2,  cd ../../
done

#you can try adding many internal for loops with many patterns, this will sneak anywhere you want

【讨论】:

【参考方案11】:
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

【讨论】:

你需要重新备份目录,或者在子shell中执行cd 投反对票:编辑丢失了cd,所以这段代码实际上并没有做任何有用的事情。【参考方案12】:

您可以在每个文件夹中的 1 行中运行一系列命令,例如:

for d in PARENT_FOLDER/*; do (cd "$d" && tar -cvzf $d.tar.gz *.*)); done

【讨论】:

以上是关于如何去每个目录执行一个命令?的主要内容,如果未能解决你的问题,请参考以下文章

如何对目录中的所有文件执行grep操作?

linux脚本中执行命令

shell多个脚本怎么并发执行

如何使用批处理脚本对目录中的每个文件执行某些操作

如何创建一个 Git 仓库

linux加载配置文件命令是啥