Wie liste ich Variablen auf, die im Skript in bash deklariert sind?

98

In meinem Skript in Bash gibt es viele Variablen, und ich muss etwas machen, um sie in einer Datei zu speichern. Meine Frage ist, wie man alle in meinem Skript deklarierten Variablen auflistet und eine Liste wie folgt erhält:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
lauriys
quelle

Antworten:

151

set gibt die Variablen aus, leider werden auch die definierten Funktionen ausgegeben.

Glücklicherweise gibt der POSIX-Modus nur die Variablen aus:

( set -o posix ; set ) | less

In den Rohrleitungen lessoder Umleitung zu dem Sie die Optionen mögen.

Um die Variablen nur im Skript zu deklarieren:

( 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

(Oder zumindest etwas basierend darauf :-))

Douglas Leeder
quelle
Sie haben bearbeitet, während ich gepostet habe. Netter Aufruf mit dem -o posixjetzt ein Diff enthält nur die Variablen.
Ezpz
8
Ohne temporäre Dateien zu verwenden : VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Dadurch werden die Variablen auch in einem speicherfertigen Format ausgegeben. Die Liste enthält die Variablen, die das Skript geändert hat (es hängt davon ab, ob dies wünschenswert ist)
ivan_pozdeev
1
@ErikAronesty Wenn Sie eine Umgebung mit neu erstellen möchten source, sollten Sie dies mit der Ausgabe von tun können declare -p.
Sechs
2
Wenn Sie eine Umgebung vorher und nachher mit diff (oder einem ähnlichen Befehl) vergleichen möchten, ohne auf die Verwendung von temporären Dateien zurückzugreifen, können Sie dies folgendermaßen tun:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Sechs
1
@ Caesar Nein, ich habe nur nicht leere Arrays ausprobiert. Bei leeren Arrays haben Sie Recht - sie werden nicht gedruckt.
Socowi
10
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

Dies muss alle Namen der Shell-Variablen drucken. Sie können eine Liste vor und nach dem Sourcing Ihrer Datei erhalten, genau wie mit "set", um zu unterscheiden, welche Variablen neu sind (wie in den anderen Antworten erläutert). Beachten Sie jedoch, dass eine solche Filterung mit diff einige Variablen herausfiltern kann, die Sie benötigen, die jedoch vor der Beschaffung Ihrer Datei vorhanden waren.

Wenn Sie in Ihrem Fall wissen, dass die Namen Ihrer Variablen mit "VARIABLE" beginnen, können Sie Ihr Skript als Quelle verwenden und Folgendes tun:

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

UPDATE: Für reine BASH-Lösung (keine externen Befehle verwendet):

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
akostadinov
quelle
1
+1 und eine Oneliner-Version (mit einer einzigen Bewertung):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj
4

Basierend auf einigen der oben genannten Antworten hat dies für mich funktioniert:

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

Quelldatei :

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

Wenn Sie nachbearbeiten können (wie bereits erwähnt), können Sie einfach setam Anfang und am Ende Ihres Skripts einen Aufruf tätigen (jeweils eine andere Datei) und die beiden Dateien unterscheiden. Beachten Sie, dass dies immer noch etwas Rauschen enthält.

Sie können dies auch programmgesteuert tun. Um die Ausgabe nur auf Ihren aktuellen Bereich zu beschränken, müssten Sie einen Wrapper für die Variablenerstellung implementieren. Beispielsweise

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

Welche ergibt

VAR1=abc
VAR2=bcd
VAR3=cde
ezpz
quelle
2

Hier ist etwas Ähnliches wie die @ GinkgoFr-Antwort, jedoch ohne die von @Tino oder @DejayClayton identifizierten Probleme, und es ist robuster als das clevere set -o posixBit von @ DouglasLeeder :

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

Der Unterschied besteht darin, dass diese Lösung nach dem ersten nicht variablen Bericht, z. B. der ersten von set

Übrigens: Das "Tino" -Problem ist gelöst. Obwohl POSIX deaktiviert ist und Funktionen von gemeldet werden set, sed ...erlaubt der Teil der Lösung nur variable Berichte durch (z VAR=VALUE. B. Zeilen). Insbesondere A2schafft es das nicht fälschlicherweise in die Ausgabe.

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

