Tại sao một biến có thể nhìn thấy trong một subshell?


18

Sách học Bash đề cập rằng một lớp con sẽ chỉ kế thừa các biến môi trường và mô tả tệp, v.v. và nó sẽ không kế thừa các biến không được xuất:

$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$

Như tôi biết, shell sẽ tạo hai lớp con cho ()và cho ./file, nhưng tại sao trong ()trường hợp, lớp con lại xác định varbiến mặc dù nó không được xuất và trong ./filetrường hợp nó không xác định được nó?

# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631

Tôi đã cố gắng sử dụng straceđể tìm hiểu làm thế nào điều này xảy ra và thật ngạc nhiên khi tôi thấy rằng bash sẽ sử dụng cùng một đối số cho cuộc gọi hệ thống nhân bản, vì vậy điều này có nghĩa là cả quá trình rẽ nhánh trong ()./filenên có cùng không gian địa chỉ tiến trình của cha mẹ, vậy tại sao trong ()trường hợp là biến có thể nhìn thấy đối với lớp con và điều tương tự không xảy ra đối với ./filetrường hợp này, mặc dù các đối số tương tự được dựa trên lệnh gọi hệ thống nhân bản?


vinc17 nói đúng, thậm chí bạn nhận được pstree khi bạn có subshell, bạn tin vấn đề này.
Tiếng Ba Tư

Câu trả lời:


15

Cuốn sách Bash học là sai. Subshells kế thừa tất cả các biến. Ngay cả $$(PID của vỏ ban đầu) được giữ. Lý do là vì đối với một lớp con, shell chỉ tạo ra và không thực thi một shell mới (ngược lại, khi bạn gõ ./file, một lệnh mới được thực thi, ví dụ như một shell mới; trong đầu ra strace, nhìn execvevà tương tự) . Vì vậy, về cơ bản, nó chỉ là một bản sao (với một số khác biệt được ghi lại).

Lưu ý: điều này không cụ thể đối với bash; Điều này đúng với mọi vỏ.


Oky nhưng tôi đã cố gắng để xóa dấu vết trên shell và tôi đã cố thực thi ./file nhưng tôi không thể tìm thấy bất kỳ cuộc gọi nào cho exec và vì vậy không gian địa chỉ giống nhau cho cả hai quá trình, vậy làm thế nào để giải thích điều này?
dùng3718463

@ user3718463 Bạn có sử dụng -ftùy chọn stracetheo dõi trẻ em không? Điều đó là cần thiết để tìm ra người thực hiện.
vinc17

yup tôi tìm ra nó cảm ơn rất nhiều, tôi đã bỏ lỡ tùy chọn -f và vì vậy tôi không thể tìm thấy cuộc gọi exec sys
user3718463

16

Hoặc bạn hoặc cuốn sách đang nhầm lẫn một lớp con với một quy trình con là một vỏ.

Một số cấu trúc vỏ dẫn đến việc vỏ forking một quá trình con. Trong Linux, forklà trường hợp đặc biệt của lệnh clonegọi hệ thống tổng quát hơn mà bạn đã quan sát trong stracenhật ký. Đứa trẻ chạy một phần của kịch bản shell. Quá trình con được gọi là một subshell . Cấu trúc trực tiếp nhất như vậy là command1 &: command1chạy trong một khung con và các lệnh tiếp theo chạy trong vỏ cha. Các cấu trúc khác tạo ra một lớp con bao gồm thay thế lệnh $(command2)và các đường ống command3 | command4( command3chạy trong một lớp con, command4chạy trong một lớp con trong hầu hết các shell nhưng không phải trong ksh hoặc zsh).

Một subshell là một bản sao của tiến trình cha, vì vậy nó không chỉ có cùng các biến môi trường, mà còn có tất cả các định nghĩa bên trong giống nhau: các biến (bao gồm $$, ID tiến trình của tiến trình shell gốc), hàm, bí danh, tùy chọn, v.v. Trước khi thực thi mã trong lớp con, bash đặt biến BASHPIDthành ID tiến trình của tiến trình con.

Khi bạn chạy ./file, điều này thực thi một lệnh bên ngoài. Đầu tiên, vỏ cho một quá trình con; sau đó tiến trình con này thực thi (với lệnh execvegọi hệ thống) tệp thực thi ./file. Một tiến trình con kế thừa các thuộc tính tiến trình của cha mẹ của nó: môi trường, thư mục hiện tại, v.v. Các khía cạnh bên trong của ứng dụng bị mất trong lệnh execvegọi: các biến không xuất, hàm, v.v. là các khái niệm bash mà kernel không biết và họ bị mất khi bash thực hiện chương trình khác. Ngay cả khi chương trình khác đó là tập lệnh bash, nó được thực thi bởi một trường hợp mới của bash mà không biết hoặc quan tâm rằng tiến trình cha của nó xảy ra cũng là một trường hợp của bash. Do đó, một biến shell (biến không xuất) không tồn tại execve.


Câu trả lời này đã làm sáng tỏ khá nhiều điều cho tôi. Điều duy nhất tôi không hiểu là câu này trong đoạn thứ hai: "Đứa trẻ chạy một phần của kịch bản shell." Kịch bản shell nào đang được nhắc đến?
Flow2k

@ Flow2k Kịch bản (tức là chương trình) mà trình bao đang diễn giải.
Gilles 'SO- ngừng trở nên xấu xa'
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.