ความแตกต่างในการใช้งานระหว่างตัวแปรเชลล์และตัวแปรสภาพแวดล้อมคืออะไร?


16

จริง ๆ แล้วฉันไม่รู้ว่ามีตัวแปรสองประเภทที่ฉันสามารถเข้าถึงได้จากบรรทัดคำสั่ง ทั้งหมดที่ฉันรู้คือว่าฉันสามารถประกาศตัวแปรเช่น:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

หรือเข้าถึงพวกเขาด้วยเครื่องหมาย $ เช่น:

echo $foo
echo ${bar[1]}

หรือใช้ตัวแปร inbuilt เช่น:

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

ตอนนี้ฉันได้ยินมาว่ามีตัวแปรสองประเภท (อย่างน้อย?) ตัวแปร: ตัวแปรเชลล์และตัวแปรสภาพแวดล้อม

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


เกี่ยวข้อง: unix.stackexchange.com/questions/3507/…
lesmana

คำตอบ:


14

ตัวแปรสภาพแวดล้อมที่มีรายชื่อของname=valueคู่ที่มีอยู่สิ่งที่โปรแกรมคือ (เปลือกแอพลิเคชัน, ภูต ... ) พวกเขามักจะได้รับมรดกโดยกระบวนการเด็ก (สร้างโดยfork/ execลำดับ): กระบวนการเด็กได้รับสำเนาของตัวแปรปกครอง

ตัวแปรเชลล์มีอยู่ในบริบทของเชลล์เท่านั้น พวกเขาจะได้รับมรดกใน subshells เท่านั้น (เช่นเมื่อเปลือกเป็นง่ามโดยไม่ต้องexecดำเนินการ) ตัวแปรอาจไม่เพียง แต่เป็นสตริงอย่างง่ายเช่นสภาพแวดล้อม แต่ยังรวมถึงอาร์เรย์, สารประกอบ, ตัวแปรที่พิมพ์เช่นจำนวนเต็มหรือทศนิยมเป็นต้น

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

อาร์เรย์และตัวแปรประเภทอื่นที่ซับซ้อนไม่สามารถส่งออกเว้นแต่ชื่อและความคุ้มค่าของพวกเขาสามารถแปลงเป็นname=valueรูปแบบหรือเมื่อกลไกเปลือกที่เฉพาะเจาะจงอยู่ในสถานที่ (เช่นbashฟังก์ชั่นการส่งออกในสภาพแวดล้อมและบางส่วนที่แปลกใหม่, หอย POSIX ที่ไม่ชอบrcและesสามารถส่งออกอาร์เรย์ )

ดังนั้นความแตกต่างที่สำคัญระหว่างตัวแปรสภาพแวดล้อมและตัวแปรเชลล์คือขอบเขต: ตัวแปรสภาพแวดล้อมเป็นแบบโกลบอลในขณะที่ตัวแปรเชลล์ที่เอ็กซ์พอร์ตไม่ใช่โลคัลสำหรับสคริปต์

โปรดทราบว่าเชลล์สมัยใหม่ (อย่างน้อยkshและbash) สนับสนุนขอบเขตตัวแปรเชลล์ที่สาม ตัวแปรที่สร้างขึ้นในฟังก์ชั่นที่มีtypesetคีย์เวิร์ดนั้นเป็นโลคัลสำหรับฟังก์ชั่นนั้น (วิธีที่ฟังก์ชั่นถูกประกาศเปิด / ปิดการใช้งานคุณลักษณะนี้ภายใต้kshและพฤติกรรมการคงอยู่จะแตกต่างกันระหว่างbashและksh) ดู/unix//a/28349/2594

1 นี้นำไปใช้เปลือกหอยที่ทันสมัยชอบksh, dash, bashและที่คล้ายกัน เชลล์เป้าหมายดั้งเดิมและเชลล์ไวยากรณ์ที่ไม่ใช่บอร์นcshนั้นมีพฤติกรรมที่แตกต่างกัน


1
ทุกอย่างได้รับการสืบทอดโดยกระบวนการลูกขณะที่เด็กถูกสร้างขึ้นเป็นทางแยก (สำเนาที่แน่นอน) ของผู้ปกครอง จุดที่มีตัวแปรสภาพแวดล้อมคือพวกมันถูกส่งผ่านไปยังการexecve()เรียกของระบบดังนั้น (โดยปกติ) จะใช้เพื่อเก็บข้อมูลผ่านการดำเนินการของคำสั่งอื่น ๆ (ในกระบวนการเดียวกัน)
Stéphane Chazelas

