如何将字符串推送到标准输入?在启动时通过 stdin 提供输入,然后以交互方式读取 stdin 输入
How do I push a string to stdin? Provide input via stdin on startup, then read stdin input interactively
有没有办法在调用程序时将字符串 "push" 发送到程序的标准输入流?
这样我们就可以达到
的效果
echo "something" | ./my_program
但是 "something"
不是在 "something"
之后读取 EOF,而是 my_program
会从原始标准输入(例如键盘)读取进一步的输入。
示例: 假设我们要启动一个 bash shell,但我们想在其中做的第一件事是调用 date
。 echo date | bash
不会完成这项工作,因为 shell 会在 运行 date
.
之后终止
这可能有效:
(echo "something"; cat -) | ./my_program
它创建了一个子shell,其中第一行输出来自echo
,其余来自cat
的标准输入,这是终端(或脚本的标准输入,无论如何)。我使用 -
来强调 cat
是从标准输入读取所必需的——这不仅仅是我忘记在 cat
命令之后指定 "$@"
或其他内容.省略 -
不会产生操作上的差异;它可能会产生可理解性差异。
请注意,my_program
的输入不再是终端,而是连接到终端的管道,这会影响程序的行为。 cat
过程也引入了延迟。
期待
如果这不起作用,那么您可能需要改用 expect
。这是一个通用工具,用于编写与其他程序交互的脚本,它使用伪 ttys (ptys) 使其看起来好像终端上的用户正在与其他程序通信。
由于 anishsane ,expect
有一个 interact
命令,可用于让用户在一些固定的序言后键入程序。
当心
将此适应您的第二种情况并不是一种舒适的体验:
(echo date; cat -) | bash -i
-i
告诉 Bash 它是一个交互式的 shell。 date
起作用了,之后我得到了提示,但我没有再执行任何命令。中断给了我更多提示;几乎其他一切似乎都被忽略了。这可能是 cat
对其输出缓冲过多。
我最终从另一个终端 window 杀死了那个 shell。我运气更好:
(echo date; cat -) | bash
但是 Bash 没有提示。当心;确保你知道如何摆脱困境。
I/O 重定向特技
rici also 在这种特殊情况下,您可以使用:
{ { echo date; echo 'exec 0<&3-';} | bash -i; } 3<&0
这很聪明,因为尾随的 3<&0
在描述符 3 上复制了原始标准输入(文件描述符 0),然后使用来自两个 [=14] 的输入运行 bash -i
=] 语句。第一个请求日期。第二个重新重定向,使标准输入现在来自文件描述符 3(即 0<&3
部分)——这是原始标准输入,又名 'the terminal' ——并关闭文件描述符 3(这是尾随 -
;它是 Bash extension over POSIX shell I/O redirection).
用可能更简单但有限的替代方案补充:
IF
./my_program
是 bash
,如 OP 的示例
- 并且启动命令产生的副作用是可以接受的:
- EITHER:对 保持打开状态的
bash
实例完全不可见
- 或:仅限于继承所有修改的变量作为导出的变量
您可以尝试以下方法:
bash -c "$(<command that produces Bash commands>); exec bash -i"
以date
命令作为启动命令为例:
bash -c "$(echo 'date'); exec bash -i"
这 执行 date
然后 将 运行 bash
实例替换为 新实例 实例,然后 保持打开状态 (因为既没有接收标准输入也没有通过 -c
向其传递命令)。
为了说明对 startup 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 创建的任何子进程也能看到它们。
有没有办法在调用程序时将字符串 "push" 发送到程序的标准输入流?
这样我们就可以达到
的效果echo "something" | ./my_program
但是 "something"
不是在 "something"
之后读取 EOF,而是 my_program
会从原始标准输入(例如键盘)读取进一步的输入。
示例: 假设我们要启动一个 bash shell,但我们想在其中做的第一件事是调用 date
。 echo date | bash
不会完成这项工作,因为 shell 会在 运行 date
.
这可能有效:
(echo "something"; cat -) | ./my_program
它创建了一个子shell,其中第一行输出来自echo
,其余来自cat
的标准输入,这是终端(或脚本的标准输入,无论如何)。我使用 -
来强调 cat
是从标准输入读取所必需的——这不仅仅是我忘记在 cat
命令之后指定 "$@"
或其他内容.省略 -
不会产生操作上的差异;它可能会产生可理解性差异。
请注意,my_program
的输入不再是终端,而是连接到终端的管道,这会影响程序的行为。 cat
过程也引入了延迟。
期待
如果这不起作用,那么您可能需要改用 expect
。这是一个通用工具,用于编写与其他程序交互的脚本,它使用伪 ttys (ptys) 使其看起来好像终端上的用户正在与其他程序通信。
由于 anishsane expect
有一个 interact
命令,可用于让用户在一些固定的序言后键入程序。
当心
将此适应您的第二种情况并不是一种舒适的体验:
(echo date; cat -) | bash -i
-i
告诉 Bash 它是一个交互式的 shell。 date
起作用了,之后我得到了提示,但我没有再执行任何命令。中断给了我更多提示;几乎其他一切似乎都被忽略了。这可能是 cat
对其输出缓冲过多。
我最终从另一个终端 window 杀死了那个 shell。我运气更好:
(echo date; cat -) | bash
但是 Bash 没有提示。当心;确保你知道如何摆脱困境。
I/O 重定向特技
rici also
{ { echo date; echo 'exec 0<&3-';} | bash -i; } 3<&0
这很聪明,因为尾随的 3<&0
在描述符 3 上复制了原始标准输入(文件描述符 0),然后使用来自两个 [=14] 的输入运行 bash -i
=] 语句。第一个请求日期。第二个重新重定向,使标准输入现在来自文件描述符 3(即 0<&3
部分)——这是原始标准输入,又名 'the terminal' ——并关闭文件描述符 3(这是尾随 -
;它是 Bash extension over POSIX shell I/O redirection).
用可能更简单但有限的替代方案补充
IF
./my_program
是bash
,如 OP 的示例- 并且启动命令产生的副作用是可以接受的:
- EITHER:对 保持打开状态的
bash
实例完全不可见 - 或:仅限于继承所有修改的变量作为导出的变量
- EITHER:对 保持打开状态的
您可以尝试以下方法:
bash -c "$(<command that produces Bash commands>); exec bash -i"
以date
命令作为启动命令为例:
bash -c "$(echo 'date'); exec bash -i"
这 执行 date
然后 将 运行 bash
实例替换为 新实例 实例,然后 保持打开状态 (因为既没有接收标准输入也没有通过 -c
向其传递命令)。
为了说明对 startup 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 创建的任何子进程也能看到它们。