Tại sao tôi không thể in một biến tôi có thể thấy trong đầu ra của env?


9

Tôi quan tâm đến việc thiết lập các biến môi trường của một thể hiện shell từ một thể hiện khác. Vì vậy, tôi quyết định thực hiện một số nghiên cứu. Sau khi đọc một số các câu hỏi về này tôi đã quyết định để kiểm tra nó ra.

Tôi sinh ra hai vỏ A và B (PID 420), cả hai đều chạy zsh. Từ vỏ AI chạy như sau.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

Từ shell B khi tôi chạy, envtôi có thể thấy biến FOO thực sự được đặt với giá trị của thanh. Điều này khiến tôi nghĩ rằng FOO đã được khởi tạo thành công trong môi trường shell B. Tuy nhiên, nếu tôi cố in FOO, tôi nhận được một dòng trống có nghĩa là nó không được đặt. Đối với tôi, nó cảm thấy như có một mâu thuẫn ở đây.

Điều này đã được thử nghiệm trên cả hệ thống Arch GNU / Linux của riêng tôi và máy ảo Ubuntu. Tôi cũng đã thử nghiệm này trên bashnơi biến thậm chí không hiển thị trong env. Điều này mặc dù làm tôi thất vọng, có ý nghĩa nếu cái vỏ lưu trữ một bản sao của môi trường của nó tại thời điểm sinh sản và chỉ sử dụng nó (được đề xuất trong một trong những câu hỏi được liên kết). Điều này vẫn không trả lời tại sao zshcó thể nhìn thấy biến.

Tại sao đầu ra của echo $FOOsản phẩm nào?


BIÊN TẬP

Sau khi nhập ý kiến, tôi quyết định thử nghiệm thêm một chút. Các kết quả có thể được nhìn thấy trong các bảng dưới đây. Trong cột đầu tiên là vỏ mà FOObiến được đưa vào. Hàng đầu tiên chứa lệnh có đầu ra có thể được nhìn thấy bên dưới nó. Biến FOOđược tiêm bằng cách sử dụng : sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)'. Các lệnh cụ thể cho zsh: zsh -c '...'cũng được kiểm tra bằng bash. Kết quả giống hệt nhau, đầu ra của chúng bị bỏ qua cho ngắn gọn.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ở trên dường như ngụ ý rằng các kết quả là bất khả tri phân phối. Điều này không cho tôi biết nhiều hơn zshbashxử lý cài đặt các biến khác nhau. Hơn nữa, export FOOcó hành vi rất khác nhau trong bối cảnh này tùy thuộc vào vỏ. Hy vọng rằng những bài kiểm tra này có thể làm cho một cái gì đó rõ ràng cho người khác.


Điều gì xảy ra, nếu bạn làm một zsh -c 'echo $FOO'(sử dụng dấu ngoặc đơn!) Thay vào đó? Bạn có thể nhìn thấy nó sau đó?
dùng1934428

Giá trị chính xác được in từ vỏ phụ mới (cũng được thử nghiệm cho bash con). Rõ ràng môi trường vẫn tồn tại một cách nào đó vì đứa trẻ có thể thừa hưởng nó nhưng tại sao cha mẹ không tôn trọng nó?
rlf

3
Đó là những gì tôi nghĩ. Tôi đoán shell có ở đâu đó một bảng ký hiệu các biến, một số trong số chúng được đánh dấu là "đã xuất", có nghĩa là khi mở một lớp con, chúng được đặt vào môi trường của tiến trình con. Ban đầu (khi shell bắt đầu), các biến từ môi trường tại thời điểm đó được sao chép vào bảng ký hiệu (tất nhiên cũng là biến "xuất"). Khi bạn thay đổi môi trường, trình bao không được chú ý để cập nhật bảng ký hiệu của chúng - nhưng các tiến trình con (như env) sẽ thấy môi trường được sửa đổi.
dùng1934428

2
Tôi đã thử nghiệm trên Ubuntu 16.04 với zsh 5.1.1 và bash 4.3.48 (1) và có vẻ như việc đặt biến môi trường zshtrong GDB không làm cho nó hiển thị dưới dạng biến shell nhưng lại khiến nó được truyền cho các tiến trình con (như bạn đã quan sát), trong khi thiết lập một cho bash làm cho nó hiển thị dưới dạng biến shell nhưng không khiến nó được truyền cho các tiến trình con! Dường như zsh và bash sử dụng các chiến lược khác nhau để quản lý các biến, với zsh theo dõi các biến không thuộc môi trường và bash lưu trữ mọi thứ trong môi trường của nó mà nó vệ sinh khi khởi chạy một đứa trẻ (không phải là con).
Eliah Kagan

@EliahKagan, thú vị; bạn nên đăng nó như một câu trả lời. Tôi cũng tự hỏi nếu nó làm cho một sự khác biệt nếu bạn chạy export FOOvào bash?
tự đại diện

Câu trả lời:


2

Hầu hết các shell không sử dụng getenv()/ setenv()/ putenv()API.

Khi khởi động, họ tạo các biến shell cho từng biến môi trường. Chúng sẽ được lưu trữ trong các cấu trúc bên trong cần mang thông tin khác như liệu biến đó có được xuất, chỉ đọc ... Họ không thể sử dụng libc environcho điều đó.

Tương tự, và vì lý do đó, họ sẽ không sử dụng execlp(), execvp()để thực hiện các lệnh nhưng execve()gọi trực tiếp cuộc gọi hệ thống, tính toán envp[]mảng dựa trên danh sách các biến được xuất của họ.

Vì vậy, trong bạn gdb, bạn cần thêm một mục vào bảng biến số nội bộ đó hoặc có thể gọi đúng hàm sẽ làm cho nó diễn giải export VAR=valuemã cho nó để tự cập nhật bảng đó.

Về lý do tại sao bạn thấy sự khác biệt giữa bashzshkhi bạn gọi setenv()vào gdb, tôi nghi ngờ đó là vì bạn đang gọi setenv()trước khi trình bao khởi động, chẳng hạn khi vào main().

Bạn sẽ nhận thấy bash's main()int main(int argc, char* argv[], char* envp[])(và bashbản đồ biến từ những env VAR trong envp[]) trong khi zsh' s là int main(int argc, char* argv[])zshđược các biến từ environđể thay thế. setenv()không sửa đổi environnhưng không thể sửa đổi envp[]tại chỗ (chỉ đọc trên một số hệ thống cũng như các chuỗi mà con trỏ trỏ tới).

Trong mọi trường hợp, sau khi shell đã đọc environkhi khởi động, việc sử dụng setenv()sẽ không hiệu quả vì shell không còn sử dụng environ(hoặc getenv()) sau đó.

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.