Jak wyświetlić zmienne zadeklarowane w skrypcie w bash?


98

W moim skrypcie w bashu jest dużo zmiennych i muszę coś zrobić, aby zapisać je do pliku. Moje pytanie brzmi, jak wyświetlić wszystkie zmienne zadeklarowane w moim skrypcie i uzyskać taką listę:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Odpowiedzi:


151

set wypisze zmienne, niestety wypisze również definicje funkcji.

Na szczęście tryb POSIX wyświetla tylko zmienne:

( set -o posix ; set ) | less

Piping do lesslub przekierowanie do miejsca, w którym chcesz mieć opcje.

Aby uzyskać zmienne zadeklarowane tylko w skrypcie:

( set -o posix ; set ) >/tmp/variables.before
source script
( set -o posix ; set ) >/tmp/variables.after
diff /tmp/variables.before /tmp/variables.after
rm /tmp/variables.before /tmp/variables.after

(A przynajmniej coś na tej podstawie :-))


Edytowałeś, kiedy publikowałem. Niezłe wywołanie z -o posixróżnicą now a będzie zawierało tylko zmienne.
ezpz

8
Bez korzystania z plików tymczasowych: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Spowoduje to również wyświetlenie zmiennych w formacie gotowym do zapisania. Lista będzie zawierała zmienne, które zmienił skrypt (zależy od tego, czy jest to pożądane)
ivan_pozdeev

1
@ErikAronesty Jeśli chcesz móc odtworzyć środowisko z source, powinieneś móc to zrobić z wyjściem declare -p.
Szósty,

2
Jeśli chcesz porównać środowisko przed i po z diff (lub jakimkolwiek podobnym poleceniem) bez uciekania się do plików tymczasowych, możesz to zrobić w następujący sposób:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Sześć

1
@Caesar Nie, próbowałem tylko niepustych tablic. Masz rację w przypadku pustych tablic - nie są one drukowane.
Socowi


10
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

To musi wypisać wszystkie nazwy zmiennych powłoki. Możesz otrzymać listę przed i po pobraniu pliku, tak jak przy „ustawieniu”, aby porównać, które zmienne są nowe (jak wyjaśniono w innych odpowiedziach). Pamiętaj jednak, że takie filtrowanie za pomocą diff może odfiltrować niektóre zmienne, których potrzebujesz, ale były obecne przed pozyskaniem pliku.

W Twoim przypadku, jeśli wiesz, że nazwy Twoich zmiennych zaczynają się od „VARIABLE”, możesz pobrać skrypt i wykonać:

for var in ${!VARIABLE@}; do
   printf "%s%q\n" "$var=" "${!var}"
done

UPDATE: Dla czystego rozwiązania BASH (bez zewnętrznych poleceń):

for i in _ {a..z} {A..Z}; do
   for var in `eval echo "\\${!$i@}"`; do
      echo $var
      # you can test if $var matches some criteria and put it in the file or ignore
   done 
done

1
+1 i wersja oneliner (z pojedynczą eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
oceną

4

Na podstawie niektórych z powyższych odpowiedzi, to zadziałało dla mnie:

before=$(set -o posix; set | sort);

plik źródłowy :

comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq) 

3

Jeśli możesz postprocesować (jak już wspomniano), możesz po prostu setwywołać na początku i na końcu swojego skryptu (każdy do innego pliku) i zrobić różnicę na dwóch plikach. Zrozum, że nadal będzie zawierał trochę hałasu.

Możesz to również zrobić programowo. Aby ograniczyć dane wyjściowe tylko do bieżącego zakresu, należałoby zaimplementować opakowanie do tworzenia zmiennych. Na przykład

store() {
    export ${1}="${*:2}"
    [[ ${STORED} =~ "(^| )${1}($| )" ]] || STORED="${STORED} ${1}"
}

store VAR1 abc
store VAR2 bcd
store VAR3 cde

for i in ${STORED}; do
    echo "${i}=${!i}"
done

Co daje

VAR1=abc
VAR2=bcd
VAR3=cde

2

Oto coś podobnego do odpowiedzi @GinkgoFr, ale bez problemów zidentyfikowanych przez @Tino lub @DejayClayton i jest bardziej wytrzymałe niż sprytny set -o posixkawałek @ DouglasLeeder :

+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }

Różnica polega na tym, że to rozwiązanie zatrzymuje się po pierwszym niezmiennym raporcie, np. Pierwszej funkcji zgłoszonej przez set

BTW: Problem „Tino” został rozwiązany. Mimo że POSIX jest wyłączony, a funkcje są raportowane przez set, sed ...część rozwiązania zezwala tylko na raportowanie zmiennych (np. VAR=VALUEWiersze). W szczególności, A2czy nie fałszywie uczynić go do wyjścia.

+ function a() { echo $'\nA2=B'; }; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999

AND: Problem „DejayClayton” został rozwiązany (osadzone znaki nowej linii w wartościach zmiennych nie zakłócają wyjścia - każdy z nich VAR=VALUEotrzymuje jedną linię wyjściową):

+ A1=$'111\nA2=222'; A0=000; A9=999; 
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999

UWAGA: Rozwiązanie dostarczone przez @DouglasLeeder cierpi na problem „DejayClayton” (wartości z osadzonymi znakami nowej linii). Poniżej A1jest błędne i A2nie powinno się w ogóle wyświetlać.

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

WRESZCIE: nie sądzę, że wersja ma bashznaczenie, ale może. Testowałem / rozwijałem na tym:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)