ตัวแปรสภาวะแวดล้อมบางตัวเท่านั้นที่ถูกแปลเป็นตัวแปรเชลล์ เฉพาะที่ถูกต้องเป็นชื่อตัวแปรเชลล์ (และมีข้อยกเว้นIFSบางอย่างเช่นในบางเชลล์)
Stéphane Chazelas

หอยชอบrc, esสามารถส่งออกอาร์เรย์โดยใช้การเข้ารหัสเฉพาะกิจ bashและrcยังสามารถส่งออกฟังก์ชั่นโดยใช้ตัวแปรสภาพแวดล้อม (อีกครั้งโดยใช้การเข้ารหัสพิเศษ)
Stéphane Chazelas

ในksh93, typesetจำกัด ขอบเขตเฉพาะในฟังก์ชั่นการประกาศกับfunction foo { ...; }ไวยากรณ์ไม่ได้อยู่กับบอร์น ( foo() cmd) ไวยากรณ์ (และก็กำหนดขอบเขตคงไม่ได้แบบไดนามิกเช่นเปลือกหอยอื่น ๆ )
Stéphane Chazelas

@ StéphaneChazelasขอบคุณสำหรับรีวิว! ตอบกลับอัปเดตเพื่อพิจารณาคำพูดของคุณ
jlliagre

17

ตัวแปรเชลล์

ตัวแปรเชลล์คือตัวแปรที่มีขอบเขตอยู่ในเซสชันเชลล์ปัจจุบันตัวอย่างเช่นในเซสชันเชลล์เชิงโต้ตอบหรือสคริปต์

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

var="hello"

การใช้ตัวแปรเชลล์คือการติดตามข้อมูลในเซสชันปัจจุบัน ตัวแปรเชลล์มักจะมีชื่อที่มีตัวอักษรพิมพ์เล็ก

ตัวแปรสภาพแวดล้อม

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

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

หรือ

export VAR="hello"

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

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

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

คอลเลกชันของตัวแปรสภาพแวดล้อมในกระบวนการมักเรียกว่า "สภาพแวดล้อมของกระบวนการ" แต่ละกระบวนการมีสภาพแวดล้อมของตัวเอง

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

ตัวแปรสภาพแวดล้อมอาจแสดงด้วยenv(โดยไม่มีข้อโต้แย้งใด ๆ ) นอกเหนือจากนั้นมันจะเหมือนกับตัวแปรเชลล์ที่ไม่ได้ถูกเอ็กซ์พอร์ตในเชลล์เซสชัน นี่เป็นบิตพิเศษสำหรับเชลล์เนื่องจากภาษาการเขียนโปรแกรมส่วนใหญ่มักจะไม่ผสมตัวแปร "ธรรมดา" กับตัวแปรสภาพแวดล้อม (ดูด้านล่าง)

env อาจใช้เพื่อตั้งค่าตัวแปรสภาพแวดล้อมหนึ่งหรือหลายตัวแปรในสภาพแวดล้อมของกระบวนการโดยไม่ต้องตั้งค่าในเซสชันปัจจุบัน:

env CC=clang CXX=clang++ make

เริ่มต้นนี้makeกับตัวแปรสภาพแวดล้อมCCตั้งค่าclangและการตั้งค่าCXXclang++

มันอาจถูกใช้เพื่อล้างสภาพแวดล้อมสำหรับกระบวนการ:

env -i bash

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

ตัวอย่างของความแตกต่าง

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

ภาษาอื่น ๆ

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

ใน C, ตัวแปรสภาพแวดล้อมอาจจะเข้าถึงได้โดยใช้getenv(), setenv(), และputenv() unsetenv()ตัวแปรที่สร้างด้วยรูทีนเหล่านี้สืบทอดในวิธีเดียวกันโดยกระบวนการใด ๆ ที่โปรแกรม C เริ่มทำงาน

ภาษาอื่น ๆ ที่อาจจะมีโครงสร้างข้อมูลพิเศษสำหรับการบรรลุในสิ่งเดียวกันเช่น%ENVกัญชาใน Perl หรืออาเรย์ในการใช้งานมากที่สุดของENVIRONawk


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

@sharkant แต่ละกระบวนการทำงานมีสภาพแวดล้อมของตัวเอง สภาพแวดล้อมนี้สืบทอดมาจากกระบวนการที่เริ่มต้นขึ้น ไม่เคยมี "cross-talk" ระหว่างสภาพแวดล้อมของกระบวนการต่าง ๆ วิธีเดียวในการเปลี่ยนตัวแปรสภาพแวดล้อมในกระบวนการนั้นใช้สำหรับกระบวนการที่แก้ไขตัวเอง
Kusalananda

