Sự khác biệt trong cách sử dụng giữa các biến shell và biến môi trường là gì?


16

Tôi thực sự không biết có hai loại biến khác nhau mà tôi có thể truy cập từ dòng lệnh. Tất cả những gì tôi biết là, tôi có thể khai báo các biến như:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

hoặc truy cập chúng bằng dấu $, như:

echo $foo
echo ${bar[1]}

hoặc sử dụng các biến sẵn có, như:

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

Bây giờ, tôi nghe nói có hai loại (ít nhất là?) Các biến: biến shell và biến môi trường.

  • Mục đích của việc có hai loại khác nhau là gì?
  • Làm thế nào để tôi biết loại biến là gì?
  • Các tập quán điển hình cho mỗi người là gì?


Câu trả lời:


14

Biến môi trường là danh sách các name=valuecặp tồn tại bất kể chương trình là gì (shell, application, daemon,). Chúng thường được kế thừa bởi các tiến trình con (được tạo bởi một fork/ execchuỗi): các tiến trình con có bản sao riêng của các biến cha.

Các biến shell chỉ tồn tại trong ngữ cảnh của shell. Chúng chỉ được kế thừa trong các lớp con (tức là khi vỏ được rẽ nhánh mà không có execthao tác). Tùy thuộc vào các tính năng hệ vỏ, các biến không chỉ có thể là các chuỗi đơn giản như môi trường mà còn là các mảng, các hợp chất, các biến được gõ như số nguyên hoặc dấu phẩy động, v.v.

Khi shell bắt đầu, tất cả các biến môi trường mà nó thừa hưởng từ cha mẹ của nó cũng trở thành biến shell (trừ khi chúng không hợp lệ là biến shell và các trường hợp góc khác như IFSđược đặt lại bởi một số shell) nhưng các biến được kế thừa này được gắn thẻ là xuất 1 . Điều đó có nghĩa là chúng sẽ luôn có sẵn cho các quy trình con với giá trị được cập nhật có khả năng được đặt bởi trình bao. Đó cũng là trường hợp với các biến được tạo dưới vỏ và được gắn thẻ là xuất với exporttừ khóa.

Mảng và các biến loại phức tạp khác không thể được xuất trừ khi tên và giá trị của chúng có thể được chuyển đổi thành name=valuemẫu hoặc khi có một cơ chế cụ thể của vỏ (ví dụ: bashxuất các hàm trong môi trường và một số vỏ POSIX kỳ lạ như rcescó thể xuất mảng ).

Vì vậy, sự khác biệt chính giữa các biến môi trường và biến shell là phạm vi của chúng: biến môi trường là toàn cục trong khi biến shell không xuất là cục bộ của tập lệnh.

Cũng lưu ý rằng các shell hiện đại (ít nhất kshbash) hỗ trợ phạm vi biến shell thứ ba. Các biến được tạo trong các hàm với typesettừ khóa là cục bộ của hàm đó (Cách khai báo hàm cho phép / vô hiệu hóa tính năng này bên dưới kshvà hành vi kiên trì là khác nhau giữa bashksh). Xem /unix//a/28349/2594

1 này áp dụng cho vỏ hiện đại thích ksh, dash, bashvà tương tự. Các shell Bourne kế thừa và các shell cú pháp không Bourne như cshcó các hành vi khác nhau.


1
Tất cả mọi thứ được kế thừa bởi các quá trình trẻ em khi trẻ em được tạo ra như một cái nĩa (một bản sao chính xác) của cha mẹ chúng. Điểm với các biến môi trường là chúng được truyền cho lệnh execve()gọi hệ thống, thường được sử dụng để duy trì dữ liệu trong quá trình thực thi các lệnh khác (trong cùng một quy trình).
Stéphane Chazelas

Không phải tất cả các biến môi trường được dịch sang các biến shell. Chỉ những cái có giá trị như một tên biến shell (và với một vài ngoại lệ như IFStrong một số shell).
Stéphane Chazelas

Shell như thế rc, escó thể xuất các mảng bằng cách sử dụng mã hóa adhoc. bashrccũng có thể xuất các hàm bằng các biến môi trường (một lần nữa, sử dụng mã hóa đặc biệt).
Stéphane Chazelas

Trong ksh93, typesetgiới hạn phạm vi chỉ trong các hàm được khai báo bằng function foo { ...; }cú pháp, không phải với foo() cmdcú pháp Bourne ( ) (và phạm vi tĩnh không động như trong các shell khác).
Stéphane Chazelas

@ StéphaneChazelas Cảm ơn bạn đã xem xét! Trả lời cập nhật để đưa vào tài khoản nhận xét của bạn.
jlliagre

