Comment imprimer uniquement les variables définies (variables d'environnement et / ou d'environnement) dans bash

57

setSi elle est appelée sans arguments, la commande intégrée bash imprimera toutes les variables d'environnement et d'environnement, mais également toutes les fonctions définies. Cela rend la sortie inutilisable pour les humains et difficile à grep.

Comment puis-je faire en sorte que la commande intégrée bash n'imprime setque les variables et non les fonctions?

Existe-t-il d'autres commandes n'imprimant que les variables du shell, sans les fonctions?

Remarque: bash distingue les variables shell et les variables d'environnement. voir ici Différence entre les variables d'environnement et les variables d'environnement exportées dans bash

lesmana
la source

Réponses:

51

"Y a-t-il d'autres commandes qui n'impriment que les variables du shell, sans les fonctions?"

Dans man bash, dans la section SHELL BUILTIN COMMANDS (dans la section set), il est écrit: "En mode posix, seules les variables shell sont répertoriées."

(set -o posix; set)

remarque: la ()syntaxe génère un sous-shell, si vous n'aimez pas falsifier, utilisez simplement la version plus détaillée.

set -o posix; set; set +o posix
temp
la source
1
De loin la meilleure solution
Ruslan
1
Il y a un tas de variables habituellement sans intérêt qui commencent par _, et qui sont faciles à éliminer:(set -o posix ; set | grep -v ^_)
hyde
Cela me dérange aussi depuis si longtemps! Embrasser les lignes qui commencent par _ ne suffit pas si vous avez des valeurs multilignes, il conservera le contenu de la valeur dans tact. Etant donné que set imprime les lignes dans l’ordre, toutes les _ lignes seront à la fin, vous pouvez donc simplement couper les résultats après la première _ ligne, en utilisant:set -o posix; set | sed -e '/^_/,$d'; set +o posix;
Scott
1
Juste une note: les crochets sont importants dans (set -o posix; set). Sans les crochets, le comportement du shell Bash actuel serait modifié pour correspondre au standard Posix. (Je ne sais pas ce que cela signifie, mais ça a l'air important.) Avec les crochets, le Bash reste inchangé.
John Red
1
Effets secondaires : Définit POSIXLY_CORRECT=yet ajoute posixà$SHELLOPTS
Tom Hale
32

Voici quelques solutions de contournement:

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

panne:

  1. declare imprime chaque variable définie (exportée ou non) et chaque fonction.
  2. declare -f imprime seulement les fonctions.
  3. comm -3supprimera toutes les lignes communes aux deux. En effet, cela supprimera les fonctions, ne laissant que les variables.

Pour n’imprimer que les variables non exportées:

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

Une autre solution de contournement:

$ declare -p

Cela n’imprimera que les variables, mais avec quelques attributs laids.

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

Vous pouvez couper les attributs en utilisant ... cut:

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

Un inconvénient est que la valeur de IFS est interprétée au lieu d'être affichée.

comparer:

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

Cela rend très difficile l'utilisation de cette sortie pour un traitement ultérieur, à cause de cette seule "ligne. Peut-être que quelques IFS-fu peuvent être faits pour empêcher cela.


Encore une autre solution de contournement, en utilisant compgen:

$ compgen -v

La bash intégrée a compgenété conçue pour être utilisée dans les scripts de complétion. À cette fin, compgen -vrépertorie toutes les variables définies. L'inconvénient: il ne répertorie que les noms de variables, pas les valeurs.

Voici un hack pour lister également les valeurs.

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

L'avantage: c'est une solution pure bash. L'inconvénient: certaines valeurs sont brouillées à cause de l'interprétation printf. De plus, le sous-shell du tuyau et / ou de la boucle ajoute des variables supplémentaires.

lesmana
la source
votre declare ...| cutconduite est-elle rompue s'il n'y a pas d'attribut pour une variable? Je suppose que cela --est utilisé dans ce cas, mais je suis toujours mal à l'aise. sedpourrait être plus sûr, c'est-àdeclare -p | sed 's/^.* \([^ ]\+\)$/\1/'
JMTD
+1 pour compgen:)
RSFalcon7
13

Le mode typesetintégré a étrangement une option pour afficher uniquement les fonctions ( -f) mais pas uniquement pour afficher les paramètres. Heureusement, typeset(ou set) affiche tous les paramètres avant que toutes les fonctions, les noms de fonctions et de paramètres ne puissent pas contenir de nouvelle ligne ou de signe égal, et les nouvelles lignes dans les valeurs de paramètre sont indiquées (elles apparaissent sous la forme \n). Vous pouvez donc vous arrêter à la première ligne sans signe égal:

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

