Tại sao, A A = 10 echo $ A không in 10?


25

Lệnh này:

A=10 echo $A

in một dòng trống. Tại sao không 10? Tại sao cài đặt môi trường tạm thời tại chỗ không hoạt động?

Tôi muốn biết lý do và giải thích hơn là giải pháp.

Tôi đã sử dụng

LANG=C gcc ...

để buộc gcc sử dụng ngôn ngữ dự phòng (tiếng Anh) thay vì ngôn ngữ hệ thống (tiếng Trung Quốc). Vì vậy, tôi giả sử một VAR=valuetiền tố sẽ thiết lập một môi trường tạm thời cho lệnh theo sau nó. Nhưng có vẻ như tôi có một số hiểu lầm.

Câu trả lời:


22

Đó là vấn đề theo thứ tự các bước khác nhau để đánh giá lệnh xảy ra.

A=10 echo $Aphân tích đầu tiên lệnh thành một lệnh đơn giản làm bằng ba chữ A=10, echo$A. Sau đó, mỗi từ trải qua thay thế biến, tức là chuyển đổi các mở rộng biến như $Avào giá trị của chúng (Tôi đang bỏ qua các bước không thể nhìn thấy gì).

Nếu Acó giá trị fooban đầu, kết quả của các bước mở rộng là một lệnh đơn giản mà vẫn có ba chữ: A=10, echofoo. (Shell cũng nhớ tại thời điểm này các ký tự ban đầu nằm trong dấu ngoặc kép - trong trường hợp này là không có.) Bước tiếp theo là thực thi lệnh. Vì A=10bắt đầu bằng một tên biến hợp lệ theo sau là một dấu bằng, nên nó được coi là một phép gán; biến Ađược đặt thành 10trong cả shell và môi trường trong quá trình thực thi lệnh. (Thông thường bạn cần phải viết export Ađể có Atrong môi trường chứ không chỉ là biến shell; đây là một ngoại lệ.) Từ tiếp theo không phải là một bài tập, vì vậy nó được coi là một tên lệnh (đó là một lệnh tích hợp). Cácecholệnh không phụ thuộc vào bất kỳ biến nào, do đó A=10 echo $Acó tác dụng chính xác như echo $A.

Nếu bạn muốn đặt một biến chỉ cho thời lượng của một lệnh, nhưng thực hiện chuyển nhượng vào tài khoản trong khi thực hiện lệnh, bạn có thể sử dụng một chuỗi con. Một subshell, được biểu thị bằng dấu ngoặc đơn, làm cho tất cả các thay đổi trạng thái (gán biến, thư mục hiện tại, định nghĩa hàm, v.v.) cục bộ cho subshell.

(A=10; echo $A)

Làm cho điều đó export A=10nếu bạn muốn xuất biến ra môi trường để nó được nhìn thấy bởi các chương trình bên ngoài.


Cảm ơn, tôi có thể nói A=10 (echo $A)và nhận được 10không?
Động cơ Trái đất

2
@EarthEngine Không, đó sẽ là một lỗi cú pháp. Việc gán phải ở đầu một lệnh đơn giản (nghĩa là chỉ một tên lệnh và một số tham số, và tùy chọn một số nhiệm vụ ban đầu và một số chuyển hướng). A=10; (echo $A)đầu ra 10nhưng cũng đặt Acho phần còn lại của tập lệnh.
Gilles 'SO- ngừng trở nên xấu xa'

2
@EarthEngine Nhưng bạn có thể nói A=10 eval 'echo $A'. Dấu ngoặc đơn dừng $Ađược giải thích cho đến khi toàn bộ dòng được đánh giá ... Đến lúc đó A = 10. Tôi xem xét câu trả lời này đúng hơn so với chấp nhận.
Oli

Tôi nghĩ rằng đây là lời giải thích chính xác. Lý do cho hành vi chỉ là thứ tự khi việc mở rộng $Avà chuyển nhượng Ađang diễn ra. Ví dụ, A=5; A=6 let 'a=A'; echo $atrả về 6không 5và tôi không nghĩ letbắt đầu một subshell, vì đó là lệnh dựng sẵn.
David Ongaro

@EarthEngine: Khi nói rằng lời giải thích chính xác là thứ tự đánh giá, nó có thể gây hiểu nhầm: A=10 echo $Asẽ không được đặt A=10cho bất kỳ lệnh nào sau đó, ngay cả khi chúng nằm trong các dòng khác nhau (khi rõ ràng bài tập đã được đánh giá). Đó không phải là về trật tự, mà là về phạm vi
MestreLion

37

Khi bạn sử dụng LANG=C gcc ... những gì xảy ra là vỏ đặt LANG cho gcc's môi trường chỉ , và không cho hiện tại môi trường tự ( xem chú thích ). Vì vậy, sau khi gcckết thúc, LANGtrở lại giá trị trước đó của nó (hoặc bỏ đặt).

