Tại sao $ RANDOM không có trong đầu ra của 'env'?


23

Tôi biết envlà một lệnh shell, nó có thể được sử dụng để in danh sách các biến môi trường hiện tại. Và theo tôi hiểu, RANDOMcũng là một biến môi trường.

Vậy tại sao khi tôi khởi chạy envtrên Linux, đầu ra không bao gồm RANDOM?


4
envkhông phải là lệnh shell vì nó thường không được tích hợp vào shell.
schily

@schily BTW cho Bash, declare -xlà tương đương trong một vỏ dựng sẵn.
wjandrea

Câu trả lời:


42

RANDOMkhông phải là một biến môi trường. Đó là một biến vỏ được duy trì bởi một số vỏ. Nó thường không được xuất theo mặc định. Đây là lý do tại sao nó không hiển thị trong đầu ra của env.

Khi nó được sử dụng ít nhất một lần, nó sẽ hiển thị trong đầu ra của setchính nó, liệt kê các biến shell (và hàm) và các giá trị của chúng trong phiên shell hiện tại. Hành vi này phụ thuộc vào trình bao và sử dụng pdkshtrên OpenBSD, RANDOMsẽ được liệt kê setngay cả khi không được sử dụng trước đó.


Phần còn lại của câu trả lời này liên quan đến những gì có thể xảy ra nếu RANDOMđược xuất khẩu (tức là biến thành một biến môi trường).

Xuất nó với nó export RANDOMsẽ biến nó thành một biến môi trường nhưng việc sử dụng nó sẽ bị hạn chế nghiêm trọng vì giá trị của nó trong một tiến trình con sẽ là "ngẫu nhiên nhưng tĩnh" (có nghĩa là nó sẽ là một số ngẫu nhiên không thay đổi). Các hành vi chính xác khác nhau giữa các vỏ.

Tôi đang sử dụng pdkshtrên OpenBSD trong ví dụ dưới đây và tôi nhận được một giá trị ngẫu nhiên mới trong mỗi lần awkchạy (nhưng cùng một giá trị mỗi lần trong cùng một awkví dụ). Sử dụng bash, tôi sẽ nhận được chính xác cùng một giá trị ngẫu nhiên trong tất cả các yêu cầu awk.

$ awk 'BEGIN { print ENVIRON["RANDOM"], ENVIRON["RANDOM"] }'
25444 25444

$ awk 'BEGIN { print ENVIRON["RANDOM"], ENVIRON["RANDOM"] }'
30906 30906

Trong bash, giá trị xuất của RANDOMsẽ vẫn tĩnh bất kể việc sử dụng RANDOMtrong trình bao (trong đó mỗi lần sử dụng $RANDOMvẫn sẽ cho một giá trị mới).

Điều này là do mỗi tham chiếu đến biến vỏ RANDOM trong bashlàm cho việc tiếp cận vỏ nội bộ của mình get_random()chức năng để cung cấp cho các biến một giá trị ngẫu nhiên mới, nhưng vỏ không cập nhật các biến môi trường RANDOM . Điều này tương tự trong hành vi như với động khác bashbiến, chẳng hạn như LINENO, SECONDS, BASHPID, vv

Để cập nhật biến môi trường RANDOMtrong bash, bạn sẽ phải gán cho nó giá trị của biến shell RANDOM tái xuất nó:

export RANDOM="$RANDOM"

Tôi không rõ liệu điều này có tác dụng phụ bổ sung cho việc tái tạo hạt giống số ngẫu nhiên trong bashhay không (nhưng một phỏng đoán có giáo dục sẽ là nó không).


1
RANDOMthậm chí có một giá trị trước khi bạn sử dụng nó? Tôi đã luôn cho rằng nó chỉ được điền khi được gọi.
terdon

1
Nó không phải là, hướng dẫn bash đề cập đến nó.
terdon

1
Mặc dù nếu bạn làm thậm chí export RANDOMhoặc declare -p RANDOM, nó xuất hiện, vì vậy tôi không chắc liệu nó có sử dụng được không trước khi được tham chiếu ...
ilkkachu

1
"Giá trị của nó trong một tiến trình con sẽ là ngẫu nhiên, nhưng tĩnh." Nếu nó là tĩnh, nó không phải là ngẫu nhiên , cho dù đó là ba byte hay mười sáu.
l0b0

3
@ l0b0 Sẽ là ngẫu nhiên theo nghĩa là bạn sẽ không thể dự đoán được. Rõ ràng, một khi bạn đã đọc nó, nó sẽ không còn ngẫu nhiên nữa vì nó sẽ không thay đổi (trừ khi tái xuất như tôi đã trình bày, trong trường hợp đó, biến môi trường sẽ nhận được giá trị ngẫu nhiên mới). Đây là lý do tại sao tôi nói nó ngẫu nhiên nhưng tĩnh. Bây giờ tôi đã làm rõ điều này trong văn bản.
Kusalananda

16

Không phải tất cả các biến được đặt trong phiên shell của bạn là biến môi trường. "Biến môi trường" chỉ đề cập đến các biến đã được xuất ra môi trường bằng cách sử dụng exportnội dung. Các envlệnh chỉ in như môi trường biến. Ví dụ:

$ foo="bar"
$ env | grep foo ## returns nothing
$ export foo
$ env | grep foo ## now, env will print it
foo=bar

Nếu bạn muốn xem tất cả các biến được đặt trong phiên của mình, bất kể chúng có được xuất hay không, bạn có thể sử dụng set:

$ set | grep foo=
foo=bar

