Come posso verificare se una variabile è vuota o contiene solo spazi?


240

La seguente sintassi bash verifica se paramnon è vuota:

 [[ !  -z  $param  ]]

Per esempio:

param=""
[[ !  -z  $param  ]] && echo "I am not zero"

Nessun output e va bene.

Ma quando paramè vuoto tranne uno (o più) caratteri di spazio, il caso è diverso:

param=" " # one space
[[ !  -z  $param  ]] && echo "I am not zero"

Viene visualizzato "I am not zero".

Come posso modificare il test per considerare vuote le variabili che contengono solo caratteri di spazio?


4
Da man test: -z STRING - the length of STRING is zero. Se vuoi rimuovere tutti gli spazi $param, usa${param// /}
dchirikov il

6
WOW, non esiste una semplice trim()funzione integrata in nessun * nix? Tanti hack diversi per ottenere qualcosa di così semplice ...
ADTC

6
@ADTC $ (stringa sed 's / ^ \ s + | \ s + $ // g' <<< $) mi sembra abbastanza semplice. Perché reinventare la ruota?
jbowman,

3
Perché potrebbe essere più
brillante

1
Solo notare che [[ ! -z $param ]]equivale a test ! -z $param.
Noah Sussman,

Risposte:


292

Innanzitutto, nota che il -ztest è esplicitamente per:

la lunghezza della stringa è zero

Cioè, una stringa contenente solo spazi non dovrebbe essere vera sotto -z, perché ha una lunghezza diversa da zero.

Quello che vuoi è rimuovere gli spazi dalla variabile usando l' espansione del parametro di sostituzione del modello :

[[ -z "${param// }" ]]

Questo espande la paramvariabile e sostituisce tutte le corrispondenze del modello (un singolo spazio) con nulla, quindi una stringa che contiene solo spazi verrà espansa in una stringa vuota.


Il nocciolo di come funziona è che ${var/pattern/string}sostituisce la prima partita più lunga di patterncon string. Quando patterninizia con /(come sopra), sostituisce tutte le partite. Poiché la sostituzione è vuota, possiamo omettere /il stringvalore finale e il valore:

$ {Parametro / modello / string}

Il modello viene espanso per produrre un modello proprio come nell'espansione del nome file. Il parametro viene espanso e la corrispondenza più lunga del modello rispetto al suo valore viene sostituita con stringa . Se il motivo inizia con '/', tutte le corrispondenze del motivo vengono sostituite con una stringa . Normalmente viene sostituita solo la prima partita. ... Se la stringa è nulla, le corrispondenze del modello vengono eliminate e il modello / seguente può essere omesso.

Dopo tutto ciò, finiamo ${param// }per eliminare tutti gli spazi.

Si noti che sebbene presente in ksh(dove ha avuto origine), zshe bash, quella sintassi non è POSIX e non dovrebbe essere usata negli shscript.


1
uso sedhanno detto ... solo echola variabile che hanno detto ...
mikezter,

1
Hmm. Se lo è ${var/pattern/string}, non dovrebbe essere [[ -z ${param/ /} ]]lo spazio tra le barre ad essere il motivo e niente dopo essere la stringa?
Jesse Chisholm,

@JesseChisholm "Poiché la sostituzione è vuota, possiamo omettere il valore finale / e stringa"; "Se la stringa è nulla, le corrispondenze del modello vengono eliminate e il modello / seguente può essere omesso."
Michael Homer,

6
@MichaelHomer La risposta [[ -z "${param// }" ]]sembra proprio che patternsia vuota e che replacement stringsia un unico spazio. AH! Lo vedo ora if pattern begins with a slashho perso quella parte. quindi il modello è / e la stringa viene lasciata fuori e quindi vuota. Grazie.
Jesse Chisholm,

nota bash: se in esecuzione in set -o nounset (set -u), e param non è impostato (piuttosto che null o pieno di spazio), questo genererà un errore variabile non associato. (set + u; .....) esonererebbe questo test.
Brian Chrisman l'

31

Il modo semplice per verificare che una stringa contenga solo caratteri in un set autorizzato è verificare la presenza di caratteri non autorizzati. Pertanto, anziché verificare se la stringa contiene solo spazi, verifica se la stringa contiene caratteri diversi dallo spazio. In bash, ksh o zsh:

if [[ $param = *[!\ ]* ]]; then
  echo "\$param contains characters other than space"
else
  echo "\$param consists of spaces only"
fi

"Consiste solo di spazi" include il caso di una variabile vuota (o non impostata).

Potresti voler testare qualsiasi carattere di spazio bianco. Utilizzare [[ $param = *[^[:space:]]* ]]per utilizzare le impostazioni locali o qualsiasi elenco esplicito di caratteri di spazi bianchi per cui si desidera verificare, ad esempio [[ $param = *[$' \t\n']* ]]per verificare lo spazio, la scheda o la nuova riga.

La corrispondenza di una stringa con uno schema con =inside [[ … ]]è un'estensione ksh (presente anche in bash e zsh). In qualsiasi stile Bourne / POSIX, è possibile utilizzare il casecostrutto per abbinare una stringa a un modello. Si noti che i modelli di shell standard usano !per negare un set di caratteri, piuttosto che ^come nella maggior parte delle sintassi delle espressioni regolari.

case "$param" in
  *[!\ ]*) echo "\$param contains characters other than space";;
  *) echo "\$param consists of spaces only";;
