การกำหนดตัวแปรโดยมีหรือไม่มีการเอ็กซ์พอร์ต


955

มีexportไว้เพื่ออะไร

อะไรคือความแตกต่างระหว่าง:

export name=value

และ

name=value

4
อาจจะสังเกตได้ว่าexport name=valueมันไม่ใช่พกพา ลองname=value; export nameใช้โซลูชันแบบพกพาทั้งนี้ขึ้นอยู่กับสิ่งที่คุณต้องการ
tripleee

คำตอบ:


1054

export ทำให้ตัวแปรพร้อมใช้งานสำหรับกระบวนการย่อย

นั่นคือ,

export name=value

หมายความว่าชื่อตัวแปรพร้อมใช้งานสำหรับกระบวนการใด ๆ ที่คุณเรียกใช้จากกระบวนการเชลล์นั้น หากคุณต้องการให้กระบวนการใช้ประโยชน์จากตัวแปรนี้ให้ใช้exportและเรียกใช้กระบวนการจากเชลล์นั้น

name=value

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

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


105
เอ็กซ์พอร์ตเฉพาะทำให้ตัวแปรพร้อมใช้งานสำหรับกระบวนการลูกผ่านสภาวะแวดล้อม
Beano

15
ฉันจะเพิ่มด้วยว่าหากการส่งออกอยู่ในไฟล์ที่คุณ "ซอร์ส" (เช่น. filename) ก็จะส่งออกไปยังสภาพแวดล้อมการทำงานของคุณเช่นกัน
rogerdpack

6
@rogerdpack คุณไม่สามารถทำได้โดยไม่ส่งออกหรือไม่ cat> blah \ na = hi \ n blah; echo $ a; เอาท์พุท 'สวัสดี' สำหรับฉัน
David Winiecki

2
ดีมันทำงานได้แม้ไม่มีการส่งออก ดังนั้นผมคิดว่าเมื่อจัดหาไฟล์ถ้าคุณใช้การส่งออกจะได้รับการสะท้อนให้เห็นในกระบวนการที่เด็กถ้าคุณไม่ได้มันก็จะส่งผลกระทบต่อสภาพแวดล้อมทุบตีท้องถิ่น ...
rogerdpack

19
มีกรณีนี้ขอบหนึ่ง; name=value command ไม่commandทำให้ตัวแปรที่มีอยู่ในกระบวนการย่อย
Oliver Charlesworth

254

เพื่อแสดงสิ่งที่คำตอบอื่น ๆ พูด:

$ foo="Hello, World"
$ echo $foo
Hello, World
$ bar="Goodbye"
$ export foo
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar

bash-3.2$ 

9
อีกหนึ่งตัวอย่างสำหรับสิ่งนี้al$ foobar="Whatever" bash
Alun

70

คนอื่นตอบว่าการส่งออกทำให้ตัวแปรพร้อมใช้งานสำหรับ subshells และนั่นถูกต้อง แต่เป็นผลข้างเคียงเท่านั้น เมื่อคุณส่งออกตัวแปรจะทำให้ตัวแปรนั้นอยู่ในสภาพแวดล้อมของเชลล์ปัจจุบัน (เช่นเชลล์เรียกputenv(3)หรือsetenv(3))
สภาพแวดล้อมของกระบวนการสืบทอดมาจาก exec ทำให้ตัวแปรมองเห็นได้ใน subshells

แก้ไข (ด้วยมุมมอง 5 ปี): นี่เป็นคำตอบที่โง่ วัตถุประสงค์ของ 'การส่งออก' คือการทำให้ตัวแปร "อยู่ในสภาพแวดล้อมของคำสั่งที่เรียกใช้งานในภายหลัง" ไม่ว่าคำสั่งเหล่านั้นจะเป็น subshells หรือกระบวนการย่อย การติดตั้งแบบไร้เดียงสาจะทำให้ตัวแปรอยู่ในสภาพแวดล้อมของเชลล์ แต่สิ่งนี้จะทำให้ไม่สามารถนำไปใช้งานexport -pได้