17

Biến Shell

Biến shell là các biến có phạm vi trong phiên shell hiện tại, ví dụ như trong phiên shell tương tác hoặc tập lệnh.

Bạn có thể tạo biến shell bằng cách gán giá trị cho tên không sử dụng:

var="hello"

Việc sử dụng các biến shell là để theo dõi dữ liệu trong phiên hiện tại. Các biến Shell thường có tên với các chữ cái viết thường.

Biến môi trường

Một biến môi trường là một biến shell đã được xuất. Điều này có nghĩa là nó sẽ được hiển thị dưới dạng một biến, không chỉ trong phiên shell đã tạo ra nó, mà còn cho bất kỳ quá trình nào (không chỉ shell) được bắt đầu từ phiên đó.

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

hoặc là

export VAR="hello"

Khi một biến shell đã được xuất, nó vẫn được xuất cho đến khi nó không được đặt hoặc cho đến khi "thuộc tính xuất" của nó bị xóa (có export -nin bash), do đó thường không cần phải xuất lại. Bỏ cài đặt một biến bằng cách unsetxóa nó (bất kể đó có phải là biến môi trường hay không).

Mảng và băm kết hợp trong bashvà các shell khác có thể không được xuất để trở thành biến môi trường. Các biến môi trường phải là các biến đơn giản có giá trị là các chuỗi và chúng thường có các tên bao gồm các chữ cái viết hoa.

Việc sử dụng các biến môi trường là để theo dõi dữ liệu trong phiên shell hiện tại, nhưng cũng cho phép bất kỳ quá trình bắt đầu nào tham gia vào dữ liệu đó. Trường hợp điển hình của điều này là PATHbiến môi trường, có thể được đặt trong trình bao và sau đó được sử dụng bởi bất kỳ chương trình nào muốn khởi động chương trình mà không chỉ định đường dẫn đầy đủ đến chúng.

Tập hợp các biến môi trường trong một quy trình thường được gọi là "môi trường của quy trình". Mỗi quá trình có môi trường riêng.

Các biến môi trường chỉ có thể được "chuyển tiếp", tức là một tiến trình con không bao giờ có thể thay đổi các biến môi trường trong tiến trình cha của nó và ngoài việc thiết lập môi trường cho một tiến trình con khi bắt đầu nó, một tiến trình cha mẹ không thể thay đổi môi trường hiện tại của một quá trình con.

Các biến môi trường có thể được liệt kê với env(không có bất kỳ đối số). Ngoài ra, chúng xuất hiện giống như các biến shell không xuất trong phiên shell. Đây là một chút đặc biệt đối với hệ vỏ vì hầu hết các ngôn ngữ lập trình khác thường không xen kẽ các biến "thông thường" với các biến môi trường (xem bên dưới).

env cũng có thể được sử dụng để đặt các giá trị của một hoặc một số biến môi trường trong môi trường của một quy trình mà không đặt chúng trong phiên hiện tại:

env CC=clang CXX=clang++ make

Điều này bắt đầu makevới biến môi trường CCđược đặt thành giá trị clangCXXđược đặt thành clang++.

Nó cũng có thể được sử dụng để xóa môi trường cho một quá trình:

env -i bash

Điều này bắt đầu bashnhưng không chuyển môi trường hiện tại sang bashquy trình mới (nó vẫn sẽ các biến môi trường khi nó tạo các biến mới từ các tập lệnh khởi tạo shell của nó).

Ví dụ về sự khác biệt

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

Những ngôn ngữ khác

Có các hàm thư viện trong hầu hết các ngôn ngữ lập trình cho phép nhận và thiết lập các biến môi trường. Lưu ý rằng vì các biến môi trường được lưu trữ dưới dạng mối quan hệ khóa-giá trị đơn giản, nên chúng thường không phải là "biến" của ngôn ngữ. Một chương trình có thể tìm nạp giá trị (luôn luôn là một chuỗi ký tự) tương ứng với một khóa (tên của biến môi trường), nhưng sau đó sẽ phải chuyển đổi nó thành một số nguyên hoặc bất kỳ loại dữ liệu nào mà ngôn ngữ mong muốn có giá trị.

Trong C, các biến môi trường có thể được truy cập bằng getenv(), setenv(), putenv()unsetenv(). Các biến được tạo với các thường trình này được kế thừa theo cùng một cách bởi bất kỳ quy trình nào mà chương trình C bắt đầu.

