2011-11-28 34 views
21

Tôi có một thời gian chờ ruby ​​mà các cuộc gọi một lệnh hệ thống (bash) như thế này ..ruby ​​thờ gian tạm ngưng và các lệnh hệ thống

Timeout::timeout(10) { 
    `my_bash_command -c12 -o text.txt` 
} 

nhưng tôi nghĩ rằng ngay cả khi sợi ruby ​​bị gián đoạn, lệnh thực tế giữ chạy trong nền .. có bình thường không? Làm thế nào tôi có thể giết nó?

+0

Điều đó không đúng. Sub-shell đang chạy lệnh sẽ kết thúc khi quá trình ruby ​​cha kết thúc. Vui lòng đưa ra một ví dụ cụ thể hơn. –

+2

@BenLee: Quy trình gốc không chấm dứt khi hết thời gian chờ. –

+0

@ MladenJablanović, trong một thử nghiệm nhanh chóng. Tôi tạo ra một tập tin ruby ​​mà không làm gì ngoài: 'require 'timeout'; Hết giờ :: thời gian chờ (100) {'ngủ 500'}'. Trong khi chạy nó, tôi làm 'ps aux | grep ngủ' và xem quá trình ngủ. Sau đó, tôi gửi SIGKILL cho quá trình ruby ​​và chạy lại lần nữa 'ps aux | grep ngủ' và không còn nhìn thấy quá trình con. –

Trả lời

30

Tôi nghĩ rằng bạn phải kill nó bằng tay:

require 'timeout' 

puts 'starting process' 
pid = Process.spawn('sleep 20') 
begin 
    Timeout.timeout(5) do 
    puts 'waiting for the process to end' 
    Process.wait(pid) 
    puts 'process finished in time' 
    end 
rescue Timeout::Error 
    puts 'process not finished in time, killing it' 
    Process.kill('TERM', pid) 
end 
+1

Điều này khác với ví dụ vì sử dụng bạn đang sử dụng 'Process.spawn'. Với lệnh đó, tiến trình con * không * chấm dứt khi tiến trình chính thực hiện. Nó cũng không ngăn chặn việc thực hiện ứng dụng trong khi đợi cho tiến trình con trở về; nó chạy song song. Nhưng khi sử dụng backticks (hoặc 'exec'), quá trình chính đợi cho tiến trình con quay trở lại và giết chết tiến trình con nếu quá trình chính bị chấm dứt. –

+1

Er .. OP không chấm dứt quá trình chính. Vấn đề là liệu một ngoại lệ được nâng lên bởi 'timeout' có chấm dứt tiến trình con hay không (và nó không). –

+0

bạn đúng Tôi hiểu lầm câu hỏi của OP. Thay đổi phiếu bầu của tôi từ downvote thành upvote. (ban đầu nó sẽ không cho phép tôi thay đổi nó nói "Phiếu bầu của bạn hiện đã bị khóa trừ khi câu trả lời này được chỉnh sửa" nên tôi đã chỉnh sửa nhanh chỉ thêm một ký tự khoảng trắng, không thay đổi bất kỳ nội dung nào). –

10

để đúng dừng cây quá trình sinh ra (không chỉ là quá trình cha mẹ) ta nên xem xét một cái gì đó như thế này:

def exec_with_timeout(cmd, timeout) 
    pid = Process.spawn(cmd, {[:err,:out] => :close, :pgroup => true}) 
    begin 
    Timeout.timeout(timeout) do 
     Process.waitpid(pid, 0) 
     $?.exitstatus == 0 
    end 
    rescue Timeout::Error 
    Process.kill(15, -Process.getpgid(pid)) 
    false 
    end 
end 

này cũng cho phép bạn theo dõi trạng thái quá trình

+1

Tôi nghĩ rằng điều này sẽ chỉ làm việc với ruby ​​1.9 – CantGetANick

+0

Giết chết cây là quan trọng và mã mà nó có lẽ nên nói chung là câu trả lời cho câu hỏi này. Hai điểm liên quan đến giải pháp của bạn: Quy trình :: diệt tài liệu nói _signal_ phải là tiêu cực để giết một nhóm tiến trình (mã của bạn có ID nhóm tiến trình là âm). Ngoài ra, Process :: spawn dường như không có các khối mã, mà làm cho nó kém thuận tiện hơn. Tuy nhiên, tôi nghĩ rằng bạn đang đi đúng hướng. – Ray

+0

Tôi nghĩ rằng Process.kill (15, -Process.getpgid (pid)) == Process.kill (-15, pid), tôi không nhớ nơi tôi đọc về nó (có thể là sai tất nhiên). Điều quan trọng ở đây là: pgroup => true – shurikk

6

Có lẽ điều này sẽ giúp người khác tìm cách đạt được chức năng thời gian chờ tương tự nhưng cần thu thập đầu ra từ lệnh shell.

Tôi đã điều chỉnh phương thức @ shurikk để làm việc với Ruby 2.0 và một số mã từ Fork child process with timeout and capture output để thu thập kết quả.

def exec_with_timeout(cmd, timeout) 
    begin 
    # stdout, stderr pipes 
    rout, wout = IO.pipe 
    rerr, werr = IO.pipe 
    stdout, stderr = nil 

    pid = Process.spawn(cmd, pgroup: true, :out => wout, :err => werr) 

    Timeout.timeout(timeout) do 
     Process.waitpid(pid) 

     # close write ends so we can read from them 
     wout.close 
     werr.close 

     stdout = rout.readlines.join 
     stderr = rerr.readlines.join 
    end 

    rescue Timeout::Error 
    Process.kill(-9, pid) 
    Process.detach(pid) 
    ensure 
    wout.close unless wout.closed? 
    werr.close unless werr.closed? 
    # dispose the read ends of the pipes 
    rout.close 
    rerr.close 
    end 
    stdout 
end 
+0

Tôi nghĩ rằng với các biến thể 'Open3.capture *' sẽ dễ dàng hơn. Tôi cho rằng nó sẽ tự động cập nhật sau khi hết thời gian chờ. – akostadinov

1

Quy trình xử lý, tín hiệu và bộ hẹn giờ không phải là điều dễ dàng. Đó là lý do tại sao bạn có thể xem xét ủy nhiệm nhiệm vụ này: Sử dụng lệnh timeout trên các phiên bản mới của Linux:

timeout --kill-after 5s 10s my_bash_command -c12 -o text.txt 
+1

trên hệ thống của tôi cú pháp là 'timeout ': ví dụ như 'timeout 10s my_bash_command'. Tùy chọn '--kill-after', cụ thể là lượng thời gian chờ sau khi gửi tín hiệu thuật ngữ. Sử dụng tùy chọn này, bạn vẫn cần chỉ định thời lượng gốc: 'timeout --kill-after = 5s 10s my_bash_command'. – yves

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