2017-06-15 38 views
7

Bumped vào một hành vi bất ngờ bash/sh và tôi tự hỏi ai đó có thể giải thích lý do đằng sau nó và cung cấp giải pháp cho câu hỏi bên dưới.Quá trình bẻ khóa 'trẻ em' của Bash khi thực hiện một lệnh đơn

Trong một phiên bash vỏ tương tác, tôi thực hiện:

$ bash -c 'sleep 10 && echo'

Với ps trên Linux nó trông như thế này:

\_ -bash \_ bash -c sleep 10 && echo \_ sleep 10

Cây quá trình là những gì tôi mong đợi:

  • quá trình bash shell tương tác của tôi ($)
  • Một con shell quá trình (bash -c ...)
  • một giấc ngủ trẻ em xử lý

Tuy nhiên, nếu lệnh phần bash -c của tôi là một đơn lệnh, ví dụ:

$ bash -c 'sleep 10'

Sau đó, sub-shell ở giữa bị nuốt chửng, và session terminal tương tác của tôi thực thi giấc ngủ "trực tiếp" như quá trình con. Cây quá trình trông như thế này:

\_ -bash \_ sleep 10

Vì vậy, từ góc độ cây quá trình, hai sản xuất cùng một kết quả:

  • $ bash -c 'sleep 10'
  • $ sleep 10

gì đang xảy ra ở đây?

Bây giờ với câu hỏi của tôi: có cách nào để buộc vỏ trung gian, bất kể sự phức tạp của biểu thức được chuyển đến bash -c ... không?

(tôi có thể nối thêm một cái gì đó giống như ; echo; lệnh thực tế của tôi và rằng "công trình", nhưng tôi không muốn Có cách nào thích hợp hơn để buộc các quá trình trung gian ra đời.?)

(chỉnh sửa: typo trong ps đầu ra; loại bỏ sh thẻ như đề xuất trong ý kiến; thêm một typo)

+0

Tại sao bạn * không * muốn tối ưu hóa này khi nó có thể? –

+0

Chủ yếu đảm bảo hành vi nhất quán khi xử lý các quy trình con trong môi trường nơi người dùng có thể chuyển các lệnh tùy ý. Tôi không chắc chắn bỏ qua tối ưu hóa này là giải pháp của tôi (vấn đề thực tế tôi đang có phải đối phó với một sự thay đổi của sudo: https://stackoverflow.com/a/34376188). Nhưng hành vi này rất thú vị và tôi muốn tìm hiểu thêm về nó. – Marco

+0

Câu hỏi hay. Ý của bạn là nói '\ _ bash -c sleep 10 && echo' thay vì' \ _ bash -c sleep 10 && sleep 10' trên dòng 2 của cây ps đầu tiên của bạn? – codeforester

Trả lời

4

có thực sự là một lời nhận xét in the bash source mô tả nhiều trong những lý do cho tính năng này:

/* If this is a simple command, tell execute_disk_command that it 
    might be able to get away without forking and simply exec. 
    This means things like (sleep 10) will only cause one fork. 
    If we're timing the command or inverting its return value, however, 
    we cannot do this optimization. */ 
if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) && 
    ((tcom->flags & CMD_TIME_PIPELINE) == 0) && 
    ((tcom->flags & CMD_INVERT_RETURN) == 0)) 
    { 
    tcom->flags |= CMD_NO_FORK; 
    if (tcom->type == cm_simple) 
     tcom->value.Simple->flags |= CMD_NO_FORK; 
    } 

Trong trường hợp bash -c '...', cờ CMD_NO_FORK được đặt khi được xác định bởi should_suppress_fork function trong builtins/evalstring.c.

Đó là luôn là vì lợi ích của bạn để cho trình bao làm điều này. Điều này chỉ xảy ra khi:

  • Đầu vào là từ một chuỗi được mã cứng và vỏ là lệnh cuối cùng trong chuỗi đó.
  • Không còn lệnh, bẫy, móc, v.v. để chạy sau khi lệnh hoàn tất.
  • Trạng thái thoát không cần đảo ngược hoặc sửa đổi.
  • Không cần phải chuyển hướng.

Điều này tiết kiệm bộ nhớ, làm cho thời gian khởi động của quá trình nhanh hơn một chút (vì nó không cần phải là fork ed) và đảm bảo tín hiệu được gửi đến PID của bạn chạy, làm cho nó có thể cho phụ huynh của sh -c 'sleep 10' để xác định chính xác tín hiệu nào bị giết sleep, trong thực tế nó có bị giết bởi tín hiệu không.

Tuy nhiên, nếu vì một lý do bạn muốn ngăn chặn nó, bạn cần nhưng đặt một cái bẫy - bất cứ cái bẫy sẽ làm:

# run the noop command (:) at exit 
bash -c 'trap : EXIT; sleep 10' 
+0

Tôi không thể nói từ nguồn, nhưng có vẻ như tôi Bash chỉ làm điều đó nếu chỉ có một lệnh trên toàn bộ dòng lệnh '-c'. Vì vậy, một cái gì đó như 'bash -c":; sleep 10 "' giữ cho shell chạy ngay cả sau khi nó bắt đầu ngủ. – ilkkachu

+0

@ilkkachu, kỳ vọng trực quan của tôi là có khả năng thay đổi theo phiên bản - 3.2 sẽ có trước khi triển khai vào cuối năm 2014 của 'should_suppress_fork', f/e. Và ngay cả khi đó là trường hợp ngày hôm nay, tôi sẽ không mong đợi nó vẫn đúng trong các bản phát hành trong tương lai (hy vọng sẽ giải quyết được cơ hội thiếu tối ưu hóa) - trong khi một cái bẫy sẽ luôn luôn * đòi hỏi phải giữ cho shell chạy. –

+0

Dường như hành động tương tự ở cả 3.2 và 4.4. – ilkkachu

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