Bash subshell tạo với dấu ngoặc nhọn


31

Theo đó , việc đặt một danh sách các lệnh giữa các dấu ngoặc nhọn làm cho danh sách được thực thi trong bối cảnh shell hiện tại. Không có subshell được tạo ra .

Sử dụng psđể thấy điều này trong hành động

Đây là hệ thống phân cấp quy trình cho một đường ống quy trình được thực hiện trực tiếp trên dòng lệnh. 4398 là PID cho shell đăng nhập:

sleep 2 | ps -H;
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29696 pts/23   00:00:00   sleep
   29697 pts/23   00:00:00   ps

Bây giờ tuân theo hệ thống phân cấp quy trình cho một đường ống quy trình giữa các dấu ngoặc nhọn được thực hiện trực tiếp trên dòng lệnh. 4398 là PID cho vỏ đăng nhập. Nó tương tự như hệ thống phân cấp ở trên chứng minh rằng mọi thứ được thực thi trong bối cảnh shell hiện tại :

{ sleep 2 | ps -H; }
   PID TTY          TIME CMD
    4398 pts/23   00:00:00 bash
    29588 pts/23   00:00:00   sleep
    29589 pts/23   00:00:00   ps

Bây giờ, đây là hệ thống phân cấp quy trình khi chính sleepđường ống được đặt bên trong dấu ngoặc nhọn (vì vậy tất cả hai cấp độ của dấu ngoặc nhọn)

{ { sleep 2; } | ps -H; }
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29869 pts/23   00:00:00   bash
   29871 pts/23   00:00:00     sleep
   29870 pts/23   00:00:00   ps

Tại sao bashphải tạo một lớp con để chạy sleeptrong trường hợp thứ 3 khi tài liệu nói rằng các lệnh giữa các dấu ngoặc nhọn được thực thi trong ngữ cảnh shell hiện tại?


Thật thú vị, tôi đoán nó bởi vì trong trường hợp thứ 3, nhóm bên trong là một phần của đường ống và do đó, nó được thực thi trong lớp vỏ phụ, ví dụ như bất kỳ lệnh gọi chức năng nào khác là một phần của đường ống. Liệu nó có ý nghĩa?
Miroslav Koškár

2
Tôi sẽ không nói "Vỏ phải" chỉ vì nó ... Đường ống không được thực thi trong bối cảnh vỏ. Nếu đường ống không có gì ngoài các lệnh bên ngoài thì việc tạo các quy trình con là đủ. { sleep 2 | command ps -H; }
Hauke ​​Laging

Câu trả lời:


26

Trong một đường ống, tất cả các lệnh chạy đồng thời (với thiết bị xuất chuẩn / stdin của chúng được kết nối bằng đường ống) vì vậy trong các quy trình khác nhau.

Trong

cmd1 | cmd2 | cmd3

Tất cả ba lệnh chạy trong các quy trình khác nhau, vì vậy ít nhất hai trong số chúng phải chạy trong một quy trình con. Một số shell chạy một trong số chúng trong quy trình shell hiện tại (nếu được dựng sẵn readhoặc nếu đường ống là lệnh cuối cùng của tập lệnh), nhưng bashchạy tất cả chúng trong quy trình riêng của chúng (ngoại trừ lastpipetùy chọn trong các bashphiên bản gần đây và trong một số điều kiện cụ thể ).

{...}nhóm lệnh. Nếu nhóm đó là một phần của một đường ống, nó phải chạy trong một quy trình riêng giống như một lệnh đơn giản.

Trong:

{ a; b "$?"; } | c

Chúng ta cần một lớp vỏ để đánh giá đó a; b "$?"là một quá trình riêng biệt, vì vậy chúng ta cần một lớp con. Shell có thể tối ưu hóa bằng cách không sử dụng bvì đây là lệnh cuối cùng được chạy trong nhóm đó. Một số vỏ làm điều đó, nhưng dường như không bash.


