我们可以通过诱使 Python 命令启动交互式会话来做到这一点。这可以使用unbuffer 来完成,通常在Linux 发行版中与expect 工具一起提供。这种方法非常通用,适用于在交互调用时表现不同的各种程序。
以下命令将启动 REPL,即使(当前为空)输入来自管道:
$ printf '' | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
一些烦人的行为使这项工作与交互式会话略有不同。首先unbuffer一遇到EOF就会退出,所以我们需要一点延迟来保证Python有足够的时间启动:
$ (sleep 1; printf '') | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
请注意,这次我们设法获得了>>> 提示。
其次,我们的输入命令不会作为输出的一部分得到回显。例如:
$ (sleep 1; printf 'print("a" * 10)\n'; sleep 1) | unbuffer -p python3
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> aaaaaaaaaa
>>>
请注意,我们的输入 print("a" * 10)\n 不会出现在 >>> 提示符之后,即使结果会被打印出来。
我们可以使用tee 来解决这个问题,将我们的命令复制到标准输出和 REPL(我们使用进程替换来执行):
$ (sleep 1; printf 'print("a" * 10)\n'; sleep 1) | tee >(unbuffer -p python3)
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print("a" * 10)
aaaaaaaaaa
>>>
这似乎表现得很好,但我们必须在每行之间放置延迟。这是一个自动执行此操作的脚本,从标准输入读取行:
#!/usr/bin/env bash
set -e
cat | (sleep 1; while IFS='' read -r LINE
do
sleep 0.2
echo "$LINE"
done; sleep 1) | tee >(unbuffer -p python3)
这似乎可以完成这项工作(我正在使用printf,但这对于单独的文件同样适用;请注意,REPL 需要两个换行符来执行缩进块,就像在交互式会话中一样):
$ printf 'if True:\n print("hello")\nelse:\n print("world")\n\n12345\n' | ./repl.sh
Python 3.8.8 (default, Feb 19 2021, 11:04:50)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> if True:
... print("hello")
... else:
... print("world")
...
hello
>>> 12345
12345
>>>
如果您想删除最终的 >>> 和启动噪音,我们可以通过标准的文本处理工具(如 head 和 tail)进行管道传输,例如
$ printf 'if True:\n print("hello")\nelse:\n print("world")\n\n12345\n' | ./repl.sh | tail -n+4 | head -n-1
>>> if True:
... print("hello")
... else:
... print("world")
...
hello
>>> 12345
12345