繁体   English   中英

shell 脚本进程替换 <() 在命令替换 $() 中

[英]shell scripting process substitution <() inside command substitution $()

我有一个 output 为 1 的命令,类似于:

cmp -bl <(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) <(echo 0) |wc -l

我需要将命令 output 与 if 语句中的值进行比较:

if [ $(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) = "1" ]; then ...

我遇到的错误与语法有关:意外标记附近的语法错误('

[和之前]之后留一个空格。 [是一个类似于test的实际命令。 您需要将命令与其参数分开。 ]不是命令,但[命令参数的语法规则也要求将其分开。

if [ $(cmp -bl <(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) <(echo 0) |wc -l) = "1" ]; then ...

编辑:使用新版本的问题,加上您所做的评论,答案很简单。 您的代码(您问题的当前版本中的代码)完全可以。 它在我所有使用 bash 的计算机上都能正常工作。 但它是bash。 不是嘘。

如果您将它与 /bin/sh 一起使用(正如您在其中一条评论中所说的那样),那么您得到的错误是

/bin/sh: 1: Syntax error: "(" unexpected

这里出乎意料的括号是<(...)语法之一。 这是 bash 特定语法。 /bin/sh 不能使用 pip/进程替换。

了解 <(command) 语法是一种特殊的替换。 它在另一个进程中运行命令,output 重定向到 pipe,其文件名被替换为 <(command) 部分。 echo <(ls)例如显示类似/dev/fd/63的内容,它是一个名为 pipe 的“文件”,其“内容”是ls的结果(即使它不是文件,也没有内容.但是读/dev/fd/63就是读ls的output。还是蛮有用的,即使大多数时候,你也不需要它。但它是最近的(好吧,也许一两年。但是当我学习 sh/bash 时它不存在),而不是 /bin/sh 的一部分。

所以答案是:你需要 bash。 你不能在 /bin/sh 中这样做

如果您想将 cmp 与两个命令的 output 一起使用,并且您真的不能只使用#!/bin/bash而不是#!/bin/sh ,更简单(但效率较低)的解决方案是使用临时文件首先存储命令的结果(希望这些命令不会产生千兆字节的输出),然后比较它们。

或者,您可以使用命名管道重现非常相似的行为。

#!/bin/sh
mknod /tmp/pipe1.$$ p
mknod /tmp/pipe2.$$ p
echo ABCDEF > /tmp/pipe1.$$ &
echo ABCxEF > /tmp/pite2.$$ &
if [ $(cmp -bl /tmp/pipe1.$$ /tmp/pite2.$$ | wc -l) = "1" ]; then
    echo One and only one diff
fi
rm /tmp/pipe1.$$
rm /tmp/pipe2.$$

它的作用大致相同 The echo stuff > /tmp/pipe1.$$ &用于echo stuff进程(将 echo 替换为您想要的任何命令)。 Whouse output 被重定向到名为 pipe /tmp/pipe1.$$ 我们在第一行创建的。 (.$$ 后缀,只是为了确保没有其他进程可以使用相同的 /tmp/pipe1 名称,因为 $$ 是我们进程的 pid)然后你有一个非常经典的if [ $(cmp /tmp/pipe1.$$ /tmp/pipe2.$$ | wc -l) ]其中, cmp 只是用两个文件名调用,它们恰好是命名管道(毕竟与 <(...) 完全相同)

但同样,只有当您有充分的理由想要一个纯 sh(而不是 bash)解决方案时。 在这种情况下,您还应该编辑您的问题以删除“bash”标签。

================= 旧答案 ==========================

这对我来说似乎很令人费解。 例如,使用 cmp foo <(echo 0) 将 foo 的内容(foo 本身是 cmp 上的 wc -l 的结果)与 0 进行比较。但我想你已经明白了,你明白这是一个 Rube-Goldberg 装置而不是“== 0”,并且您了解每个 <() 派生一个进程,并从该进程返回一个管道名称(当您需要传递包含执行命令的结果)。

如果不是,你应该解释你真正想要做什么,因为,即使用真正的命令替换 <(echo...) (我也推测这些只是例子),cmp 和 wc 的这几个阶段是不可能的是正确的方法。

话虽这么说,只需在 [ 和 ] 周围添加一个空格,它就会完全按照您的意图进行。 (编辑:正如其他人在更快的回复和评论中所说的那样。我输入的答案太长......我不会删除这个答案,因为序言可能是相关的)

编辑:回复更多关于使用部分

  • 正如评论中所见,不需要两层 wc -l (一个已经是一种奇怪的方式,但如果它有效......)
  • 小心你如何比较事物。
    • 我的(在评论中),使用==仅在 linux 中有效,因为 wc output 是一串数字,仅此而已。 由于标题空格,它不在mac上。 所以11不同。
    • 你的,也是一样。 但是由于您使用[而不是[[[是和外部命令,而[[是 bash 语法( [[更好:没有进程的分叉),在将参数传递给[命令的过程中,空格1会丢失
    • 最干净的解决方案可能是使用 -eq 来比较整数,而不管它们是如何打印的。

所以,总的来说

if [[ $(cmp -bl <(echo string1) <(echo string2) | wc -l) -eq 1 ]]
then
   echo one and only one difference
fi

应该管用。 同样,不确定它是最快的,也不是最短的,也不是最优雅的解决方案。 但只要没有人提供更好的...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM