简单的文件循环 - “文件名太长”

简单的文件循环 - “文件名太长”

我正在尝试做最基本的事情:对特定文件夹中的所有文件执行一个或多个命令。

在本例中,它使用 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 文件。

** 我已经尝试注释掉echopstopdf命令,所以我知道每个命令都会生成一个文件名列表。

如果我pstopdf <file.eps>在终端中运行,我不会在 CLI 中得到任何输出(例如,没有列出文件名),但文件会被处理并创建一个 PDF 文件。

我敢说我可以xargsfind命令中使用,尽管我更喜欢带有参数的循环的结构化方法,尤其是因为它提供了多个命令和其他逻辑的选项,并且更易于阅读。

这里有一个类似的问题:运行 for..in 并触摸时收到消息“文件名太长”

但我不明白答案如何适用。它说“或一一触摸它们”(这就是我想要的),但如果我这样做:

FILES=/Users/Ben/Pictures/Stock\ Illustrations/*

我只是得到“没有这样的文件或目录”。

答案1

FILES=$(find "/Users/Ben/Pictures/Stock Illustrations" -type f)
for f in "$FILES"

正如链接的帖子中所示,$FILES这里是一个包含所有文件名的字符串。如果文件足够多,单个文件名就太长了。如果只有几个名称,您将尝试访问名称中嵌入换行符的文件。

在这里,你最好打电话findpstopf自己,例如

$ 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因为zshglob 可以执行相同的操作:

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[@]}"循环遍历所有元素,但这里没有什么区别,因为保证数组不包含空元素。

相关内容