ทำไม $ RANDOM ถึงไม่รวมอยู่ในผลลัพธ์ของ 'env'


23

ฉันรู้ว่าenvเป็นคำสั่งเชลล์มันสามารถใช้ในการพิมพ์รายการของตัวแปรสภาพแวดล้อมปัจจุบัน และเท่าที่ฉันเข้าใจRANDOMก็เป็นตัวแปรสภาพแวดล้อม

ดังนั้นทำไมเมื่อฉันเปิดใช้งานenvบน Linux ผลลัพธ์ไม่รวมRANDOM?


4
envไม่ใช่คำสั่งเชลล์เนื่องจากโดยปกติจะไม่ได้สร้างไว้ในเชลล์
schily

@ schily BTW สำหรับ Bash declare -xเทียบเท่ากับเชลล์ในตัว
wjandrea

คำตอบ:


42

RANDOMไม่ใช่ตัวแปรสภาพแวดล้อม มันเป็นตัวแปรของเชลล์ที่ดูแลโดยเชลล์บางตัว โดยทั่วไปจะไม่ส่งออกโดยค่าเริ่มต้น envนี่คือเหตุผลที่มันไม่ได้แสดงขึ้นในการส่งออกของ

เมื่อมีการใช้งานอย่างน้อยหนึ่งครั้งมันจะปรากฏขึ้นในผลลัพธ์setซึ่งโดยตัวมันเองจะแสดงรายการตัวแปรเชลล์ (และฟังก์ชั่น) และค่าของพวกเขาในเซสชั่นเปลือกปัจจุบัน พฤติกรรมนี้ขึ้นอยู่กับเชลล์และการใช้งานpdkshบน OpenBSD RANDOMจะแสดงรายการโดยsetแม้ว่าไม่เคยใช้มาก่อน


ส่วนที่เหลือของคำตอบนี้เกี่ยวข้องกับสิ่งที่คาดว่าจะเกิดขึ้นหากRANDOMมีการส่งออก (เช่นเปลี่ยนเป็นตัวแปรสภาพแวดล้อม)

การส่งออกด้วยexport RANDOMจะทำให้ตัวแปรสภาพแวดล้อม แต่การใช้งานจะถูก จำกัด อย่างรุนแรงเนื่องจากค่าในกระบวนการลูกจะเป็น "สุ่ม แต่คงที่" (หมายถึงมันจะเป็นหมายเลขสุ่มไม่เปลี่ยนแปลง) พฤติกรรมที่แน่นอนแตกต่างกันระหว่างเปลือกหอย

ฉันกำลังใช้pdkshOpenBSD ในตัวอย่างด้านล่างและฉันได้รับค่าสุ่มใหม่ในการawkทำงานแต่ละครั้ง(แต่ค่าเดียวกันทุกครั้งภายในawkอินสแตนซ์เดียวกัน) ใช้bashผมจะได้รับสิ่งมูลค่าสุ่มเหมือนกันในทุกawkสวดของ

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

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

ในbashค่าที่ส่งออกของRANDOMจะคงที่โดยไม่คำนึงถึงการใช้งานของRANDOMเชลล์ (ซึ่งการใช้งานแต่ละครั้ง$RANDOMจะยังคงให้ค่าใหม่)

นี้เป็นเพราะแต่ละอ้างอิงถึงตัวแปรเปลือก RANDOMในbashทำให้การเข้าถึงเปลือกภายในของget_random()ฟังก์ชั่นที่จะให้ตัวแปรสุ่มค่าใหม่ แต่เปลือกไม่ปรับปรุงตัวแปรสภาพแวดล้อม RANDOMนี่คือที่คล้ายกันในลักษณะการทำงานเช่นเดียวกับแบบไดนามิกอื่น ๆbashตัวแปรเช่นLINENO, SECONDS, BASHPIDฯลฯ

การปรับปรุงตัวแปรสภาพแวดล้อมRANDOMในการbashที่คุณจะต้องมีการกำหนดค่าของตัวแปรเปลือกRANDOM และการส่งออกมัน

export RANDOM="$RANDOM"

ฉันไม่ทราบแน่ชัดว่าจะมีผลข้างเคียงเพิ่มเติมจากการสร้างตัวสร้างตัวเลขสุ่มอีกครั้งbashหรือไม่ (แต่การคาดเดาที่ได้รับการศึกษาน่าจะไม่เกิดขึ้น)


