Come stampare solo variabili definite (variabili shell e / o ambiente) in bash


57

Il comando incorporato bash set, se invocato senza argomenti, stamperà tutte le variabili shell e ambiente, ma anche tutte le funzioni definite. Ciò rende l'output inutilizzabile per l'uomo e difficile da realizzare grep.

Come posso fare in modo che il comando incorporato bash setstampi solo le variabili e non le funzioni?

Ci sono altri comandi che stampano solo le variabili della shell, senza le funzioni?

Nota: bash distingue tra variabili shell e variabili d'ambiente. vedi qui Differenza tra variabili d'ambiente e variabili d'ambiente esportate in bash

Risposte:


51

"Esistono altri comandi che stampano solo le variabili della shell, senza le funzioni?"

In man bash, nella sezione SHELL BUILTIN COMMANDS (nella sezione set) dice: "In modalità posix, sono elencate solo le variabili shell".

(set -o posix; set)

nota: la ()sintassi genera una subshell, se non ti piace il biforcazione basta usare la versione più dettagliata

set -o posix; set; set +o posix

1
Di gran lunga la soluzione migliore
Ruslan,

1
Ci sono un sacco di variabili di solito poco interessanti che iniziano con _, che sono facili da eliminare:(set -o posix ; set | grep -v ^_)
hyde

Anche questo mi ha infastidito per così tanto tempo! Eliminare le righe che iniziano con _ non è sufficiente se si hanno valori su più righe, lascerà comunque intatto il contenuto del valore. Poiché set stampa le linee in ordine, tutte le linee _ saranno alla fine, quindi puoi semplicemente tagliare i risultati dopo la prima linea _, usando:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott

1
Solo una nota: le parentesi sono importanti in (set -o posix; set). Senza parentesi il comportamento dell'attuale shell Bash verrebbe modificato in modo da corrispondere allo standard Posix. (Non so cosa significhi, ma sembra importante.) Con le parentesi, il Bash rimane invariato.
John Red,

1
Effetti collaterali : imposta POSIXLY_CORRECT=ye aggiunge posixa$SHELLOPTS
Tom Hale

32

Ecco alcune soluzioni alternative:

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

abbattersi:

  1. declare stampa ogni variabile definita (esportata o meno) e funzione.
  2. declare -f stampa solo funzioni.
  3. comm -3rimuoverà tutte le linee comuni ad entrambi. In effetti questo rimuoverà le funzioni, lasciando solo le variabili.

Per stampare solo variabili che non vengono esportate:

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

Un'altra soluzione alternativa:

$ declare -p

Questo stamperà solo le variabili, ma con alcuni brutti attributi.

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

Puoi tagliare gli attributi usando ... cut:

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

Un aspetto negativo è che il valore di IFS viene interpretato anziché visualizzato.

confrontare:

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

Questo rende abbastanza difficile usare quell'output per ulteriori elaborazioni, a causa di quel solitario "in una riga. Forse un po 'di IFS-fu può essere fatto per evitarlo.


Ancora un'altra soluzione alternativa, usando compgen:

$ compgen -v

Il built-in bash compgendoveva essere usato negli script di completamento. A tal fine, compgen -velenca tutte le variabili definite. Il rovescio della medaglia: elenca solo i nomi delle variabili, non i valori.

Ecco un trucco per elencare anche i valori.

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

Il vantaggio: è una soluzione bash pura. Lo svantaggio: alcuni valori sono incasinati a causa dell'interpretazione printf. Anche la subshell dalla pipe e / o dal loop aggiungono alcune variabili extra.


la tua declare ...| cutpipe si rompe se non ci sono attributi per una variabile? Immagino che --sia usato in quel caso, ma sono ancora a disagio. sedpotrebbe essere più sicuro, ovverodeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd,

+1 per compgen:)
RSFalcon7

13

La funzione typesetincorporata stranamente ha un'opzione per mostrare solo le funzioni ( -f) ma non per mostrare solo i parametri. Fortunatamente, typeset(o set) visualizza tutti i parametri prima che tutte le funzioni, i nomi delle funzioni e dei parametri non possano contenere newline o segni di uguale, e le nuove righe nei valori dei parametri siano quotate (appaiono come \n). Quindi puoi fermarti alla prima riga che non contiene un segno uguale:

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

