Exec 允许我们一次性传递所有参数,{} +
或者使用 逐个传递它们{} \;
现在假设我想重命名所有JPEG,这样做没有问题:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;
但如果我需要重定向输出,则'{}'
重定向后无法访问。
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;
这是行不通的。我必须使用 for 循环,在使用 find 之前将其输出存储到变量中。我们承认吧,这很麻烦。
for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;
那么有没有更好的办法呢?
答案1
您可以在命令bash -c
中使用find -exec
并将位置参数与 bash 命令一起使用:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec bash -c 'cjpeg -quality 80 "$1" > "$(dirname "$1")/optimized_$(basename "$1")"' sh {} \;
{}
提供了这种方式$1
。
sh
在告诉内壳它的“名称”之前,{}
这里使用的字符串用于例如错误消息。这在中进行了更多讨论stackoverflow 上的这个答案。
答案2
你有一个答案(https://unix.stackexchange.com/a/481687/4778),但这就是原因。
重定向>
、管道|
和$
扩展都是由 shell 在执行命令之前完成的。因此,stdout 在启动optimized_{}
之前被重定向到, 。find
答案3
需要引用重定向以避免当前 shell 解释它。
但引用它也会避免命令的输出被重定向。
已知的解决方案是调用 shell:
find . -name '*.jpg' -exec sh -c 'echo "$1" >"$1".new' called_shell '{}' \;
在这种情况下,重定向 ( >
) 在当前 shell 上被引用,并在被调用的 shell 内正常工作。用作子 shell ( ) 的参数(名称called_shell
)。$0
sh
如果在文件名中添加后缀,则效果很好,但如果使用前缀,则效果不佳。要使前缀起作用,您需要删除./
文件名前面的 find${1#./}
并使用该-execdir
选项。
您可能(或可能不)想要使用该-iname
选项,以便也包含名为*.JPG
或或其他变体的文件。*.JpG
find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
cjpeg -quality 80 "$1" > optimized_"${1#./}"
' called_shell '{}' \;
并且,您可能(或可能不)还希望通过在末尾添加循环 ( for f do … ; done
) 和 a 来每个目录调用一次 shell,而不是每个文件调用一次:+
find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
for f; do cjpeg -quality 80 "$f" > optimized_"${f#./}"; done
' called_shell '{}' \+
最后,由于cjpeg
能够直接写入文件,因此可以避免重定向:
find . \( -iname '*.jpg' -o -iname '*.jpeg' \) -execdir sh -c '
for f; do cjpeg -quality 80 "$f" -outfile optimized_"${f#./}"; done
' called_shell '{}' \+
答案4
创建脚本cjq80:
#!/bin/bash
cjpeg -quality 80 "$1" > "${1%/*}"/optimized_"${1##*/}"
使其可执行
chmod u+x cjq80
并在 -exec 中使用它:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjq80 '{}' \;