Como imprimir apenas variáveis ​​definidas (shell e / ou variáveis ​​de ambiente) no bash

57

O comando bash builtin set, se chamado sem argumentos, imprimirá todas as variáveis ​​de shell e ambiente, mas também todas as funções definidas. Isso torna a saída inutilizável para humanos e difícil grep.

Como posso fazer com que o comando bash builtin setimprima apenas as variáveis ​​e não as funções?

Existem outros comandos que imprimem apenas as variáveis ​​do shell, sem as funções?

Nota: o bash diferencia entre variáveis ​​de shell e variáveis ​​de ambiente. veja aqui Diferença entre variáveis ​​de ambiente e variáveis ​​de ambiente exportadas no bash

lesmana
fonte

Respostas:

51

"Existem outros comandos que imprimem apenas as variáveis ​​do shell, sem as funções?"

No man bash, na seção SHELL BUILTIN COMMANDS (na seção set), diz: "No modo posix, apenas as variáveis ​​shell são listadas".

(set -o posix; set)

note: a ()sintaxe gera um subshell, se você não gosta de bifurcação, use a versão mais detalhada

set -o posix; set; set +o posix
temp
fonte
11
De longe a melhor solução
Ruslan
11
Há um monte de variáveis normalmente desinteressante começando com _, que são fáceis de se livrar de:(set -o posix ; set | grep -v ^_)
Hyde
Isso também me incomoda há tanto tempo! Manter as linhas que começam com _ não é suficiente se você tiver valores de várias linhas; ele ainda deixará o conteúdo do valor intacto. Desde conjunto de impressões as linhas em ordem, todos os _ linhas será no final, então você pode simplesmente cortar os resultados depois da primeira linha _, utilizando:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott
11
Apenas uma observação: os colchetes são importantes (set -o posix; set). Sem os colchetes, o comportamento do shell Bash atual seria alterado para corresponder ao padrão Posix. (Não sei o que isso significa, mas parece importante.) Com os colchetes, o Bash permanece inalterado.
John Red
11
Efeitos colaterais : define POSIXLY_CORRECT=ye adiciona posixa$SHELLOPTS
Tom Hale
31

Aqui estão algumas soluções alternativas:

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

demolir:

  1. declare imprime todas as variáveis ​​definidas (exportadas ou não) e funções.
  2. declare -f imprime apenas funções.
  3. comm -3removerá todas as linhas comuns a ambos. Com efeito, isso removerá as funções, deixando apenas as variáveis.

Para imprimir apenas variáveis ​​que não são exportadas:

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

Outra solução alternativa:

$ declare -p

Isso imprimirá apenas as variáveis, mas com alguns atributos feios.

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

Você pode cortar os atributos usando ... cut:

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

Uma desvantagem é que o valor do IFS é interpretado em vez de exibido.

comparar:

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

Isso torna bastante difícil usar essa saída para processamento adicional, por causa disso isolado "em uma linha. Talvez algum IFS-fu possa ser feito para evitar isso.


Outra solução alternativa, usando compgen:

$ compgen -v

O bash interno compgenfoi criado para ser usado em scripts de conclusão. Para esse fim, compgen -vlista todas as variáveis ​​definidas. A desvantagem: lista apenas os nomes das variáveis, não os valores.

Aqui está um hack para listar também os valores.

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

A vantagem: é uma solução pura para o bash. A desvantagem: alguns valores são confusos por causa da interpretação printf. Além disso, o subshell do pipe e / ou loop adiciona algumas variáveis ​​extras.

lesmana
fonte
seu declare ...| cutpipe quebra se não houver atributos para uma variável? Acho que --é usado nesse caso, mas ainda estou desconfortável. sedpode ser mais seguro, iedeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd
+1 para compgen:)
RSFalcon7
13

O typesetbuiltin estranhamente tem uma opção para mostrar apenas funções ( -f), mas não para mostrar apenas parâmetros. Felizmente, typeset(ou set) exibe todos os parâmetros antes que todas as funções, funções e nomes de parâmetros não possam conter novas linhas ou sinais de igual, e as novas linhas nos valores dos parâmetros sejam citadas (elas aparecem como \n). Portanto, você pode parar na primeira linha que não contém um sinal de igual:

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

