Como listar as variáveis ​​declaradas no script em bash?


98

No meu script em bash, há muitas variáveis ​​e tenho que fazer algo para salvá-las em arquivo. Minha pergunta é como listar todas as variáveis ​​declaradas em meu script e obter uma lista como esta:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Respostas:


151

set irá exibir as variáveis, infelizmente também irá exibir as funções definidas também.

Felizmente, o modo POSIX exibe apenas as variáveis:

( set -o posix ; set ) | less

Canalizando lessou redirecionando para onde você deseja as opções.

Portanto, para obter as variáveis ​​declaradas apenas no 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

(Ou pelo menos algo baseado nisso :-))


Você editou enquanto eu estava postando. Boa chamada com o -o posixagora, um diff conterá apenas as variáveis.
ezpz de

8
Sem o uso de arquivos temporários: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Isso também produzirá os vars em um formato pronto para salvar. A lista incluirá as variáveis ​​que o script alterou (depende se isso é desejável)
ivan_pozdeev

1
@ErikAronesty Se você deseja recriar um ambiente com source, deve ser capaz de fazê-lo com a saída de declare -p.
Seis de

2
Se você deseja comparar um ambiente antes e depois com diff (ou qualquer comando semelhante) sem recorrer ao uso de arquivos temporários, você pode fazer o seguinte:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Seis de

1
@Caesar Não, só tentei arrays não vazios. Você está certo no caso de matrizes vazias - elas não são impressas.
Socowi


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

Isso deve imprimir todos os nomes de variáveis ​​do shell. Você pode obter uma lista antes e depois de fornecer seu arquivo como em "set" para diff quais variáveis ​​são novas (como explicado nas outras respostas). Mas tenha em mente que essa filtragem com diff pode filtrar algumas variáveis ​​que você precisa, mas que estavam presentes antes de fornecer seu arquivo.

No seu caso, se você sabe que os nomes das variáveis ​​começam com "VARIÁVEL", você pode originar seu script e fazer:

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

ATUALIZAÇÃO: Para solução BASH pura (sem comandos externos usados):

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 uma versão oneliner (com uma única avaliação):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj

4

Com base em algumas das respostas acima, isso funcionou para mim:

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

arquivo fonte :

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

3

Se você pode pós-processar, (como já mencionado) você pode apenas fazer uma setchamada no início e no final do seu script (cada um para um arquivo diferente) e fazer uma comparação entre os dois arquivos. Perceba que isso ainda conterá algum ruído.

Você também pode fazer isso programaticamente. Para limitar a saída apenas ao seu escopo atual, você teria que implementar um wrapper para a criação de variáveis. Por exemplo

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

Que rende

VAR1=abc
VAR2=bcd
VAR3=cde

2

Aqui está algo semelhante à resposta de @GinkgoFr, mas sem os problemas identificados por @Tino ou @DejayClayton, e é mais robusto do que a parte inteligente de @ DouglasLeeder set -o posix:

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

A diferença é que esta solução PARA após o primeiro relatório não variável, por exemplo, a primeira função relatada por set

BTW: O problema "Tino" está resolvido. Mesmo que POSIX esteja desligado e as funções sejam relatadas por set, a sed ...parte da solução permite apenas relatórios de variáveis ​​(por exemplo, VAR=VALUElinhas). Em particular, o A2que não spuriously fazê-lo na saída.

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

AND: O problema "DejayClayton" foi resolvido (novas linhas incorporadas em valores de variáveis não interrompem a saída - cada VAR=VALUEuma obtém uma única linha de saída):

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

NOTA: A solução fornecida por @DouglasLeeder sofre do problema "DejayClayton" (valores com novas linhas embutidas). Abaixo deA1 está errado e A2não deve ser exibido.

$ 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: Eu não acho que a versão de bash importe, mas pode. Eu fiz meu teste / desenvolvimento neste:

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

PÓS-SCRIPT: dadas algumas das outras respostas ao OP, tenho <100% de certeza de que set sempre converte novas linhas dentro do valor para \n, no qual essa solução depende para evitar o problema "DejayClayton". Talvez seja um comportamento moderno? Ou uma variação em tempo de compilação? Ou uma configuração de opção set -oou shopt? Se você souber de tais variações, por favor, adicione um comentário ...


0

Tente usar um script (vamos chamá-lo de "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

A partir de uma perspectiva de segurança, quer de @ akostadinov resposta ou de @ JuvenXu resposta é preferível confiando na saída desestruturado do setcomando, devido ao seguinte potencial falha de segurança:

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

A função acima doLogicusa setpara verificar a presença de variável PS1para determinar se o script é interativo ou não (não importa se esta é a melhor maneira de atingir esse objetivo; este é apenas um exemplo).

No entanto, a saída de set não é estruturada, o que significa que qualquer variável que contenha uma nova linha pode contaminar totalmente os resultados.

Isso, é claro, é um risco potencial à segurança. Em vez disso, use o suporte do Bash para expansão de nome de variável indireta ou compgen -v.


0

Experimente o seguinte: set | egrep "^\w+="(com ou sem o| less tubulação)

A primeira solução proposta,, ( set -o posix ; set ) | lessfunciona, mas tem uma desvantagem: ela transmite códigos de controle para o terminal, de modo que eles não são exibidos corretamente. Então, por exemplo, se houver (provavelmente) uma IFS=$' \t\n'variável, podemos ver:

IFS='
'

…em vez de.

Minha egrepsolução exibe isso (e eventualmente outros semelhantes) corretamente.


já se passaram 8 anos, você sabe
lauriys

Votação negativa porque é simplesmente bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A exibições erradasA=B" -> Falha!
Tino

Teste estranho que parece implicar apenas na pseudo-variável "_" (último argumento do comando anterior), mas não falha em outras, especialmente IFS. Executá-lo sob "bash -c" também pode torná-lo insignificante. Você deveria pelo menos ter ficado neutro em vez de votar contra ele.
GingkoFr

0

Provavelmente roubei a resposta há algum tempo ... de qualquer maneira, um pouco diferente em função:

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

O printenvcomando:

printenvimprime environment variablesjunto com seus valores.

Boa sorte...


0

Uma maneira simples de fazer isso é usar o modo estrito bash definindo as variáveis ​​de ambiente do sistema antes de executar seu script e usar diff para classificar apenas as do seu 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

Agora você pode recuperar variáveis ​​de seu script em /tmp/script_vars.log. Ou pelo menos algo baseado nisso!


0

Um pouco tarde para a festa, mas aqui vai outra sugestão:

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

A linha de comando do pipeline diff + sed exibe todas as variáveis ​​definidas pelo script no formato desejado (conforme especificado na postagem do OP):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.