esac

Per verificare i caratteri degli spazi bianchi, la $'…'sintassi è specifica di ksh / bash / zsh; puoi inserire questi caratteri nel tuo script letteralmente (nota che una nuova riga dovrà essere racchiusa tra virgolette, poiché la barra rovesciata + la nuova riga non si espande nel nulla) o generarli, ad es.

whitespace=$(printf '\n\t ')
case "$param" in
  *[!$whitespace]*) echo "\$param contains non-whitespace characters";;
  *) echo "\$param consists of whitespace only";;
esac

Questo è quasi eccellente - ma, al momento, non risponde alla domanda come è stata posta. Attualmente può dirti la differenza tra una variabile di shell vuota o non impostata e una che contiene solo spazi. Se accetterai il mio suggerimento, aggiungerai il modulo di espansione param non ancora convertito da nessuna altra risposta che testa esplicitamente una variabile shell per essere impostato - voglio dire ${var?not set}- superando quel test e sopravvivendo assicureresti che una variabile abbinata dal tuo *) caseeffettuerebbe un definitivo risposta, per esempio.
Mikeserv,

Correzione - questo, in effetti, sarebbe rispondere alla domanda originariamente chiesto, ma da allora è stato modificato in modo tale che cambia radicalmente il senso della domanda e di conseguenza invalida la vostra risposta.
Mikeserv,

Hai bisogno [[ $param = *[!\ ]* ]]di mksh.
Stéphane Chazelas,

20

POSIXly:

case $var in
  (*[![:blank:]]*) echo '$var contains non blank';;
  (*) echo '$var contains only blanks or is empty or unset'
esac

Per distinguere tra vuoto , non vuoto , vuoto , non impostato :

case ${var+x$var} in
  (x) echo empty;;
  ("") echo unset;;
  (x*[![:blank:]]*) echo non-blank;;
  (*) echo blank
esac

[:blank:]è per i caratteri di spaziatura orizzontale (spazio e tabulazione in ASCII, ma probabilmente ce ne sono alcuni nella tua locale; alcuni sistemi includeranno lo spazio non-break (dove disponibile), altri no). Se vuoi anche caratteri di spaziatura verticale (come newline o feed di form), sostituiscili [:blank:]con [:space:].


POSIXly è l'ideale perché funzionerà con il trattino (probabilmente migliore).
Ken Sharp,