6
โปรดทราบว่านี่ไม่เป็นความจริงทั้งหมด ในbashการส่งออกไม่แน่นอนเพิ่มตัวแปรสภาพแวดล้อมของเปลือกปัจจุบัน dashแต่นี้ไม่ได้เป็นกรณีที่มี สำหรับฉันแล้วดูเหมือนว่าการเพิ่มตัวแปรให้กับสภาพแวดล้อมของเชลล์ปัจจุบันเป็นวิธีที่ง่ายที่สุดในการใช้ซีแมนทิกส์ของสิ่งที่ง่ายที่สุดexportแต่พฤติกรรมนั้นไม่ได้รับคำสั่ง
William Pursell

7
ฉันไม่แน่ใจว่าdashจะทำอย่างไรกับสิ่งนี้ bashโปสเตอร์เดิมถูกถามเฉพาะเกี่ยวกับ
ปลาดาว

14
คำถามถูกแท็กbashแต่ใช้กับตัวแปร bourne-shell อย่างเท่าเทียมกัน การเจาะจงมากเกินไปและให้คำตอบที่นำไปใช้เฉพาะกับbashเป็นความชั่วร้ายที่ยิ่งใหญ่
วิลเลียม Pursell

12
bashคือ jQuery ของเชลล์
Potherca

2
export makes the variable available to subshells, and that is correctนี่เป็นการใช้คำศัพท์ที่สับสนอย่างมาก Subshells ไม่จำเป็นต้องexportสืบทอดตัวแปร กระบวนการย่อยทำ
Amit Naidu

62

มันบอกว่ามันไม่จำเป็นที่จะต้องส่งออกในทุบตีเมื่อวางไข่ subshells ในขณะที่คนอื่นพูดตรงข้ามแน่นอน มันเป็นสิ่งสำคัญที่จะต้องทราบความแตกต่างระหว่าง subshells (ผู้ที่ถูกสร้างขึ้นโดย(), ``, $()หรือลูป) และกระบวนการย่อย (กระบวนการที่จะเรียกตามชื่อเช่นตัวอักษรbashที่ปรากฏในสคริปต์ของคุณ)

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

สิ่งที่พบได้ทั่วไปในโครงสร้างทั้งสองนี้คือไม่สามารถส่งผ่านตัวแปรกลับไปยังเชลล์พาเรนต์ได้

$ noexport=noexport; export export=export; (echo subshell: $noexport $export; subshell=subshell); bash -c 'echo subprocess: $noexport $export; subprocess=subprocess'; echo parent: $subshell $subprocess
subshell: noexport export
subprocess: export
parent:

มีอีกหนึ่งแหล่งที่มาของความสับสน: บางคนคิดว่าการแยกย่อย 'forked' เป็นสิ่งที่ไม่เห็นตัวแปรที่ไม่ถูกส่งออก โดยปกติ fork () s จะตามมาทันทีด้วย exec () s และนั่นคือสาเหตุที่ดูเหมือนว่า fork () เป็นสิ่งที่มองหาในขณะที่ในความเป็นจริงมันคือ exec () คุณสามารถเรียกใช้คำสั่งโดยไม่ต้อง fork () ก่อนอื่นด้วยexecคำสั่งและกระบวนการที่เริ่มต้นด้วยวิธีนี้จะไม่มีการเข้าถึงตัวแปรที่ไม่ได้ส่งออก:

$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export

โปรดทราบว่าเราไม่เห็นparent:บรรทัดในเวลานี้เนื่องจากเราได้เปลี่ยนพาเรนต์เชลล์ด้วยexecคำสั่งดังนั้นจึงไม่มีอะไรเหลือให้ดำเนินการคำสั่งนั้น