ขอบคุณสำหรับ claryfing ความเข้าใจของฉัน ปลาแต่ละตัวอยู่ในชามปลาของมันเอง วิธีการเกี่ยวกับกระบวนการที่วางไข่กระบวนการอื่น ๆ ? กระบวนการและกระบวนการลูกของพวกเขาทั้งหมดอยู่ในสภาพแวดล้อมเดียวหรือมีกระบวนการของตัวเองหรือไม่?
sharkant

1
@sharkant มีฟังก์ชั่นห้องสมุดในภาษาส่วนใหญ่ที่ช่วยให้การรับและการตั้งค่าตัวแปรสภาพแวดล้อม ใน C นี้จะทำกับgetenv(), setenv(), และputenv() unsetenv()ตัวแปรที่สร้างด้วยรูทีนเหล่านี้สืบทอดในวิธีเดียวกันโดยกระบวนการใด ๆ ที่โปรแกรม C เริ่มทำงาน ภาษาอื่นอาจมีโครงสร้างข้อมูลพิเศษสำหรับสิ่งเดียวกันเช่น%ENVใน Perl
Kusalananda

1
FWIW: exec*()ตระกูลของฟังก์ชันสามารถตั้งค่าสภาพแวดล้อมสำหรับกระบวนการที่กำลังดำเนินการได้
Satō Katsura

5

ตัวแปรเชลล์ยากที่จะทำซ้ำ

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

อย่างไรก็ตามตัวแปรสภาพแวดล้อมสามารถทำซ้ำได้ มันเป็นเพียงรายการและรายการสามารถมีรายการซ้ำได้ นี่คือenvdup.cสิ่งที่ต้องทำ

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

ซึ่งเราสามารถรวบรวมและเรียกใช้envdupเพื่อบอกให้เรียกใช้envเพื่อแสดงให้เราเห็นว่าตัวแปรสภาพแวดล้อมมีการตั้งค่า ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

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

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

ดูเหมือนว่า Python 3.6 ที่นี่สุ่มสี่สุ่มห้าผ่านที่ซ้ำกัน (สิ่งที่เป็นนามธรรมรั่ว) ในขณะที่ Perl 5.24 ไม่ได้ เปลือกหอยล่ะ?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

เอ้ยจะเกิดอะไรขึ้นถ้าsudoมีการฆ่าเชื้อในรายการสภาพแวดล้อมแรก แต่bashจะทำงานในลำดับที่สอง? สวัสดีPATHหรือLD_RUN_PATHใช้ประโยชน์ เป็นของคุณsudo(และทุกอย่างอื่น ) แก้ไขสำหรับหลุมที่ ? การหาช่องโหว่ด้านความปลอดภัยไม่ใช่ "ความแตกต่างเล็กน้อย" และ "จุดบกพร่อง" ในโปรแกรมโทร


1
นั่นเป็นเรื่องจริง แต่ความแตกต่างเล็กน้อยและข้อผิดพลาดของโปรแกรมที่ตั้งค่าตัวแปรที่ซ้ำกัน
jlliagre


0

ตัวแปรสภาพแวดล้อมเป็นเหมือนตัวแปรเปลือกแต่ก็ไม่ได้เฉพาะเจาะจงไปที่เปลือก กระบวนการทั้งหมดบนระบบปฏิบัติการ Unixมีการจัดเก็บตัวแปรสภาพแวดล้อม ความแตกต่างที่สำคัญระหว่างสภาพแวดล้อมและตัวแปรเปลือกคือ ว่าการดำเนินงานระบบการส่งผ่านของคุณทั้งหมดตัวแปรสภาพแวดล้อมของเปลือกกับโปรแกรมที่เปลือกวิ่งในขณะที่ตัวแปรเปลือกไม่สามารถเข้าถึงได้ในคำสั่งที่คุณเรียก

env –คำสั่งอนุญาตให้คุณเรียกใช้โปรแกรมอื่นในสภาพแวดล้อมแบบกำหนดเองโดยไม่ต้องแก้ไขโปรแกรมปัจจุบัน เมื่อใช้โดยไม่มีอาร์กิวเมนต์มันจะพิมพ์รายการตัวแปรสภาพแวดล้อมปัจจุบัน printenv –คำสั่งพิมพ์ตัวแปรสภาพแวดล้อมทั้งหมดหรือที่ระบุ set –คำสั่งตั้งค่าหรือยกเลิกตัวแปรเชลล์ เมื่อใช้โดยไม่มีอาร์กิวเมนต์มันจะพิมพ์รายการตัวแปรทั้งหมดรวมถึงตัวแปรสภาพแวดล้อมและตัวแปรเชลล์และฟังก์ชันเชลล์ unset –คำสั่งลบตัวแปรเชลล์และสภาวะแวดล้อม export –คำสั่งตั้งค่าตัวแปรสภาพแวดล้อม

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