Apa perbedaan penggunaan antara variabel shell dan variabel lingkungan?

16

Saya sebenarnya tidak tahu ada dua jenis variabel yang dapat saya akses dari baris perintah. Yang saya tahu adalah, bahwa saya dapat mendeklarasikan variabel seperti:

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

atau mengaksesnya dengan tanda $, seperti:

echo $foo
echo ${bar[1]}

atau menggunakan variabel bawaan, seperti:

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

Sekarang, saya dengar ada dua (setidaknya?) Jenis variabel: variabel shell dan variabel lingkungan.

  • Apa tujuan dari memiliki dua tipe yang berbeda?
  • Bagaimana saya tahu tipe variabel apa?
  • Apa penggunaan khas untuk masing-masing?
sharkant
sumber

Jawaban:

14

Variabel lingkungan adalah daftar name=valuepasangan yang ada apa pun programnya (shell, aplikasi, daemon ...). Mereka biasanya diwarisi oleh proses anak-anak (dibuat oleh fork/ execurutan): proses anak-anak mendapatkan salinan variabel orangtua mereka sendiri.

Variabel shell memang hanya ada dalam konteks shell. Mereka hanya diwariskan dalam subkulit (yaitu ketika shell bercabang tanpa execoperasi). Tergantung pada fitur shell, variabel mungkin tidak hanya string sederhana seperti yang lingkungan tetapi juga array, senyawa, variabel yang diketik seperti integer atau floating point, dll.

Ketika shell dimulai, semua variabel lingkungan yang diwarisi dari induknya juga menjadi variabel shell (kecuali jika variabel tersebut tidak valid sebagai variabel shell dan kasus sudut lainnya seperti IFSyang disetel ulang oleh beberapa shell) tetapi variabel yang diwarisi ini ditandai sebagai diekspor 1 . Itu berarti mereka akan tetap tersedia untuk proses anak-anak dengan nilai yang berpotensi diperbarui yang ditetapkan oleh shell. Itu juga kasus dengan variabel yang dibuat di bawah shell dan ditandai sebagai diekspor dengan exportkata kunci.

Array dan variabel tipe kompleks lainnya tidak dapat diekspor kecuali nama dan nilainya dapat dikonversi ke name=valuepola, atau ketika mekanisme spesifik shell ada (misalnya: bashfungsi ekspor di lingkungan dan beberapa shell eksotis, non POSIX seperti rcdan esdapat mengekspor array ).

Jadi perbedaan utama antara variabel lingkungan dan variabel shell adalah cakupannya: variabel lingkungan bersifat global sedangkan variabel shell yang tidak diekspor adalah lokal untuk skrip.

Perhatikan juga bahwa shell modern (setidaknya kshdan bash) mendukung cakupan variabel shell ketiga. Variabel yang dibuat dalam fungsi dengan typesetkata kunci bersifat lokal untuk fungsi tersebut (Cara fungsi ini dideklarasikan mengaktifkan / menonaktifkan fitur ini ksh, dan perilaku persistensi berbeda antara bashdan ksh). Lihat /unix//a/28349/2594

1 ini berlaku untuk kerang yang modern seperti ksh, dash, bashdan yang sejenis. Cangkang Bourne warisan dan cangkang sintaksis bukan Bourne cshmemiliki perilaku yang berbeda.

Jlliagre
sumber
1
Semuanya diwarisi oleh proses anak-anak karena anak-anak dibuat sebagai garpu (salinan persis) dari orang tua mereka. Poin dengan variabel lingkungan adalah bahwa mereka dilewatkan ke execve()panggilan sistem demikian juga (biasanya) digunakan untuk bertahan data selama eksekusi perintah lain (dalam proses yang sama).
Stéphane Chazelas
Tidak semua variabel lingkungan diterjemahkan ke variabel shell. Hanya yang valid sebagai nama variabel shell (dan dengan beberapa pengecualian seperti IFSpada beberapa shell).
Stéphane Chazelas
Kerang seperti rc, esdapat mengekspor array menggunakan pengkodean adhoc. bashdan rcjuga dapat mengekspor fungsi menggunakan variabel lingkungan (sekali lagi, menggunakan pengkodean khusus).
Stéphane Chazelas
Dalam ksh93, typesetmembatasi ruang lingkup hanya dalam fungsi yang dideklarasikan dengan function foo { ...; }sintaks, bukan dengan foo() cmdsintaks Bourne ( ) (dan itu pelingkupan statis tidak dinamis seperti di shell lain).
Stéphane Chazelas
@ StéphaneChazelas Terima kasih telah mengulas! Balas diperbarui untuk memperhitungkan komentar Anda.
jlliagre
17