POST-SCRIPT: Biorąc pod uwagę niektóre inne odpowiedzi na OP, mam <100% pewności, że set zawsze konwertuje znaki nowej linii w wartości \n, na której opiera się to rozwiązanie, aby uniknąć problemu „DejayClayton”. Może to nowoczesne zachowanie? A może zmiana w czasie kompilacji? Lub ustawienie opcji set -olub shopt? Jeżeli ty wiesz o takich zmian, należy dodać komentarz ...


0

Spróbuj użyć skryptu (nazwijmy go „ls_vars”):

  #!/bin/bash
  set -a
  env > /tmp/a
  source $1
  env > /tmp/b
  diff /tmp/{a,b} | sed -ne 's/^> //p'

chmod + x it i:

  ls_vars your-script.sh > vars.files.save

0

Z punktu widzenia bezpieczeństwa, zarówno na @ akostadinov odpowiedź lub @ JuvenXu na odpowiedź korzystne jest poleganie na niestrukturalnych wyjściu setpolecenia, ze względu na następujące potencjalne luki bezpieczeństwa:

#!/bin/bash

function doLogic()
{
    local COMMAND="${1}"
    if ( set -o posix; set | grep -q '^PS1=' )
    then
        echo 'Script is interactive'
    else
        echo 'Script is NOT interactive'
    fi
}

doLogic 'hello'   # Script is NOT interactive
doLogic $'\nPS1=' # Script is interactive

Powyższa funkcja doLogicsłuży setdo sprawdzania obecności zmiennej w PS1celu określenia, czy skrypt jest interaktywny, czy nie (nieważne, czy jest to najlepszy sposób na osiągnięcie tego celu; to tylko przykład).

Jednak wynik setjest nieustrukturyzowany, co oznacza, że ​​każda zmienna zawierająca znak nowej linii może całkowicie zanieczyścić wyniki.

Jest to oczywiście potencjalne zagrożenie bezpieczeństwa. Zamiast tego użyj obsługi Bash do pośredniego rozwijania nazw zmiennych lub compgen -v.


0

Spróbuj tego: set | egrep "^\w+="(z | lessorurowaniem lub bez )

Pierwsze proponowane rozwiązanie ( set -o posix ; set ) | lessdziała, ale ma wadę: przekazuje kody sterujące do terminala, więc nie są one poprawnie wyświetlane. Tak na przykład, jeśli jest (prawdopodobnie)IFS=$' \t\n' zmienna, możemy zobaczyć:

IFS='
'

…zamiast.

Moje egreprozwiązanie wyświetla to (i ewentualnie inne podobne) poprawnie.


minęło już 8 lat, wiesz
Lauriys

Ocena negatywna, ponieważ jest to po prostu złe bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A wyświetlacze A=B"-> Niepowodzenie!
Tino

Dziwny test, który wydaje się sugerować tylko pseudozmienną „_” (ostatni argument poprzedniego polecenia), ale nie zawodzi na innych, zwłaszcza IFS. Uruchomienie go pod "bash -c" może również sprawić, że nie będzie to znaczące. Powinieneś przynajmniej zachować neutralność, zamiast głosować w dół.
GingkoFr

0

Prawdopodobnie ukradłem odpowiedź jakiś czas temu ... w każdym razie trochę inaczej jako func:

    ##
    # usage source bin/nps-bash-util-funcs
    # doEchoVars
    doEchoVars(){

        # if the tmp dir does not exist
        test -z ${tmp_dir} && \
        export tmp_dir="$(cd "$(dirname $0)/../../.."; pwd)""/dat/log/.tmp.$$" && \
        mkdir -p "$tmp_dir" && \
        ( set -o posix ; set )| sort >"$tmp_dir/.vars.before"


        ( set -o posix ; set ) | sort >"$tmp_dir/.vars.after"
        cmd="$(comm -3 $tmp_dir/.vars.before $tmp_dir/.vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
        echo -e "$cmd"
    } 

0

printenvPolecenie:

printenvwypisuje wszystko environment variableswraz z ich wartościami.

Powodzenia...


0

Prostym sposobem na to jest użycie trybu ścisłego bash poprzez ustawienie zmiennych środowiskowych systemu przed uruchomieniem skryptu i użycie diff do sortowania tylko tych w skrypcie:

# Add this line at the top of your script :
set > /tmp/old_vars.log

# Add this line at the end of your script :
set > /tmp/new_vars.log

# Alternatively you can remove unwanted variables with grep (e.g., passwords) :
set | grep -v "PASSWORD1=\|PASSWORD2=\|PASSWORD3=" > /tmp/new_vars.log

# Now you can compare to sort variables of your script :
diff /tmp/old_vars.log /tmp/new_vars.log | grep "^>" > /tmp/script_vars.log

Możesz teraz pobrać zmienne swojego skryptu w /tmp/script_vars.log. A przynajmniej coś opartego na tym!


0

Trochę za późno na imprezę, ale oto kolejna sugestia:

#!/bin/bash

set_before=$( set -o posix; set | sed -e '/^_=*/d' )

# create/set some variables
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c

set_after=$( set -o posix; unset set_before; set | sed -e '/^_=/d' )
diff  <(echo "$set_before") <(echo "$set_after") | sed -e 's/^> //' -e '/^[[:digit:]].*/d'

Różn + sed Linia komend rurociąg wysyła wszystkie zmienne skryptów zdefiniowane w żądanym formacie (jak określono na stanowiskach w OP)

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.