1
ไม่RANDOMได้มีค่าก่อนที่คุณจะใช้มันได้หรือไม่ ฉันคิดเสมอว่ามันจะมีประชากรเพียงเมื่อเรียก
terdon

1
มันไม่ใช่คู่มือทุบตีพูดถึงมัน
terdon

1
แม้ว่าคุณจะทำexport RANDOMหรือแม้กระทั่งdeclare -p RANDOMมันก็ปรากฏขึ้น แต่ฉันก็ไม่แน่ใจว่ามันมีประโยชน์อะไรที่มันไม่ได้มีอยู่ก่อนที่จะถูกอ้างอิง ...
ilkkachu

1
"ค่าของมันในกระบวนการลูกจะสุ่ม แต่คงที่" ถ้าเป็นแบบคงที่จะไม่สุ่มไม่ว่าจะเป็นสามไบต์หรือสิบหก
l0b0

3
@ l0b0 มันจะเป็นการสุ่มในแง่ที่ว่าคุณจะไม่สามารถทำนายได้ เห็นได้ชัดว่าเมื่อคุณอ่านแล้วมันจะไม่สุ่มอีกต่อไปเนื่องจากจะไม่เปลี่ยนแปลง (เว้นแต่จะส่งออกอีกครั้งตามที่ฉันแสดงซึ่งในกรณีนี้ตัวแปรสภาพแวดล้อมจะได้รับค่าสุ่มใหม่) นี่คือเหตุผลที่ฉันบอกว่ามันสุ่ม แต่คงที่ ฉันได้อธิบายสิ่งนี้ในข้อความตอนนี้
Kusalananda

16

ไม่ใช่ตัวแปรทั้งหมดที่ตั้งค่าไว้ในเชลล์เซสชั่นของคุณเป็นตัวแปรสภาพแวดล้อม "ตัวแปรสภาพแวดล้อม" หมายถึงเฉพาะตัวแปรที่ส่งออกไปยังสภาพแวดล้อมโดยใช้exportbuiltin envคำสั่งเพียงพิมพ์เช่นสภาพแวดล้อมตัวแปร ตัวอย่างเช่น:

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

หากคุณต้องการดูตัวแปรทั้งหมดที่ตั้งค่าในเซสชันของคุณโดยไม่คำนึงว่าพวกเขาได้ถูกส่งออกหรือไม่คุณสามารถใช้set:

$ set | grep foo=
foo=bar

setbuiltin ยังผลตอบแทนที่ได้ฟังก์ชั่นเพื่อที่จะดูตัวแปรเท่านั้นคุณสามารถใช้:

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

ในที่สุดRANDOMตัวแปรนี้มีความพิเศษที่จะกำหนดค่าเฉพาะเมื่อคุณอ้างอิงเท่านั้น สิ่งนี้ถูกกล่าวถึงในbash (1) :

RANDOM

    แต่ละครั้งที่มีการอ้างอิงพารามิเตอร์นี้จะมีการสร้างจำนวนเต็มแบบสุ่มระหว่าง 0 ถึง 32767 RANDOMลำดับของตัวเลขสุ่มอาจจะเริ่มต้นด้วยการกำหนดค่าให้กับ หากRANDOMไม่ได้ตั้งค่าจะสูญเสียคุณสมบัติพิเศษแม้ว่าจะถูกรีเซ็ตในภายหลัง

ดังนั้นแม้ว่ามันจะเป็นตัวแปรสภาพแวดล้อมอย่างที่คุณคิดมันก็จะไม่ปรากฏในenvเนื่องจากมันจะไม่ถูกตั้งค่าจนกว่าจะเป็นครั้งแรกที่คุณเรียกมันว่า นี่คือสาเหตุที่ไม่ปรากฏใน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

set | grep RANนั่นคือการค้นพบที่น่าสนใจเกี่ยวกับ ฉันจะไม่คาดหวังมัน FWIW ฉันเชื่อว่าเอกสารไม่สามารถคาดการณ์ได้
G-Man พูดว่า 'Reinstate Monica'

1
ป. ล. ขอแสดงความยินดีกับการเข้าถึง 120,000 (ฉันเดาว่าฉันจะพาคุณไป)
G-Man พูดว่า 'Reinstate Monica'

