Wie man in bash nur definierte Variablen (Shell- und / oder Umgebungsvariablen) druckt

57

Wenn der integrierte Befehl bash setohne Argumente aufgerufen wird, werden alle Shell- und Umgebungsvariablen, aber auch alle definierten Funktionen gedruckt. Dies macht die Ausgabe für den Menschen unbrauchbar und schwierig grep.

Wie kann ich den eingebauten Bash-Befehl veranlassen set, nur die Variablen und nicht die Funktionen zu drucken?

Gibt es andere Befehle, die nur die Shell-Variablen ohne die Funktionen ausgeben?

Hinweis: bash unterscheidet zwischen Shell-Variablen und Umgebungsvariablen. siehe hier Unterschied zwischen Umgebungsvariablen und exportierten Umgebungsvariablen in bash

Lesmana
quelle

Antworten:

51

"Gibt es andere Befehle, die nur die Shell-Variablen ohne die Funktionen ausgeben?"

In man bash heißt es im Abschnitt SHELL BUILTIN COMMANDS (im set-Abschnitt): "Im posix-Modus werden nur Shell-Variablen aufgelistet."

(set -o posix; set)

Hinweis: ()Syntax erzeugt eine Subshell. Wenn Sie nicht forken möchten, verwenden Sie einfach die ausführlichere Version

set -o posix; set; set +o posix
temp
quelle
1
Mit Abstand die beste Lösung
Ruslan
1
Es gibt eine Reihe von normalerweise uninteressanten Variablen _, die leicht zu entfernen sind:(set -o posix ; set | grep -v ^_)
Hyde
Das hat mich auch so lange gestört! Das Entfernen der Zeilen, die mit _ beginnen, reicht nicht aus, wenn Sie mehrzeilige Werte haben. Der Inhalt des Werts bleibt jedoch unverändert. Da set die Zeilen der Reihe nach druckt, befinden sich alle _ Zeilen am Ende, sodass Sie die Ergebnisse einfach nach der ersten _ Zeile ausschneiden können, indem Sie set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott
1
Nur eine Anmerkung: die Klammern sind wichtig in (set -o posix; set). Ohne die Klammern würde sich das Verhalten der aktuellen Bash-Shell an den Posix-Standard anpassen. (Keine Ahnung, was das bedeutet, aber es sieht wichtig aus.) Mit den Klammern bleibt die Bash unverändert.
John Red
1
Nebenwirkungen : Die Sätze POSIXLY_CORRECT=yund fügt posixan$SHELLOPTS
Tom Hale
31

Hier sind einige Problemumgehungen:

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

Nervenzusammenbruch:

  1. declare druckt jede definierte Variable (exportiert oder nicht) und Funktion.
  2. declare -f druckt nur Funktionen.
  3. comm -3entfernt alle beiden gemeinsamen Zeilen. Tatsächlich werden dadurch die Funktionen entfernt, und nur die Variablen bleiben erhalten.

So drucken Sie nur Variablen, die nicht exportiert werden:

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

Eine andere Problemumgehung:

$ declare -p

Dadurch werden nur die Variablen mit einigen hässlichen Attributen gedruckt.

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

Sie können die Attribute mit ... cut entfernen:

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

Ein Nachteil ist, dass der Wert von IFS interpretiert und nicht angezeigt wird.

vergleichen Sie:

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

Dies macht es ziemlich schwierig, diese Ausgabe für die weitere Verarbeitung zu verwenden, da sie nur "in einer Zeile vorhanden ist. Vielleicht kann etwas IFS-Fu getan werden, um dies zu verhindern.


Noch eine Problemumgehung mit compgen:

$ compgen -v

Die eingebaute Bash compgensollte in Vervollständigungsskripten verwendet werden. Zu diesem Zweck compgen -vwerden alle definierten Variablen. Der Nachteil: Es werden nur die Variablennamen aufgeführt, nicht die Werte.

Hier ist ein Hack, um auch die Werte aufzulisten.

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

Der Vorteil: Es ist eine reine Bash-Lösung. Der Nachteil: Einige Werte sind durch die Interpretation durcheinander printf. Auch die Subshell aus der Pipe und / oder der Schleife fügt einige zusätzliche Variablen hinzu.

Lesmana
quelle
bricht Ihre declare ...| cutPipe, wenn es keine Attribute für eine Variable gibt? Ich vermute, --das wird in diesem Fall verwendet, aber ich bin immer noch unruhig. sedkönnte sicherer sein, dhdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd
+1 für compgen:)
RSFalcon7
13

Das typeseteingebaute hat seltsamerweise die Option, nur Funktionen anzuzeigen ( -f), aber nicht nur Parameter. Glücklicherweise zeigt typeset(oder set) alle Parameter an, bevor alle Funktionen, Funktions- und Parameternamen keine Zeilenumbrüche oder Gleichheitszeichen enthalten können, und Zeilenumbrüche in Parameterwerten werden in Anführungszeichen gesetzt (sie erscheinen als \n). Sie können also in der ersten Zeile anhalten, die kein Gleichheitszeichen enthält:

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

