有没有办法在调用程序时将字符串"推送"到程序的 stdin 流?
这样我们就会有
echo "something" | ./my_program
但my_program
不是在"something"
后读取EOF,而是从原始标准输入(例如键盘(读取其进一步的输入。
示例:假设我们想启动一个 bash shell,但我们想在其中做的第一件事就是调用 date
。 echo date | bash
不会完成这项工作,因为 shell 会在运行 date
后终止。
这可能有效:
(echo "something"; cat -) | ./my_program
它创建了一个子外壳,其中第一行输出来自echo
,其余来自标准输入到 cat
,这是终端(或脚本的标准输入,无论如何(。 我使用-
来强调cat
需要从标准输入中读取 - 这不仅仅是因为我忘记在cat
命令后指定"$@"
或其他内容。 省略-
不会对操作产生影响;它可能会产生可理解性差异。
请注意,my_program
的输入不再是终端,而是连接到终端的管道,这可能会影响程序的行为。 cat
过程也会带来延迟。
期望
如果这不能完成工作,那么您可能需要改用expect
。 这是一个通用工具,用于编写与其他程序交互的脚本,它使用伪 ttys (ptys( 使其看起来好像终端上的用户正在与其他程序通信。正如 anishsane 所指出的,expect
有一个interact
命令,可用于让用户在某个固定的序言之后键入程序。
小心
将其适应第二种情况并不是一种舒适的体验:
(echo date; cat -) | bash -i
-i
告诉Bash这是一个交互式外壳。 date
工作了,之后我收到了提示,但我没有再执行任何命令。 中断给了我更多的提示;几乎所有其他事情似乎都被忽略了。 这可能是cat
对其输出进行过多缓冲。
我最终从另一个终端窗口杀死了那个外壳。 我运气更好:
(echo date; cat -) | bash
但是没有来自Bash的提示。 小心;确保你知道如何摆脱麻烦。
I/O 重定向特技
Rici还指出,在这种特殊情况下,您可以使用:
{ { echo date; echo 'exec 0<&3-';} | bash -i; } 3<&0
这是相当聪明的,因为尾随3<&0
在描述符 3 上复制原始标准输入(文件描述符 0(,然后运行bash -i
,其输入来自两个echo
语句。 第一个请求日期。 第二个重定向内容,以便标准输入现在来自文件描述符 3(这是0<&3
部分(——这是原始标准输入,又名"终端"——并关闭文件描述符 3(这是尾随-
;它是 POSIX shell I/O 重定向的 Bash 扩展(。
为了用一个可能更简单但有限的替代方案来补充Jonathan Leffler的有用答案:
如果
-
./my_program
是bash
的,如OP的例子 - 并且可以接受启动命令产生的副作用是:
- 任选之一:对保持打开状态的
bash
实例完全不可见 - OR:仅限于继承所有修改的变量作为导出变量
- 任选之一:对保持打开状态的
您可以尝试以下操作:
bash -c "$(<command that produces Bash commands>); exec bash -i"
使用 date
命令的示例作为启动命令:
bash -c "$(echo 'date'); exec bash -i"
这将执行date
,然后将正在运行的bash
实例替换为新实例,然后保持打开状态(由于既没有接收 stdin 输入,也没有通过 -c
向其传递命令(。
为了说明对启动 shell 环境所做的任何更改都不会传播到最终保持打开状态的环境,请考虑以下事项:
bash -c "$(echo 'date; foo=bar; echo $foo'); exec bash -i"
这将打印日期并分配和打印变量$foo
,但是如果您再次执行echo $foo
,则不会定义它,因为最终保持打开状态的 shell 实例不会从执行启动命令的实例继承其状态。
您可以通过将-a
传递给 bash
来部分解决此限制,然后导致自动导出由启动命令修改或创建的所有变量:
bash -ac "$(echo 'date; foo=bar; echo $foo'); exec bash -i"
现在,如果您在保持打开的实例中再次执行echo $foo
,它将打印bar
.
注意:
- 这些变量实际上成为环境变量,这意味着由保持打开的 shell 创建的任何子进程也会看到它们。