[英]Script works differently when ran from the terminal and ran from Python
I have a short bash script foo.sh
我有一个简短的bash脚本
foo.sh
#!/bin/bash
cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1
When I run it directly from the shell, it runs fine, exiting when it is done 当我直接从shell运行它时,它运行正常,完成时退出
$ ./foo.sh
m1un
$
but when I run it from Python 但是当我从Python运行它时
$ python -c "import subprocess; subprocess.call(['./foo.sh'])"
ygs9
it outputs the line but then just hangs forever. 它输出线但然后永远挂起。 What is causing this discrepancy?
造成这种差异的原因是什么?
Adding the trap -p
command to the bash script, stopping the hung python process and running ps
shows what's going on: 将
trap -p
命令添加到bash脚本,停止挂起的python进程并运行ps
显示正在发生的事情:
$ cat foo.sh
#!/bin/bash
trap -p
cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1
$ python -c "import subprocess; subprocess.call(['./foo.sh'])"
trap -- '' SIGPIPE
trap -- '' SIGXFSZ
ko5o
^Z
[1]+ Stopped python -c "import subprocess; subprocess.call(['./foo.sh'])"
$ ps -H -o comm
COMMAND
bash
python
foo.sh
cat
tr
fold
ps
Thus, subprocess.call()
executes the command with the SIGPIPE
signal masked. 因此,
subprocess.call()
执行命令,并屏蔽SIGPIPE
信号。 When head
does its job and exits, the remaining processes do not receive the broken pipe signal and do not terminate. 当
head
完成其工作并退出时,其余进程不会收到损坏的管道信号并且不会终止。
Having the explanation of the problem at hand, it was easy to find the bug in the python bugtracker, which turned out to be issue#1652 . 解释了手头的问题,很容易在python bugtracker中找到bug,结果证明是问题#1652 。
The problem with Python 2 handling SIGPIPE
in a non-standard way (ie, being ignored) is already coined in Leon's answer, and the fix is given in the link: set SIGPIPE
to default ( SIG_DFL
) with, eg, Python 2以非标准的方式处理
SIGPIPE
的问题(即被忽略)已经在Leon的答案中被创造出来了,修复在链接中给出:将SIGPIPE
设置为默认值( SIG_DFL
),例如,
import signal
signal.signal(signal.SIGPIPE,signal.SIG_DFL)
You can try to unset SIGPIPE
from within your script with, eg, 您可以尝试从脚本中取消设置
SIGPIPE
,例如,
#!/bin/bash
trap SIGPIPE # reset SIGPIPE
cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1
but, unfortunately, it doesn't work, as per the Bash reference manual 但是,不幸的是,根据Bash参考手册 ,它不起作用
Signals ignored upon entry to the shell cannot be trapped or reset.
进入shell时忽略的信号不能被捕获或重置。
A final comment: you have a useless use of cat
here; 最后的评论:你在这里使用了无用的
cat
; it's better to write your script as: 最好将脚本编写为:
#!/bin/bash
tr -dc 'a-z1-9' < /dev/urandom | fold -w 4 | head -n 1
Yet, since you're using Bash, you might as well use the read
builtin as follows (this will advantageously replace fold
and head
): 然而,既然你正在使用Bash,你也可以使用如下内置的
read
(这将有利地取代fold
和head
):
#!/bin/bash
read -n4 a < <(tr -dc 'a-z1-9' < /dev/urandom)
printf '%s\n' "$a"
It turns out that with this version, you'll have a clear idea of what's going on (and the script will not hang): 事实证明,使用此版本,您将清楚地知道发生了什么(并且脚本不会挂起):
$ python -c "import subprocess; subprocess.call(['./foo'])"
hcwh
tr: write error: Broken pipe
tr: write error
$
$ # script didn't hang
(Of course, it works well with no errors with Python3). (当然,它适用于Python3没有错误)。 And telling Python to use the default signal for
SIGPIPE
works well too: 告诉Python使用
SIGPIPE
的默认信号也很有效:
$ python -c "import signal; import subprocess; signal.signal(signal.SIGPIPE,signal.SIG_DFL); subprocess.call(['./foo'])"
jc1p
$
(and also works with Python3). (也适用于Python3)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.