Variabel shell

Variabel Shell adalah variabel yang ruang lingkupnya dalam sesi shell saat ini, misalnya dalam sesi shell interaktif atau skrip.

Anda dapat membuat variabel shell dengan menetapkan nilai ke nama yang tidak digunakan:

var="hello"

Penggunaan variabel shell adalah untuk melacak data di sesi saat ini. Variabel Shell biasanya memiliki nama dengan huruf kecil.

Variabel lingkungan

Variabel lingkungan adalah variabel shell yang telah diekspor. Ini berarti bahwa itu akan terlihat sebagai variabel, tidak hanya di sesi shell yang membuatnya, tetapi juga untuk setiap proses (bukan hanya shell) yang dimulai dari sesi itu.

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

atau

export VAR="hello"

Setelah variabel shell telah diekspor, ia tetap diekspor sampai tidak disetel, atau sampai "properti ekspor" dihapus (dengan export -nin bash), jadi biasanya tidak perlu untuk mengekspornya kembali. Membatalkan pengaturan variabel dengan unsetmenghapusnya (tidak peduli apakah itu variabel lingkungan atau tidak).

Array dan hash asosiatif dalam bash dan shell lain mungkin tidak diekspor untuk menjadi variabel lingkungan. Variabel lingkungan harus berupa variabel sederhana yang nilainya berupa string, dan mereka sering memiliki nama yang terdiri dari huruf besar.

Penggunaan variabel lingkungan adalah untuk melacak data dalam sesi shell saat ini, tetapi juga untuk memungkinkan setiap proses yang dimulai untuk mengambil bagian dari data itu. Kasus khas ini adalahPATH variabel lingkungan, yang dapat diatur dalam shell dan kemudian digunakan oleh program apa pun yang ingin memulai program tanpa menentukan path lengkap ke mereka.

Pengumpulan variabel lingkungan dalam suatu proses sering disebut sebagai "lingkungan proses". Setiap proses memiliki lingkungannya sendiri.

Variabel lingkungan hanya dapat "diteruskan", yaitu proses anak tidak pernah dapat mengubah variabel lingkungan dalam proses induknya, dan selain menyiapkan lingkungan untuk proses anak setelah memulainya, proses induk mungkin tidak mengubah lingkungan yang ada dari suatu proses anak.

Variabel lingkungan dapat didaftar dengan env(tanpa argumen). Selain itu, mereka muncul sama dengan variabel shell yang tidak diekspor dalam sesi shell. Ini sedikit istimewa untuk shell karena kebanyakan bahasa pemrograman lain biasanya tidak mencampurkan variabel "biasa" dengan variabel lingkungan (lihat di bawah).

env dapat juga digunakan untuk mengatur nilai satu atau beberapa variabel lingkungan di lingkungan suatu proses tanpa mengaturnya di sesi saat ini:

env CC=clang CXX=clang++ make

Ini dimulai makedengan variabel lingkungan CCdiatur ke nilai clangdan CXXdiatur ke clang++.

Ini juga dapat digunakan untuk membersihkan lingkungan untuk suatu proses:

env -i bash

Ini dimulai bashtetapi tidak mentransfer lingkungan saat ini ke bashproses baru (masih akan memiliki variabel lingkungan karena menciptakan yang baru dari skrip inisialisasi shell-nya).

Contoh perbedaan

$ 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

Bahasa lainnya

Ada fungsi perpustakaan di sebagian besar bahasa pemrograman yang memungkinkan untuk mendapatkan dan mengatur variabel lingkungan. Perhatikan bahwa karena variabel lingkungan disimpan sebagai hubungan nilai kunci yang sederhana, mereka biasanya bukan "variabel" bahasa. Suatu program dapat mengambil nilai (yang selalu berupa string karakter) yang terkait dengan kunci (nama variabel lingkungan), tetapi kemudian harus mengonversinya menjadi bilangan bulat atau apa pun tipe data apa pun yang diharapkan memiliki nilai oleh bahasa.

Dalam C, variabel lingkungan dapat diakses menggunakan getenv(), setenv(), putenv()dan unsetenv(). Variabel yang dibuat dengan rutinitas ini diwarisi dengan cara yang sama dengan proses apa pun yang dimulai program C.

Bahasa lain mungkin memiliki struktur data khusus untuk mencapai hal yang sama, seperti %ENVhash di Perl, atau ENVIRONarray asosiatif di sebagian besar implementasi awk.

