2016-09-08 26 views
8

Tôi có một kịch bản bash ngắn foo.shScript công trình khác nhau khi chạy từ nhà ga và chạy từ Python

#!/bin/bash 

cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1 

Khi tôi chạy nó trực tiếp từ vỏ, nó chạy tốt, thoát khi nó được thực hiện

$ ./foo.sh 
m1un 
$ 

nhưng khi tôi chạy nó từ Python

$ python -c "import subprocess; subprocess.call(['./foo.sh'])" 
ygs9 

nó ra đường nhưng sau đó chỉ cần treo forev er. Điều gì gây ra sự khác biệt này?

+0

thử trong terminal 'cat/dev/urandom' và bạn thấy rằng nó không bao giờ dừng lại. Có lẽ 'call()' nghĩ rằng 'cat/dev/urandom' nên nó không ngừng chạy. Tôi không thể giải thích rõ hơn. Nếu bạn thử với 'cat some_normal_file thay vì' của 'cat/devurandom' thì bạn không phải gặp vấn đề này - có thể là tập tin chuẩn gửi thông tin EOF. – furas

+0

Đây là sự cố với phiên bản python. Tôi đã thử với 2.6 và nó treo cứng. Nhưng nó đã làm việc với 3.5.1. Bạn đang sử dụng phiên bản nào? Bạn đã cố gắng chuyển sang phiên bản mới hơn chưa? –

+0

@DawidGrabowski Tôi đang sử dụng 2.7.6 - Tôi cũng có thể làm cho nó hoạt động khi tôi sử dụng 3 (3.4.3). Điều thú vị là, mặc dù 'python3 -c" nhập subprocess; subprocess.call (['./ foo.sh']) "' không treo, 'python3 -c" os nhập khẩu; os.system ('./ foo.sh') "' * không * treo ... –

Trả lời

8

Thêm lệnh trap -p để kịch bản bash, dừng quá trình trăn treo và chạy ps cho thấy những gì đang xảy ra:

$ 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 

Như vậy, subprocess.call() thực thi lệnh với SIGPIPE tín hiệu đeo mặt nạ. Khi head thực hiện công việc và lối ra, các quy trình còn lại không nhận tín hiệu đường ống bị hỏng và không chấm dứt.

Có giải thích về sự cố trong tay, thật dễ dàng tìm thấy lỗi trong trình gỡ lỗi python, được bật là issue#1652.

+0

Cảm ơn bạn rất nhiều. –

+0

Cảm ơn bạn vì tiền thưởng! – Leon

1

Vấn đề với xử lý Python 2 SIGPIPE theo cách không chuẩn (tức là bị bỏ qua) đã được đặt trong câu trả lời của Leon và sửa lỗi được đưa ra trong liên kết: đặt SIGPIPE thành mặc định (SIG_DFL) với, ví dụ:

import signal 
signal.signal(signal.SIGPIPE,signal.SIG_DFL) 

bạn có thể cố gắng để unset SIGPIPE từ bên trong kịch bản của bạn với, ví dụ như,

#!/bin/bash 

trap SIGPIPE # reset SIGPIPE 

cat /dev/urandom | tr -dc 'a-z1-9' | fold -w 4 | head -n 1 

nhưng, unfortu nately, nó không hoạt động, theo các số Bash reference manual

Tín hiệu bị bỏ qua khi nhập vào trình bao không thể bị bẫy hoặc đặt lại.


Một bình luận cuối cùng: Bạn có sử dụng vô dụng của cat đây; nó tốt hơn để viết kịch bản của bạn như:

#!/bin/bash 

tr -dc 'a-z1-9' < /dev/urandom | fold -w 4 | head -n 1 

Tuy nhiên, kể từ khi bạn đang sử dụng Bash, bạn cũng có thể sử dụng read BUILTIN như sau (điều này sẽ thuận lợi thay thế foldhead):

#!/bin/bash 

read -n4 a < <(tr -dc 'a-z1-9' < /dev/urandom) 
printf '%s\n' "$a" 

Nó chỉ ra rằng với phiên bản này, bạn sẽ có một ý tưởng rõ ràng về những gì đang xảy ra (và kịch bản sẽ không treo):

$ python -c "import subprocess; subprocess.call(['./foo'])" 
hcwh 
tr: write error: Broken pipe 
tr: write error 
$ 
$ # script didn't hang 

(Tất nhiên, nó hoạt động tốt mà không có lỗi với Python3).Và nói với Python sử dụng tín hiệu mặc định cho SIGPIPE cũng hoạt động tốt:

$ python -c "import signal; import subprocess; signal.signal(signal.SIGPIPE,signal.SIG_DFL); subprocess.call(['./foo'])" 
jc1p 
$ 

(và cũng hoạt động với Python3).

Các vấn đề liên quan