管道连接到另一个管道的结果未按预期工作

管道连接到另一个管道的结果未按预期工作

我是 bash 编程的新手,当我遇到这种奇怪的行为时,我正在尝试一些命令,试图了解如何使用管道和分组命令传递多个参数,这让我很困惑。我知道其他方法可以实现我想要的目标,但是,我试图理解为什么会发生这种情况。

write 我正在尝试使用内置函数程序向连接到我的 SSH 服务器的用户(让我们将其用户 ID 视为 USER,将 TTY 视为 TTY)发送消息。

虽然我能够使用以下命令很好地发送消息:

$ echo "some message" | write USER TTY

但是当我尝试使用另一个管道传递 USER 和 TTY 时,消息没有被发送:

$ echo "some message" | { echo "USER TTY" | xargs -o write; }

从结果来看,bash 似乎忽略了第一部分($ echo "some message"),并且需要在执行命令后输入消息。

请注意,{ echo "USER TTY" | xargs -o write; }write USER TTY执行相同的工作(显然?我怀疑这里存在我不知道的差异)。

再说一次,我知道有更简单的方法可以做到这一点,但我只是想了解 bash 在分组命令、管道和将输入参数传递给函数方面的工作原理。非常感谢对这些领域的任何评论。


对于那些怀疑我要求做作业的人,很高兴看到人们关心这些东西,我真的在尝试创建一个别名,向我的 ssh 服务器上的每个用户发送消息,我发现这wall更容易,但发现弄清楚我在这里提到的内容很有趣。

答案1

你的

echo "some message" | { echo "USER TTY" | xargs -o write; }

命令无法工作,因为它执行以下操作:

                        |-------------------------------------------|
echo "some message"   --+->   echo "USER TTY"  -->  xargs -o write; |
                        |-------------------------------------------|

的标准输入xargs是来自 的管道echo "USER TTY"。 getsecho "some message"通过管道传输到复合命令中 { echo "USER TTY" | xargs -o write; },这实际上意味着 gets 通过echo "some message"管道传输到echo "USER TTY";这意味着该write命令看不到some message.
PS 由于您已指定 -o 选项xargs, write因此 的标准输入设置为终端。如果你想说的话,这会起作用

echo "USER TTY" | xargs -o write

然后只需键入消息,但它不会连接write到最外面的echo.

如果你有一些Command B输出USER和 TTY (但只有一对),你可以尝试

