Làm cách nào để đặt biến môi trường trên dòng lệnh và để nó xuất hiện trong các lệnh?


152

Nếu tôi chạy

export TEST=foo
echo $TEST

Nó xuất ra foo.

Nếu tôi chạy

TEST=foo echo $TEST

Nó không. Làm thế nào tôi có thể có được chức năng này mà không cần sử dụng xuất hoặc tập lệnh?


Cẩn thận, có nhiều điều cho câu chuyện này hơn ban đầu xuất hiện. Tôi mời bạn kiểm tra câu trả lời của tôi.
jasonleonhard

Câu trả lời:


180

Điều này là do shell mở rộng biến trong dòng lệnh trước khi nó thực sự chạy lệnh và tại thời điểm đó biến không tồn tại. Nếu bạn dùng

TEST=foo; echo $TEST

nó sẽ làm việc

exportsẽ làm cho biến xuất hiện trong môi trường của các lệnh được thực hiện sau đó (để biết cách thức hoạt động của bash này help export). Nếu bạn chỉ cần biến xuất hiện trong môi trường của một lệnh, hãy sử dụng những gì bạn đã thử, tức là:

TEST=foo your-application

1
Còn / usr / bin / env thì sao? điều đó cũng không hiệu quả, bạn có biết tại sao không?
Benubird

5
Lý do tương tự - shell mở rộng $TESTtrước khi dòng lệnh được thực thi. Khi echođang chạy (cũng lưu ý rằng echothường sẽ dịch sang lệnh tích hợp shell chứ không phải /bin/echo) nó sẽ thấy biến được đặt trong môi trường của nó. Tuy nhiên, echo $TESTkhông cho biết echođể xuất nội dung của biến TESTtừ môi trường của nó. Nó bảo shell chạy echovới đối số là bất cứ thứ gì hiện có trong biến được gọi TEST- và đó là hai thứ rất khác nhau.
peterph

@peterph Nếu shell mở rộng biến trong dòng lệnh trước khi nó thực sự chạy lệnh thì tại sao nó không mở rộng nó trong var=value sh -c 'echo "$var"'?
túp lều

