2017-08-22 19 views
6

Tại saomã trả Tại sao subprocess.Popen khác cho các lệnh tương tự với bash

import subprocess 

p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5 2>/dev/null"]) 
p.wait() 
print(p.returncode) 

lợi nhuận

[stderr:] /bin/bash: line 1: 963663 Killed     timeout -s KILL 1 sleep 5 2> /dev/null 
[stdout:] 137 

khi

import subprocess 

p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5"]) 
p.wait() 
print(p.returncode) 

lợi nhuận

[stdout:] -9 

Nếu bạn thay đổi dấu gạch ngang thành dấu gạch ngang, bạn sẽ nhận được 137 trong cả hai trường hợp. Tôi biết rằng -9 là KILL mã và 137 là 128 + 9. Nhưng có vẻ kỳ lạ cho mã tương tự để có được mã trả lại khác nhau.

xảy ra trên Python 2.7.12 và python 3.4.3

Hình như Popen.wait() không gọi Popen._handle_exitstatushttps://github.com/python/cpython/blob/3.4/Lib/subprocess.py#L1468 khi sử dụng /bin/bash nhưng tôi không thể tìm ra lý do tại sao.

+0

Có vẻ như 'timeout' là Linux cụ thể, nhân tiện. Tôi nhận được mã trả về 127 với cả hai phiên bản mã trên 2.7.10 và 3.6.0 trên OS X. – cdarke

Trả lời

4

Điều này là do thực tế như thế nào bash thực hiện timeout có hoặc không có chuyển hướng/ống dẫn hoặc bất kỳ tính năng bash khác:

  • Với chuyển hướng

    1. python bắt đầu bash
    2. bash bắt đầu timeout , giám sát quá trình và xử lý đường ống.
    3. timeout chuyển mình thành một nhóm quy trình mới và bắt đầu sleep
    4. Sau một giây, timeout gửi SIGKILL vào nhóm quá trình của nó
    5. Khi nhóm quá trình chết, bash lợi nhuận từ chờ đợi timeout, thấy SIGKILL và in thông điệp được dán ở trên đến stderr. Sau đó nó đặt trạng thái thoát riêng của nó là 128 + 9 (một hành vi được mô phỏng bởi timeout).
  • Nếu không có chuyển hướng

    1. python bắt đầu bash.
    2. bash thấy rằng nó không có gì để làm một mình và gọi execve() để tự thay thế chính nó bằng timeout.
    3. timeout hoạt động như trên, toàn bộ nhóm quá trình chết với SIGKILL.
    4. python get là một trạng thái thoát của 9 và hiện một số mangling để tắt chức năng này vào (SIGKILL)

Nói cách khác, mà không cần chuyển hướng/Ống/etc. bash rút khỏi chuỗi cuộc gọi.Ví dụ thứ hai của bạn trông giống như subprocess.Popen() đang thực hiện bash, nhưng hiệu quả là không. bash không còn ở đó khi timeout thực hiện hành động của nó, đó là lý do tại sao bạn không nhận được bất kỳ thư nào và trạng thái thoát không được thoát.

Nếu bạn muốn có hành vi nhất quán, hãy sử dụng timeout --foreground; bạn sẽ nhận được trạng thái thoát là 124 trong cả hai trường hợp.

Tôi không biết gì về dấu gạch ngang; nhưng giả sử nó không thực hiện bất kỳ thủ thuật nào execve() để thay thế chính nó bằng chương trình duy nhất mà nó thực thi. Do đó, bạn luôn thấy trạng thái thoát xén là 128 + 9 trong dấu gạch ngang.

Cập nhật: zsh hiển thị cùng một hành vi, trong khi nó giảm ngay cả khi chuyển hướng đơn giản như timeout -s KILL 1 sleep 5 >/tmp/foo và các loại tương tự, cho bạn trạng thái thoát là -9. timeout -s KILL 1 sleep 5 && echo $? cũng sẽ cung cấp cho bạn trạng thái 137 trong số zsh.

+0

Xin lỗi nhưng tôi chưa hiểu đầy đủ. Nếu trong trường hợp đầu tiên thời gian chờ trở lại 128 + 9 lý do tại sao trong trường hợp thứ hai trên bước 4 python được một trạng thái thoát của 9. Kể từ thời gian chờ là một trong những trách nhiệm cho 128 + 9 mã, không nên vượt qua mã này trong trường hợp thứ hai trên bước 4? – Lehych

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