Hiermit werden nur die Parameternamen gedruckt. Wenn Sie die Werte möchten, ändern Sie print $1zu print $0.

Beachten Sie, dass hiermit alle Parameternamen gedruckt werden (Parameter und Variablen werden hier synonym verwendet), nicht nur Umgebungsvariablen („Umgebungsvariable“ ist synonym mit „exportierten Parametern“).

Beachten Sie auch, dass davon ausgegangen wird, dass es keine Umgebungsvariable mit einem Namen gibt, der nicht den Einschränkungen von bash für Parameternamen entspricht. Solche Variablen können nicht innerhalb von bash erstellt werden, sondern können zu Beginn von bash von der Umgebung geerbt worden sein:

env 'foo ()
{
  =oops' bash
Gilles 'SO - hör auf böse zu sein'
quelle
Tolle Lösung! Ich habe nicht einmal daran gedacht, bei der ersten Zeile anzuhalten, die kein '=' enthält. +1
Steven D
Gleichermaßen mit sed: set | sed '/=/!Q'
Toby Speight
7

Ich bin mir nicht sicher, wie man setnur Variablen drucken kann. Wenn setich mir jedoch die Ausgabe von anschaue , konnte ich Folgendes herausfinden, das nur Variablen zu erfassen scheint:

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

Grundsätzlich suche ich nach Zeilen, die mit Buchstaben, Zahlen oder Interpunktion beginnen, gefolgt von einem "=". Aus der Ausgabe, die ich gesehen habe, werden alle Variablen erfasst. Ich bezweifle jedoch, dass dies sehr portabel ist.

Wenn Sie, wie Ihr Titel schon sagt, die exportierten Variablen von dieser Liste abziehen möchten, um eine Liste der nicht exportierten Variablen zu erhalten, können Sie so etwas tun

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

Um dies ein wenig aufzuschlüsseln, ist hier, was es tut:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Damit werden hoffentlich alle Variablen (wie oben beschrieben) abgerufen, sortiert und das Ergebnis in einer Datei gespeichert.
  2. && env | sort: Nachdem der vorherige Befehl abgeschlossen ist, rufen wir envseine Ausgabe auf und sortieren sie.
  3. | comm -23 ./setvars -: Schließlich leiten wir die sortierte Ausgabe von envin um commund verwenden die -23Option, um die Zeilen zu drucken, die für das erste Argument eindeutig sind, in diesem Fall die Zeilen, die für unsere Ausgabe von set eindeutig sind.

Wenn Sie fertig sind, möchten Sie möglicherweise die temporäre Datei bereinigen, die mit dem Befehl erstellt wurde rm ./setvars

Steven D
quelle
Das Problem mit Ihrem Befehl ist nicht die Portierbarkeit (es ist natürlich spezifisch für Bash, aber auf der grep-Seite gibt es kein Problem). Das Problem ist, dass Sie einige Zeilen in Funktionsdefinitionen erfassen. Bash rückt den größten Teil des Funktionscodes ein, jedoch nicht den gesamten Code, insbesondere nicht den gesamten Text in den hier enthaltenen Dokumenten ( f () {|cat <<EOF|foo=bar|EOF|}wobei |ein Zeilenumbruch dargestellt wird).
Gilles 'SO - hör auf böse zu sein'
6

Verwenden Sie einfach den Befehl env. Es werden keine Funktionen gedruckt.

entmutigen
quelle
1
Es funktioniert, wenn es in einem Skript verwendet wird.
DocSalvager
2

Versuchen Sie den Befehl printenv:

$printenv
Emilio R.
quelle
6
printenv und env drucken nur exportierte (Umgebungs) Variablen und keine nicht exportierten (Shell) Variablen.
Lri
@Lri Danke! Ich habe mich gefragt, wie ich nur die exportierten Variablen ausdrucken soll.
Mark Stewart
1

In bash werden nur Variablennamen ausgegeben:

compgen -v

Oder, wenn auch Werte benötigt werden, verwenden Sie:

declare -p
Isaac
quelle
vielen Dank für Ihre Mühe. Bitte beachten Sie, dass ich diese Möglichkeit bereits in meiner Antwort erwähnt habe.
Lesmana
0

Ein Unterschied zu einer sauberen Shell, um sicherzustellen, dass auch Variablen aus RC-Skripten ausgelassen werden

## 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/^> ?|^\+ ?//'
}

die version mit commwäre zu kompliziert

untore
quelle
0

Die akzeptierte Antwort ist großartig. Ich biete eine Alternative an, bei der der POSIX-Modus nicht festgelegt wird:

Hier ist die Technik, mit der ich den Funktionsstatus in einer Datei gespeichert habe, damit ich ihn später fortsetzen kann. Das erste erzeugt einen State Dump und das zweite erzeugt nur eine Liste der Variablennamen.

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 

Beide arbeiten so lange, bis eine Zeile ohne '=' Zeichen gefunden wird. Dies tritt auf, wenn die erste Funktion aufgelistet wird. Letzterer beendet den Auftrag.

Wil
quelle