Jaka jest różnica w użyciu między zmiennymi powłoki a zmiennymi środowiskowymi?

16

W rzeczywistości nie wiedziałem, że istnieją dwa różne typy zmiennych, do których mogę uzyskać dostęp z wiersza poleceń. Wiedziałem tylko, że mogę zadeklarować zmienne takie jak:

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

lub dostęp do nich ze znakiem $, np .:

echo $foo
echo ${bar[1]}

lub używając wbudowanych zmiennych, takich jak:

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

Teraz słyszę, że istnieją dwa (przynajmniej?) Typy zmiennych: zmienne powłoki i zmienne środowiskowe.

  • Jaki jest cel posiadania dwóch różnych typów?
  • Skąd mam wiedzieć, jaki typ jest zmienna?
  • Jakie są typowe zastosowania dla każdego z nich?
rekin
źródło

Odpowiedzi:

14

Zmienne środowiskowe to lista name=valuepar istniejących niezależnie od programu (powłoka, aplikacja, demon…). Zazwyczaj są dziedziczone przez procesy potomne (tworzone przez sekwencję fork/ exec): procesy potomne otrzymują własną kopię zmiennych rodzicielskich.

Zmienne powłoki istnieją tylko w kontekście powłoki. Są one dziedziczone tylko w podpowłokach (tzn. Gdy skorupa jest rozwidlona bez execoperacji). W zależności od funkcji powłoki zmienne mogą być nie tylko prostymi ciągami, takimi jak środowiskowe, ale także tablicami, zmiennymi złożonymi, zmiennymi typowymi, takimi jak liczba całkowita lub zmiennoprzecinkowa itp.

Kiedy powłoka się uruchamia, wszystkie zmienne środowiskowe, które dziedziczy od swojego rodzica, stają się również zmiennymi powłoki (chyba że są one niepoprawne jako zmienne powłoki i inne przypadki narożne, takie jak IFSresetowane przez niektóre powłoki), ale te odziedziczone zmienne są oznaczane jako eksportowane 1 . Oznacza to, że pozostaną one dostępne dla procesów potomnych z potencjalnie zaktualizowaną wartością ustawioną przez powłokę. Dotyczy to również zmiennych utworzonych pod powłoką i oznaczonych jako wyeksportowane exportsłowem kluczowym.

Nie można wyeksportować tablicy i innych zmiennych typu złożonego, chyba że ich nazwa i wartość mogą zostać przekonwertowane na name=valuewzorzec lub gdy istnieje mechanizm specyficzny dla powłoki (np .: bashfunkcje eksportu w środowisku i niektóre egzotyczne powłoki inne niż POSIX, takie jak rci esmogą eksportować tablice ).

Główną różnicą między zmiennymi środowiskowymi a zmiennymi powłoki jest ich zakres: zmienne środowiskowe są globalne, podczas gdy niewyeksportowane zmienne powłoki są lokalne dla skryptu.

Zauważ też, że nowoczesne powłoki (przynajmniej kshi bash) obsługują trzeci zakres zmiennych powłoki. Zmienne utworzone w funkcjach ze typesetsłowem kluczowym są lokalne dla tej funkcji (sposób, w jaki funkcja jest deklarowana, włącza / wyłącza tę funkcję ksh, a zachowanie trwałości jest różne pomiędzy bashi ksh). Zobacz /unix//a/28349/2594

1 Dotyczy to nowoczesne pociski podoba ksh, dash, bashi podobne. Starsze powłoki Bourne'a i powłoki inne niż Bourne cshmają różne zachowania.

jlliagre
źródło
1
Wszystko jest dziedziczone przez procesy potomne, ponieważ dzieci są tworzone jako rozwidlenie (dokładna kopia) ich rodzica. Chodzi o to, że zmienne środowiskowe są przekazywane do execve()wywołania systemowego, więc (zwykle) są używane do utrwalania danych w trakcie wykonywania innych poleceń (w tym samym procesie).
Stéphane Chazelas
Nie wszystkie zmienne środowiskowe są tłumaczone na zmienne powłoki. Tylko te, które są poprawne jako nazwa zmiennej powłoki (i z kilkoma wyjątkami, jak IFSw niektórych powłokach).
Stéphane Chazelas
Muszle podoba rc, esmoże eksportować tablic używając kodowania adhoc. bashi rcmoże również eksportować funkcje przy użyciu zmiennych środowiskowych (ponownie, używając specjalnego kodowania).
Stéphane Chazelas
W ksh93, typesetogranicza zakres jedynie funkcje zadeklarowane ze function foo { ...; }składni, a nie z Bourne ( foo() cmdskładni) (i to statyczne scoping nie dynamiczny jak w innych powłok).
Stéphane Chazelas
@ StéphaneChazelas Dziękujemy za recenzję! Odpowiedz zaktualizowano, aby uwzględnić Twoje uwagi.
jlliagre
17

