Comment lister les variables déclarées dans le script en bash?

98

Dans mon script en bash, il y a beaucoup de variables, et je dois faire quelque chose pour les enregistrer dans un fichier. Ma question est de savoir comment lister toutes les variables déclarées dans mon script et obtenir une liste comme celle-ci:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
lauriys
la source

Réponses:

151

set affichera les variables, malheureusement, il affichera également les fonctions définies.

Heureusement, le mode POSIX ne produit que les variables:

( set -o posix ; set ) | less

Piping vers lessou rediriger vers où vous voulez les options.

Donc, pour obtenir les variables déclarées uniquement dans le 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 du moins quelque chose basé sur ça :-))

Douglas Leeder
la source
Vous avez édité pendant que je publiais. Bel appel avec le -o posixnow a diff ne contiendra que les variables.
ezpz
8
Sans l' aide de fichiers temporaires: VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;. Cela produira également les variables dans un format prêt à enregistrer. La liste comprendra les variables que le script a modifiées (cela dépend si cela est souhaitable)
ivan_pozdeev
1
@ErikAronesty Si vous souhaitez pouvoir recréer un environnement avec source, vous devriez pouvoir le faire avec la sortie de declare -p.
Six
2
Si vous souhaitez comparer un environnement avant et après avec diff (ou toute commande similaire) sans recourir à l'utilisation de fichiers temporaires, vous pouvez le faire avec ce qui suit:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
Six
1
@Caesar Non, j'ai seulement essayé des tableaux non vides. Vous avez raison en cas de tableaux vides - ils ne sont pas imprimés.
Socowi
40
compgen -v

Il répertorie toutes les variables, y compris les variables locales. Je l'ai appris grâce à Get list of variables dont le nom correspond à un certain modèle et je l' ai utilisé dans mon script .

Juven Xu
la source
2
Malheureusement, compgen -vrépertorie également les variables globales non définies localement. Je ne sais pas si c'est un bug de longue date ou le comportement souhaité.
Michał Górny
10
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

Cela doit afficher tous les noms de variables shell. Vous pouvez obtenir une liste avant et après le sourcing de votre fichier, tout comme avec "set" pour différencier les variables nouvelles (comme expliqué dans les autres réponses). Mais gardez à l'esprit qu'un tel filtrage avec diff peut filtrer certaines variables dont vous avez besoin mais qui étaient présentes avant de sourcer votre fichier.

Dans votre cas, si vous savez que les noms de vos variables commencent par "VARIABLE", alors vous pouvez rechercher votre script et faire:

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

MISE À JOUR: Pour une solution pure BASH (aucune commande externe utilisée):

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
la source
1
+1 et une version oneliner (avec une seule évaluation):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj
4

Sur la base de certaines des réponses ci-dessus, cela a fonctionné pour moi:

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

fichier source :

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

Si vous pouvez effectuer un post-traitement (comme déjà mentionné), vous pouvez simplement placer un setappel au début et à la fin de votre script (chacun dans un fichier différent) et faire une comparaison sur les deux fichiers. Sachez que cela contiendra encore du bruit.

Vous pouvez également le faire par programme. Pour limiter la sortie à votre portée actuelle, vous devrez implémenter un wrapper pour la création de variables. Par exemple

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

Quels rendements

VAR1=abc
VAR2=bcd
VAR3=cde
ezpz
la source
2

Voici quelque chose de similaire à la réponse @GinkgoFr, mais sans les problèmes identifiés par @Tino ou @DejayClayton, et est plus robuste que le set -o posixpeu intelligent de @ DouglasLeeder :

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

La différence est que cette solution S'ARRÊTE après le premier rapport non variable, par exemple la première fonction rapportée par set

BTW: Le problème "Tino" est résolu. Même si POSIX est désactivé et que les fonctions sont signalées par set, la sed ...partie de la solution autorise uniquement les rapports de variables via (par exemple, des VAR=VALUElignes). En particulier, le A2ne le fait pas indûment dans la sortie.

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

