通过管道传入JSON启动Python REPL后立即退出的问题排查
REPL启动后立即退出的核心原因是标准输入(stdin)的绑定对象不符合交互要求:
- 你手动执行的可正常运行的命令中,管道
|仅会把kubectl的输出传递给紧接在管道符后的cp命令;cp执行完成后,通过&&串联启动的python命令的stdin仍然绑定在当前操作终端,因此可以正常进入交互模式。 - 当你把逻辑封装为独立脚本,用
kubectl ... | new_script形式执行时,整个bash脚本的stdin都会被绑定到管道输出端:脚本内的cp命令读完管道里传输的JSON数据后,管道已经到达EOF;后续启动的Python/IPython进程会默认继承脚本的stdin,也就是已经读空、处于EOF状态的管道。REPL启动后尝试读取用户交互输入时直接碰到EOF,就会触发退出逻辑,IPython环境下就会弹出你看到的"Do you really want to exit"确认提示。
你尝试直接读取sys.stdin加载JSON的写法也会触发相同问题:Python代码把管道内的JSON全部读取完成后,stdin已经抵达EOF,REPL没有可以读取交互输入的终端数据源,自然会直接退出。
方案1:脚本内重定向REPL的stdin到终端
修改bash脚本,启动Python/IPython时主动将stdin重定向到当前终端设备/dev/tty,绕开已经处于EOF状态的管道:
#!/bin/bash tmp_file=$(mktemp /tmp/ipyjson.XXXXXX) # 从管道stdin读取JSON存入临时文件 cat > "$tmp_file" # 启动REPL时将stdin重定向到终端,保证交互功能正常 python -i -c "import json; data = json.load(open('$tmp_file')); print('JSON已加载至data变量')" < /dev/tty # 清理临时文件 rm "$tmp_file"
如果使用IPython,只需要把上述命令里的python替换为ipython即可,保留< /dev/tty的重定向逻辑。
脚本调用方式保持不变,仍然执行kubectl get po -o json | new_script,启动后就可以直接在REPL中通过data变量处理传入的JSON数据。
注意:脚本的shebang行不需要加
-i参数,去掉即可,交互模式参数是给交互式shell使用的,普通执行脚本加这个参数反而可能引发终端信号处理异常。
方案2:Python代码内切换stdin,省去临时文件
不需要创建临时文件,在Python启动逻辑中先读取管道内的JSON数据,再手动将stdin重定向到终端设备:
#!/bin/bash python -i -c " import json, sys # 先从管道stdin读取完整JSON内容 data = json.load(sys.stdin) # 重定向stdin到当前终端,恢复交互输入能力 sys.stdin = open('/dev/tty', 'r') print('JSON已加载至data变量') "
调用方式和方案1一致,省去了临时文件创建、清理的步骤,逻辑更简洁。
方案3:使用进程替换传参,避免管道占用stdin
如果不想修改脚本内部的stdin处理逻辑,调用时可以用Bash的进程替换语法,把JSON内容作为文件输入传给脚本,而不是通过管道传入,这种方式下脚本本身的stdin始终绑定在终端,不会出现EOF问题。
对应脚本修改为读取第一个参数指定的文件即可,调用示例:
new_script <(kubectl get po -o json)
这种方式适合临时使用,缺点是调用命令比管道写法稍长。
内容的提问来源于stack exchange,提问作者satoru