Các ngôn ngữ khác có thể có cấu trúc dữ liệu đặc biệt để thực hiện điều tương tự, như %ENVhàm băm trong Perl hoặc ENVIRONmảng kết hợp trong hầu hết các triển khai awk.


thx, giải thích rõ ràng rõ ràng. Vì vậy, môi trường giống như một lĩnh vực lớn trong đó các chương trình khác có thể sống và xem từng biến môi trường. Một số chương trình có các biến riêng tư của chúng, chỉ bản thân chúng có thể nhìn thấy chúng, như shell. nhưng có một cơ chế để làm cho các biến riêng tư được nhìn thấy bởi tất cả được gọi là "xuất khẩu". Nếu điều này được hiểu, thì điều duy nhất tôi không chắc chắn là liệu có thể tồn tại nhiều hơn một môi trường cùng một lúc không?
cá mập

@sharkant Mỗi quá trình chạy có môi trường riêng. Môi trường này được kế thừa từ quá trình bắt đầu nó. Không bao giờ "nói chuyện chéo" giữa các môi trường của các quá trình khác nhau. Cách duy nhất để thay đổi một biến môi trường trong một quy trình là cho chính quá trình sửa đổi nó.
Kusalananda

thx cho claryfing sự hiểu biết của tôi. Mỗi con cá trong bát cá của riêng mình. Làm thế nào về các quá trình sinh ra các quá trình khác? Là các quy trình và quy trình con của chúng tất cả trong một môi trường hay có mỗi quy trình riêng?
cá mập

1
@sharkant Có các hàm thư viện trong hầu hết các ngôn ngữ cho phép nhận và đặt các biến môi trường. Trong C, điều này được thực hiện với getenv(), setenv(), putenv()unsetenv(). Các biến được tạo với các thường trình này được kế thừa theo cùng một cách bởi bất kỳ quy trình nào mà chương trình C bắt đầu. Các ngôn ngữ khác có thể có cấu trúc dữ liệu đặc biệt cho cùng một thứ, như %ENVtrong Perl.
Kusalananda

1
FWIW: exec*()họ các hàm cũng có thể đặt môi trường cho quá trình được thực thi.
Satō Katsura

5

Biến Shell rất khó nhân đôi.

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

Các biến môi trường tuy nhiên có thể được nhân đôi; chúng chỉ là một danh sách và một danh sách có thể có các mục trùng lặp. Đây là envdup.cđể làm điều đó.

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

Chúng ta có thể biên dịch và chạy nói envdupđể chạy sau đó envcho chúng ta thấy các biến môi trường nào được đặt ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

Điều này có lẽ chỉ hữu ích cho việc tìm lỗi hoặc những điều kỳ quặc khác trong cách các chương trình xử lý tốt **environ.

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

Có vẻ như Python 3.6 ở đây mù quáng đi dọc theo các bản sao (một bản tóm tắt bị rò rỉ) trong khi Perl 5.24 thì không. Làm thế nào về vỏ?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

Trời ạ, chuyện gì xảy ra nếu sudochỉ vệ sinh mục nhập môi trường đầu tiên nhưng sau đó bashchạy với cái thứ hai? Xin chào PATHhoặc LD_RUN_PATHkhai thác. Là của bạn sudo(và mọi thứ khác ?) Đã vá cho lỗ đó ? Khai thác bảo mật không phải là "sự khác biệt giai thoại" hay "lỗi" trong chương trình gọi điện.


1
Điều đó đúng nhưng là một sự khác biệt giai thoại và có thể là lỗi của chương trình đặt biến trùng lặp.
jlliagre


0

Một biến môi trường giống như một biến shell , nhưng nó không đặc trưng cho shell . Tất cả các quy trình trên hệ thống Unixlưu trữ biến môi trường . Sự khác biệt chính giữa môi trường và biến vỏ là: rằng điều hành hệ thống vượt qua tất cả các bạn biến môi trường của vỏ để chương trình chạy vỏ, trong khi các biến shell không thể truy cập vào các lệnh mà bạn chạy.

env –Lệnh cho phép bạn chạy một chương trình khác trong môi trường tùy chỉnh mà không cần sửa đổi chương trình hiện tại. Khi được sử dụng mà không có đối số, nó sẽ in một danh sách các biến môi trường hiện tại. printenv –Lệnh in tất cả hoặc các biến môi trường được chỉ định. set –Lệnh đặt hoặc bỏ đặt các biến shell. Khi được sử dụng mà không có đối số, nó sẽ in một danh sách tất cả các biến bao gồm các biến môi trường và shell và các hàm shell. unset –Lệnh xóa các biến shell và môi trường. export –Lệnh đặt các biến môi trường

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.