将字符串发送到标准输入(stdin)

225

有没有一种在bash中有效地实现这个的方法:

/my/bash/script < echo 'This string will be sent to stdin.'

我知道可以将echo的输出通过管道传递,如下所示:

echo 'This string will be piped to stdin.' | /my/bash/script

3
"effectively"的意思是有效地。为什么管道不是实现这一目标的途径? - Andy
你列出的这两个命令应该完全相同。你遇到了什么问题? - Jonathan Hall
9
据我所知,第一个方法不会按预期工作,它会尝试打开一个名为“echo”的文件。 - Andy
哦,对了。那第二种形式有什么问题吗? - Jonathan Hall
2
@Flimzy:此外,> 重定向形式将打开一个文件描述符作为 fd 0(stdin),而 |则是启动一个子进程并将其 stdout(fd 1)连接到另一个进程的 stdin。这个事实可能会有非平凡的影响(考虑共享环境变量?) - sehe
7个回答

348

您可以使用一行的heredoc

cat <<< "This is coming from the stdin"

以上内容相当于:

cat <<EOF
This is coming from the stdin
EOF

或者你可以将一个命令的输出重定向,例如:

diff <(ls /bin) <(ls /usr/bin)

或者你可以理解为

while read line
do
   echo =$line=
done < some_file

或者简单地说

echo something | read param

1
如何以这种方式输入二进制值?不使用管道(我知道可以使用管道,如下所示:echo -e "\x01\x02..." | ./script)。 - cprcrack
1
@jm666 令人难过的是,我昨天学到了如果你使用bash别名,你只需使用形式command_alias <<< "stdin string",否则它会说命令未找到。 - Pyrolistical
1
@mekb 谈论你的例子/无关紧要。但是想象一下 sed 's/x/y/' <<<'text'。现在,sed 从其标准输入读取单词 text 并输出 teyt。所以,现在这里有一个区别。答案中的示例命令只是示例。您可以将 cat 替换为任何从 STDIN 读取的程序。 - clt60
1
一行“here doc”被称为“here string”。 - Brent Bradburn
1
除非你想要计算标准输入的哈希或Base64 :) 问我是如何发现那个额外字符的。 - Kentzo
显示剩余8条评论

78
你差不多猜对了。
/my/bash/script <<< 'This string will be sent to stdin.'

对于多行输入,here-docs很适合:

/my/bash/script <<STDIN -o other --options
line 1
line 2
STDIN

编辑 对于评论:

为了实现二进制输入,请使用以下方式:

xxd -r -p <<BINARY | iconv -f UCS-4BE -t UTF-8 | /my/bash/script
0000 79c1 0000 306f 0000 3061 0000 3093 0000 3077 0000 3093 0000 304b 0000 3093 0000 3077 0000 3093 0000 306a 0000 8a71 0000 306b 0000 30ca 0000 30f3 0000 30bb
0000 30f3 0000 30b9 0000 3092 0000 7ffb 0000 8a33 0000 3059 0000 308b 0000 3053 0000 3068 0000 304c 0000 3067 0000 304d 0000 000a
BINARY

如果您将 /my/bash/script 替换为 cat(或者干脆省略最后一个管道符),则会打印出以下内容:
私はちんぷんかんぷんな話にナンセンスを翻訳することができ