Nội dung setcũng trả về các hàm, vì vậy để chỉ nhìn thấy các biến, bạn có thể sử dụng:

set | grep  '^[^[:space:]]*='

Cuối cùng, RANDOMbiến đặc biệt ở chỗ nó chỉ được gán một giá trị khi bạn tham chiếu nó. Điều này được đề cập trong bash (1) :

RANDOM

    Mỗi lần tham số này được tham chiếu, một số nguyên ngẫu nhiên trong khoảng từ 0 đến 32767 được tạo ra. Chuỗi các số ngẫu nhiên có thể được khởi tạo bằng cách gán giá trị cho RANDOM. Nếu RANDOMkhông được đặt, nó sẽ mất các thuộc tính đặc biệt, ngay cả khi sau đó được đặt lại.

Vì vậy, ngay cả khi đó là một biến môi trường như bạn nghĩ, nó sẽ không được hiển thị envvì nó sẽ không được đặt cho đến lần đầu tiên bạn gọi nó. Đó cũng là lý do tại sao nó không được hiển thị trong set:

$ set | grep RAN   ## returns nothing, RANDOM is unset
$ echo "$RANDOM"   ## this will assign a value to RANDOM
1234
$ set | grep RAN   ## so now it will also appear in the output of set 
RANDOM=1234

Đó là một khám phá thú vị, liên quan set | grep RAN. Tôi sẽ không mong đợi nó. FWIW, tôi tin rằng tài liệu này không thể dự đoán được.
G-Man nói 'Phục hồi Monica'

1
PS Chúc mừng bạn đã đạt 120.000. (Tôi đoán là tôi vừa đưa bạn qua.)
G-Man nói 'Tái lập lại Monica'

4

Hầu hết các shell sẽ có một số biến khác được đặt hoặc sử dụng bởi shell không được xuất sang các tiến trình con theo mặc định.

Trong Bash, có một số cái rõ ràng cụ thể của Bash:

$ echo "${!BASH*}"
BASH BASHOPTS BASHPID BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
$ echo $BASH_VERSION
4.4.12(1)-release
$ env|grep -c BASH
0

Sau đó, có thêm các tiêu chuẩn như OPTINDOPTERR(được sử dụng bởi getopts), và PS2, PS3(các dấu nhắc phụ) và thậm chí một biến "ma thuật" khác: SECONDS(hiển thị thời gian tính bằng giây kể từ khi vỏ bắt đầu)

Trong Bash, bạn có thể thấy tất cả các biến và trạng thái xuất của chúng với declare -p. Những cái được đánh dấu -xđược xuất khẩu, những cái không xcó. (Một số sẽ có các cờ khác như icho số nguyên hoặc rchỉ đọc.)

Trong Zsh hoặc ksh93, bạn có thể sử dụng typeset -p, mặc dù Zsh đánh dấu các biến được xuất bằng cách thay đổi typesetthành exportđầu ra, thay vì sử dụng cờ. exporttự nó cũng sẽ hiển thị tất cả các biến được xuất, nhưng đó là về cùng một kết quả bạn nhận được bằng cách chạy env.


2

Nếu bạn google cho điều này, các tài liệu nêu sau:

$RANDOMlà một hàm Bash nội bộ (không phải là hằng số) trả về số nguyên giả [1] trong phạm vi 0 - 32767. Không nên sử dụng nó để tạo khóa mã hóa.

Nếu bạn sử dụng, stracebạn có thể thấy $RANDOM"biến" được truyền trực tiếp vào các lệnh như thể đó là bất kỳ biến shell thông thường hoặc biến môi trường nào, nhưng đó chỉ là một hàm bên trong được tích hợp trong shell, Bash, đó là thực hiện mở rộng.

$ strace -t echo "random value: $RANDOM"
04:37:58 execve("/bin/echo", ["echo", "random value: 30795"], [/* 27 vars */]) = 0
04:37:58 brk(NULL)                      = 0x19c1000
04:37:58 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9841351000
...

so với biến thông thường này:

$ strace -t echo "random value: $SOMEVAR"
04:40:19 execve("/bin/echo", ["echo", "random value: helloworld"], [/* 27 vars */]) = 0
04:40:19 brk(NULL)                      = 0x154b000
04:40:19 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f659d2eb000
...

Các biến không được truyền vào như một tài liệu tham khảo.

Tài liệu tham khảo


1
tốt, không phải là vượt qua giá trị mở rộng của $RANDOMhoặc $SOMEVARthông qua một đối số dòng lệnh, và không phải là một biến môi trường? Bạn cần exportcả hai để vượt qua môi trường.
ilkkachu

Không có điều đó sẽ làm cho không có sự khác biệt. Vỏ mở rộng chúng bất kể. Cách tôi chỉ ra về cơ bản là làm nổi bật thực tế là lớp vỏ đang thực hiện việc mở rộng.
slm

2
Đầu stracera dường như không bắt được chức năng bên trong được chạy bởi shell. Trong cả hai trường hợp, biến đã được mở rộng trong dòng đầu tiên của strace. Tôi không hiểu sự khác biệt mà bạn đang chỉ đến. Tôi đang thiếu gì?
terdon

Cho thấy việc $RANDOMmở rộng được thực hiện bên trong vỏ. Về cơ bản, nó xác nhận rằng shell đang xác định giá trị và không chuyển tham chiếu đến một biến. Shell khi nó mở rộng dòng lệnh để thực thi các phân tích cú pháp $RANDOMvà chuyển biểu mẫu được mở rộng tới echo.
slm

2
Vì vậy, không có gì giống như một biến môi trường , sau đó.
Toby Speight
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.