考虑以下 shell 脚本
echo foo; read; echo bar
运行bash my_script
输出“foo”,等待返回键并输出“bar”。
虽然以这种方式运行它可以很好地工作,但如果通过管道传输到 /bin/bash ,它就不再工作了:
$ echo 'echo foo;read;echo bar'|bash
直接输出“foo”和“bar”,无需等待按键。
为什么这样使用时不再起作用?
有没有办法以文件脚本文件以及通过管道传输到 /bin/bash 的脚本字符串的方式重写脚本?
答案1
实际上,这非常简单,首先,您需要在一些记住的描述符中保留您的标准输入:
exec 9<&0
那里。你已经复印了一份。现在,让我们在 shell 中通过管道传输命令。
echo 'echo foo; read <&9; echo bar' | bash
...嗯,这很简单。当然,我们还没有真正完成。我们应该清理一下。
exec 9<&-
好的,现在我们完成了。
但如果我们稍微对命令进行分组,我们就可以避免清理......
{ echo 'echo foo; read <&9; echo bar' | bash; } 9<&0
在这种情况下,描述符仅在其分配的复合命令有效时才有效。
答案2
bash 内置命令read
从标准输入 (fd0) 读取数据,但它无法打开标准输入,因为 bash 命令本身已经消耗了标准输入。
你可以eval
管什么
eval 'echo foo;read;echo bar'
是:我想不出另一种方法来通过管道传输本身需要从标准输入读取的 shell 脚本。当您通过管道进入 shell 时,该 shell 不是交互式的。
我从来不知道如何复制标准输入文件描述符。请参阅麦克对此的回答。
顺便说一句:你可以看到读取时发生了什么
echo 'echo foo;read;echo bar'|strace bash
答案3
ikrabbe 的回答正确地指出了为什么你的命令行不能按你期望的方式工作。为一个命令行调用新 shell 的常规方法是:
$ sh -c 'echo foo; read; echo bar'
当然,bash
如果需要,您可以使用,但是您想要在一行 shell 脚本中执行的操作 98% 都可以在普通的旧 shell 中完成。 (请注意“plain old shell”是缩写词“POS”。)
如果您的目标是让单行 shell 脚本暂停,等待用户输入(可能只是确认/确认),那么这可能就是您应该做的。如果您的目标是更好地了解 shell 的工作原理,请尝试以下操作:
$(echo 'echo foo; read; echo "$REPLY bar"'; echo candy) | $(echo 'echo foo; read; echo "$REPLY bar"'; echo candy) |嘘 富 糖果条 (无需等待) $