ฉันไม่เคยเห็นลูปที่ (โดยตัวมันเอง) สร้าง subshell; OTOH ไปป์ไลน์ (เสมอสำหรับชิ้นส่วนอื่นนอกเหนือจากที่ผ่านมาบางครั้งสำหรับครั้งสุดท้ายขึ้นอยู่กับเปลือกของคุณรุ่นและตัวเลือก) การสร้างพื้นหลัง ( &) ยังสร้าง subshell
dave_thompson_085

สิ่งที่เกี่ยวกับเหล่านี้var=asdf bash -c 'echo $var'หรือvar=asdf exec bash -c 'echo $var'? asdfเอาท์พุทเป็น ;ทำให้ความแตกต่างถ้าวางอยู่หลังคำนิยามตัวแปร คำอธิบายจะเป็นอย่างไร ดูเหมือนว่าvar(โดยไม่;) เกี่ยวข้องกับการเกิด subprocess อย่างใดเนื่องจากเปลือกต้นกำเนิดไม่มีอะไรเกี่ยวข้องกับมัน echo $varพิมพ์อะไรถ้าดำเนินการในบรรทัดที่สอง แต่สิ่งหนึ่งที่เรียงรายให้var=asdf bash -c 'echo $var'; echo $var asdf\nasdf
4xy

31

export NAME=value สำหรับการตั้งค่าและตัวแปรที่มีความหมายต่อกระบวนการย่อย