zshsembra avere problemi con [![:blank:]], si alza :bè modificatore non valido. In shed kshemula, [
genera un

@cuonglm, cosa hai provato? var=foo zsh -c 'case ${var+x$var} in (x*[![:blank:]]*) echo x; esac'va bene per me. L'evento non trovato sarebbe solo per shell interattive.
Stéphane Chazelas,

@ StéphaneChazelas: Ah, giusto, ho provato con shell interattive. Il :bmodificatore non valido è un po 'strano.
cuonglm,

1
@FranklinYu, su OS / X shè bashcostruito con --enable-xpg-echo-defaulte in --enable-strict-posix-defaultmodo tale da essere conforme a Unix (che comporta l' echo '\t'output di una scheda).
Stéphane Chazelas,

4

L'unico motivo rimanente per scrivere uno script di shell, anziché uno script in un buon linguaggio di scripting, è se la portabilità estrema è una preoccupazione prioritaria. L'eredità /bin/shè l'unica cosa che puoi essere certo di avere, ma Perl, ad esempio, è più probabile che sia disponibile multipiattaforma rispetto a Bash. Pertanto, non scrivere mai script di shell che utilizzano funzionalità che non sono veramente universali e tenere presente che diversi fornitori Unix proprietari hanno bloccato il loro ambiente di shell prima di POSIX.1-2001 .

Esiste un modo portatile per eseguire questo test, ma è necessario utilizzare tr:

[ "x`printf '%s' "$var" | tr -d "$IFS"`" = x ]

(Il valore predefinito di $IFS, convenientemente, è uno spazio, una scheda e una nuova riga.)

(L'interno printfè di per sé portatile, ma fare affidamento su di esso è molto meno seccante di capire quale variante echohai.)


1
È un po 'contorto. Il solito modo portatile per verificare se una stringa contiene determinati caratteri è concase .
Gilles,

@Gilles Non penso che sia possibile scrivere un caseglob per rilevare una stringa che è vuota o contiene solo caratteri di spazi bianchi. (Sarebbe facile con una regexp, ma casenon fa regexps. Il costrutto nella tua risposta non funzionerà se ti preoccupi delle newline.)
zwol

1
Ho dato degli spazi come esempio nella mia risposta perché è quello che ha posto la domanda. E 'altrettanto facile per verificare spazi bianchi: case $param in *[![:space:]]*) …. Se vuoi testare i IFSpersonaggi, puoi usare il modello *[$IFS]*.
Gilles,

1
@mikeserv In un davvero sul serio sceneggiatura difensiva, è impostata in modo esplicito IFS al <space><tab><newline>all'inizio e poi non toccare mai più. Ho pensato che sarebbe stata una distrazione.
zwol,

1
Per quanto riguarda le variabili nei modelli, penso che funzioni in tutte le versioni di Bourne e in tutte le shell POSIX; richiederebbe un codice aggiuntivo per rilevare modelli di casi e disattivare l'espansione variabile e non riesco a pensare a un motivo per farlo. Ne ho un paio di esempi nel mio .profileche è stato eseguito da molte shell Bourne. Ovviamente [$IFS]non farà ciò che era previsto se IFSha determinati valori non predefiniti (vuoto (o non impostato), contenente ], che inizia con !o ^).
Gilles,

4
if [[ -n "${variable_name/[ ]*\n/}" ]]
then
    #execute if the the variable is not empty and contains non space characters
else
    #execute if the variable is empty or contains only spaces
fi

questo elimina qualsiasi carattere di spazio bianco, non solo un singolo spazio, giusto? grazie
Alexander Mills l'

0

Per verificare se la variabile è vuota o contiene spazi, è anche possibile utilizzare questo codice:

${name:?variable is empty}

2
Penso che la tua risposta sia sottovalutata perché "o contiene spazi" non è vero.
Bernhard,

fai attenzione - questo uccide la shell corrente. Meglio metterlo in un (: $ {subshell?}). Inoltre - questo scriverà a stderr - probabilmente vorrai il reindirizzamento. E la cosa più importante che valuta il valore effettivo della variabile se non è disinserito o nullo. Se c'è un comando lì dentro, l'hai appena eseguito. È meglio eseguire quel test :.
Mikeserv,

come ucciderà shell. e puoi anche provare questo, prima definire name=aaaaquindi eseguire questo comando echo ${name:?variable is empty}stamperà il valore del nome della variabile e ora eseguirà questo comando echo ${name1:?variable is empty}stamperà -sh: nome1: la variabile è vuota
pratik

Sì - echo ${name:?variable is empty}valuterà il $namevalore non nullo o ucciderà la shell. Quindi, per lo stesso motivo, non prompt > $randomvardovresti farlo nemmeno tu ${var?}- se c'è un comando lì, probabilmente funzionerà - e non sai di cosa si tratta. Non è necessario uscire da una shell interattiva, motivo per cui la tua no. Tuttavia, se vuoi vedere alcuni test, ce ne sono molti qui. . Questo non è così lungo ...
Mikeserv,

0

Per verificare se una variabile non è impostata, vuota (ha un valore null) o contiene solo spazi:

 [ -z "${param#"${param%%[! ]*}"}" ] && echo "empty"

Spiegazione:

  • L'espansione di ${param%%[! ]*}rimuove dal primo non-spazio alla fine.
  • Questo lascia solo gli spazi principali.
  • L'espansione successiva rimuove gli spazi iniziali dalla variabile.
  • Se il risultato è vuoto, la variabile era vuota per cominciare.
  • Oppure, se la variabile non era vuota, la rimozione degli spazi iniziali la rendeva vuota.
  • Se il risultato è vuoto -z, echo empty.

Quanto sopra è compatibile POSIX e funziona con qualsiasi discendente Bourne.

Se si desidera estenderlo anche alle schede, includere una scheda esplicita:

 [ -z "${param#"${param%%[!     ]*}"}" ] && echo "empty"

o usa una variabile con i caratteri che devi rimuovere:

 var=$' \t'   # in ksh, bash, zsh
 [ -z "${param#"${param%%[!$var]*}"}" ] && echo "empty"

oppure puoi usare alcune "espressioni di classe di caratteri" POSIX (spazio vuoto significa spazio e tabulazione):

 [ -z "${param#"${param%%[![:blank:]]*}"}" ] && echo "empty"

Ma ciò fallirà in mksh, lksh e posh.


-1

Prova questo:

$ [[ -z \`echo $n\` ]] && echo zero

zero
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.