递归循环 bash 中的文件并通过命令操作每个文件
Recursively loop through files in bash and manipulate each file through a command
我想递归循环 bash 文件夹中的每个文件,并对它们进行某种简单的操作。比如修改权限,修改时间戳,用ImageMagick调整图片大小等等,你懂的。
我知道(像大多数初学者一样)如何在目录中执行此操作,但是递归...?
$ for f in *.jpg ; do convert $f -scale 25% resized/$f ; done
让我们保持简单。说,
$ for f in *; do touch $f; done
使用globstar
:
shopt -s globstar
for f in ./**; do
touch "$f"
done
来自Bash manual:
globstar
If set, the pattern ‘**
’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/
’, only directories and subdirectories match.
顺便说一句,always quote your variables, and use ./
in case of filenames that look like options。
这是基于codeforester's answer
当您说 bash 时,您的意思是在 bash 内还是仅使用常用命令行工具编写脚本?
一起编写脚本工具
find . -type f -exec chmod u+x {} \;
这意味着当前目录中的每个文件都可以执行。 \;
将 ;
传递给 find
命令,该命令将其解释为“分别调用每个找到的路径上的 exec 字符串”。您可以将 \;
替换为 \+
,这会告诉 find 首先收集所有路径并一次性将其替换为 {}
。通常 \+
可以更有效,但您必须小心命令行长度,因为有限制。然后你可以做的是将它与 xargs:
结合起来
find . -type f -print0 | xargs -0 -P $(nproc) -I{} chmod u+x {}
它的作用是告诉 find 使用空字符作为终止符而不是换行符。这可确保您正确处理每个条目,即使它具有任意空格或随机 UTF 字符([=20=]
不是路径的有效部分)。 xargs 的 -0
选项告诉它在读取参数而不是 newliens 时使用 [=20=]
作为分隔符。 -P
选项表示 运行 命令并行 N 次,在这种情况下,N 是 nproc
命令的输出,它打印处理器的数量。 -I
是替换字符串,其余是要处理的命令字符串。
find
和 xargs
的手册页值得探索。
本地 Bash
如果您正在寻找一个完全在 Bash 内并且没有外部工具的解决方案,它会稍微复杂一些并且会涉及一些更高级的 Bash 特定语言结构,其中你自己实现。要遍历目录的内容,您可以执行类似 for path in <dir>; do
的操作。然后你会使用内置测试 [[ -d "$path" ]]
来确定它是否是一个目录,[[ -f "$path" ]]
如果它是一个文件等(man test
有很多解释,但请注意这是独立的 test
可执行文件与功能更丰富、更安全的 bash 版本 [[ ]]
.
有细微差别和缺陷
使用 bash 个数组:https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_10_02.html
Bash测试介绍:https://www.tldp.org/LDP/abs/html/testconstructs.html
该测试介绍没有提到的是正则表达式之类的东西,它们将成为该语法的一部分。 Bash 还有强大的操作变量内容的选项:https://www.tldp.org/LDP/abs/html/parameter-substitution.html
但在实践中,即使是适度复杂的东西(无论是在 bash 中还是通过组合工具),在 Python 中可能会得到更好的维护并且更容易阅读(作为拥有大量知识的人来说) Bash).
方面的丰富经验
find_files() {
if [[ ! -x "" ]]; then
echo " isn't a directory" >&2
return 1
fi
local dirs=("")
while [[ "${#dirs[@]}" -gt 0 ]]; do
local dir="${dirs[0]}"
dirs=("${dirs[@]:1}") # pop the element from above
# whitespace in filenames iterated will be a problem. Look to the IFS
# variable to handle those more gracefully.
for p in "${dir}"/*; do
if [[ -d "$p" ]]; then
dirs+=("$p")
elif [[ -f "$p" ]]; then
echo "$p"
fi
done
done
}
for f in $(find_files .); do
chmod u+x "$f"
done
如您所见,这比仅使用 find/xargs 二进制文件更复杂、更棘手且更慢。在现实中你永远不会想写这样的东西。你甚至可以在将 find_files
转换为 process
的地方变得更有趣,方法是让它接受一个命令,然后在你通过 eval
迭代(而不是回显路径)时对其进行评估。 eval
非常棘手并且可能是一个安全漏洞。
我想递归循环 bash 文件夹中的每个文件,并对它们进行某种简单的操作。比如修改权限,修改时间戳,用ImageMagick调整图片大小等等,你懂的。
我知道(像大多数初学者一样)如何在目录中执行此操作,但是递归...?
$ for f in *.jpg ; do convert $f -scale 25% resized/$f ; done
让我们保持简单。说,
$ for f in *; do touch $f; done
使用globstar
:
shopt -s globstar
for f in ./**; do
touch "$f"
done
来自Bash manual:
globstar
If set, the pattern ‘
**
’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/
’, only directories and subdirectories match.
顺便说一句,always quote your variables, and use ./
in case of filenames that look like options。
这是基于codeforester's answer
当您说 bash 时,您的意思是在 bash 内还是仅使用常用命令行工具编写脚本?
一起编写脚本工具
find . -type f -exec chmod u+x {} \;
这意味着当前目录中的每个文件都可以执行。 \;
将 ;
传递给 find
命令,该命令将其解释为“分别调用每个找到的路径上的 exec 字符串”。您可以将 \;
替换为 \+
,这会告诉 find 首先收集所有路径并一次性将其替换为 {}
。通常 \+
可以更有效,但您必须小心命令行长度,因为有限制。然后你可以做的是将它与 xargs:
find . -type f -print0 | xargs -0 -P $(nproc) -I{} chmod u+x {}
它的作用是告诉 find 使用空字符作为终止符而不是换行符。这可确保您正确处理每个条目,即使它具有任意空格或随机 UTF 字符([=20=]
不是路径的有效部分)。 xargs 的 -0
选项告诉它在读取参数而不是 newliens 时使用 [=20=]
作为分隔符。 -P
选项表示 运行 命令并行 N 次,在这种情况下,N 是 nproc
命令的输出,它打印处理器的数量。 -I
是替换字符串,其余是要处理的命令字符串。
find
和 xargs
的手册页值得探索。
本地 Bash
如果您正在寻找一个完全在 Bash 内并且没有外部工具的解决方案,它会稍微复杂一些并且会涉及一些更高级的 Bash 特定语言结构,其中你自己实现。要遍历目录的内容,您可以执行类似 for path in <dir>; do
的操作。然后你会使用内置测试 [[ -d "$path" ]]
来确定它是否是一个目录,[[ -f "$path" ]]
如果它是一个文件等(man test
有很多解释,但请注意这是独立的 test
可执行文件与功能更丰富、更安全的 bash 版本 [[ ]]
.
使用 bash 个数组:https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_10_02.html Bash测试介绍:https://www.tldp.org/LDP/abs/html/testconstructs.html
该测试介绍没有提到的是正则表达式之类的东西,它们将成为该语法的一部分。 Bash 还有强大的操作变量内容的选项:https://www.tldp.org/LDP/abs/html/parameter-substitution.html
但在实践中,即使是适度复杂的东西(无论是在 bash 中还是通过组合工具),在 Python 中可能会得到更好的维护并且更容易阅读(作为拥有大量知识的人来说) Bash).
方面的丰富经验find_files() {
if [[ ! -x "" ]]; then
echo " isn't a directory" >&2
return 1
fi
local dirs=("")
while [[ "${#dirs[@]}" -gt 0 ]]; do
local dir="${dirs[0]}"
dirs=("${dirs[@]:1}") # pop the element from above
# whitespace in filenames iterated will be a problem. Look to the IFS
# variable to handle those more gracefully.
for p in "${dir}"/*; do
if [[ -d "$p" ]]; then
dirs+=("$p")
elif [[ -f "$p" ]]; then
echo "$p"
fi
done
done
}
for f in $(find_files .); do
chmod u+x "$f"
done
如您所见,这比仅使用 find/xargs 二进制文件更复杂、更棘手且更慢。在现实中你永远不会想写这样的东西。你甚至可以在将 find_files
转换为 process
的地方变得更有趣,方法是让它接受一个命令,然后在你通过 eval
迭代(而不是回显路径)时对其进行评估。 eval
非常棘手并且可能是一个安全漏洞。