Như tôi đã giải thích cho bạn ở đây : đó là vì các biến được mở rộng bên trong dấu ngoặc kép (ví dụ "… $var …":) nhưng không nằm trong dấu ngoặc đơn (ví dụ '… $var …':). Vì echo "$var"nằm trong dấu ngoặc đơn, toàn bộ chuỗi được chuyển sang trình bao ( sh -c) mới mà không bị hiểu bởi lớp vỏ tương tác bên ngoài. Tiết (Cont'd)
G-Man

(Tiếp theo) Như Asgal đã nói hôm qua , có hai vòng phân tích cú pháp - mặc dù tôi tin rằng igal đã đọc sai câu hỏi của bạn. Không có hai vòng phân tích cú pháp ngoài việc phân tích cú pháp được thực hiện bởi trình bao cha. Có hai vòng phân tích cú pháp - một vòng được thực hiện bởi lớp vỏ bên ngoài, tương tác (cha mẹ) và một vòng bởi sh -clớp vỏ con ( ) mới .
G-Man

39

Tôi nghi ngờ bạn muốn có các biến shell để có phạm vi giới hạn, thay vì các biến môi trường. Biến môi trường là một danh sách các chuỗi được truyền cho các lệnh khi chúng được thực thi .

Trong

var=value echo whatever

Bạn đang truyền var=valuechuỗi đến môi trường mà tiếng vang nhận được. Tuy nhiên, echokhông làm bất cứ điều gì với danh sách môi trường của nó và dù sao trong hầu hết các shell, echođược tích hợp và do đó không được thực thi .

Nếu bạn đã viết

var=value sh -c 'echo "$var"'

Đó sẽ là một vấn đề khác. Ở đây, chúng ta đang chuyển var=valueđến shlệnh và shthực sự sử dụng môi trường của nó. Shell chuyển đổi từng biến mà chúng nhận được từ môi trường của chúng thành biến shell, vì vậy varbiến môi trường shnhận được sẽ được chuyển đổi thành $varbiến và khi nó mở rộng nó trong echodòng lệnh đó, nó sẽ trở thành echo value. Bởi vì môi trường theo mặc định được kế thừa, echocũng sẽ nhận được var=valuetrong môi trường của nó (hoặc nếu nó được thực thi), nhưng một lần nữa, echokhông quan tâm đến môi trường.

Bây giờ, nếu như tôi nghi ngờ, điều bạn muốn là giới hạn phạm vi của các biến shell, có một số cách tiếp cận có thể.

Có thể di chuyển (Bourne và POSIX):

(var=value; echo "1: $var"); echo "2: $var"

(...) ở trên bắt đầu một lớp vỏ phụ (một quy trình shell mới trong hầu hết các shell), do đó, bất kỳ biến nào được khai báo ở đó sẽ chỉ ảnh hưởng đến lớp vỏ phụ đó, vì vậy tôi mong mã ở trên sẽ xuất ra "1: value" và "2:" hoặc "2: anything-var-was-set-to-before".

Với hầu hết các shell giống như Bourne, bạn có thể sử dụng các hàm và hàm dựng sẵn "cục bộ":

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

Với zsh, bạn có thể sử dụng các hàm nội tuyến:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

hoặc là:

function { local var=value; echo "1: $var"; }; echo "2: $var"

Với bash và zsh (nhưng không phải tro, pdksh hoặc AT & T ksh), thủ thuật này cũng hoạt động:

var=value eval 'echo "1: $var"'; echo "2: $var"

Một biến thể mà làm việc trong một vài vỏ hơn ( dash, mksh, yash) nhưng không zsh(trừ trường hợp trong sh/ kshthi đua):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(sử dụng commandtrước một nội dung đặc biệt (ở đây eval) trong các vỏ POSIX sẽ loại bỏ tính đặc biệt của chúng (ở đây, các phép gán biến trong số chúng vẫn có hiệu lực sau khi chúng quay trở lại))


Đối với mục đích của tôi, đây là giải pháp chính xác. Tôi không muốn biến 'var' gây ô nhiễm môi trường như trong câu trả lời được chấp nhận.
kronenpj

6

Bạn đang làm điều đó một cách chính xác, nhưng cú pháp bash rất dễ hiểu sai: bạn có thể nghĩ rằng đó là echo $TESTnguyên nhân echođể tìm nạp TESTvar sau đó in nó, nhưng không. Vì vậy

export TEST=123

sau đó

TEST=456 echo $TEST

liên quan đến trình tự sau:

  1. Shell phân tích toàn bộ dòng lệnh và thực hiện tất cả các thay thế biến, vì vậy dòng lệnh trở thành

    TEST=456 echo 123
  2. Nó tạo ra các lọ tạm thời được đặt trước lệnh, vì vậy nó lưu giá trị hiện tại của TESTvà ghi đè lên nó bằng 456; dòng lệnh bây giờ

    echo 123
  3. Nó thực thi lệnh còn lại, trong trường hợp này sẽ in 123 thành stdout (vì vậy lệnh shell vẫn không sử dụng giá trị tạm thời của TEST)

  4. Nó khôi phục giá trị của TEST

Sử dụng printenv thay vào đó, vì nó không liên quan đến việc thay thế biến:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>

+1 Tôi thấy printenvhữu ích cho việc thử nghiệm / bằng chứng về khái niệm (hoạt động giống như một kịch bản, trái ngược với echo)
Daryn

5

Bạn có thể làm việc này bằng cách sử dụng:

TEST=foo && echo $TEST

6
Trong trường hợp này, TEST=foođang chạy như một câu lệnh riêng - nó không chỉ được đặt trong ngữ cảnh echo.
tiền mã hóa
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.