Cómo imprimir solo variables definidas (shell y / o variables de entorno) en bash

57

El comando bash builtin set, si se invoca sin argumentos, imprimirá todas las variables de entorno y shell, pero también todas las funciones definidas. Esto hace que la salida sea inutilizable para los humanos y difícil grep.

¿Cómo puedo hacer que el comando bash builtin setimprima solo las variables y no las funciones?

¿Hay otros comandos que imprimen solo las variables de shell, sin las funciones?

Nota: bash diferencia entre las variables de shell y las variables de entorno. ver aquí Diferencia entre variables de entorno y variables de entorno exportadas en bash

lesmana
fuente

Respuestas:

51

"¿Hay otros comandos que imprimen solo las variables de shell, sin las funciones?"

En man bash, en la sección SHELL BUILTIN COMMANDS (en la sección set) dice: "En el modo posix, solo se enumeran las variables de shell".

(set -o posix; set)

nota: la ()sintaxis genera una subshell, si no te gusta bifurcar solo usa la versión más detallada

set -o posix; set; set +o posix
temperatura
fuente
1
Con mucho, la mejor solución
Ruslan,
1
Hay un montón de variables generalmente poco interesantes _, que son fáciles de eliminar:(set -o posix ; set | grep -v ^_)
hyde
¡Esto también me ha estado molestando durante tanto tiempo! Separar las líneas que comienzan con _ no es suficiente si tiene valores de varias líneas, todavía dejará intacto el contenido del valor. Dado que set imprime las líneas en orden, todas las líneas estarán al final, por lo que simplemente puede cortar los resultados después de la primera línea, usando:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott
1
Solo una nota: los corchetes son importantes en (set -o posix; set). Sin los corchetes, el comportamiento del shell Bash actual se cambiaría para que coincida con el estándar Posix. (No sé qué significa eso, pero parece importante.) Con los corchetes, el Bash permanece sin cambios.
John Red
1
Efectos secundarios : establece POSIXLY_CORRECT=yy agrega posixa$SHELLOPTS
Tom Hale
32

Aquí hay algunas soluciones:

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

Descompostura:

  1. declare imprime cada variable definida (exportada o no) y función.
  2. declare -f imprime solo funciones.
  3. comm -3eliminará todas las líneas comunes a ambos. En efecto, esto eliminará las funciones, dejando solo las variables.

Para imprimir solo variables que no se exportan:

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

Otra solución alternativa:

$ declare -p

Esto solo imprimirá las variables, pero con algunos atributos feos.

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

Puede cortar los atributos usando ... cortar:

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

Una desventaja es que el valor de IFS se interpreta en lugar de mostrarse.

comparar:

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

Esto hace que sea bastante difícil usar esa salida para un procesamiento posterior, debido a que está solo "en una línea. Quizás se pueda hacer algo de IFS-fu para evitar esto.


Otra solución alternativa, usando compgen:

$ compgen -v

El bash builtin compgenestaba destinado a ser utilizado en scripts de finalización. Para este fin, compgen -venumera todas las variables definidas. La desventaja: enumera solo los nombres de las variables, no los valores.

Aquí hay un truco para enumerar también los valores.

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

La ventaja: es una solución pura de bash. La desventaja: algunos valores están en mal estado debido a la interpretación a través de printf. Además, la subshell de la tubería y / o el bucle agrega algunas variables adicionales.

lesmana
fuente
¿se declare ...| cutrompe su tubería si no hay atributos para una variable? Supongo que --se usa en ese caso, pero todavía estoy incómodo. sedpodría ser más seguro, es decirdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
jmtd
+1 para compgen:)
RSFalcon7
13

El typesetincorporado tiene extrañamente una opción para mostrar solo funciones ( -f) pero no solo para mostrar parámetros. Afortunadamente, typeset(o set) muestra todos los parámetros antes de que todas las funciones, funciones y nombres de parámetros no puedan contener líneas nuevas o signos iguales, y las líneas nuevas en los valores de los parámetros se citan (aparecen como \n). Entonces puede detenerse en la primera línea que no contiene un signo igual:

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