4

เชลล์ส่วนใหญ่จะมีตัวแปรอื่น ๆ จำนวนหนึ่งที่ตั้งค่าหรือใช้งานโดยเชลล์ที่ไม่ได้ถูกส่งออกไปยังกระบวนการลูกโดยค่าเริ่มต้น

ใน Bash มีบางอย่างที่เจาะจง 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

จากนั้นก็มีคนมาตรฐานมากขึ้นเช่นOPTINDและOPTERR(ใช้getopts) และPS2, PS3(แจ้งรอง) และแม้กระทั่งอีก "วิเศษ" ตัวแปร: SECONDS(แสดงเวลาเป็นวินาทีตั้งแต่เปลือกเริ่มต้น)

declare -pในทุบตีคุณสามารถดูตัวแปรทั้งหมดและสถานะของการส่งออกของพวกเขาด้วย คนที่มีเครื่องหมาย-xถูกส่งออก, คนที่ไม่มีxไม่ได้ (บางส่วนจะมีค่าสถานะอื่นเช่นiค่าจำนวนเต็มหรือแบบrอ่านอย่างเดียว)

ใน Zsh หรือ ksh93 คุณสามารถใช้งานtypeset -pได้แม้ว่า Zsh จะทำเครื่องหมายตัวแปรที่ส่งออกโดยเปลี่ยนtypesetเป็นexportในเอาต์พุตแทนที่จะใช้แฟล็ก exportด้วยตัวเองนอกจากนี้ยังจะแสดงตัวแปรทั้งหมดที่ส่งออก envแต่ที่เกี่ยวกับผลเดียวกันคุณจะได้รับจากการทำงาน


2

หากคุณทำเช่นนี้เอกสารจะระบุสิ่งต่อไปนี้:

$RANDOMเป็นฟังก์ชัน Bash ภายใน(ไม่ใช่ค่าคงที่) ที่ส่งคืนจำนวนเต็มเทียม[1]ในช่วง 0 - 32767 ไม่ควรใช้เพื่อสร้างคีย์การเข้ารหัส

หากคุณใช้straceคุณจะเห็นว่า$RANDOM"ตัวแปร" ถูกส่งโดยตรงไปยังคำสั่งราวกับว่ามันเป็นตัวแปรเชลล์สามัญหรือตัวแปรสภาพแวดล้อม แต่มันเป็นเพียงฟังก์ชั่นภายในที่สร้างไว้ในเชลล์ Bash นั่นเป็นการขยายตัว

$ 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
...

เทียบกับตัวแปรปกตินี้:

$ 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
...

ตัวแปรไม่ถูกส่งผ่านเป็นข้อมูลอ้างอิง

อ้างอิง


1
ทีนี้การส่งค่าที่เพิ่มขึ้นของ$RANDOMหรือ$SOMEVARผ่านอาร์กิวเมนต์บรรทัดคำสั่งไม่ใช่ตัวแปรสภาพแวดล้อมใช่ไหม คุณต้องการให้exportทั้งคู่ส่งผ่านสภาพแวดล้อม
ilkkachu

ไม่ว่าจะสร้างความแตกต่างไม่ เปลือกขยายพวกเขาโดยไม่คำนึงถึง วิธีที่ฉันแสดงให้เห็นว่ามันเน้นความจริงที่ว่าเชลล์ทำการขยายตัว
slm

2
straceการส่งออกไม่ได้ดูเหมือนจะจับการทำงานฟังก์ชั่นภายในโดยเปลือก straceในทั้งสองกรณีตัวแปรได้รับการขยายตัวในบรรทัดแรกของ ฉันไม่เข้าใจว่าคุณชี้ไปที่ใด ฉันพลาดอะไรไป
terdon

แสดงให้เห็นว่าการ$RANDOMขยายตัวจะทำภายในกับเปลือก โดยพื้นฐานแล้วเป็นการยืนยันว่าเชลล์กำหนดค่าและไม่ส่งการอ้างอิงไปยังตัวแปร เปลือกเมื่อมันขยายบรรทัดคำสั่งที่จะดำเนินการแยกวิเคราะห์และส่งแบบฟอร์มการขยายไปยัง$RANDOM echo
slm

2
ดังนั้นไม่เหมือนตัวแปรสภาพแวดล้อมแล้ว
Toby Speight
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.