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


8

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;

那有更好的方法吗?


>第三代码示例中是否没有缺少内容?
choroba

1
甚至您的第一行都是非标准的,因此是不可移植的。尽量避免{}在较长的字符串中出现命令行,因为此类字符串通常不会扩展。
schily

第一个示例不符合您的要求。
ctrl-alt-delor

1
您已经解决了您的问题:我将不再更改它。
ctrl-alt-delor

1
出于其价值,重定向无法按您期望的方式工作的主要原因是,它由您启动外壳程序处理find一次,然后应用于find命令本身。该{}在这方面没有任何特殊含义。重定向不是的参数find,它当然也不是该-exec子句的一部分。
John Bollinger

Answers:


13

您可以bash -cfind -exec命令内使用,并在bash命令中使用positional参数:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec bash -c 'cjpeg -quality 80 "$1" > "$(dirname "$1")/optimized_$(basename "$1")"' sh {} \;

{}提供了这种方式$1

sh该前{}告诉内壳的“名”,这里使用的字符串中如错误信息时使用。关于stackoverflow的更多答案对此进行了讨论。



3

需要引用重定向以避免当前shell对其进行解释。
但是引用它也会避免命令的输出被重定向。
已知的解决方案是调用shell:

find . -name '*.jpg' -exec sh -c 'echo "$1" >"$1".new' called_shell '{}' \;

在这种情况下,重定向(>)在当前外壳程序上被引用,并且在被调用的外壳程序内可以正常工作。将called_shell用作$0孩子壳的参数(名称)( sh)。

如果在文件名中添加了后缀,则效果很好,但是如果使用前缀则不能。对于一个前缀的工作,你需要同时删除./与查找前面加上到文件名${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 '{}' \+

3

cjpeg有一个选项可让您写入命名文件,而不是标准输出。如果您的find支持版本支持该-execdir选项,则可以利用它来使重定向变得不必要。

find . \( -name '*.jpg' -o -name '*.jpeg' \) \
  -execdir cjpeg -quality 80 -outfile optimized_'{}' '{}' \;

注意:这实际上是假设使用的BSD版本find./当扩展为时,似乎会删除文件名中的前导{}。(或者相反,GNU find 添加 ./了名称。没有标准说明哪个行为是“正确的”。)


如果您有find支持-execdir,则可以使用它代替-exec。它使命令在找到文件的目录中运行,并且{}aa.jpg代替./t2/aa.jpg
chepner

仍然存在错误:cjpeg: can't open optimized_./aa.jpg
以撒

嗯,这似乎是GNU find和BSD 之间的区别find。(使用非标准扩展名的风险。)
chepner

不带前导的文件名./似乎更容易出错。在此答案中提出了一个合理的解决方案。
以撒

1

创建脚本cjq80:

#!/bin/bash
cjpeg -quality 80 "$1" > "${1%/*}"/optimized_"${1##*/}"

使它可执行

chmod u+x cjq80

并在-exec中使用它:

find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjq80 '{}' \;

我虽然对此有所了解,但是它并不十分方便。特别是因为我不得不将其纳入构建过程中
-Buzut

只需将脚本添加到其他构建脚本中,我发现它比双重嵌套的bash -c更具可读性。
choroba

好吧,这是一个品味问题。现在指定了每个选项,因此,如果有人遇到相同的问题,他会自己选择:)
Buzut
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.