Esto solo imprime los nombres de los parámetros; si desea los valores, cambie print $1a print $0.

Tenga en cuenta que esto imprime todos los nombres de parámetros (los parámetros y las variables se usan como sinónimos aquí), no solo las variables de entorno ("variable de entorno" es sinónimo de "parámetros exportados").

Tenga en cuenta también que esto supone que no hay una variable de entorno con un nombre que no coincida con las restricciones de bash en los nombres de los parámetros. Dichas variables no se pueden crear dentro de bash, pero se pueden heredar del entorno cuando se inicia bash:

env 'foo ()
{
  =oops' bash
Gilles 'SO- deja de ser malvado'
fuente
¡Gran solución! Ni siquiera pensé en detenerme en la primera línea que no tiene un '='. +1
Steven D
De manera equivalente, con sed: set | sed '/=/!Q'
Toby Speight
7

No estoy seguro de cómo se pueden hacer setvariables de solo impresión. Sin embargo, al observar la salida de set, pude llegar a lo siguiente que parece tomar solo variables:

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

Básicamente, estoy buscando líneas que comiencen con letras, números o signos de puntuación seguidos de un "=". De la salida que vi, esto toma todas las variables; Sin embargo, dudo que esto sea muy portátil.

Si, como sugiere su título, desea restar de esta lista las variables que se exportan y, por lo tanto, obtener una lista de variables no exportadas, entonces podría hacer algo como esto

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

Para desglosar esto un poco, esto es lo que hace:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Con suerte, obtiene todas las variables (como se discutió antes), las ordena y pega el resultado en un archivo.
  2. && env | sort: Después de completar el comando anterior, vamos a llamar envy ordenar su salida.
  3. | comm -23 ./setvars -: Por último, canalizar la salida ordenada de envdentro commy utilizar la -23opción de imprimir las líneas que son únicos para el primer argumento, en este caso las líneas únicas para nuestra salida del conjunto.

Cuando haya terminado, es posible que desee limpiar el archivo temporal que creó con el comando rm ./setvars

Steven D
fuente
El problema con su comando no es la portabilidad (es específico de bash, por supuesto, pero no hay ningún problema en el lado grep). El problema es que estás tomando algunas líneas en las definiciones de funciones. Bash sangra la mayor parte del código de función, pero no todo, en particular el texto dentro de los documentos ( f () {|cat <<EOF|foo=bar|EOF|}donde |representa un salto de línea).
Gilles 'SO- deja de ser malvado'
6

Solo usa el comando env. No imprime funciones.

unxnut
fuente
1
Lo hace si se usa dentro de un script.
DocSalvager
2

Prueba el comando printenv:

$printenv
Emilio R.
fuente
66
printenv y env solo imprimen variables exportadas (entorno) y no variables no exportadas (shell).
Lri
@Lri Gracias! Me preguntaba cómo imprimir solo las variables exportadas.
Mark Stewart
1

En bash, esto imprimirá solo nombres de variables:

compgen -v

O, si también se necesitan valores, use:

declare -p
Isaac
fuente
gracias por su esfuerzo. tenga en cuenta que ya he mencionado esa posibilidad en mi respuesta unix.stackexchange.com/a/5691/1170 tiene un voto positivo de todos modos.
lesmana
0

diferenciarse contra un shell limpio para garantizar que también se omitan los vars de los scripts 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 versión con commsería demasiado complicada

deshacer
fuente
0

La respuesta aceptada es genial. Estoy ofreciendo una alternativa que no implica configurar el modo POSIX:

Aquí está la técnica que he estado usando para almacenar el estado de la función en un archivo para poder continuar más tarde. El primero produce un volcado de estado y el segundo produce solo una lista de los nombres de las variables.

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 funcionan volcando líneas hasta encontrar uno sin un carácter '=', que ocurre cuando se enumera la primera función. Este último recorta la tarea.

Wil
fuente