Các dấu ngoặc đơn có thực sự đặt lệnh trong một subshell không?


94

Từ những gì tôi đã đọc, đặt một lệnh trong ngoặc đơn sẽ chạy nó trong một khung con, tương tự như chạy tập lệnh. Nếu điều này là đúng, làm thế nào để xem biến x nếu x không được xuất?

x=1

Chạy (echo $x)trên dòng lệnh cho kết quả là 1

Chạy echo $xtrong một kịch bản không có kết quả, như mong đợi

Câu trả lời:


134

Một subshell bắt đầu như một bản sao gần như giống hệt của quá trình shell ban đầu. Trong mui xe, shell gọi forkhệ thống gọi 1 , tạo ra một quy trình mới có mã và bộ nhớ là bản sao 2 . Khi subshell được tạo, có rất ít sự khác biệt giữa nó và cha mẹ của nó. Đặc biệt, chúng có cùng các biến. Ngay cả $$biến đặc biệt cũng giữ cùng một giá trị trong các lớp con: đó là ID tiến trình của shell gốc. Tương tự $PPIDlà PID của cha mẹ của vỏ ban đầu.

Một vài shell thay đổi một vài biến trong subshell. Bash thiết lập BASHPIDcho PID của quá trình shell, thay đổi trong các lớp con. Bash, zsh và mksh sắp xếp $RANDOMđể mang lại các giá trị khác nhau trong cha mẹ và trong lớp con. Nhưng ngoài các trường hợp đặc biệt tích hợp như thế này, tất cả các biến có cùng giá trị trong lớp con như trong vỏ ban đầu, cùng trạng thái xuất, cùng trạng thái chỉ đọc, v.v. Tất cả các định nghĩa hàm, định nghĩa bí danh, tùy chọn vỏ và các cài đặt khác cũng được kế thừa.

Một subshell được tạo bởi (…)có cùng mô tả tập tin như người tạo nó. Một số phương tiện khác để tạo các lớp con sửa đổi một số mô tả tệp trước khi thực thi mã người dùng; ví dụ, phía bên trái của một đường ống chạy trong một khung con 3 với đầu ra tiêu chuẩn được kết nối với đường ống. Subshell cũng bắt đầu với cùng một thư mục hiện tại, cùng mặt nạ tín hiệu, v.v ... Một trong vài trường hợp ngoại lệ là các subshell không kế thừa các bẫy tùy chỉnh: các tín hiệu bị bỏ qua ( ) vẫn bị bỏ qua trong subshell, nhưng các bẫy khác ( TÍN HIỆU ) được đặt lại đến hành động mặc định 4 .trap '' SIGNALtrap CODE

Do đó, một subshell khác với việc thực thi một tập lệnh. Một kịch bản là một chương trình riêng biệt. Chương trình riêng biệt này có thể ngẫu nhiên cũng là một tập lệnh được thực hiện bởi cùng một trình thông dịch như cha mẹ, nhưng sự trùng hợp này không cung cấp cho chương trình riêng biệt bất kỳ khả năng hiển thị đặc biệt nào về dữ liệu nội bộ của cha mẹ. Các biến không xuất là dữ liệu nội bộ, do đó, khi trình thông dịch cho tập lệnh shell con được thực thi , nó không nhìn thấy các biến này. Các biến được xuất, tức là biến môi trường, được truyền đến các chương trình đã thực hiện.

Do vậy:

x=1
(echo $x)

in 1bởi vì lớp vỏ là một bản sao của vỏ sinh ra nó.

x=1
sh -c 'echo $x'

tình cờ chạy shell như một tiến trình con của shell, nhưng xtrên dòng thứ hai không có nhiều kết nối với xdòng thứ hai hơn trong

x=1
perl -le 'print $x'

hoặc là

x=1
python -c 'print x'

1 Một ngoại lệ là ksh93lớp vỏ trong đó việc rèn được tối ưu hóa và hầu hết các tác dụng phụ của nó được mô phỏng.
2 Về mặt ngữ nghĩa, chúng là bản sao. Từ góc độ thực hiện, có rất nhiều chia sẻ đang diễn ra.
3 Đối với phía bên tay phải, nó phụ thuộc vào vỏ.
4 Nếu bạn kiểm tra điều này, lưu ý rằng những thứ như$(trap) có thể báo cáo các bẫy của vỏ ban đầu. Cũng lưu ý rằng nhiều vỏ có lỗi trong các trường hợp góc liên quan đến bẫy. Ví dụ ninjalj lưu ý rằng kể từ bash 4.3, bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'chạy ERRbẫy từ lớp con được lồng trong trường hợp hai lớp vỏ con, nhưng không phải ERRbẫy từ lớp con trung gian - set -Etùy chọn nên truyền báERRbẫy tất cả các lớp con nhưng lớp con trung gian được tối ưu hóa và do đó không có để chạy ERRbẫy của nó .