" Tất cả ba lệnh chạy trong các quy trình khác nhau, vì vậy ít nhất hai trong số chúng phải chạy trong một lớp con. " Tại sao một lớp con cần thiết trong trường hợp này? Vỏ cha mẹ không thể sinh sản quá trình cây? Hoặc, khi bạn nói " phải chạy trong một subshell ", bạn có nghĩa là shell sẽ tự rẽ nhánh, và sau đó thực thi cho mỗi cmd1 cmd2 và cmd3? Nếu tôi thực hiện điều này, bash -c "sleep 112345 | cat | cat "tôi chỉ thấy một bash được tạo và sau đó 3 con cho nó mà không có bất kỳ bash phụ xen kẽ nào khác.
Hakan Baba

" Chúng tôi cần một cái vỏ để đánh giá rằng a; b" $? "Là một quá trình riêng biệt, vì vậy chúng tôi cần một lớp con. " Bạn cũng có thể mở rộng lý do không? Tại sao chúng ta cần một subsheel để hiểu điều đó? Nó cần gì để hiểu điều đó? Tôi đoán phân tích là cần thiết nhưng những gì khác? . Vỏ cha mẹ không thể phân tích cú pháp a; b "$?"? Có thực sự là một nhu cầu cơ bản cho một subsheel, hoặc có thể đó là một quyết định thiết kế / thực hiện trên bash?
Hakan Baba

@HakanBaba, tôi đã thay đổi điều đó thành "quá trình con" để tránh sự nhầm lẫn tiềm ẩn.
Stéphane Chazelas

1
@HakanBaba, việc phân tích cú pháp được thực hiện trong phần cha mẹ (quá trình đọc mã, quá trình thực thi trình thông dịch trừ khi mã đó được chuyển đến eval), nhưng việc đánh giá (chạy lệnh đầu tiên, đợi mã, chạy mã thứ hai) là thực hiện ở trẻ, một trong đó có thiết bị xuất chuẩn kết nối với đường ống.
Stéphane Chazelas

trong { sleep 2 | ps -H; }bash cha thấy sleep 2rằng cần một fork / exec. Nhưng trong { { sleep 2; } | ps -H; }bash cha nhìn thấy { sleep 2; }theo cách khác, một số mã bash. Có vẻ như cha mẹ có thể xử lý fork / exec cho sleep 2nhưng sinh ra một bash mới theo cách đệ quy để xử lý mã bash gặp phải. Đó là sự hiểu biết của tôi, nó có ý nghĩa?
Hakan Baba

19

Việc lồng các dấu ngoặc nhọn dường như biểu thị rằng bạn đang tạo một mức độ phạm vi bổ sung, đòi hỏi một lớp vỏ phụ mới được gọi. Bạn có thể thấy hiệu ứng này với bản sao thứ 2 của Bash trong ps -Hđầu ra của bạn .

Chỉ các quy trình được quy định ở cấp độ đầu tiên của dấu ngoặc nhọn được chạy trong phạm vi của vỏ Bash gốc. Bất kỳ dấu ngoặc nhọn lồng nhau nào sẽ chạy trong vỏ Bash có phạm vi riêng của chúng.

Thí dụ

$ { { { sleep 20; } | sleep 20; } | ps -H; }
  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5012 pts/1    00:00:00   bash
 5014 pts/1    00:00:00     bash
 5016 pts/1    00:00:00       sleep
 5015 pts/1    00:00:00     sleep
 5013 pts/1    00:00:00   ps

Lấy | ps -Hra khỏi hỗn hợp chỉ để chúng ta có thể thấy các dấu ngoặc nhọn lồng nhau, chúng ta có thể chạy ps auxf | lesstrong một vỏ khác.