NAME=value สำหรับตัวแปรชั่วคราวหรือตัวแปรลูปส่วนตัวในกระบวนการเชลล์ปัจจุบัน

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

  • ข้อผิดพลาดทั่วไปคือการวางช่องว่างรอบเครื่องหมายเท่ากับ:

    $ export FOO = "bar"  
    bash: export: `=': not a valid identifier
  • เฉพาะตัวแปรที่ส่งออก ( B) เท่านั้นที่เห็นโดยกระบวนการย่อย:

    $ A="Alice"; export B="Bob"; echo "echo A is \$A. B is \$B" | bash
    A is . B is Bob
  • การเปลี่ยนแปลงในกระบวนการย่อยไม่เปลี่ยนเชลล์หลัก:

    $ export B="Bob"; echo 'B="Banana"' | bash; echo $B
    Bob
  • ตัวแปรที่ทำเครื่องหมายเพื่อส่งออกมีค่าที่คัดลอกเมื่อสร้างกระบวนการย่อย:

    $ export B="Bob"; echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash &
    [1] 3306
    $ B="Banana"; echo '(sleep 30; echo "Subprocess 2 has B=$B")' | bash 
    Subprocess 1 has B=Bob
    Subprocess 2 has B=Banana
    [1]+  Done         echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash
  • เฉพาะตัวแปรที่ส่งออกเท่านั้นที่กลายเป็นส่วนหนึ่งของสภาพแวดล้อม ( man environ):

     $ ALICE="Alice"; export BOB="Bob"; env | grep "ALICE\|BOB"
     BOB=Bob

ดังนั้นตอนนี้มันควรจะชัดเจนเช่นเดียวกับดวงอาทิตย์ของฤดูร้อน! ขอบคุณ Brain Agnew, alexp และ William Prusell



11

ควรสังเกตว่าคุณสามารถส่งออกตัวแปรและเปลี่ยนค่าในภายหลัง ค่าที่เปลี่ยนแปลงของตัวแปรจะพร้อมใช้งานสำหรับกระบวนการลูก เมื่อส่งออกการตั้งค่าสำหรับตัวแปรคุณต้องทำexport -n <var>เพื่อลบคุณสมบัติ

$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset

ขอบคุณนี่เป็นข้อมูลที่ฉันต้องการเพราะฉันเห็นสคริปต์ที่ใช้ตัวแปรสภาพแวดล้อมจากนั้น "ส่งออกอีกครั้ง" ด้วยค่าใหม่และฉันสงสัยว่ามันจำเป็นหรือไม่
Mike Lippert

8

ดังที่คุณทราบแล้ว UNIX อนุญาตให้กระบวนการมีชุดของตัวแปรสภาพแวดล้อมซึ่งเป็นคู่ของคีย์ / ค่าทั้งที่เป็นคีย์และค่าเป็นสตริง ระบบปฏิบัติการมีหน้าที่เก็บคู่เหล่านี้ไว้สำหรับแต่ละกระบวนการแยกกัน

โปรแกรมสามารถเข้าถึงตัวแปรสภาพแวดล้อมผ่าน UNIX API นี้:

  • char *getenv(const char *name);
  • int setenv(const char *name, const char *value, int override);
  • int unsetenv(const char *name);

กระบวนการสืบทอดตัวแปรสภาพแวดล้อมจากกระบวนการพาเรนต์ ระบบปฏิบัติการมีหน้าที่สร้างสำเนาของ "envars" ทั้งหมดในขณะที่กระบวนการลูกถูกสร้างขึ้น

Bashในบรรดา shell อื่น ๆ สามารถตั้งค่าตัวแปรสภาพแวดล้อมตามคำขอของผู้ใช้ นี่คือสิ่งที่exportมีอยู่สำหรับ

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

เพิ่มเติมเกี่ยวกับสิ่งแวดล้อมใน Bash

ตัวแปรอีกประเภทใน Bash คือตัวแปรภายใน เนื่องจาก Bash ไม่ได้เป็นเพียงเชลล์แบบโต้ตอบเท่านั้นจริงๆแล้วมันเป็นล่ามสคริปต์เช่นเดียวกับล่ามอื่น ๆ (เช่น Python) จึงสามารถเก็บชุดตัวแปรไว้ได้ ควรกล่าวว่า Bash (ซึ่งแตกต่างจาก Python) รองรับเฉพาะตัวแปรสตริงเท่านั้น

name=valueสัญกรณ์สำหรับการกำหนดตัวแปรทุบตี ตัวแปรเหล่านี้อยู่ใน Bash และไม่มีส่วนเกี่ยวข้องกับตัวแปรสภาพแวดล้อมที่เก็บไว้ในระบบปฏิบัติการ

เพิ่มเติมเกี่ยวกับพารามิเตอร์ของเชลล์ (รวมถึงตัวแปร)

นอกจากนี้ยังควรสังเกตว่าตามคู่มืออ้างอิง Bash:

สภาพแวดล้อมสำหรับคำสั่งง่ายๆใด ๆ หรือฟังก์ชั่นอาจจะเติมชั่วคราวโดย prefixing ด้วยการมอบหมายพารามิเตอร์ที่อธิบายไว้ในเชลล์พารามิเตอร์ คำสั่งการมอบหมายเหล่านี้มีผลกับสภาพแวดล้อมที่เห็นโดยคำสั่ง


เพื่อสรุปสิ่งต่างๆ:

  • exportใช้เพื่อตั้งค่าตัวแปรสภาพแวดล้อมในระบบปฏิบัติการ ตัวแปรนี้จะพร้อมใช้งานสำหรับกระบวนการลูกทั้งหมดที่สร้างโดยกระบวนการทุบตีปัจจุบันหลังจากนั้น
  • สัญกรณ์ตัวแปร Bash (name = value) ใช้เพื่อตั้งค่าตัวแปรโลคอลที่มีเฉพาะกับกระบวนการปัจจุบันของ bash
  • สัญกรณ์ตัวแปร Bash นำหน้าคำสั่งอื่นสร้างตัวแปรสภาพแวดล้อมสำหรับขอบเขตของคำสั่งนั้นเท่านั้น

1
bash vars ไม่รองรับ Python แต่มีสตริงจำนวนเต็มและอาร์เรย์สองชนิด ('ดัชนี' / ดั้งเดิมและ 'เชื่อมโยง' ซึ่งคล้ายกับอาร์เรย์ awk, perl hash หรือ Pict dython) กระสุนอื่น ๆ แตกต่างกันไป; สตริงเพียงอย่างเดียวคือแบบพกพา
dave_thompson_085

7

คำตอบที่ได้รับการยอมรับหมายถึงนี้ แต่ฉันต้องการให้ชัดเจนการเชื่อมต่อกับ builtins เปลือก:

ดังกล่าวแล้วexportจะทำให้ตัวแปรพร้อมใช้งานทั้งเชลล์และลูก ๆ หากexportจะไม่ใช้ตัวแปรเดียวที่จะสามารถใช้ได้ในเปลือกและมีเพียงเปลือกbuiltinsสามารถเข้าถึงได้

นั่นคือ,

tango=3
env | grep tango # prints nothing, since env is a child process
set | grep tango # prints tango=3 - "type set" shows `set` is a shell builtin

3

นี่เป็นอีกตัวอย่าง:

VARTEST="value of VARTEST" 
#export VARTEST="value of VARTEST" 
sudo env | grep -i vartest 
sudo echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}" 
sudo bash -c 'echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"'  

โดยใช้การส่งออก VARTEST เท่านั้นมูลค่าของ VARTEST นั้นมีอยู่ใน sudo bash -c '... '!

สำหรับตัวอย่างเพิ่มเติมดู:


3

ผู้สร้างสองคนของ UNIX คือ Brian Kernighan และ Rob Pike อธิบายเรื่องนี้ในหนังสือ "The UNIX Programming Environment" Google สำหรับชื่อเรื่องและคุณจะพบเวอร์ชัน pdf ได้อย่างง่ายดาย

พวกเขากล่าวถึงตัวแปรเชลล์ในส่วนที่ 3.6 และมุ่งเน้นไปที่การใช้exportคำสั่งที่ส่วนท้ายของส่วนนั้น:

เมื่อคุณต้องการทำให้ค่าของตัวแปรเข้าถึงได้ใน sub-shells คำสั่ง export ของ shell ควรถูกใช้ (คุณอาจคิดว่าทำไมไม่มีวิธีส่งออกค่าของตัวแปรจากเชลล์ย่อยไปยังพาเรนต์ของมัน)


2

เพียงเพื่อแสดงความแตกต่างระหว่างตัวแปรที่ส่งออกที่อยู่ในสภาพแวดล้อม (env) และตัวแปรที่ไม่ได้ส่งออกซึ่งไม่ได้อยู่ในสภาพแวดล้อม:

ถ้าฉันทำสิ่งนี้:

$ MYNAME=Fred
$ export OURNAME=Jim

ดังนั้นจะมีเพียง $ OURNAME เท่านั้นที่ปรากฏใน env ตัวแปร $ MYNAME ไม่ได้อยู่ใน env

$ env | grep NAME
OURNAME=Jim

แต่ตัวแปร $ MYNAME มีอยู่ในเชลล์

$ echo $MYNAME
Fred

1

โดยค่าเริ่มต้นตัวแปรที่สร้างขึ้นภายในสคริปต์จะพร้อมใช้งานสำหรับเชลล์ปัจจุบันเท่านั้น กระบวนการลูก (sub-shells) จะไม่สามารถเข้าถึงค่าที่ถูกตั้งค่าหรือแก้ไข การอนุญาตให้กระบวนการลูกดูค่าใช้ประโยชน์จากคำสั่งส่งออก


0

แม้ว่าจะไม่ได้กล่าวถึงอย่างชัดเจนในการอภิปราย แต่ก็ไม่จำเป็นต้องใช้การส่งออกเมื่อวางไข่ subshell จากภายใน bash เนื่องจากตัวแปรทั้งหมดจะถูกคัดลอกลงในกระบวนการลูก


โปรดอธิบายว่าสิ่งที่คุณพูดดูเหมือนจะขัดแย้งกับคำตอบโดยตรงกับตัวอย่างข้างต้น
Mike Lippert

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