Jak drukować tylko zdefiniowane zmienne (powłoki i / lub zmienne środowiskowe) w bash

57

Wbudowane polecenie bash set, jeśli zostanie wywołane bez argumentów, wyświetli wszystkie zmienne powłoki i środowiska, ale także wszystkie zdefiniowane funkcje. To sprawia, że ​​wyjście jest bezużyteczne dla ludzi i trudne grep.

Jak mogę sprawić, aby wbudowane polecenie bash wyświetlało settylko zmienne, a nie funkcje?

Czy istnieją inne polecenia, które wypisują tylko zmienne powłoki, bez funkcji?

Uwaga: bash rozróżnia zmienne powłoki i zmienne środowiskowe. zobacz tutaj Różnica między zmiennymi środowiskowymi a eksportowanymi zmiennymi środowiskowymi w bash

lesmana
źródło

Odpowiedzi:

51

„Czy istnieją inne polecenia, które wypisują tylko zmienne powłoki, bez funkcji?”

W man bash w sekcji SHELL BUILTIN COMMANDS (w sekcji set) jest napisane: „W trybie posix wymienione są tylko zmienne powłoki.”

(set -o posix; set)

Uwaga: ()składnia odradza podpowłokę, jeśli nie lubisz rozwidlać, użyj bardziej pełnej wersji

set -o posix; set; set +o posix
temp. temp
źródło
1
Zdecydowanie najlepsze rozwiązanie
Ruslan
1
Na początku jest kilka zwykle nieciekawych zmiennych _, których łatwo się pozbyć:(set -o posix ; set | grep -v ^_)
hyde
Tak długo mi to przeszkadzało! Pomijanie linii zaczynających się od _ nie wystarczy, jeśli masz wartości wieloliniowe, nadal pozostawi zawartość wartości w takcie. Ponieważ zestaw drukuje wiersze w kolejności, wszystkie _ wiersze będą na końcu, więc możesz po prostu wyciąć wyniki po pierwszym _ wierszu, używając:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott
1
Tylko uwaga: nawiasy są ważne w (set -o posix; set). Bez nawiasów zachowanie bieżącej powłoki Bash zmieniłoby się zgodnie ze standardem Posix. (Nie wiem, co to znaczy, ale wygląda na ważne.) W nawiasach Bash pozostaje niezmieniony.
John Red,
1
Efekty uboczne : Ustawia POSIXLY_CORRECT=yi dodaje posixdo$SHELLOPTS
Tom Hale
31

Oto kilka obejść:

$ comm -3 <(declare | sort) <(declare -f | sort)

awaria:

  1. declare wypisuje każdą zdefiniowaną zmienną (eksportowaną lub nie) i funkcję.
  2. declare -f drukuje tylko funkcje.
  3. comm -3usunie wszystkie linie wspólne dla obu. W efekcie spowoduje to usunięcie funkcji, pozostawiając tylko zmienne.

Aby wydrukować tylko zmienne, które nie są eksportowane:

$ comm -3 <(comm -3 <(declare | sort) <(declare -f | sort)) <(env | sort)

Kolejne obejście:

$ declare -p

Spowoduje to wydrukowanie tylko zmiennych, ale z pewnymi brzydkimi atrybutami.

declare -- BASH="/bin/bash"
declare -ir BASHPID=""
declare -A BASH_ALIASES='()'
declare -a BASH_ARGC='()'
...

Możesz wyciąć atrybuty za pomocą ... cut:

$ declare -p | cut -d " " -f 3

Jednym minusem jest to, że wartość IFS jest interpretowana zamiast wyświetlana.

porównać:

$ comm -3 <(declare | sort) <(declare -f | sort)
...
IFS=$' \t\n'
...
$ declare -p | cut -d " " -f 3
...
IFS="
"
...

To sprawia, że ​​dość trudno jest użyć tego wyjścia do dalszego przetwarzania, ponieważ jest on samotny "w jednej linii. Być może można to zrobić za pomocą IFS-fu, aby temu zapobiec.


Jeszcze jedno obejście, używając compgen:

$ compgen -v

Wbudowane bash compgenmiało być używane w skryptach ukończenia. W tym celu compgen -vwymienia wszystkie zdefiniowane zmienne. Wada: zawiera tylko nazwy zmiennych, a nie wartości.

Oto hack, aby również wymienić wartości.

$ compgen -v | while read var; do printf "%s=%q\n" "$var" "${!var}"; done

Zaleta: jest to czyste rozwiązanie. Wada: niektóre wartości są pomieszane z powodu interpretacji printf. Również podpowłoka z potoku i / lub pętli dodaje dodatkowe zmienne.

lesmana
źródło
czy declare ...| cutrura się zepsuje, jeśli nie ma atrybutów dla zmiennej? Zgaduję, że --w takim przypadku jest to używane, ale nadal jestem niespokojny. sedmoże być bezpieczniejszy, tj.declare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd
+1 za compgen:)
RSFalcon7
13

typesetWbudowane dziwnie posiada opcję, aby pokazać tylko funkcje ( -f), ale nie pokazują tylko parametry. Na szczęście typeset(lub set) wyświetla wszystkie parametry, zanim wszystkie funkcje, nazwy funkcji i parametrów nie mogą zawierać znaków nowej linii lub znaków równości, a znaki nowej linii w wartościach parametrów są cytowane (pojawiają się jako \n). Możesz więc zatrzymać się w pierwszym wierszu, który nie zawiera znaku równości:

set | awk -F '=' '! /^[0-9A-Z_a-z]+=/ {exit} {print $1}'

Spowoduje to jedynie wydrukowanie nazw parametrów; jeśli chcesz wartości, zmień print $1na print $0.

Zauważ, że drukuje to wszystkie nazwy parametrów (parametry i zmienne są tutaj używane synonimicznie), nie tylko zmienne środowiskowe („zmienna środowiskowa” jest synonimem „eksportowanych parametrów”).

Zauważ również, że zakłada to, że nie ma zmiennej środowiskowej o nazwie, która nie pasuje do ograniczeń bash dotyczących nazw parametrów. Takie zmienne nie mogą być tworzone w bash, ale mogą zostać odziedziczone ze środowiska po uruchomieniu bash:

env 'foo ()
{
  =oops' bash
Gilles „SO- przestań być zły”
źródło
Świetne rozwiązanie! Nawet nie pomyślałem o zatrzymaniu się na pierwszej linii, która nie ma „=”. +1
Steven D
Równoważnie z sed: set | sed '/=/!Q'
Toby Speight
7

Nie jestem pewien, jak można tworzyć setzmienne tylko do drukowania. Jednak patrząc na dane wyjściowe set, byłem w stanie wymyślić, co wydaje się chwytać tylko zmienne:

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" 

Zasadniczo szukam linii rozpoczynających się od liter, cyfr lub znaków interpunkcyjnych, po których następuje „=”. Z wyników, które widziałem, pobiera wszystkie zmienne; wątpię jednak, aby było to bardzo przenośne.

Jeśli, jak sugeruje tytuł, chcesz odjąć od tej listy zmienne, które są eksportowane, a tym samym uzyskać listę nieeksportowanych zmiennych, możesz zrobić coś takiego

$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars && env | sort | comm -23 ./setvars - 

Aby to nieco rozbić, oto, co robi:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Mam nadzieję, że pobiera wszystkie zmienne (jak omówiono wcześniej), sortuje je i umieszcza wynik w pliku.
  2. && env | sort: Po zakończeniu poprzedniego polecenia wywołamy envi posortujemy jego wynik.
  3. | comm -23 ./setvars -: Na koniec przesyłamy potokowo posortowane dane wyjściowe envdo commi używamy -23opcji do drukowania linii unikatowych dla pierwszego argumentu, w tym przypadku linii unikatowych dla naszego wyniku z zestawu.

Po zakończeniu możesz wyczyścić plik tymczasowy utworzony za pomocą polecenia rm ./setvars

Steven D.
źródło
Problem z twoją komendą nie polega na przenośności (jest oczywiście specyficzna dla bash, ale nie ma problemu po grep). Problem polega na tym, że pobierasz niektóre linie w definicjach funkcji. Bash wcina większość kodu funkcji, ale nie całość, w szczególności nie tekst w dokumentach ( f () {|cat <<EOF|foo=bar|EOF|}gdzie |reprezentuje podział wiersza).
Gilles „SO- przestań być zły”
6

Wystarczy użyć polecenia env. Nie drukuje funkcji.

unxnut
źródło
1
Dzieje się tak, jeśli jest używany w skrypcie.
DocSalvager,
2

Spróbuj wykonać polecenie printenv:

$printenv
Emilio R.
źródło
6
printenv i env drukują tylko zmienne eksportowane (środowiskowe), a nie zmienne (eksportowane) (shell).
Lri
@Lri Thanks! Zastanawiałem się, jak wydrukować tylko wyeksportowane zmienne.
Mark Stewart
1

W bash wyświetli tylko nazwy zmiennych:

compgen -v

Lub, jeśli potrzebne są również wartości, użyj:

declare -p
Izaak
źródło
dziękuję za Twój wysiłek. proszę zauważyć, że już wspomniałem o tej możliwości w mojej odpowiedzi unix.stackexchange.com/a/5691/1170 i tak mam opinię pozytywną.
lesmana,
0

różnicowanie się w stosunku do czystej powłoki, aby upewnić się, że pominięto także zmienne skryptów rc

## get mostly local vars
diff_env(){
    diff <(bash -cl 'set -o posix && set') \
        <(set -o posix && set && set +o posix) | \
        grep -E "^>|^\+" | \
        grep -Ev "^(>|\+|\+\+) ?(BASH|COLUMNS|LINES|HIST|PPID|SHLVL|PS(1|2)|SHELL|FUNC)" | \
        sed -r 's/^> ?|^\+ ?//'
}

wersja z commbyłaby zbyt skomplikowana

odtąd
źródło
0

Przyjęta odpowiedź jest świetna. Oferuję alternatywę, która nie wymaga ustawienia trybu POSIX:

Oto technika, której użyłem do przechowywania stanu funkcji w pliku, aby móc kontynuować ją później. Pierwszy tworzy zrzut stanu, a drugi tylko listę nazw zmiennych.

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo $a; done 

set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo ${a/=*}; done 

Oba działają przez zrzucanie wierszy, aż znajdzie jeden bez znaku „=”, co występuje, gdy wymieniona jest pierwsza funkcja. Ten ostatni przycina zadanie.

Wil
źródło