或者,如果你想要更加极客一些的东西:
0000000: 0000 0000 bef9 0e3c 59f8 8e3c 0a71 d63c  .......<Y..<.q.<
0000010: c6f2 0e3d 3eaa 323d 3a5e 563d 090e 7a3d  ...=>.2=:^V=..z=
0000020: 7bdc 8e3d 2aaf a03d b67e b23d c74a c43d  {..=*..=.~.=.J.=
0000030: 0513 d63d 16d7 e73d a296 f93d a8a8 053e  ...=...=...=...>
0000040: 6583 0e3e 5a5b 173e 5b30 203e 3d02 293e  e..>Z[.>[0 >=.)>
0000050: d4d0 313e f39b 3a3e 6f63 433e 1c27 4c3e  ..1>..:>ocC>.'L>
0000060: cde6 543e 59a2 5d3e 9259 663e 4d0c 6f3e  ..T>Y.]>.Yf>M.o>
0000070: 60ba 773e cf31 803e ee83 843e 78d3 883e  `.w>.1.>...>x..>
0000080: 5720 8d3e 766a 913e beb1 953e 1cf6 993e  W .>vj.>...>...>
0000090: 7a37 9e3e c275 a23e dfb0 a63e bce8 aa3e  z7.>.u.>...>...>
00000a0: 441d af3e 624e b33e 017c b73e 0ca6 bb3e  D..>bN.>.|.>...>
00000b0: 6fcc bf3e 15ef c33e e90d c83e d728 cc3e  o..>...>...>.(.>
00000c0: c93f d03e ac52 d43e 6c61 d83e f36b dc3e  .?.>.R.>la.>.k.>
00000d0: 2f72 e03e 0a74 e43e 7171 e83e 506a ec3e  /r.>.t.>qq.>Pj.>
00000e0: 945e f03e 274e f43e f738 f83e f11e fc3e  .^.>'N.>.8.>...>
00000f0: 0000 003f 09ee 013f 89d9 033f 77c2 053f  ...?...?...?w..?
0000100: caa8 073f 788c 093f 776d 0b3f be4b 0d3f  ...?x..?wm.?.K.?
0000110: 4427 0f3f 0000 113f e8d5 123f f3a8 143f  D'.?...?...?...?
0000120: 1879 163f 4e46 183f 8d10 1a3f cad7 1b3f  .y.?NF.?...?...?
0000130: fe9b 1d3f 1f5d 1f3f 241b 213f 06d6 223f  ...?.].?$.!?.."?
0000140: bb8d 243f 3a42 263f 7cf3 273f 78a1 293f  ..$?:B&?|.'?x.)?
0000150: 254c 2b3f 7bf3 2c3f 7297 2e3f 0138 303f  %L+?{.,?r..?.80?
0000160: 22d5 313f ca6e 333f                      ".1?.n3?

4字节二进制浮点数中,前90度的正弦值是多少?


这样如何输入二进制值? - cprcrack
1
请勿在脚本中包含原始二进制数据,而是通过诸如xxd之类的程序进行过滤。 - sehe
为什么要这样做:“/my/bash/script <<< 'This string will be sent to stdin.'”,当你可以直接说:“/my/bash/script 'This string will be sent to stdin.'”? - Baiyan Huang
@BaiyanHuang 因为后者是谎言!它是 /my/bash/script '这将是程序参数'。如果脚本需要 (a) 标准输入 (b) 参数列表太小 (c) 你需要在流模式下工作(处理第一个数据时,其余数据仍在到达)(d) 你不想在进程列表中看到参数,那么你可以使用标准输入。 - sehe
我们可以像这样做:/my/bash/script -o other --options <<STDIN - PT Huynh
显示剩余2条评论

23

解决方案

您想要(1)在一个进程中创建标准输出(例如echo '...'),并且(2)将该输出重定向到另一个进程的标准输入,但是(3)不使用bash管道机制。以下是符合所有三个条件的解决方案:

/my/bash/script < <(echo 'This string will be sent to stdin.')

<是标准输入的常规重定向符号。<(…)是Bash进程替代。大致上,它会创建一个包含替代命令输出的/dev/fd/…文件,并将该文件名作为<(…)的位置参数传递,因此在这个例子中会得到script < /dev/fd/123。更多详细信息,请参见this answer

与其他解决方案的比较

  • 通过将单行heredoc发送到标准输入script <<< 'string'只能发送静态字符串,而无法发送其他命令的输出。

  • 仅使用进程替代,例如diff <(ls /bin) <(ls /usr/bin),不会将任何内容发送到标准输入。相反,进程输出会保存到文件中,并将其路径作为命令行参数传递。对于上述示例,这等同于diff /dev/fd/10 /dev/fd/11,其中diff不从标准输入接收任何输入。

用例

我喜欢这个 "< <(…)" 机制,与管道机制不同,它允许先放置命令,然后再放置所有输入,这是从命令行选项输入的标准方式。

然而,除了命令行美观之外,还有一些情况下不能使用管道机制。例如,在另一个命令的参数中需要使用特定命令(如sshscp),就像在这个使用sshpass的示例中一样。


1
有趣的替代品。这个与 <<< 不同,它是否符合 POSIX 标准? - Uyghur Lives Matter
1
不,这不是POSIX;进程替换只有Bash才有,就像这里的字符串一样。 - tripleee
这个有什么想法,它的性能如何与管道相比? - jspencer
1
你还可以使用带有变量的单行heredoc - 当我不想反复重新运行该命令时,我经常将其用于从另一个命令捕获的输出。 - IanM_Matrix1

3
您也可以像这样使用read
echo "enter your name"
read name
echo $name

1
cat | /my/bash/script

允许用户在程序中输入多行文本,而这些输入不会被保存在历史记录中,也不会在 ps 中可见。输入完毕后,请换行并按下 Ctrl + D 以结束 cat


0
我知道这是一个老问题,但这是一个我经常被问到的问题。
我使用:
/my/bash/script $(echo 'This string will be sent to stdin.')

Bash在子shell中运行命令,并将输出传递给原始命令的标准输入。

这实际上并不通过stdin传递你的字符串。它将子shell的stdout作为命令行参数传递给你的脚本。这与运行/my/bash/script This string will be sent to stdin.没有什么不同。 - undefined
如果您要传递给第一个命令的字符串不是静态字符串,并且您需要另一个命令来生成该字符串,那么它可以很好地工作,并且与仅仅键入字符串非常不同,因为您无法仅仅键入动态字符串。 - undefined

-4
别名可以处理管道输入,也可以不处理...
这里我们创建了三行输出。
$ echo -e "line 1\nline 2\nline 3"
line 1
line 2
line 3

我们将输出管道传输到sed命令的标准输入,以便将它们全部放在一行上。
$ echo -e "line 1\nline 2\nline 3" |  sed -e ":a;N;\$!ba ;s?\n? ?g"
line 1 line 2 line 3

如果我们定义了相同的 sed 命令的别名。
$ alias oline='sed -e ":a;N;\$!ba ;s?\n? ?g"'

我们可以将输出通过管道传输到别名的标准输入,它会表现得完全一样。
$ echo -e "line 1\nline 2\nline 3" |  oline
line 1 line 2 line 3

当我们尝试将别名定义为函数时,问题就会出现。
$ alias oline='function _oline(){ sed -e ":a;N;\$!ba ;s?\n? ?g";}_oline'

将别名定义为函数会破坏管道。
$ echo -e "line 1\nline 2\nline 3" |  oline
>

1
首先,这并没有回答问题 - 也许你回答了错误的问题?其次,为什么你会在别名中定义一个函数?你应该只需执行 function oline() { sed -e ":a;N;\$!ba ;s?\n? ?g"; }。第三,使用 awk 可以使这个 sed 更易于理解:awk -F '\n' 'ORS=" " { print } END { printf "\n"}' - Leonardo Dagnino

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