Non c'è una buona ragione per cui
[[ $a = a|b ]]
Dovrebbe segnalare un errore invece di verificare se $ a è la a|bstringa, mentre [[ $a =~ a|b ]]non restituisce un errore.
L'unica ragione è che |generalmente (fuori e dentro [[ ... ]]) è un personaggio speciale. In quella [[ $a =posizione, si bashaspetta un tipo di token che sia una normale WORD come gli argomenti o i target dei reindirizzamenti in una normale riga di comando della shell (ma come se l' extglobopzione fosse stata abilitata da bash 4.1).
(da WORD qui, mi riferisco a una parola in un'ipotetica grammatica della shell come quella descritta dalla specifica POSIX , che è qualcosa che la shell analizzerebbe come un token in una semplice riga di comando della shell, non un'altra definizione di parole come l'inglese uno di una sequenza di lettere o una sequenza di caratteri senza spaziatura. foo"bar baz", $(echo x y), sono due WORD s).
In una normale riga di comando della shell:
echo a|b
Viene echo aconvogliato a b. a|bnon è una WORD , è tre token: una a WORD , un |token e un token b WORD .
Se utilizzato in [[ $a = a|b ]], si bashaspetta una WORD che ottiene ( a), ma trova quindi un |token imprevisto che causa l'errore.
È interessante notare che bashnon si lamenta di:
[[ $a = a||b ]]
Perché ora è un atoken seguito da un ||token seguito da b, quindi viene analizzato nello stesso modo di:
[[ $a = a || b ]]
Che sta verificando che $aè ao che la bstringa non è vuota.
Ora in:
[[ $a =~ a|b ]]
bashnon può avere la stessa regola di analisi. Avere la stessa regola di analisi significherebbe che quanto sopra darebbe un errore e che bisognerebbe citare ciò |per assicurarsi che a|bsia una singola WORD . Ma, dal bash 3.2, se lo fai:
[[ $a =~ 'a|b' ]]
Questo non corrisponde più al a|bregexp ma al a\|bregexp. Cioè, la quotazione della shell ha l'effetto collaterale di rimuovere il significato speciale degli operatori regexp. È una caratteristica, quindi il comportamento è simile a [[ $a = "?" ]]quello, ma i pattern jolly (usati in [[ $a = pattern ]]) sono WORD shell (usati ad esempio nei globs), mentre i regexps non lo sono.
Quindi bashdeve trattare tutti gli operatori regexp estesi che sono altrimenti normalmente caratteri speciali shell come |, (, )differentemente durante l'analisi di un argomento della =~dell'operatore.
Tuttavia, nota che mentre
[[ $a =~ (ab)*c ]]
ora funziona,
[[ $a =~ [)}] ]]
non lo fa. Hai bisogno:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
Che nelle versioni precedenti bashcorrisponderebbe erroneamente alla barra rovesciata. Quello è stato risolto, ma
[[ $a =~ [^]')'] ]]
Non non corrispondere il backslash come dovrebbe per esempio. Perché bashnon riesce a capire che si )trova tra parentesi, quindi sfugge )al risultato in una [^]\)]regexp che corrisponde a qualsiasi carattere ma ], \e ).
ksh93 ha bug molto peggiori su quel fronte.
In zsh, è una normale parola shell prevista e la citazione di operatori regexp non influisce sul significato degli operatori regexp.
[[ $a =~ 'a|b' ]]
Corrisponde al a|bregexp.
Ciò significa che =~può anche essere aggiunto al comando [/ test:
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(funziona anche yash. =~Bisogna citare zshcome =somethingè un operatore shell speciale lì).
bash 3.1 si comportava come zsh. È cambiato in 3.2, presumibilmente per allinearsi con ksh93(anche se è bashstata la shell che è venuta per la prima volta [[ =~ ]]), ma puoi ancora fare BASH_COMPAT=31o shopt -s compat31tornare al comportamento precedente (tranne che mentre [[ $a =~ a|b ]]restituirebbe un errore in bash3.1, non lo fa più in bash -O compat31con le versioni più recenti di bash).
Spero che chiarisca perché ho detto che le regole erano confuse e perché usare:
[[ $a =~ $var ]]
aiuta anche con la portabilità ad altre shell.
|è speciale) è attivata per impostazione predefinita nella parte destra di[[ $var = $pattern ]]. Sarebbe interessante isolare le versioni e leshoptconfigurazioni delle opzioni in cui si vede questo comportamento - se sono solo quelle in cui siextglobtrova, sia per impostazione predefinita o esplicita, beh, eccoci qui.