Domanda Perché i confronti degli script di shell spesso usano x $ VAR = xyes?


Lo vedo spesso negli script di build dei progetti che usano autotools (autoconf, automake). Quando qualcuno vuole controllare il valore di una variabile di shell, usa frequentemente questo idioma:

if test "x$SHELL_VAR" = "xyes"; then
...

Qual è il vantaggio di questo semplicemente controllando il valore in questo modo:

if test $SHELL_VAR = "yes"; then
...

Immagino ci debba essere una ragione per cui lo vedo così spesso, ma non riesco a capire di cosa si tratta.


44
2017-10-06 12:50


origine


risposte:


Se stai usando una shell che lo fa semplice sostituzione e il SHELL_VAR la variabile non esiste (o è vuota), quindi è necessario fare attenzione ai casi limite. Le seguenti traduzioni avverranno:

if test $SHELL_VAR = yes; then        -->  if test = yes; then
if test x$SHELL_VAR = xyes; then      -->  if test x = xyes; then

Il primo di questi genererà un errore dal primo argomento a test è scomparso Il secondo non ha questo problema.

Il tuo caso si traduce come segue:

if test "x$SHELL_VAR" = "xyes"; then  -->  if test "x" = "xyes"; then

Esso può sembrare un po 'ridondante dato che ha sia le virgolette che la "x" ma gestirà anche una variabile con spazi al suo interno, senza darlo come Due argomenti al test comando.

L'altro motivo (diverso dalle variabili vuote) ha a che fare con l'elaborazione delle opzioni. Se scrivi:

if test "$1" = "abc" ; then ...

e $1 ha il valore -n o -z o qualsiasi altra opzione valida per test comando, la sintassi è ambigua. Il x nella parte anteriore impedisce che un trattino principale venga prelevato come opzione test.

Tieni presente che questo dipende dalla shell. Alcuni gusci (csh per esempio, penso) si lamenterà amaramente se la variabile d'ambiente non esiste piuttosto che restituire semplicemente una stringa vuota).


70
2017-10-06 13:29



L'altra ragione che nessun altro ha ancora menzionato riguarda l'elaborazione delle opzioni. Se scrivi:

if [ "$1" = "abc" ]; then ...

e $ 1 ha il valore '-n', la sintassi del comando test è ambigua; non è chiaro cosa stavi testando. La 'x' nella parte anteriore impedisce a un trattino principale di causare problemi.

Devi cercare shell molto antiche per trovarne una in cui il comando di test non ha supporto -n o -z; the Version 7 (1978) test il comando li ha inclusi. Non è del tutto irrilevante: alcuni file UNIX della Versione 6 sono sfuggiti a BSD, ma al giorno d'oggi, sarebbe estremamente difficile trovare qualcosa di così antico nell'uso corrente.

Non usare le virgolette sui valori è pericoloso, come hanno sottolineato altre persone. Infatti, se esiste la possibilità che i nomi dei file possano contenere spazi (MacOS X e Windows lo incoraggiano entrambi in qualche misura, e Unix lo ha sempre supportato, sebbene strumenti come xargs renderlo più difficile), quindi dovresti racchiudere i nomi dei file tra virgolette ogni volta che li usi. A meno che tu non sia il responsabile del valore (ad esempio durante la gestione delle opzioni, e imposta la variabile su "no" all'avvio e "sì" quando un flag è incluso nella riga di comando) non è sicuro usare forme non quotate di variabili fino a quando non li avrai dimostrati al sicuro - e potresti anche farlo tutto il tempo per molti scopi. Oppure documenta che i tuoi script falliranno in modo orribile se gli utenti tentano di elaborare file con spazi vuoti nei nomi. (E ci sono anche altri personaggi di cui preoccuparsi - anche i backtick potrebbero essere piuttosto cattivi, per esempio.)


18
2017-10-07 22:00



Ci sono due ragioni per cui so di questa convenzione:

http://tldp.org/LDP/abs/html/comparison-ops.html

In un test composto, anche quotare la variabile stringa potrebbe non essere sufficiente.   [-n "$ string" -o "$ a" = "$ b"] potrebbe causare un errore con alcune versioni di   Bash se $ string è vuoto. Il modo sicuro è aggiungere un carattere extra a   variabili probabilmente vuote, ["x $ stringa"! = x -o "x $ a" = "x $ b"] (le "x's" cancellano).

In secondo luogo, in altri shell di Bash, specialmente quelli più vecchi, le condizioni di test come '-z' per testare una variabile vuota non esistevano, quindi mentre questo:

if [ -z "$SOME_VAR" ]; then
  echo "this variable is not defined"
fi

funzionerà bene in BASH, se stai mirando alla portabilità in vari ambienti UNIX dove non puoi essere sicuro che la shell di default sarà Bash e se supporta la condizione -z test, è più sicuro usare il modulo se [" x $ SOME_VAR "=" x "] dal momento che avrà sempre l'effetto desiderato. Essenzialmente si tratta di un vecchio trucco di shell scripting per trovare una variabile vuota, ed è ancora usato oggi per compatibilità all'indietro nonostante siano disponibili metodi più puliti.


10
2017-10-06 13:41



Raccomando invece:

if test "yes" = "$SHELL_VAR"; then

dal momento che elimina il brutto xe risolve ancora il problema menzionato da https://stackoverflow.com/a/174288/895245 quella $SHELL_VAR può iniziare con - e da leggere come opzione.


3
2018-06-30 05:04



Credo che sia dovuto a

SHELLVAR=$(true)
if test $SHELLVAR  = "yes" ; then echo "yep" ; fi 

# bash: test: =: unary operator expected

così come

if test $UNDEFINEDED = "yes" ; then echo "yep" ; fi
# bash: test: =: unary operator expected

e

SHELLVAR=" hello" 
if test $SHELLVAR = "hello" ; then echo "yep" ; fi
# yep 

tuttavia, di solito dovrebbe funzionare

SHELLVAR=" hello"
if test "$SHELLVAR" = "hello" ; then echo "yep" ; fi 
#<no output>

ma quando si lamenta in uscita da qualche altra parte, è difficile dire che cosa si lamenti di suppongo, quindi

SHELLVAR=" hello"
if test "x$SHELLVAR" = "xhello" ; then echo "yep" ; fi 

funziona altrettanto bene, ma sarebbe più facile eseguire il debug.


2
2017-10-06 13:00



Lo facevo in DOS quando SHELL_VAR poteva essere indefinito.


1
2017-10-06 12:51



Se non si fa la cosa "x $ SHELL_VAR", se $ SHELL_VAR non è definito, si ottiene un errore sul fatto che "=" non è un operatore monadico o qualcosa del genere.


1
2017-10-06 13:01