Zmienne powłoki

Zmienne powłoki to zmienne, których zakres znajduje się w bieżącej sesji powłoki, na przykład w interaktywnej sesji powłoki lub skrypcie.

Możesz utworzyć zmienną powłoki, przypisując wartość do nieużywanej nazwy:

var="hello"

Zastosowanie zmiennych powłoki służy do śledzenia danych w bieżącej sesji. Zmienne powłoki zwykle mają nazwy z małymi literami.

Zmienne środowiska

Zmienna środowiskowa to zmienna powłoki, która została wyeksportowana. Oznacza to, że będzie ona widoczna jako zmienna, nie tylko w sesji powłoki, która ją utworzyła, ale także dla każdego procesu (nie tylko powłoki), który jest uruchamiany z tej sesji.

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

lub

export VAR="hello"

Po wyeksportowaniu zmiennej powłoki, pozostaje ona wyeksportowana, dopóki nie zostanie rozbrojona lub dopóki jej „właściwość eksportu” nie zostanie usunięta („ export -nin” bash), więc zazwyczaj nie trzeba jej ponownie eksportować. Wyłączenie zmiennej unsetpowoduje jej usunięcie (bez względu na to, czy jest to zmienna środowiskowa, czy nie).

Tablice i asocjacyjne skróty w bashoraz inne powłoki nie mogą być eksportowane jako zmienne środowiskowe. Zmienne środowiskowe muszą być prostymi zmiennymi, których wartości są łańcuchami, i często mają nazwy składające się z wielkich liter.

Wykorzystanie zmiennych środowiskowych służy do śledzenia danych w bieżącej sesji powłoki, ale także do umożliwienia każdemu rozpoczętemu procesowi uwzględnienia tych danych. Typowym tego przykładem jest PATHzmienna środowiskowa, która może być ustawiona w powłoce, a następnie używana przez dowolny program, który chce uruchomić programy bez podawania pełnej ścieżki do nich.

Zbiór zmiennych środowiskowych w procesie jest często określany jako „środowisko procesu”. Każdy proces ma swoje własne środowisko.

Zmienne środowiskowe można tylko „przekazywać”, tj. Proces potomny nigdy nie może zmienić zmiennych środowiskowych w procesie macierzystym, a oprócz skonfigurowania środowiska dla procesu potomnego po uruchomieniu, proces macierzysty nie może zmienić istniejącego środowiska proces potomny.

Zmienne środowiskowe mogą być wyświetlane z env(bez żadnych argumentów). Poza tym wyglądają tak samo jak nieeksportowane zmienne powłoki w sesji powłoki. Jest to trochę wyjątkowe dla powłoki, ponieważ większość innych języków programowania zwykle nie miesza „zwykłych” zmiennych ze zmiennymi środowiskowymi (patrz poniżej).

env może być również użyty do ustawienia wartości jednej lub kilku zmiennych środowiskowych w środowisku procesu bez ustawiania ich w bieżącej sesji:

env CC=clang CXX=clang++ make

Zaczyna makesię to od zmiennej środowiskowej CCustawionej na wartość clangi CXXustawioną na clang++.

Może być również użyty do oczyszczenia środowiska dla procesu:

env -i bash

To się uruchamia, bashale nie przenosi bieżącego środowiska do nowego bashprocesu (nadal będzie mieć zmienne środowiskowe, ponieważ tworzy nowe ze swoich skryptów inicjujących powłokę).

Przykład różnicy

$ 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

Inne języki

W większości języków programowania dostępne są funkcje biblioteczne, które umożliwiają pobieranie i ustawianie zmiennych środowiskowych. Zauważ, że ponieważ zmienne środowiskowe są przechowywane jako prosta relacja klucz-wartość, zwykle nie są one „zmiennymi” języka. Program może pobrać wartość (która jest zawsze ciągiem znaków) odpowiadającą kluczowi (nazwie zmiennej środowiskowej), ale będzie musiał następnie przekonwertować ją na liczbę całkowitą lub inny typ danych, jakiego oczekuje język.

W C, zmienne środowiska można uzyskać za pomocą getenv(), setenv(), putenv()i unsetenv(). Zmienne utworzone za pomocą tych procedur są dziedziczone w ten sam sposób przez dowolny proces uruchamiany przez program C.