Isso imprime apenas os nomes dos parâmetros; se você deseja os valores, mude print $1para print $0.

Observe que isso imprime todos os nomes de parâmetros (parâmetros e variáveis ​​são usados ​​como sinônimos aqui), não apenas variáveis ​​de ambiente ("variável de ambiente" é sinônimo de "parâmetros exportados").

Observe também que isso pressupõe que não há variável de ambiente com um nome que não corresponda às restrições do bash nos nomes de parâmetros. Tais variáveis ​​não podem ser criadas dentro do bash, mas podem ter sido herdadas do ambiente quando o bash é iniciado:

env 'foo ()
{
  =oops' bash
Gilles 'SO- parar de ser mau'
fonte
Ótima solução! Eu nem pensei em parar na primeira linha que não tem um '='. 1
Steven D
Equivalentemente, com sed: set | sed '/=/!Q'
Toby Speight 05/07
7

Não tenho certeza de como se pode setimprimir variáveis ​​apenas. No entanto, olhando para a saída de set, eu consegui criar o seguinte que parece pegar apenas variáveis:

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

Basicamente, estou procurando linhas que começam com letras, números ou pontuação seguidas por um "=". Da saída que eu vi, isso pega todas as variáveis; no entanto, duvido que isso seja muito portátil.

Se, como o título sugere, você deseja subtrair desta lista as variáveis ​​que são exportadas e, assim, obter uma lista de variáveis ​​não exportadas, poderá fazer algo assim

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

Para resumir um pouco, aqui está o que ele faz:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Esperamos que isso obtenha todas as variáveis ​​(como discutido anteriormente), classifique-as e cole o resultado em um arquivo.
  2. && env | sort: Após a conclusão do comando anterior, vamos chamar enve classificar sua saída.
  3. | comm -23 ./setvars -: Finalmente, canalizamos a saída classificada de envpara comme usamos a -23opção para imprimir as linhas que são únicas para o primeiro argumento; nesse caso, as linhas exclusivas para nossa saída do conjunto.

Quando terminar, você pode limpar o arquivo temporário que ele criou com o comando rm ./setvars

Steven D
fonte
O problema com seu comando não é portabilidade (é específico do bash, é claro, mas não há nenhum problema no lado grep). O problema é que você está pegando algumas linhas nas definições de função. O Bash recua a maior parte do código da função, mas não todo, em particular o texto dentro dos documentos aqui ( f () {|cat <<EOF|foo=bar|EOF|}onde |representa uma quebra de linha).
Gilles 'SO- stop be evil'
6

Basta usar o comando env. Não imprime funções.

unxnut
fonte
11
Faz se usado dentro de um script.
DocSalvager 5/09
2

Experimente o comando printenv:

$printenv
Emilio R.
fonte
6
printenv e env imprimem apenas variáveis ​​exportadas (ambiente) e não variáveis ​​não exportadas (shell).
Lri
@Lri Thanks! Gostaria de saber como imprimir apenas as variáveis ​​exportadas.
Mark Stewart
1

No bash, isso imprimirá apenas nomes de variáveis:

compgen -v

Ou, se também forem necessários valores, use:

declare -p
Isaac
fonte
obrigado pelo seu esforço. observe que eu já mencionei essa possibilidade na minha resposta unix.stackexchange.com/a/5691/1170 , de qualquer maneira, com um voto positivo .
Lesmana
0

diferindo de um shell limpo para garantir também que vários dos scripts rc sejam deixados de fora

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

a versão com commseria muito complicada

desenterrar
fonte
0

A resposta aceita é ótima. Estou oferecendo uma alternativa que não envolve a configuração do modo POSIX:

Aqui está a técnica que eu tenho usado para armazenar o estado da função em um arquivo para que eu possa continuar mais tarde. O primeiro produz um despejo de estado e o segundo produz apenas uma lista dos nomes das variáveis.

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 

Ambos funcionam despejando linhas até encontrar um sem um caractere '=', o que ocorre quando a primeira função é listada. O último corta a tarefa.

Wil
fonte