¿Cómo enumerar las variables declaradas en el script en bash?

98

En mi script en bash, hay muchas variables y tengo que hacer algo para guardarlas en un archivo. Mi pregunta es cómo enumerar todas las variables declaradas en mi script y obtener una lista como esta:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
lauriys
fuente

Respuestas:

151

set generará las variables, desafortunadamente también generará las funciones definidas.

Afortunadamente, el modo POSIX solo genera las variables:

( set -o posix ; set ) | less

Conectando lesso redireccionando a donde quieras las opciones.

Entonces, para obtener las variables declaradas solo en el 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

(O al menos algo basado en eso :-))

Douglas Leeder
fuente
Editaste mientras yo publicaba. Buena llamada con el -o posixahora un diff solo contendrá las variables.
ezpz
8
Sin usar archivos temporales: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Esto también generará las variables en un formato listo para guardar. La lista incluirá las variables que el guión cambiado (que depende de si esto es deseable)
ivan_pozdeev
1
@ErikAronesty Si desea poder recrear un entorno con source, debería poder hacerlo con la salida de declare -p.
Seis
2
Si desea comparar un entorno antes y después con diff (o cualquier comando similar) sin recurrir al uso de archivos temporales, puede hacerlo con lo siguiente:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Seis
1
@Caesar No, solo probé matrices no vacías. Tiene razón en el caso de matrices vacías, no se imprimen.
Socowi
10
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

Esto debe imprimir todos los nombres de las variables de shell. Puede obtener una lista antes y después de obtener su archivo al igual que con "set" para diferenciar qué variables son nuevas (como se explica en las otras respuestas). Pero tenga en cuenta que dicho filtrado con diff puede filtrar algunas variables que necesita pero que estaban presentes antes de obtener su archivo.

En su caso, si sabe que los nombres de sus variables comienzan con "VARIABLE", entonces puede obtener su script y hacer:

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

ACTUALIZACIÓN: Para una solución BASH pura (no se utilizan comandos externos):

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
fuente
1
+1 y una versión en línea (con una única evaluación):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj
4

Según algunas de las respuestas anteriores, esto funcionó para mí:

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

archivo fuente :

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

Si puede posprocesar (como ya se mencionó), puede realizar una setllamada al principio y al final de su script (cada uno a un archivo diferente) y hacer una diferencia en los dos archivos. Tenga en cuenta que esto todavía contendrá algo de ruido.

También puede hacer esto programáticamente. Para limitar la salida a su alcance actual, tendría que implementar un contenedor para la creación de variables. Por ejemplo

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

Cuyos rendimientos

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

Aquí hay algo similar a la respuesta de @GinkgoFr, pero sin los problemas identificados por @Tino o @DejayClayton, y es más robusto que el set -o posixbit inteligente de @ DouglasLeeder :

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

La diferencia es que esta solución SE DETIENE después del primer informe no variable, por ejemplo, la primera función informada por set

Por cierto: El problema de "Tino" está resuelto. Aunque POSIX está desactivado y las funciones son informadas por set, la sed ...parte de la solución solo permite informes variables (por ejemplo, VAR=VALUElíneas). En particular, noA2 aparece de forma falsa en la salida.

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

Y: El problema "DejayClayton" está resuelto (las líneas nuevas incrustadas en los valores de las variables no interrumpen la salida; cada VAR=VALUEuna obtiene una única línea de salida):

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

NOTA: La solución proporcionada por @DouglasLeeder sufre el problema "DejayClayton" (valores con nuevas líneas incrustadas). A continuación, A1está mal y A2no debería mostrarse en absoluto.

$ 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: No creo que la versión sea bashimportante, pero podría. Hice mis pruebas / desarrollo en este:

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

POST-SCRIPT: Dadas algunas de las otras respuestas al OP, me queda <100% seguro de que set siempre convierte nuevas líneas dentro del valor al \nque se basa esta solución para evitar el problema "DejayClayton". ¿Quizás ese sea un comportamiento moderno? ¿O una variación en tiempo de compilación? O una set -oo shoptajuste de la opción? Si usted sabe de tales variaciones, por favor añadir un comentario ...

Stevel
fuente
0

Intente usar un script (llamémoslo "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, y:

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

Desde una perspectiva de seguridad, ya sea de @ akostadinov respuesta o @ de JuvenXu respuesta es preferible confiar en la salida no estructurada del setmando, debido a lo siguiente falla de seguridad potencial:

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

La función anterior se doLogicutiliza setpara verificar la presencia de una variable PS1para determinar si el script es interactivo o no (no importa si esta es la mejor manera de lograr ese objetivo; esto es solo un ejemplo).

Sin embargo, la salida de setno está estructurada, lo que significa que cualquier variable que contenga una nueva línea puede contaminar totalmente los resultados.

Esto, por supuesto, es un riesgo potencial para la seguridad. En su lugar, use el soporte de Bash para la expansión indirecta de nombres de variables o compgen -v.

Dejay Clayton
fuente
0

Pruebe esto: set | egrep "^\w+="(con o sin la | lesstubería)

La primera solución propuesta ( set -o posix ; set ) | less, funciona pero tiene un inconveniente: transmite códigos de control al terminal, por lo que no se visualizan correctamente. Entonces, por ejemplo, si hay (probablemente) una IFS=$' \t\n'variable, podemos ver:

IFS='
'

…en lugar.

Mi egrepsolución muestra esto (y eventualmente otros similares) correctamente.

GingkoFr
fuente
Han
Votado en contra porque es simplemente bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A pantallas incorrectasA=B" -> ¡Falla!
Tino
Prueba extraña que parece implicar solo la pseudovariable "_" (último argumento del comando anterior), pero no falla en otras, especialmente en IFS. Ejecutarlo bajo "bash -c" también podría hacer que no sea significativo. Al menos debería haberse mantenido neutral en lugar de votar negativamente.
GingkoFr
0

Probablemente he robado la respuesta hace un tiempo ... de todos modos, un poco diferente como función:

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

El printenvcomando:

printenvimprime todo environment variablesjunto con sus valores.

Buena suerte...

Akash
fuente
0

Una forma sencilla de hacer esto es usar el modo estricto bash configurando las variables de entorno del sistema antes de ejecutar su script y usar diff para ordenar solo las de su 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

Ahora puede recuperar variables de su script en /tmp/script_vars.log. ¡O al menos algo basado en eso!

grm34
fuente
0

Un poco tarde para la fiesta, pero aquí hay otra sugerencia:

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

La línea de comando diff + sed pipeline genera todas las variables definidas por script en el formato deseado (como se especifica en la publicación del OP):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Jim Fischer
fuente