ChatGPT解决这个技术问题 Extra ChatGPT

使用 Bash 在每个子目录中执行操作

我正在编写一个需要在特定文件夹的每个子目录中执行操作的脚本。

写那个最有效的方法是什么?

请考虑返回并重新评估答案的正确性 - 尽管存在重大错误,但您已经获得了一个已接受的答案,获得了很多视图(f/e,在以前有人运行 mkdir 'foo * bar' 的目录上运行它会导致 foobar 即使它们不存在也会被迭代,并且 * 将被替换为 all 文件名列表,甚至是非目录文件名)。
...更糟糕的是,如果有人运行 mkdir -p '/tmp/ /etc/passwd /' - 如果有人在 /tmp 上按照这种做法运行脚本以查找要删除的目录,他们最终可能会删除 /etc/passwd

C
Community

避免创建子流程的版本:

for D in *; do
    if [ -d "${D}" ]; then
        echo "${D}"   # your processing here
    fi
done

或者,如果您的操作是单个命令,则更简洁:

for D in *; do [ -d "${D}" ] && my_command; done

或者更简洁的版本 (thanks @enzotib)。请注意,在此版本中,D 的每个值都将有一个尾部斜杠:

for D in */; do my_command; done

您可以通过以下方式避免 if[for D in */; do
+1,因为目录名称不以 ./ 开头,而不是接受的答案
即使目录名称中的空格+1,这个也是正确的
最后一个命令有一个问题:如果你在一个没有子目录的目录中;它将返回“*/”。所以最好使用第二个命令 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
M
Mike Clark
for D in `find . -type d`
do
    //Do whatever you need with D
done

这将打破空白
另外,请注意,如果您想要递归或非递归行为,则需要调整 find 参数。
上面的答案也给了我自己的目录,所以以下对我来说效果更好:find . -mindepth 1 -type d
@JoshC,这两种变体都会将 mkdir 'directory name with spaces' 创建的目录分成四个单独的单词。
我需要添加 -mindepth 1 -maxdepth 1 否则它太深了。
d
d0x

最简单的非递归方式是:

for d in */; do
    echo "$d"
done

最后的 / 告诉我们,只使用目录。

没有必要

寻找

awk

...


注意:这不包括点目录(这可能是一件好事,但重要的是要知道)。
需要注意的是,您可以在扩展通配符时使用 shopt -s dotglob 来包含点文件/点目录。另请参阅:gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
我认为您的意思是 /* 而不是 */,其中 / 代表您要使用的路径。
@Shule /* 用于绝对路径,而 */ 将包含当前位置的子目录
有用的提示:如果您需要从 $d 中删除尾随的“/”,请使用 ${d%/*}
C
Community

使用查找命令。

在 GNU find 中,您可以使用 -execdir 参数:

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

或使用 -exec 参数:

find . -type d -exec sh -c 'cd -P "$0" && pwd -P' {} \;

或使用 xargs 命令:

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

或使用 for 循环:

for d in */; { echo "$d"; }

对于递归性,请尝试扩展通配符 (**/)(通过:shopt -s extglob 启用)。

有关更多示例,请参阅:How to go to each directory and execute a command? 在 SO


-exec {} + 是 POSIX 指定的,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} + 是另一个合法选项,并且调用的 shell 少于 -exec sh -c '...' {} \;
S
Sriram Murali

方便的单线

for D in *; do echo "$D"; done
for D in *; do find "$D" -type d; done ### Option A

find * -type d ### Option B

选项 A 对于中间有空格的文件夹是正确的。此外,通常更快,因为它不会将文件夹名称中的每个单词作为单独的实体打印。

# Option A
$ time for D in ./big_dir/*; do find "$D" -type d > /dev/null; done
real    0m0.327s
user    0m0.084s
sys     0m0.236s

# Option B
$ time for D in `find ./big_dir/* -type d`; do echo "$D" > /dev/null; done
real    0m0.787s
user    0m0.484s
sys     0m0.308s

P
Paul Tomblin

find . -type d -print0 | xargs -0 -n 1 my_command


我可以将该 find 的返回值输入 for 循环吗?这是更大脚本的一部分...
@Mike,不太可能。美元?可能会为您提供 find 或 xargs 命令的状态,而不是 my_command
D
Dennis Williamson

这将创建一个子 shell(这意味着当 while 循环退出时变量值将丢失):

find . -type d | while read -r dir
do
    something
done

这不会:

while read -r dir
do
    something
done < <(find . -type d)

如果目录名称中有空格,则任何一个都可以使用。


为了更好地处理奇怪的文件名(包括以空格结尾和/或包含换行符的名称),请使用 find ... -print0while IFS="" read -r -d $'\000' dir
@GordonDavisson,...确实,我什至认为 -d '' 对 bash 语法和功能的误导性较小,因为 -d $'\000' 暗示(错误地)$'\000' 在某种程度上不同于 '' - 确实,人们可以很容易地(并且再次错误地)从中推断出 bash 支持 Pascal 样式的字符串(指定长度,能够包含 NUL 文字)而不是 C 字符串(NUL 分隔,无法包含 NUL)。
l
leesei

你可以试试:

#!/bin/bash
### $1 == the first args to this script
### usage: script.sh /path/to/dir/

for f in `find . -maxdepth 1 -mindepth 1 -type d`; do
  cd "$f"
  <your job here>
done

或类似的...

解释:

find . -maxdepth 1 -mindepth 1 -type d:只查找最大递归深度为 1(仅 $1 的子目录)和最小深度为 1(不包括当前文件夹 .)的目录


这是错误的 - 尝试使用带有空格的目录名称。请参阅 BashPitfalls #1DontReadLinesWithFor
带空格的目录名称用引号引起来,因此可以工作,并且 OP 不会尝试从文件中读取行。
它适用于 cd "$f"。当 find 的输出是字符串拆分时,它不起作用,因此您将在 $f 中将名称的单独部分作为单独的值,从而确定您是否引用 $f' s 扩张辩论。
我没有说他们正在试图从文件中读取行。 find 的输出是面向行的(理论上一个名称对应一行,但请参见下文),默认操作为 -print
find -print 开始的面向行的输出不是传递任意文件名的安全方式,因为可以运行 mkdir -p $'foo\n/etc/passwd\nbar' 并获得一个目录,其中包含 /etc/passwd 作为单独的行姓名。不小心处理 /upload/tmp 目录中文件的名称是获得权限提升攻击的好方法。
D
Danny Varod

如果目录名称中有空格,则接受的答案将在空格处中断,并且 bash/ksh 的首选语法是 $()。将 GNU find -exec 选项与 +; 一起使用,例如

find .... -exec mycommand +; #this is same as passing to xargs

或使用 while 循环

find .... | while read -r D
do
    # use variable `D` or whatever variable name you defined instead here
done 

什么参数将保存目录名称?例如,chmod +x $DIR_NAME(是的,我知道只有目录有一个 chmod 选项)
find -exec 和传递给 xargs 之间有一个细微的区别:find 将忽略正在执行的命令的退出值,而 xargs 将在非零退出时失败。两者都可能是正确的,这取决于您的需要。
find ... -print0 | while IFS= read -r d 更安全 - 支持以空格开头或结尾的名称,以及包含换行文字的名称。