Come elencare le variabili dichiarate nello script in bash?


98

Nel mio script in bash, ci sono molte variabili e devo creare qualcosa per salvarle su file. La mia domanda è come elencare tutte le variabili dichiarate nel mio script e ottenere un elenco in questo modo:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Risposte:


151

set produrrà le variabili, sfortunatamente produrrà anche le definizioni delle funzioni.

Fortunatamente la modalità POSIX emette solo le variabili:

( set -o posix ; set ) | less

Piping lesso reindirizzamento a dove desideri le opzioni.

Quindi, per ottenere le variabili dichiarate solo nello script:

( 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

(O almeno qualcosa basato su quello :-))


Hai modificato mentre stavo postando. Bella chiamata con -o posixora un diff conterrà solo le variabili.
ezpz

8
Senza usare i file temporanei: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Questo produrrà anche le variabili in un formato pronto per il salvataggio. L'elenco includerà le variabili modificate dallo script (dipende se ciò è desiderabile)
ivan_pozdeev

1
@ErikAronesty Se vuoi essere in grado di ricreare un ambiente con source, dovresti essere in grado di farlo con l'output di declare -p.
Sei

2
Se vuoi confrontare un ambiente prima e dopo con diff (o qualsiasi comando simile) senza ricorrere all'uso di file temporanei, puoi farlo con quanto segue:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Six

1
@ Caesar No, ho provato solo array non vuoti. Hai ragione in caso di array vuoti: non vengono stampati.
Socowi


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

Questo deve stampare tutti i nomi delle variabili di shell. Puoi ottenere un elenco prima e dopo il sourcing del tuo file proprio come con "set" a diff quali variabili sono nuove (come spiegato nelle altre risposte). Ma tieni presente che questo filtro con diff può filtrare alcune variabili di cui hai bisogno ma che erano presenti prima di acquisire il tuo file.

Nel tuo caso, se sai che i nomi delle tue variabili iniziano con "VARIABILE", puoi procurarti lo script e fare:

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

AGGIORNAMENTO: Per la pura soluzione BASH (nessun comando esterno utilizzato):

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 e una versione oneliner (con una singola eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
valutazione

4

Sulla base di alcune delle risposte precedenti, questo ha funzionato per me:

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

file sorgente :

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

3

Se puoi post-processare, (come già accennato) potresti semplicemente effettuare una setchiamata all'inizio e alla fine del tuo script (ciascuno su un file diverso) e fare una differenza sui due file. Renditi conto che questo conterrà ancora del rumore.

Puoi anche farlo in modo programmatico. Per limitare l'output solo all'ambito corrente, è necessario implementare un wrapper per la creazione di variabili. Per esempio

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

Che produce

VAR1=abc
VAR2=bcd
VAR3=cde

2

Ecco qualcosa di simile alla risposta @GinkgoFr, ma senza i problemi identificati da @Tino o @DejayClayton, ed è più robusto del set -o posixbit intelligente di @ DouglasLeeder :

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

La differenza è che questa soluzione si FERMA dopo il primo report non variabile, ad esempio la prima funzione riportata da set

BTW: Il problema "Tino" è risolto. Anche se POSIX è disattivato e le funzioni sono riportate da set, la sed ...parte della soluzione consente solo il passaggio di rapporti variabili (ad esempio VAR=VALUErighe). In particolare, la A2fa non spurio rendono nell'uscita.

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

AND: Il problema "DejayClayton" è stato risolto (le nuove righe incorporate nei valori delle variabili non interrompono l'output - ognuna VAR=VALUEriceve una singola riga di output):

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

NOTA: la soluzione fornita da @DouglasLeeder soffre del problema "DejayClayton" (valori con newline incorporati). Di seguito, il A1è sbagliato e A2non dovrebbe essere visualizzato affatto.

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

FINALMENTE: Non credo che la versione delle bashcose, ma potrebbe. Ho fatto i miei test / sviluppo su questo:

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

POST-SCRIPT: date alcune delle altre risposte all'OP, sono rimasto <100% sicuro che converte set sempre le nuove righe all'interno del valore in \ncui si basa questa soluzione per evitare il problema "DejayClayton". Forse è un comportamento moderno? O una variazione in fase di compilazione? O un'opzione set -oo shoptun'impostazione? Se sei a conoscenza di tali variazioni, aggiungi un commento ...


0

Prova a usare uno script (chiamiamolo "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 e:

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

0

Dal punto di vista della sicurezza, sia di @ akostadinov risposta o di @ JuvenXu risposta è preferibile far valere l'uscita non strutturato del setcomando, per il seguente potenziale problema di sicurezza:

#!/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

La funzione sopra doLogicutilizza setper verificare la presenza di variabili PS1per determinare se lo script è interattivo o meno (non importa se questo è il modo migliore per raggiungere tale obiettivo; questo è solo un esempio).

Tuttavia, l'output di setnon è strutturato, il che significa che qualsiasi variabile che contiene una nuova riga può contaminare totalmente i risultati.

Questo, ovviamente, è un potenziale rischio per la sicurezza. Utilizzare invece il supporto di Bash per l'espansione indiretta del nome di variabile o compgen -v.


0

Prova questo: set | egrep "^\w+="(con o senza | lesstubazioni)

La prima soluzione proposta,, ( set -o posix ; set ) | lessfunziona ma ha un inconveniente: trasmette i codici di controllo al terminale, quindi non vengono visualizzati correttamente. Quindi, ad esempio, se c'è (probabilmente) una IFS=$' \t\n'variabile, possiamo vedere:

IFS='
'

…anziché.

La mia egrepsoluzione mostra questo (ed eventualmente altri simili) correttamente.


sono passati 8 anni, sai
lauriys

Downvoted perché si tratta di bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A visualizzazioni semplicemente sbagliateA=B" -> Fail!
Tino

Strano test che sembra implicare solo la pseudo-variabile "_" (ultimo argomento del comando precedente), ma non fallisce su altri, specialmente IFS. Anche eseguirlo sotto "bash -c" potrebbe renderlo non significativo. Avresti dovuto almeno rimanere neutrale invece di votare negativamente.
GingkoFr

0

Probabilmente ho rubato la risposta tempo fa ... comunque leggermente diversa come funzione:

    ##
    # 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

Il printenvcomando:

printenvstampa tutto environment variablesinsieme ai loro valori.

In bocca al lupo...


0

Un modo semplice per farlo è usare la modalità rigorosa bash impostando le variabili d'ambiente di sistema prima di eseguire lo script e usare diff per ordinare solo quelle del tuo script:

# 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

Ora puoi recuperare le variabili del tuo script in /tmp/script_vars.log. O almeno qualcosa basato su quello!


0

Un po 'tardi per la festa, ma ecco un altro suggerimento:

#!/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'

La riga di comando diff + sed pipeline restituisce tutte le variabili definite dallo script nel formato desiderato (come specificato nel post dell'OP):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.