Ngoài ra, khi bạn sử dụng, A=10 echo $Anó là lớp vỏ thay thế $ A, không phải tiếng vang và sự thay thế này (được gọi là "mở rộng") xảy ra trước khi câu lệnh được ước tính (bao gồm cả phép gán), do đó, phải hoạt động như Agiá trị mong đợi trong môi trường hiện tại trước tuyên bố đó.

Đó là lý do tại sao A=10 echo $Akhông hoạt động như mong đợi: A=10sẽ được đặt cho tiếng vang, nhưng tiếng vang bên trong bỏ qua giá trị của biến môi trường A. Và $Ađược thay thế bằng giá trị được đặt trong trình bao hiện tại (không có giá trị nào), và sau đó được chuyển làm đối số cho tiếng vang.

Vì vậy, giả định của bạn là chính xác: VAR=value command không hoạt động, nhưng điều này chỉ có liên quan nếu commandnội bộ sử dụng VAR. Nếu không, bạn vẫn có thể chuyển valuelàm đối số cho command, nhưng đối số được thay thế bằng trình bao hiện tại , do đó chúng phải được đặt trước khi sử dụng:VAR=value; command "$VAR"

Nếu bạn biết cách tạo một tập lệnh thực thi, bạn có thể thử điều này dưới dạng thử nghiệm:

#!/bin/sh
echo "1st argument is $1"
echo "A is $A"

Lưu nó dưới dạng testscriptvà thử:

$ A=5; A=10 testscript "$A"; echo "$A"
1st argument is 5
A is 10
5

Cuối cùng nhưng không kém phần quan trọng, đáng để biết sự khác biệt giữa các biến shell và biến môi trường và các đối số chương trình .

Dưới đây là một số tài liệu tham khảo tốt:

.

(*) Lưu ý: về mặt kỹ thuật, shell cũng được đặt trong môi trường hiện tại và đây là lý do: Một số lệnh, như echo, readtestlà các nội dung shell , và do đó chúng không sinh ra một tiến trình con. Họ chạy trong môi trường hiện tại. Nhưng shell chăm sóc cho nhiệm vụ chỉ tồn tại cho đến khi lệnh đang chạy, vì vậy đối với tất cả các mục đích thực tế, hiệu quả là như nhau: việc chuyển nhượng chỉ được nhìn thấy bởi lệnh đơn lẻ đó.


2
Giải thích này thực sự không chính xác, mặc dù nó dẫn đến kết luận chính xác trong tất cả nhưng một vài trường hợp góc. Giải thích thực sự là thứ tự mở rộng: $Ađược đánh giá trước khi chuyển nhượng diễn ra. Tôi nghĩ rằng lời giải thích của bạn chỉ thất bại trong trường hợp các tiện ích tích hợp thông thường có hành vi phụ thuộc vào giá trị của biến: tích hợp không thấy giá trị được gán. Một ví dụ phổ biến là IFS=: read one two three rest, đọc các trường được phân tách bằng dấu hai chấm: readnội trang sẽ thấy giá trị của IFS.
Gilles 'SO- ngừng trở nên xấu xa'

Bản thân nó không dành cho shell hiện tại. Lỗi là sai: biến được đặt trong shell hiện tại, nhưng nó chỉ tồn tại cho lệnh đơn giản hiện tại. echosẽ thấy giá trị 10cho A, nếu nó quan tâm.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: cảm ơn rất nhiều vì đã làm rõ! Tôi đã không nhận thức được sự tinh tế này. Vì vậy, nếu tôi hiểu đúng, bash để thiết lập cho hiện tại môi trường khác builtins (mà không đẻ trứng một mới pid) sẽ không nhìn thấy sự phân công như khác lệnh "regurlar" sẽ. Nhưng nó bỏ cài đặt sau khi lệnh được thực hiện để giới hạn phạm vi gán. Điều này có đúng không, tôi sẽ sửa câu trả lời của mình cho phù hợp. PS: kỹ thuật sang một bên, tôi vẫn nghĩ rằng một câu trả lời nên nhấn mạnh vào khía cạnh phạm vi, không phải thứ tự đánh giá, nếu không người ta có thể nghĩ A=10 test; echo $Asẽ in 10
MestreLion

3

Một cách có thể sạch sẽ để làm những gì bạn rõ ràng mong muốn là đưa ra lệnh:

A=10 eval 'echo $A'

Điều này sẽ có hiệu lực trì hoãn việc thay thế giá trị 10 vào vị trí của $ A sang bối cảnh sau này (nghĩa là 'bên trong' eval, đã biết về việc chuyển nhượng). Lưu ý rằng các trích dẫn duy nhất là cần thiết. Cấu trúc như vậy truyền đạt rõ ràng việc gán cho lệnh mong muốn của bạn (echo trong trường hợp này) mà không có nguy cơ gây ô nhiễm môi trường hiện tại của bạn.

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.