我是 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,并且你有 GNUxargs
(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 B
xargs
命令A | {命令B|参数(选项) 命令 Y; }
即,Command B
向 提供输入xargs
并向Command A
提供输入Command Y
,由于上面讨论的原因,它不会起作用:无法在Command A
和 之间建立数据流Command Y
。然而,
命令A |参数(选项) -a <(命令B) 命令 Y
由于不同的原因而存在问题:这里的工作xargs
是Command 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 B
write
Command B
name1 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 ttyn
fred tty17
(新队)wilma tty42
(新队)barney ttyS23
(新队) ),那么你可以做回声”一些消息> tmp文件;\ 命令B|当读ut时;写 "$u" "$t" < tmpfile;完毕; \ rm 临时文件
深入探讨并解决问题中的几个要点:
在
回声”一些消息“ | { 回声 ”用户 电传打字员“ | xargs -o 写;}
外壳实际上并没有忽略该部分,echo "some message"
本身。 但是,正如我在答案的第一部分中所描述的,它有效地将其输送到该部分中。并且 不读取其标准输入;因此,echo "USER TTY"
echo
message
没有被处理。-
请注意,
{ echo "USER TTY" | xargs -o write; }
和write USER TTY
做同样的工作(显然?我怀疑这里有我不知道的差异)。好的,以防万一还不清楚:两种变体都使用
write
参数调用程序USER
和TTY
。 是一个简单的命令,不会修改其标准 I/O 流,而只是从现有的 stdin 中读取。这可以是 tty、管道或文件。该 变体将 的 stdin 重定向为,因为这就是它的作用。通常(如果您不指定 ),的 stdin 是要执行的命令的参数源,因此无法访问更大环境中的流行标准输入(这将是write USER TTY
xargs
write
/dev/tty
-o
-a
xargs
xargs
信息对于write
) - 因此它不能使那可供执行程序使用的数据流。因此,如果您不指定-a
or-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'