我有一个简短的bash脚本foo.sh
#!/bin/bash
cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1
当我直接从shell运行它时,它运行正常,完成后退出
$ ./foo.sh
m1un
$
但是当我从Python运行它时
$ python -c "import subprocess; subprocess.call(['./foo.sh'])"
ygs9
它输出线但后来永远挂起。造成这种差异的原因是什么?
答案 0 :(得分:8)
将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
因此,subprocess.call()
执行命令并屏蔽SIGPIPE
信号。当head
完成其工作并退出时,其余进程不会收到损坏的管道信号,也不会终止。
对手头的问题进行了解释,很容易在python bugtracker中找到bug,结果证明是issue#1652。
答案 1 :(得分:1)
Python 2以非标准方式处理SIGPIPE
的问题(即被忽略)已经在Leon的答案中被创造出来了,修复在链接中给出:将SIGPIPE
设置为默认值(SIG_DFL
),例如,
import signal
signal.signal(signal.SIGPIPE,signal.SIG_DFL)
您可以尝试在脚本中取消设置SIGPIPE
,例如
#!/bin/bash
trap SIGPIPE # reset SIGPIPE
cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1
但不幸的是,根据Bash reference manual
,它不起作用进入shell时忽略的信号无法被捕获或重置。
最后的评论:你在这里无用cat
;最好将脚本编写为:
#!/bin/bash
tr -dc 'a-z1-9' < /dev/urandom | fold -w 4 | head -n 1
然而,既然你正在使用Bash,你也可以使用read
内置如下(这将有利地取代fold
和head
):
#!/bin/bash
read -n4 a < <(tr -dc 'a-z1-9' < /dev/urandom)
printf '%s\n' "$a"
事实证明,使用此版本,您将清楚地知道发生了什么(并且脚本不会挂起):
$ python -c "import subprocess; subprocess.call(['./foo'])"
hcwh
tr: write error: Broken pipe
tr: write error
$
$ # script didn't hang
(当然,它适用于Python3没有错误)。告诉Python使用SIGPIPE
的默认信号也很有效:
$ python -c "import signal; import subprocess; signal.signal(signal.SIGPIPE,signal.SIG_DFL); subprocess.call(['./foo'])"
jc1p
$
(也适用于Python3)。