echo“一些消息”|写$(命令B

更新:以下内容可能适用于假设的情况:Command B仅输出一对 USER和 TTY,但可能不适合您的实际情况。阅读它,然后再看下面的讨论。

如果,正如你所说,你正在使用 bash,并且你有 GNU xargs (Linux 上的标准;可能可用于其他类 Unix 操作系统),你也可以尝试

echo“一些消息”| xargs -a <(命令B) 写

正如 rici 在中所建议的 从使用 xargs 执行的命令从 stdin 读取

我刚刚意识到您可能有 GNU xargs,因为 -o POSIX 未指定该选项。


更多想法:

  • 这可能会解决您的部分问题:顺便说一句,我想不出任何例子

    命令A| {命令B|命令C; }
    不同于
    命令A|命令B|命令C
    即分组产生差异的地方。我希望我忽略了一些明显的事情,并且我期待有人向我指出一个例子。同样地,
    {命令A|命令B; } |命令C
    是等价的。

  • 管道如

    命令A|命令B|命令C
    可能有用。举一个简单的例子:

    $ echo GET | tr BEG VAC | od -cb
    0000000   C   A   T  \n
            103 101 124 012
    0000004
    
  • 像这样的管道

    命令A|命令B|参数(选项)  命令 Y
    如果您认为它是有用的
    {命令A|命令B; } |参数(选项)  命令 Y
    即,提供输入 。如果你认为它是Command A | Command Bxargs
    命令A  | {命令B|参数(选项)  命令 Y; }
    即,Command B向 提供输入xargs并向 Command A提供输入 Command Y,由于上面讨论的原因,它不会起作用:无法在 Command A和 之间建立数据流Command Y

  • 然而,

    命令A  |参数(选项)  -a <(命令B  命令 Y
    由于不同的原因而存在问题:这里的工作 xargsCommand Y(可能)运行多次。但没有多次运行的机制,因此它无法多次Command A提供输入 。Command Y

    如果你真的想运行多次,你可以这样做Command A | Command Y

    命令B|参数(选项)  sh-c'命令A|命令 Y' 什
    例如,

    $ echo S F u o n b | xargs -n2 sh -c 'date | tr "$1" "$2"; sleep 2' sh
    Fun, Mar 27, 2022 11:11:07 AM
    Son, Mar 27, 2022 11:11:09 AM
    Sub, Mar 27, 2022 11:11:11 AM
    

    这运行

    date | tr S F
    date | tr u o
    date | tr n b
    

    date由于秒数不同(07、 09 和 11),您可以看到它实际上运行了三次。

    这将适用于你的情况。 (请注意,在上面的示例中,一次Command Y 从两个参数中获取参数,就像您真正想要/需要做的那样。)如果 产生看起来像 (例如,)的输出,那么您可以这样做Command BwriteCommand Bname1 tty1 name2 tty2 name3 tty3 …fred tty17 wilma tty42 barney ttyS23

    命令B| xargs-n2(也许还有其他选择)  sh-c'回声“一些消息" | write "$1" "$2"' sh

  • 但是,如果您不需要多次运行Command A(在您的情况下,echo "some message")(因为它每次都会产生相同的输出),您可以利用这个事实,并运行Command A一次,保存输出:

    回声”一些消息> tmp文件;\
            命令B| xargs-n2(也许还有其他选择)  sh -c '写入 "$1" "$2" < tmpfile' sh; \
            rm 临时文件

  • 因此,上述解决方案避免了Command A多次运行,但它仍然为每个用​​户运行一次 shell。这是不希望的。我没有看到任何方法可以处理这种情况,使用xargs,而不需要多次启动 shell。 (再次,我预计会有人向我指出。)如果不使用 shell 就很难处理它无论如何, 因为xargs只能处理“简单”命令——没有重定向 ( <) 或管道 ( |)。  ,我们可以不用 来处理它xargs,而是更创造性地使用 shell。假设每行Command B产生一对输出(例如,namen ttynfred tty17 (新队) wilma tty42 (新队) barney ttyS23 (新队) ),那么你可以做

    回声”一些消息> tmp文件;\
                  命令B|当读ut时;写 "$u" "$t" < tmpfile;完毕; \
                  rm 临时文件


深入探讨并解决问题中的几个要点:

  • 回声”一些消息“ | { 回声 ”用户 电传打字员“ | xargs -o 写;}
    外壳实际上并没有忽略该部分,echo "some message"本身。  但是,正如我在答案的第一部分中所描述的,它有效地将其输送到该部分中。并且 不读取其标准输入;因此,echo "USER TTY"echomessage没有被处理。

  • 请注意,{ echo "USER TTY" | xargs -o write; }write USER TTY 做同样的工作(显然?我怀疑这里有我不知道的差异)。

    好的,以防万一还不清楚:两种变体都使用write参数调用程序USER和 TTY。  是一个简单的命令,不会修改其标准 I/O 流,而只是从现有的 stdin 中读取。这可以是 tty、管道或文件。该 变体将 的 stdin 重定向为,因为这就是它的作用。通常(如果您不指定 ),的 stdin 是要执行的命令的参数源,因此无法访问更大环境中的流行标准输入(这将是 write USER TTYxargswrite/dev/tty-o-axargsxargs信息对于 write) - 因此它不能使可供执行程序使用的数据流。因此,如果您不指定-aor  -o,  xargs则将执行程序的标准输入设置为 /dev/null

  • 注意

    命令B| xargs -n2 -o 写入
    会有点工作,但它需要你输入输入信息为每个用户调用一次,因为它write为每个用户调用一次,而没有任何方法在迭代之间保存消息。你 可以理论上,如果您想向不同的用户发送不同的消息,则可以使用此选项,但这并不实用,因为它不会给您任何提示或反馈来告诉您正在与哪个用户交谈。

答案2

第一点

  • 您无法使用,echo "<command arguments>" | command因为您正在回显参数作为输入。如果您想使用命令参数的输入,则必须使用 xargs 作为“转换器”来将输入转换为命令行参数。

xargs是一个外部二进制文件,不属于 shell(即 bash)的一部分。 xargs 有许多版本,但带有该-o标志的版本可以在 GNU 用户空间中找到。一般来说,在 Linux 上,您可以使用 来man <command name>查找有关特定命令的文档。来自一个网页版手册的:

本手册页记录了 GNU 版本 xargs。 xargs 从标准输入读取项目,由以下分隔空白可以用双引号、单引号或反斜杠进行保护) 或者 换行符,并使用任何初始参数执行命令(默认为 echo)一次或多次,后跟从标准输入读取的项目。

xargs 的作用是将输入分解为字段,就像表中的一行一样。对于每个字段,(默认情况下)xargs 向程序传递一个参数,直到达到系统定义的限制。将空格和换行符视为分隔符,即|表中的 。由于您的输入“USER TTY”在 USER 和 TTY 之间有一个空格,因此 xargs 创建类似于表行的内容,如下所示

ARG1 ARG2
用户 电传打字员

第二点

  • 如果您想在每次运行命令时通过管道传输文件,请使用 xargs 将文件通过管道传输到运行命令,如下所示。但请注意,这会打开一个新的 shell,因此无法使用当前会话中的环境变量。
echo "USER TTY" | xargs -I REPL sh -c 'echo "some message" | write REPL'

细分: -I标志采用一个参数REPL,该参数在命令运行时被字段替换。

用户 电传打字员

因此 xargs 将运行

sh -c 'echo "some message" | write USER TTY'

相关内容