ET: Le problème "DejayClayton" est résolu (les retours à la ligne intégrés dans les valeurs des variables ne perturbent pas la sortie - chacun VAR=VALUEobtient une seule ligne de sortie):

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

NOTE: La solution fournie par @DouglasLeeder souffre du problème "DejayClayton" (valeurs avec retours à la ligne incorporés). Ci-dessous, le A1est faux et A2ne devrait pas du tout apparaître.

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

ENFIN: je ne pense pas que la version des bashchoses compte, mais ça pourrait. J'ai fait mes tests / développement sur celui-ci:

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

POST-SCRIPT: Compte tenu de certaines des autres réponses à l'OP, je suis sûr à <100% que cela convertit set toujours les nouvelles lignes dans la valeur en \n, sur laquelle cette solution s'appuie pour éviter le problème "DejayClayton". C'est peut-être un comportement moderne? Ou une variation au moment de la compilation? Ou un paramètre set -oou une shoptoption? Si vous connaissez de telles variations, veuillez ajouter un commentaire ...

Stevel
la source
0

Essayez d'utiliser un script (appelons-le "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, et:

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

Du point de vue de la sécurité, que ce soit @ akostadinov de réponse ou @ JuvenXu de réponse est préférable de compter sur la sortie non structurée de la setcommande, en raison de la faille de sécurité potentielle:

#!/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 fonction ci-dessus doLogicutilise setpour vérifier la présence d'une variable PS1pour déterminer si le script est interactif ou non (peu importe si c'est le meilleur moyen d'atteindre cet objectif; ce n'est qu'un exemple.)

Cependant, la sortie de set n'est pas structurée, ce qui signifie que toute variable contenant une nouvelle ligne peut contaminer totalement les résultats.

Ceci, bien sûr, est un risque de sécurité potentiel. Au lieu de cela, utilisez soit la prise en charge de Bash pour l'extension de nom de variable indirecte, soit compgen -v.

Dejay Clayton
la source
0

Essayez ceci: set | egrep "^\w+="(avec ou sans la | lesstuyauterie)

La première solution proposée ( set -o posix ; set ) | less, fonctionne mais présente un inconvénient: elle transmet les codes de contrôle au terminal, de sorte qu'ils ne s'affichent pas correctement. Ainsi, par exemple, s'il y a (probablement) une IFS=$' \t\n'variable, nous pouvons voir:

IFS='
'

…au lieu.

Ma egrepsolution affiche correctement cela (et éventuellement d'autres similaires) .

GingkoFr
la source
ça fait 8 ans, tu sais
lauriys
Downvoté car il est tout simplement faux bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A affiche A=B"-> Échec!
Tino
Test étrange qui semble n'impliquer que la pseudo-variable "_" (dernier argument de la commande précédente), mais qui n'échoue pas sur les autres, en particulier IFS. L'exécuter sous "bash -c" pourrait également le rendre non significatif. Vous auriez au moins dû rester neutre au lieu de voter contre.
GingkoFr
0

J'ai probablement volé la réponse il y a quelque temps ... de toute façon légèrement différent en tant que fonction:

    ##
    # 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
la source
0

La printenvcommande:

printenvimprime tout environment variablesavec leurs valeurs.

Bonne chance...

Akash
la source
0

Un moyen simple de le faire est d'utiliser le mode strict de bash en définissant des variables d'environnement système avant d'exécuter votre script et d'utiliser diff pour trier uniquement celles de votre 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

Vous pouvez maintenant récupérer les variables de votre script dans /tmp/script_vars.log. Ou du moins quelque chose basé sur ça!

grm34
la source
0

Un peu tard à la fête, mais voici une autre suggestion:

#!/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 ligne de commande du pipeline diff + sed génère toutes les variables définies par le script dans le format souhaité (comme spécifié dans l'article de l'OP):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
Jim Fischer
la source