查找 exec '{}' 在 > 之后不可用

查找 exec '{}' 在 > 之后不可用

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)。$0sh

如果在文件名中添加后缀,则效果很好,但如果使用前缀,则效果不佳。要使前缀起作用,您需要删除./文件名前面的 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 '{}' \;

相关内容