在find的-exec选项中进行输入重定向

4

为什么这个命令执行不成功?

find / -type f -name "*.c" -exec wc -c < \{} \;

我正在尝试隐藏文件名并显示字符数。
3个回答

4
您可以执行以下操作:
find / -type f -name "*.c" -exec wc -c {} + | awk '{print $1}'

<无法使用,因为重定向应用于find命令本身。所以你可以使用awk仅打印第一列。

正如@Charles Duffy指出的那样,这更加高效,因为它意味着我们只启动一个wc进程和没有shell包装器,每个文件只运行一次(因为awk实例是共享的,不是每个文件都被调用)。


3
иҝҷз§Қж–№жі•жҜ”д№ӢеүҚеҸ‘еёғзҡ„е…¶д»–и§ЈеҶіж–№жЎҲжӣҙеҠ й«ҳж•ҲпјҢеӣ дёәе®ғж„Ҹе‘ізқҖжҲ‘们жҜҸдёӘж–Ү件еҸӘеҗҜеҠЁдёҖдёӘwcиҝӣзЁӢе’ҢжІЎжңүshellеҢ…иЈ…еҷЁ(еӣ дёәawkе®һдҫӢжҳҜе…ұдә«зҡ„пјҢиҖҢдёҚжҳҜй’ҲеҜ№жҜҸдёӘж–Ү件и°ғз”Ё)гҖӮ - Charles Duffy
话虽如此,你可以通过使用“-exec wc -c {} +”进一步改进它,将多个文件名传递给每个“wc”实例。 - Charles Duffy
@CharlesDuffy 感谢您的建议,已添加! - M. Becerra

2
使用类似下面的带有 exec 的 bash 命令。
find . -type f -name "*.c" -exec bash -c 'wc -c < "$1"' _ {} \;

在您的情况下,重定向符号 < 作用于 find 命令本身,导致出现语法错误,因为没有一个称为 {} 的文件。
一个小提示:在Bash shell中,位置参数应该用双引号括起来处理非标准文件名,比如带有空格的文件名。

编辑

为了支付调用多个子shell的额外成本,我们可以进一步改进exec部分,如下所示:

find . -type f -name "*.c" -exec bash -c 'for arg; do wc -c <"$arg"; done' _ {} +

感谢@Charles Duffy提供this建议。



我会考虑使用-print0 | while IFS= read -r -d '' name; do wc -c <"$name"; done这样的命令。目前我们为每个文件启动不仅是一个wc实例,还有一个bash shell,这导致了很多额外的开销。 - Charles Duffy
1
或者使用 -exec bash -c 'for arg; do wc -c <"$arg"; done' _ {} +,将尽可能多的文件名传递给每个 bash 副本。 - Charles Duffy
@CharlesDuffy 很好的建议,我会采纳第二个建议来编辑我的回答.. :-) - sjsam

0

如果你真的想要重定向,你可以在子shell中执行:

find / -type f -name "*.c" -exec sh -c 'wc -c < "{}"' \;

编辑:不要这样做 - 请参见下面的评论!


2
{} 放在传递给 -c 的参数中会导致安全漏洞。如果有人将 touch '$(rm -rf ~).c' 创建的文件检查到你正在运行的代码库中,你将会度过非常 非常 糟糕的一天。最好将它作为带外传输来传递,就像 sjsam 的答案中所示。 - Charles Duffy
1
(此外,find 的 POSIX 标准不能保证如果 {} 是参数的子字符串而不是整个参数,则会被替换。它适用于常见的实现,包括 GNU 和 BSD,但在旧的或小型嵌入式系统上可能会有问题。) - Charles Duffy
2
@sjsam,对的 - 有些人尝试使用单引号,但攻击者可以轻易地通过在文件名中插入字面单引号来对其进行反制(考虑使用 touch $'$(rm -rf ~)\'$(rm -rf ~)\'.c' 来创建一个双向攻击的例子)。将数据与代码分离是正确的做法。 - Charles Duffy
1
@Tom,那个测试用例要求你拥有一个名为$(rm 的目录,在其中创建一个子目录tmp,并在其中创建一个名为test).c的文件。我的示例没有任何斜杠是有原因的。如果你不想费力去创建一些奇怪命名的目录,可以考虑使用touch $'$(touch pwned)\'$(touch pwned)\'.c' - Charles Duffy
1
@Tom,...或者针对您的原始测试用例:mkdir -p '$(rm /tmp' && touch '$(rm /tmp/test).c'(不需要使用 $'' 语法,因为我们没有在名称中放置文字单引号)。 - Charles Duffy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接