saml     29190  0.0  0.0 117056  3004 pts/1    Ss   13:39   0:00  \_ bash
saml      5191  0.0  0.0 117056  2336 pts/1    S+   14:42   0:00  |   \_ bash
saml      5193  0.0  0.0 107892   512 pts/1    S+   14:42   0:00  |   |   \_ sleep 20
saml      5192  0.0  0.0 107892   508 pts/1    S+   14:42   0:00  |   \_ sleep 20
saml      5068  0.2  0.0 116824  3416 pts/6    Ss   14:42   0:00  \_ bash
saml      5195  0.0  0.0 115020  1272 pts/6    R+   14:42   0:00      \_ ps auxf
saml      5196  0.0  0.0 110244   880 pts/6    S+   14:42   0:00      \_ less

Nhưng xin chờ chút nữa!

Nếu bạn lấy ra các đường ống mặc dù và sử dụng hình thức lệnh này, chúng tôi sẽ thấy những gì bạn thực sự mong đợi:

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"

Bây giờ trong cửa sổ xem kết quả, chúng tôi nhận được cập nhật cứ sau 2 giây về những gì đang diễn ra:

Đây là lần đầu tiên sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5678 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5681 pts/1    00:00:00     watch
 5682 pts/1    00:00:00       ps

Đây là cái thứ hai sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5691 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5694 pts/1    00:00:00     watch
 5695 pts/1    00:00:00       ps

Đây là thứ ba sleep 10:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5704 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5710 pts/1    00:00:00     watch
 5711 pts/1    00:00:00       ps

Lưu ý rằng cả ba giấc ngủ mặc dù được gọi ở các mức lồng khác nhau của các dấu ngoặc nhọn không còn tồn tại trong phạm vi PID 5676 của Bash. Vì vậy, tôi tin rằng vấn đề của bạn là tự gây ra với việc sử dụng | ps -H.

Kết luận

Việc sử dụng | ps -H(tức là đường ống) đang gây ra một lớp vỏ phụ bổ sung, vì vậy đừng sử dụng phương pháp đó khi cố gắng thẩm vấn những gì đang diễn ra.


vì vậy, "Chỉ các quy trình được quy định ở cấp đầu tiên của dấu ngoặc nhọn được chạy trong phạm vi của vỏ Bash gốc."
xealits 30/11/14

@xealits - đó có phải là theo dõi Q bạn đang hỏi tôi không?
slm

@slm đó chỉ là sự nhấn mạnh vào điểm chính của câu trả lời, như tôi đã thấy. Các lệnh ở cấp độ đầu tiên của dấu ngoặc nhọn chạy trong lớp vỏ hiện tại, dấu ngoặc nhọn lồng nhau tạo ra lớp vỏ mới. Dấu ngoặc đơn khác nhau trong việc tạo subshell ngay lập tức, ở cấp độ đầu tiên. Nếu tôi hiểu sai - hãy sửa cho tôi. Nhưng, như những người khác, câu hỏi ban đầu cũng có đường ống dẫn. Do đó việc tạo ra các quá trình riêng biệt. Và niềng răng xoăn phải tạo vỏ khi sử dụng cho một quy trình riêng. Vì vậy, có lẽ, nó là lý do cho hành vi trong câu hỏi.
xealits

Bây giờ, sau khi đọc lại bài đăng của bạn, tôi thấy tôi đã sai - tuyên bố lồng nhau chỉ liên quan đến trường hợp với đường ống dẫn. Vì vậy, các dấu ngoặc nhọn không bao giờ tạo ra các vỏ mới, trừ khi bạn bọc một quy trình riêng với chúng - thì chúng phải như vậy.
xealits

@xealits - Điều đó đúng.
slm

7

Tôi sẽ đăng kết quả các bài kiểm tra của mình, điều này dẫn đến kết luận rằng bash tạo lớp vỏ phụ cho lệnh nhóm nếu và chỉ khi đó là một phần của đường ống, tương tự như khi người ta gọi một số hàm cũng được gọi là hàm trong vỏ phụ.

$ { A=1; { A=2; sleep 2; } ; echo $A; }
2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1

My A đang thể hiện điều này là tốt.
slm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.