Zsh脚本后台运行Python脚本出现EOFError,如何实现命令行同款行为?
问题与解决方案:后台运行Python输入脚本的终端挂起行为差异
场景重现
Python脚本tests.py代码:
while True: msg = input() if msg == 'lol': break
直接命令行运行的预期行为
执行python3 tests.py &,终端输出如下,进程会被挂起等待输入:
[1] 54951 me@mycomputer current_dir % [1] + suspended (tty input) python3 tests.py
通过Zsh脚本test.sh运行的异常行为
脚本内容:
#!/bin/zsh python3 tests.py &
执行./test.sh后,Python进程抛出EOFError:
me@mycomputer current_dir % ./test.sh me@mycomputer current_dir % Traceback (most recent call last): File "path/tests.py", line 2, in <module> msg = input() EOFError: EOF when reading a line
原因分析
核心差异在于进程的标准输入(stdin)关联对象:
- 直接在命令行后台运行时,Python进程的stdin直接绑定到当前的交互式终端(TTY)。当后台进程尝试读取终端输入时,操作系统会检测到这个操作,将进程挂起(标记为
suspended (tty input)),直到用户通过fg命令将进程调回前台,才允许它读取终端输入。 - 通过Zsh脚本运行时,脚本自身的stdin默认不关联交互式终端(或者脚本执行完毕后stdin已处于EOF状态)。后台启动的Python进程继承了脚本的stdin,此时stdin没有可读取的内容,
input()调用直接触发EOFError。
解决方案
修改Zsh脚本,显式将Python进程的stdin重定向到当前终端设备/dev/tty,让它直接绑定到交互式终端:
#!/bin/zsh python3 tests.py < /dev/tty &
修改后运行./test.sh,就能得到和直接命令行运行完全一致的挂起行为,之后可以用fg命令将进程调回前台输入内容,输入lol即可正常退出。
关于捕获EOFError的问题
当stdin处于EOF状态时,input()会每次都立刻抛出EOFError,此时用pass或continue会导致无限循环,用break则直接终止进程——这和直接运行时的场景本质不同,直接运行时stdin是活跃的终端,只是后台进程无法获取输入才被挂起,并非已经处于EOF状态,所以捕获异常的方式无法模拟挂起行为。
内容的提问来源于stack exchange,提问作者chaka
相关产品推荐
相关产品推荐

