Questo è un caso molto oscuro che si potrebbe considerare un bug nel modo [
in cui è definito il test integrato; tuttavia, corrisponde al comportamento del file [
binario effettivo disponibile su molti sistemi. Per quanto posso dire, colpisce solo alcuni casi e una variabile che ha un valore che corrisponde a un [
operatore come (
, !
, =
, -e
, e così via.
Lasciami spiegare perché e come aggirarlo nelle shell Bash e POSIX.
Spiegazione:
Considera quanto segue:
x="("
[ "$x" = "(" ] && echo yes || echo no
Nessun problema; quanto sopra non produce alcun errore e produce yes
. Ecco come ci aspettiamo che le cose funzionino. '1'
Se lo desideri, puoi modificare la stringa di confronto e il valore di x
, e funzionerà come previsto.
Si noti che l'attuale /usr/bin/[
binario si comporta allo stesso modo. Se si esegue ad es. Non si '/usr/bin/[' '(' = '(' ']'
verifica alcun errore, poiché il programma è in grado di rilevare che gli argomenti consistono in una singola operazione di confronto di stringhe.
Il bug si verifica quando noi e con una seconda espressione. Non importa quale sia la seconda espressione, purché sia valida. Per esempio,
[ '1' = '1' ] && echo yes || echo no
produce yes
ed è ovviamente un'espressione valida; ma, se combiniamo i due,
[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Bash rifiuta l'espressione se e solo se x
è (
o !
.
Se dovessimo eseguire quanto sopra usando il [
programma vero e proprio
'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no
l'errore sarebbe comprensibile: dal momento che la shell esegue le sostituzioni variabili, il /usr/bin/[
binario riceve solo i parametri (
=
(
-a
1
=
1
e la terminazione ]
, comprensibilmente non riesce a capire se le parentesi aperte avviano o meno un'espressione secondaria, essendo coinvolta un'operazione e un'operazione. Certo, analizzarlo come due confronti di stringhe è possibile, ma farlo avidamente in quel modo potrebbe causare problemi quando applicato a espressioni appropriate con sottoespressioni tra parentesi.
Il problema, in realtà, è che la shell [
integrata si comporta allo stesso modo, come se espandesse il valore di x
prima di esaminare l'espressione.
(Queste ambiguità, e altre relative all'espansione variabile, sono state una delle ragioni principali per cui Bash ha implementato e ora consiglia invece di utilizzare le [[ ... ]]
espressioni di test.)
La soluzione è banale e spesso si vede negli script che usano sh
shell più vecchie . Aggiungi un carattere "sicuro", spesso x
, davanti alle stringhe (entrambi i valori vengono confrontati), per garantire che l'espressione sia riconosciuta come confronto di stringhe:
[ "x$x" = "x(" -a "x$y" = "x1" ]
[[ "$x" = '1' && "$y" = '1' ]]