Inne języki mogą mieć specjalne struktury danych do osiągnięcia tego samego, jak %ENVskrót w Perlu lub ENVIRONtablica asocjacyjna w większości implementacji awk.

Kusalananda
źródło
dzięki, doskonale jasne wytłumaczenie. Więc środowisko jest jak duże pole, w którym inne programy mogą żyć i widzieć każdą ze zmiennych środowiskowych. Niektóre programy mają swoje prywatne zmienne, tylko oni sami je widzą, podobnie jak powłoka. ale istnieje mechanizm, dzięki któremu zmienne prywatne są widziane przez wszystkich zwanych „eksport”. Jeśli to zrozumiałe, to jedyne, czego nie jestem pewien, to czy może istnieć więcej niż jedno środowisko w tym samym czasie?
rekinant
@sharkant Każdy uruchomiony proces ma swoje własne środowisko. To środowisko jest dziedziczone z procesu, który go uruchomił. Nigdy nie dochodzi do „cross-talk” między środowiskami różnych procesów. Jedynym sposobem na zmianę zmiennej środowiskowej w procesie jest modyfikacja samego procesu.
Kusalananda
dziękuję za wyjaśnienie mojego zrozumienia. Każda ryba w swojej własnej misce. A co z procesami, które spawnują inne procesy? Czy procesy i ich procesy potomne są w jednym środowisku, czy też każde ma swoje?
rekinant
1
@sharkant Istnieją funkcje biblioteczne w większości języków, które umożliwiają pobieranie i ustawianie zmiennych środowiskowych. W C, odbywa się to z getenv(), setenv(), putenv()i unsetenv(). Zmienne utworzone za pomocą tych procedur są dziedziczone w ten sam sposób przez dowolny proces uruchamiany przez program C. Inne języki mogą mieć specjalne struktury danych dla tego samego, jak %ENVw Perlu.
Kusalananda
1
FWIW: exec*()rodzina funkcji może również ustawić środowisko dla wykonywanego procesu.
Satō Katsura
5

Zmienne powłoki są trudne do powielenia.

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

Zmienne środowiskowe można jednak powielać; są tylko listą, a lista może zawierać zduplikowane wpisy. Oto, envdup.cco możesz zrobić.

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

Które możemy skompilować i uruchomić, envdupa następnie uruchomić, envaby pokazać nam, jakie zmienne środowiskowe są ustawione ...

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

Może to być przydatne tylko do znajdowania błędów lub innych dziwności w tym, jak dobrze radzą sobie programy **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
$ 

Wygląda na to, że Python 3.6 ślepo przekazuje duplikaty (nieszczelna abstrakcja), podczas gdy Perl 5.24 nie. Co powiesz na muszle?

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

Boże, co się stanie, jeśli sudotylko zdezynfekuje pierwszy wpis środowiska, a następnie bashuruchomi się z drugim? Witaj PATHlub LD_RUN_PATHwykorzystaj. Czy twoja sudo(i wszystko inne ?) Załatała tę dziurę ? Exploity bezpieczeństwa nie są ani „anegdotyczną różnicą”, ani „błędem” w programie wywołującym.

gałązka
źródło
1
To prawda, ale niepotwierdzona różnica i prawdopodobnie błąd programu ustawiającego zduplikowaną zmienną.
jlliagre
1
Zobacz rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas
0

Zmienna jest jak zmiennej powłoki , ale to nie jest właściwy do muszli . Wszystkie procesy w systemach Unix mają pamięć zmienną środowiskową . Główna różnica między zmiennymi środowiskowymi a powłokowymi polega na tym, że system operacyjny przekazuje wszystkie zmienne środowiskowe powłoki do programów uruchamianych przez powłokę, podczas gdy do zmiennych powłoki nie można uzyskać dostępu za pomocą uruchamianych poleceń.

env –Polecenie pozwala na uruchomienie innego programu w środowisku niestandardowym bez modyfikowania bieżącego. Jeśli zostanie użyte bez argumentu, wydrukuje listę bieżących zmiennych środowiskowych. printenv –Polecenie drukuje wszystkie lub określone zmienne środowiskowe. set –Polecenie ustawia lub usuwa zmienne powłoki. Jeśli zostanie użyta bez argumentu, wydrukuje listę wszystkich zmiennych, w tym zmiennych środowiskowych i zmiennych powłoki oraz funkcji powłoki. unset –Polecenie usuwa powłoki i zmienne środowiskowe. export –Polecenie ustawia zmienne środowiskowe

Mehdi sellami
źródło