Verifica se sono impostate più variabili


19

Vorrei assicurarmi che a un certo punto di uno script, dopo aver sourceinserito un file di configurazione, siano impostate diverse variabili e, in caso contrario, interrompere l'esecuzione, informando l'utente della variabile mancante. Ho provato

for var in $one $two $three ; do
    ...

ma se ad esempio $twonon è impostato, il ciclo non viene mai eseguito per $two. La prossima cosa che ho provato è stata

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Ma se due non sono impostati, ottengo ancora "due è impostato su" anziché "due non è impostato".

Come posso assicurarmi che tutte le variabili richieste siano impostate?

Aggiornamento / soluzione: so che esiste una differenza tra "set" e "set, ma vuoto". Ora uso (grazie a /programming//a/16753536/3456281 e le risposte a questa domanda) quanto segue:

if [ -n "${!var:-}" ] ; then

quindi, se varimpostato ma vuoto, è comunque considerato non valido.


1
Puoi anche aggiungere set -uall'inizio dello script per terminarlo immediatamente quando viene utilizzata una variabile non impostata.
n.

Risposte:


8

Errore di citazione.

if [ -n "${!var}" ] ; then

Per il futuro: impostazione

set -x

prima di eseguire il codice ti avrebbe mostrato il problema. Invece di aggiungerlo al codice con cui puoi chiamare il tuo script

bash -vx ./my/script.sh

Funziona, ma cosa sta succedendo con / senza virgolette? Il mio approccio generale è corretto al primo posto?
Jasper,

Sì, è precognitivo: ho risposto alla domanda prima che fosse posta ...
Cool

4
@Jasper Dovresti sempre citare le variabili. Ciò non costa più tempo che pensare se questo è necessario ogni volta. Ma evita gli errori.
Hauke ​​Laging,

Al contrario, la citazione protegge solo dall'espansione. Se l'espansione è ciò che ti interessa, la citazione non è certamente la strada da percorrere. Ad esempio: cs = 673.290.765,; set - $ (IFS =,; echo $ cs); echo $ #; uscita: 3
mikeserv

2
@mikeserv Solo le virgolette singole proteggono dall'espansione ciò che ovviamente non suggerisco. Le doppie virgolette proteggono dalla divisione delle parole. Non ho negato che potrebbero esserci casi in cui la citazione deve essere omessa. Ma questo non è un argomento contro la mia regola generale. Che tipo di persone userà qualcosa del genere set -- $(IFS=, ; echo $cs)? Il tipo di persone che devono chiedere qui perché if [ -n ${!var} ] ; thennon funziona? Probabilmente no.
Hauke ​​Laging,

5

L'unica cosa di cui hai bisogno sono le citazioni nel tuo test:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

Per me va bene.


4

Se si desidera arrestare il programma:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

Questo farà il trucco. Ciascuno dei messaggi che seguono il punto interrogativo viene stampato stderre la shell genitore muore. Bene, OK, quindi non ogni messaggio - solo uno - solo il primo che non riesce stampa un messaggio perché la shell muore. Mi piace usare questi test in questo modo:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

Ho avuto molto altro da dire al riguardo qui .


2

Puoi aggiungere

set -u

all'inizio dello script per farlo terminare quando tenta di utilizzare una variabile non impostata.

Una sceneggiatura simile

#!/bin/sh
set -u
echo $foo

si tradurrà in

script.sh: 3: script.sh: foo: parametro non impostato

Se si utilizza bashinvece, l'errore sarà simile al seguente:

script.sh: linea 3: foo: variabile non associata


E secondo te questo ha senso per i controlli del tempo di esecuzione?
Hauke ​​Laging,

2
@HaukeLaging Non ti seguo del tutto - set -uimpedisce esattamente il tipo di errore che l'OP sta cercando di evitare e (contrariamente a tutte le altre soluzioni) non è limitato a un insieme specifico di variabili. È in effetti un'utile precauzione per quasi tutti gli script di shell farli fallire in sicurezza invece di fare cose inaspettate quando una variabile non è impostata. Questo articolo è un utile riferimento sull'argomento.
n.

@ n.st Non sono d'accordo - null può essere altrettanto utile un valore che non null se lo pianifichi.
Mikeserv,

1
@ n.st Questo non ha alcun senso per l'OP poiché non vuole una protezione contro l'accesso alle variabili non impostate (A proposito: una variabile set ma vuota provocherebbe lo stesso errore ma non reagirebbe alla tua "protezione"). Vuole un tempo di esecuzione per verificare se una variabile non è impostata / vuota. Il tuo suggerimento può essere utile come aiuto generale allo sviluppo ma non risolve il problema del PO.
Hauke ​​Laging,

set -upenso che possa essere usato anche io, ma non è flessibile quanto la corretta espansione dei parametri (vedi la mia domanda aggiornata). E con set -udovrei fare un accesso fittizio a tutte le variabili target se voglio controllare la loro presenza in un posto.
Jasper,

2

Una soluzione estremamente amichevole verifica tutti i requisiti e li riporta insieme, anziché fallire nel primo e richiedere avanti e indietro per ottenere le cose giuste:

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

Uso una variabile array per tenere traccia delle variabili non impostate e utilizzo il risultato per comporre un messaggio rivolto all'utente.

Appunti:

  • Ho citato ${required_vars[@]}nel forciclo principalmente per abitudine - Non consiglierei di includere i metacaratteri della shell nei nomi delle variabili!
  • Non ho citato ${#missing_vars[@]}, perché è sempre un numero intero, anche se sei abbastanza perverso da ignorare il consiglio precedente.
  • Ho usato %qdurante la stampa; %ssarebbe normalmente sufficiente.
  • L'output dell'errore va sempre al flusso dell'errore con >&2, quindi non viene reindirizzato ai comandi a valle
  • Il silenzio è d'oro: non stampare le informazioni sull'avanzamento o le informazioni di debug se non richiesto specificamente. Ciò rende gli errori più evidenti.

1

bash4.2 consente di verificare se una variabile è impostata con l' -voperatore; una variabile non impostata e una variabile impostata sulla stringa vuota sono due condizioni diverse:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

Stavo chiedendo informazioni su variabili "multiple", quindi stavo cercando qualcosa di indiretto ...
Jasper

Non hai bisogno di indirezione, dal momento che -vprende il nome di una variabile, quindi for var in one two three; [[ -v $var ]] && ...; donesarebbe verificare se ciascuno dei one, twoe threesi trovano in sequenza.
Chepner,

1

Penso che se vuoi dire not set, quindi la variabile non deve mai essere inizializzata. Se si utilizza [ -n "${!var}" ], quindi la variabile vuota come two=""verrà fallita, mentre è impostata . Puoi provare questo:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

Una soluzione concisa (anche se leggermente confusa), per gestire il caso in cui è giusto che lo sourcescript d imposti le variabili su un valore nullo, consiste nell'inizializzare le variabili su un valore speciale prima di approvare lo script, quindi controllare quel valore in seguito , per esempio

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
un'altra bella soluzione, ma vorrei dare un messaggio di errore più specifico di "una o più variabili non impostate" ...
Jasper

0

Ho esteso la risposta di @ mikeserv .

Questa versione accetta un elenco di variabili per verificare la presenza, stampando il nome della variabile mancante e attivando una chiamata all'utilizzo () in caso di errore.

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

Esempio di output quando manca un valore:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

Nota che puoi cambiare la variabile chiamata VALUE con un nome alternativo che si adatti meglio all'output desiderato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.