![简单的文件循环 - “文件名太长”](https://linux22.com/image/210026/%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%87%E4%BB%B6%E5%BE%AA%E7%8E%AF%20-%20%E2%80%9C%E6%96%87%E4%BB%B6%E5%90%8D%E5%A4%AA%E9%95%BF%E2%80%9D.png)
我正在尝试做最基本的事情:对特定文件夹中的所有文件执行一个或多个命令。
在本例中,它使用 macOS 的pstopdf
命令将 eps 文件转换为 PDF。
#!/bin/zsh
FILES=$(find "/Users/Ben/Pictures/Stock Illustrations" -type f)
for f in "$FILES"
do
echo "$f"
pstopdf "$f"
done
echo "$f"
生成所有文件的正确列表;但随后我得到了第二个文件列表——看似来自pstopdf
其本身**——以 开头File name too long: /Users/Ben/Pictures/Stock Illustrations/Flock wallpaper.eps
,但其余文件都正确列出。
但是,该pstopdf
命令不会创建任何 PDF 文件。
** 我已经尝试注释掉echo
和pstopdf
命令,所以我知道每个命令都会生成一个文件名列表。
如果我pstopdf <file.eps>
在终端中运行,我不会在 CLI 中得到任何输出(例如,没有列出文件名),但文件会被处理并创建一个 PDF 文件。
我敢说我可以xargs
在find
命令中使用,尽管我更喜欢带有参数的循环的结构化方法,尤其是因为它提供了多个命令和其他逻辑的选项,并且更易于阅读。
这里有一个类似的问题:运行 for..in 并触摸时收到消息“文件名太长”
但我不明白答案如何适用。它说“或一一触摸它们”(这就是我想要的),但如果我这样做:
FILES=/Users/Ben/Pictures/Stock\ Illustrations/*
我只是得到“没有这样的文件或目录”。
答案1
FILES=$(find "/Users/Ben/Pictures/Stock Illustrations" -type f) for f in "$FILES"
正如链接的帖子中所示,$FILES
这里是一个包含所有文件名的字符串。如果文件足够多,单个文件名就太长了。如果只有几个名称,您将尝试访问名称中嵌入换行符的文件。
在这里,你最好打电话find
给pstopf
自己,例如
$ find "/Users/Ben/Pictures/Stock Illustrations" -type f -print -exec pstopdf {} \;
如果你想在 find 的输出上使用 shell 循环,你必须这样做(在 Bash 中,我没有提到 zsh ):
set -f # disable globbing
IFS=$'\n' # set IFS to just the newline (Bash/ksh/zsh, not POSIX)
files=$(find "/Users/Ben/Pictures/Stock Illustrations" -type f)
for f in $files; do
echo "$f"
pstopdf "$f"
done
或者find ... -print0 | while IFS= read -r -d '' f; do ...; done
在 Bash 中使用。
或者for f in "/Users/Ben/Pictures/Stock Illustrations"/**/*; do ...
在 Bash(使用shopt -s globstar
)、ksh 或 zsh 中使用(shell 之间的细节存在一些差异。
两种 shell 解决方案都会遇到包含换行符的文件名问题,我希望您没有换行符,但遗憾的是允许使用换行符。
答案2
FILES=$(find "/Users/Ben/Pictures/Stock Illustrations" -type f)
是标量变量赋值的语法(在 zsh 或任何其他类似 Korn 的 shell 中),因此您得到的 a$FILES
只有一个值,即 的整个输出find
。
在这里,您宁愿想要:
files=( ${(0)"$(find "/Users/Ben/Pictures/Stock Illustrations" -type f -print0)"} )
如果您想$files
成为一个数组,其元素是 找到的每个文件find
。您需要find
提供以 0 分隔的列表,并0
使用参数扩展标志将其拆分为 s 0
,因为这是唯一不能出现在文件路径中的字节值。
但在这里,您不需要,find
因为zsh
glob 可以执行相同的操作:
files=( "/Users/Ben/Pictures/Stock Illustrations"/**/*(ND.) )
递归**/*
通配符 and.
相当于-type f
.
要循环它们,您需要:
for f in $files; do
print -r -- $f
pstopdf $f
done
或者:
for f ($files) {
print -r -- $f
pstopdf $f
}
"$files"
会将数组的元素与第一个字符$IFS
(与 ksh 中相同"${files[*]}"
)连接起来,所以这不是您想要的。在 zsh 中使用$files
不带引号的循环遍历数组的非空元素。"${files[@]}"
循环遍历所有元素,但这里没有什么区别,因为保证数组不包含空元素。