Questo stampa solo i nomi dei parametri; se vuoi i valori, cambia print $1in print $0.

Si noti che questo stampa tutti i nomi dei parametri (parametri e variabili sono usati sinonimo qui), non solo variabili d'ambiente ("variabile d'ambiente" è sinonimo di "parametri esportati").

Si noti inoltre che ciò presuppone che non vi siano variabili d'ambiente con un nome che non corrisponda ai vincoli di bash sui nomi dei parametri. Tali variabili non possono essere create all'interno di bash ma possono essere ereditate dall'ambiente all'avvio di bash:

env 'foo ()
{
  =oops' bash

Ottima soluzione! Non ho nemmeno pensato di fermarmi alla prima riga che non ha un '='. +1
Steven D

Equivalentemente, con sed: set | sed '/=/!Q'
Toby Speight,

7

Non sono sicuro di come si possano rendere setvariabili solo la stampa. Tuttavia, guardando l'output di set, sono stato in grado di elaborare quanto segue che sembra afferrare solo le variabili:

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

Fondamentalmente, sto cercando le righe che iniziano con lettere, numeri o punteggiatura seguiti da un "=". Dall'output che ho visto, questo afferra tutte le variabili; tuttavia, dubito che questo sia molto portatile.

Se, come suggerisce il titolo, vuoi sottrarre da questo elenco le variabili che vengono esportate e quindi ottenere un elenco di variabili non esportate, allora potresti fare qualcosa del genere

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

Per scomporlo un po ', ecco cosa fa:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Si spera che ottenga tutte le variabili (come discusso in precedenza), le ordina e inserisce il risultato in un file.
  2. && env | sort: Al termine del comando precedente, chiameremo enve ordineremo il suo output.
  3. | comm -23 ./setvars -: Infine, eseguiamo il pipe dell'output ordinato di envin comme utilizziamo l' -23opzione per stampare le righe che sono uniche per il primo argomento, in questo caso le righe uniche per il nostro output dal set.

Al termine, è possibile che si desideri ripulire il file temporaneo creato con il comando rm ./setvars


Il problema con il tuo comando non è la portabilità (è specifico per bash, ovviamente, ma non ci sono problemi sul lato grep). Il problema è che stai afferrando alcune righe nelle definizioni delle funzioni. Bash fa rientrare la maggior parte del codice funzione, ma non tutto, in particolare non i documenti di testo all'interno ( f () {|cat <<EOF|foo=bar|EOF|}dove |rappresenta un'interruzione di riga).
Gilles 'SO- smetti di essere malvagio' il


2

Prova il comando printenv:

$printenv

6
printenv ed env stampano solo variabili esportate (ambiente) e non non esportate (shell).
Lri,

@Lri Grazie! Mi chiedevo come stampare solo le variabili esportate.
Mark Stewart,

1

In bash, questo stamperà solo i nomi delle variabili:

compgen -v

Oppure, se sono necessari anche valori, utilizzare:

declare -p

grazie per l'impegno. si prega di notare che ho già menzionato tale possibilità nella mia risposta unix.stackexchange.com/a/5691/1170 avere comunque un voto .
lesmana,

0

differendo da una shell pulita per garantire che vengano lasciati fuori anche gli errori dagli script 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/^> ?|^\+ ?//'
}

la versione con commsarebbe troppo contorta


0

La risposta accettata è ottima. Offro un'alternativa che non prevede l'impostazione della modalità POSIX:

Ecco la tecnica che ho usato per memorizzare lo stato della funzione in un file in modo da poterlo continuare in seguito. Il primo produce un dump di stato e il secondo produce solo un elenco dei nomi delle variabili.

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 

Entrambi funzionano eseguendo il dumping delle righe fino a quando non ne trova uno senza un carattere '=', che si verifica quando viene elencata la prima funzione. Quest'ultimo ritaglia l'incarico.

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.