2
@Kusalananda số ( x=out; (x=in; echo $x))
Gilles

2
@ Flow2k Đây là thứ tự mở rộng cho những thứ xảy ra ở cùng cấp độ. Nhưng bạn cũng cần xem xét cách mở rộng được trộn lẫn với đánh giá. Khi mở rộng yêu cầu đánh giá một cấu trúc lồng nhau, cấu trúc bên trong được đánh giá đầu tiên. Vì vậy, ví dụ, để đánh giá echo $(x=2; echo $x), đoạn $(x=2; echo $x)cần được mở rộng. Điều này đòi hỏi phải đánh giá lệnh x=2; echo $x. Việc mở rộng $xxảy ra trong quá trình đánh giá này, sau khi đánh giá một phần x=2.
Gilles

2
@ Flow2k Không có thứ tự giữa mở rộng tham số và thay thế lệnh. Lưu ý rằng câu này sử dụng dấu chấm phẩy để phân tách các bước mở rộng, nhưng mở rộng tham số và thay thế lệnh nằm trong cùng một mệnh đề được phân cách bằng dấu chấm phẩy (vâng, nó rất tinh tế). Thứ tự quan trọng khi một trong các bộ phận có tác dụng phụ ảnh hưởng đến phần khác, ví dụ (với xunset) echo $(echo foo >somefile)${x-$(cat somefile)}hoặc echo $(echo $x),${x=1}.
Gilles

1
@Gilles; Tôi bị bối rối. Nếu một subshell là khác so với thực hiện một kịch bản thì tại sao người ta nói rằng: Chạy một kịch bản shell khởi động một tiến trình mới, một subshell. ? Ngoài ra, một môi trường lớp con sẽ được tạo như một bản sao của môi trường shell . Do đó, ./file sẽ được thực thi trong môi trường mạng con và do đó, nó sẽ kế thừa các tham số shell được đặt bằng phép gán biến.
túp lều

2
@haccks Định nghĩa trong ABS là gần đúng và không phải là một định nghĩa tốt. Các ví dụ là tốt, nhưng hai dòng đầu tiên của trang đó quá đơn giản đến mức chúng sai. Chạy một kịch bản từ một kịch bản khác sẽ khởi chạy một quy trình mới không phải là một nhánh con. Trong SUS, các định nghĩa là chính xác (nhưng không phải lúc nào cũng rất dễ hiểu). ./filekhông được thực hiện trong một subshell. Xem thêm unix.stackexchange.com/q/261638unix.stackexchange.com/a/157962
Gilles

15

Rõ ràng, có, như tất cả các tài liệu nói, một lệnh ngoặc đơn được chạy trong một lớp con.

Subshell thừa hưởng một bản sao của tất cả các biến của cha mẹ. Sự khác biệt là bất kỳ thay đổi nào bạn thực hiện trong phần con cũng không được thực hiện ở phần gốc.

Trang ksh man làm cho điều này rõ ràng hơn một chút so với trang bash:

man ksh:

Một lệnh ngoặc đơn được thực thi trong shell phụ mà không loại bỏ các biến không xuất.

man bash:

(danh sách)

danh sách được thực thi trong môi trường mạng con (xem MÔI TRƯỜNG THỰC HIỆN QUY TẮC bên dưới). Các phép gán biến và các lệnh dựng sẵn ảnh hưởng đến môi trường của shell không còn hiệu lực sau khi lệnh hoàn thành.

MÔI TRƯỜNG THỰC HÀNH

Shell có môi trường thực thi, bao gồm các tham số sau: [...] shell được đặt bằng phép gán biến [...].
Thay thế lệnh, các lệnh được nhóm với dấu ngoặc đơn và các lệnh không đồng bộ được gọi trong môi trường lớp con là bản sao của môi trường shell, [...]


3
Điều này trái ngược với When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following., trong đó có mục: · shell variables and functions marked for export, along with variables exported for the command, passed in the environment(từ cùng một man bashphần) giải thích tại sao một echo $xbản mô tả không in gì nếu xkhông được xuất.
Johan E
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.