Kusalananda
sumber
Terima kasih, penjelasan yang sangat jelas. Jadi lingkungan seperti lapangan besar di mana program lain dapat hidup dan melihat masing-masing variabel lingkungan. Beberapa program memiliki variabel pribadi, hanya mereka sendiri yang dapat melihatnya, seperti shell. tetapi ada mekanisme untuk membuat variabel pribadi dilihat oleh semua yang disebut "ekspor". Jika ini dimengerti, maka satu-satunya hal yang saya tidak yakin adalah apakah ada lebih dari satu lingkungan pada saat yang sama?
sharkant
@sharkant Setiap proses yang berjalan memiliki lingkungannya sendiri. Lingkungan ini diwarisi dari proses yang memulainya. Tidak pernah ada "cross-talk" antara lingkungan proses yang berbeda. Satu-satunya cara untuk mengubah variabel lingkungan dalam proses adalah untuk proses itu sendiri yang memodifikasinya.
Kusalananda
Terima kasih untuk mengklarifikasi pemahaman saya. Setiap ikan di dalam mangkuk ikannya sendiri. Bagaimana dengan Proses yang menghasilkan proses lain? Apakah proses dan proses anak mereka semuanya dalam satu lingkungan atau masing-masing memiliki lingkungannya sendiri?
sharkant
1
@sharkant Ada fungsi perpustakaan dalam sebagian besar bahasa yang memungkinkan untuk mendapatkan dan mengatur variabel lingkungan. Dalam C, hal ini dilakukan dengan getenv(), setenv(), putenv()dan unsetenv(). Variabel yang dibuat dengan rutinitas ini diwarisi dengan cara yang sama dengan proses apa pun yang dimulai program C. Bahasa lain mungkin memiliki struktur data khusus untuk hal yang sama, seperti %ENVdi Perl.
Kusalananda
1
FWIW: exec*()keluarga fungsi juga dapat mengatur lingkungan untuk proses yang dieksekusi.
Satō Katsura
5

Variabel Shell sulit untuk diduplikasi.

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

Namun variabel lingkungan dapat diduplikasi; mereka hanya daftar, dan daftar dapat memiliki entri duplikat. Ini envdup.cuntuk melakukan hal itu.

#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);
}

Yang dapat kita kompilasi dan jalankan jitu envdupuntuk kemudian dijalankan envuntuk menunjukkan kepada kita variabel lingkungan apa yang ditetapkan ...

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

Ini mungkin hanya berguna untuk menemukan bug atau keanehan lain dalam seberapa baik program menangani **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
$ 

Sepertinya Python 3.6 di sini secara membabi buta melewati duplikat (abstraksi yang bocor) sementara Perl 5.24 tidak. Bagaimana dengan kerang?

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

Astaga, apa yang terjadi jika sudohanya membersihkan entri lingkungan pertama tetapi kemudian bashberjalan dengan yang kedua? Halo PATHatau LD_RUN_PATHexploit. Apakah Anda sudo(dan yang lainnya ?) Ditambal untuk lubang itu ? Eksploitasi keamanan bukanlah "perbedaan anekdotal" atau hanya "bug" dalam program panggilan.

thrig
sumber
1
Itu benar tetapi perbedaan anekdotal dan bisa dibilang bug dari program pengaturan variabel duplikat.
jlliagre
1
Lihat rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas
0

Sebuah variabel lingkungan seperti variabel shell , tapi tidak spesifik untuk shell . Semua proses pada sistem Unix memiliki penyimpanan variabel lingkungan . Perbedaan utama antara variabel lingkungan dan shell adalah: bahwa sistem operasi meneruskan semua variabel lingkungan shell Anda ke program yang dijalankan shell, sedangkan variabel shell tidak dapat diakses dalam perintah yang Anda jalankan.

env –Perintah ini memungkinkan Anda untuk menjalankan program lain di lingkungan kustom tanpa mengubah yang saat ini. Ketika digunakan tanpa argumen, ia akan mencetak daftar variabel lingkungan saat ini. printenv –Perintah mencetak semua atau variabel lingkungan yang ditentukan. set –Perintah mengatur atau membatalkan variabel shell. Ketika digunakan tanpa argumen, ia akan mencetak daftar semua variabel termasuk variabel lingkungan dan shell, dan fungsi shell. unset –Perintah menghapus variabel shell dan lingkungan. export –Perintah ini mengatur variabel lingkungan

Mehdi sellami
sumber