UND: Die „DejayClayton“ Problem ist gelöst (eingebettete Zeilenumbrüche in Variablenwerte haben nicht stören die Ausgabe - jeweils VAR=VALUEeine einzelne Ausgangsleitung erhalten):

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

HINWEIS: Die von @DouglasLeeder bereitgestellte Lösung leidet unter dem Problem "DejayClayton" (Werte mit eingebetteten Zeilenumbrüchen). Unten ist das A1falsch und A2sollte überhaupt nicht angezeigt werden.

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

ENDLICH: Ich denke nicht, dass die Version der bashDinge, aber es könnte. Ich habe meine Tests / Entwicklungen an diesem durchgeführt:

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

POST-SCRIPT: Angesichts einiger anderer Antworten auf das OP bin ich <100% sicher, dass set immer Zeilenumbrüche innerhalb des Werts konvertiert werden \n, auf den sich diese Lösung stützt, um das Problem "DejayClayton" zu vermeiden. Vielleicht ist das ein modernes Verhalten? Oder eine Variation zur Kompilierungszeit? Oder eine set -ooder shoptOption? Wenn Sie solche Variationen kennen, fügen Sie bitte einen Kommentar hinzu ...

Stevel
quelle
0

Versuchen Sie es mit einem Skript (nennen wir es "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 und:

  ls_vars your-script.sh > vars.files.save
Chen Levy
quelle
0

Aus Sicht der Sicherheit entweder @ akostadinov die Antwort oder @ JuvenXu die Antwort auf Berufung auf die unstrukturierte Ausgang des vorzuziehen ist setBefehl, aufgrund der folgenden potenziellen Sicherheitslücke:

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

Mit der obigen Funktion doLogicwird setüberprüft PS1, ob eine Variable vorhanden ist, um festzustellen, ob das Skript interaktiv ist oder nicht (egal, ob dies der beste Weg ist, um dieses Ziel zu erreichen; dies ist nur ein Beispiel).

Die Ausgabe von setist jedoch unstrukturiert, was bedeutet, dass jede Variable, die eine neue Zeile enthält, die Ergebnisse vollständig verunreinigen kann.

Dies ist natürlich ein potenzielles Sicherheitsrisiko. Verwenden Sie stattdessen entweder die Unterstützung von Bash für die Erweiterung indirekter Variablennamen oder compgen -v.

Dejay Clayton
quelle
0

Versuchen Sie Folgendes: set | egrep "^\w+="(mit oder ohne | lessRohrleitung)

Die erste vorgeschlagene Lösung ( set -o posix ; set ) | lessfunktioniert, hat jedoch einen Nachteil: Sie überträgt Steuercodes an das Terminal, sodass sie nicht richtig angezeigt werden. Wenn es zum Beispiel (wahrscheinlich) eine IFS=$' \t\n'Variable gibt, können wir sehen:

IFS='
'

…stattdessen.

Meine egrepLösung zeigt dies (und eventuell andere ähnliche) richtig an.

GingkoFr
quelle
Es ist 8 Jahre her, weißt du
Lauriys
Downvoted, weil es sich eindeutig um falsche bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A Anzeigen handelt A=B"-> Fail!
Tino
Seltsamer Test, der nur die Pseudovariable "_" (letztes Argument des vorherigen Befehls) impliziert, bei anderen jedoch nicht fehlschlägt, insbesondere bei IFS. Wenn Sie es unter "bash -c" ausführen, ist es möglicherweise auch nicht signifikant. Sie hätten zumindest neutral bleiben sollen, anstatt abzustimmen.
GingkoFr
0

Ich habe die Antwort wahrscheinlich schon vor einiger Zeit gestohlen ... sowieso etwas anders als eine Funktion:

    ##
    # 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"
    } 
Yordan Georgiev
quelle
0

Der printenvBefehl:

printenvdruckt alle environment variableszusammen mit ihren Werten.

Viel Glück...

Akash
quelle
0

Eine einfache Möglichkeit, dies zu tun, besteht darin, den Bash-Strict-Modus zu verwenden, indem Sie Systemumgebungsvariablen festlegen, bevor Sie Ihr Skript ausführen, und diff verwenden, um nur diejenigen Ihres Skripts zu sortieren:

# 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

Sie können jetzt Variablen Ihres Skripts in /tmp/script_vars.log abrufen. Oder zumindest etwas basierend darauf!

grm34
quelle