Cela n’imprime que les noms de paramètres; si vous voulez les valeurs, changez print $1en print $0.

Notez que ceci affiche tous les noms de paramètres (les paramètres et les variables sont utilisés ici de manière synonyme), pas uniquement les variables d'environnement («variable d'environnement» est synonyme de «paramètres exportés»).

Notez également que cela suppose qu'il n'y a pas de variable d'environnement dont le nom ne correspond pas aux contraintes de bash sur les noms de paramètres. De telles variables ne peuvent pas être créées dans bash, mais peuvent avoir été héritées de l'environnement au démarrage de bash:

env 'foo ()
{
  =oops' bash
Gilles, arrête de faire le mal
la source
Excellente solution! Je n'ai même pas pensé à m'arrêter à la première ligne sans '='. +1
Steven D
De manière équivalente, avec sed: set | sed '/=/!Q'
Toby Speight
7

Je ne sais pas comment on peut setimprimer uniquement des variables. Cependant, en examinant le résultat de set, j’ai été en mesure de proposer les éléments suivants qui semblent ne saisir que des variables:

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

En gros, je cherche des lignes qui commencent par des lettres, des chiffres ou des signes de ponctuation suivis d'un "=". D'après la sortie que j'ai vue, cela saisit toutes les variables. Cependant, je doute que cela soit très portable.

Si, comme le suggère votre titre, vous souhaitez soustraire de cette liste les variables exportées et obtenir ainsi une liste de variables non exportées, vous pouvez procéder de la sorte.

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

Pour décomposer un peu, voici ce qu'il fait:

  1. set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars : Cela espère obtenir toutes les variables (comme discuté précédemment), les trier et coller le résultat dans un fichier.
  2. && env | sort: Une fois la commande précédente terminée, nous allons appeler envet trier sa sortie.
  3. | comm -23 ./setvars -: Enfin, nous canalisons la sortie triée de envdans commet utilisons l' -23option pour imprimer les lignes qui sont uniques au premier argument, dans ce cas les lignes uniques à notre sortie de set.

Lorsque vous avez terminé, vous voudrez peut-être nettoyer le fichier temporaire qu’il a créé avec la commande rm ./setvars

Steven D
la source
Le problème avec votre commande n'est pas la portabilité (c'est spécifique à bash, bien sûr, mais il n'y a pas de problème du côté de grep). Le problème est que vous saisissez des lignes dans les définitions de fonctions. Bash indente la plupart du code de fonction, mais pas tout, en particulier pas le texte contenu dans les documents ( f () {|cat <<EOF|foo=bar|EOF|}|représente un saut de ligne).
Gilles, arrête de faire le mal '26
6

Il suffit d'utiliser la commande env. Il n'imprime pas les fonctions.

unxnut
la source
1
C'est le cas si utilisé dans un script.
DocSalvager
2

Essayez la commande printenv:

$printenv
Emilio R.
la source
6
printenv et env n’impriment que les variables exportées (environnement) et non les variables non exportées (shell).
Lri
@Lri Merci! Je me suis demandé comment imprimer uniquement les variables exportées.
Mark Stewart
1

En bash, cela n'imprimera que les noms de variables:

compgen -v

Ou, si des valeurs sont également nécessaires, utilisez:

declare -p
Isaac
la source
je vous remercie pour vos efforts. veuillez noter que j'ai déjà mentionné cette possibilité dans ma réponse unix.stackexchange.com/a/5691/1170 a un vote positif de toute façon.
Lesmana
0

diffing contre un shell propre pour assurer également vars de scripts rc sont laissés de côté

## 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 version avec commserait trop compliquée

untore
la source
0

La réponse acceptée est géniale. Je propose une alternative qui n'implique pas la définition du mode POSIX:

Voici la technique que j'ai utilisée pour stocker l'état de la fonction dans un fichier afin de pouvoir le continuer plus tard. Le premier produit un vidage d'état et le second ne produit qu'une liste des noms de 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 

Les deux fonctionnent en vidant les lignes jusqu'à ce qu'il en trouve une sans caractère '=', ce qui se produit lorsque la première fonction est listée. Ce dernier coupe la tâche.

Wil
la source