Perché `|` non viene trattato letteralmente in un modello glob?


13

La mia domanda viene da Come l'archiviazione dell'espressione regolare in una variabile di shell evita problemi con la citazione di caratteri speciali per la shell? .

  1. Perché c'è un errore:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'

    All'interno [[ ... ]]del secondo operando di =dovrebbe esserci un modello sconvolgente.

    Non a|bè un modello globbing valido? Puoi indicare quale regola di sintassi viola?

  2. Alcuni commenti sotto sottolineano che |viene interpretato come pipe.

    Quindi cambiando =per il modello glob in =~per il modello regex, far |funzionare

    $ [[ $a =~ a|b ]]

    Ho imparato da Learning Bash p180 nel mio precedente post che |è riconosciuto come pipe all'inizio dell'interpretazione, anche prima di qualsiasi altro passaggio di interpretazione (incluso l'analisi delle espressioni condizionali negli esempi). Quindi, come può |essere riconosciuto come operatore regex quando si usa =~, senza essere riconosciuto come pipe in uso non valido, proprio come quando si usa =? Questo mi fa pensare che l'errore di sintassi nella parte 1 non significhi che |viene interpretato come una pipe.

    Ogni riga che la shell legge dallo standard input o da uno script è chiamata pipeline; contiene uno o più comandi separati da zero o più caratteri pipe (|). Per ogni pipeline che legge, la shell lo suddivide in comandi, imposta l'I / O per la pipeline, quindi procede come segue per ciascun comando (Figura 7-1):

Grazie.


1
Si noti che in alcune versioni di bash, l'analisi di extglob (dove |è speciale) è attivata per impostazione predefinita nella parte destra di [[ $var = $pattern ]]. Sarebbe interessante isolare le versioni e le shoptconfigurazioni delle opzioni in cui si vede questo comportamento - se sono solo quelle in cui si extglobtrova, sia per impostazione predefinita o esplicita, beh, eccoci qui.
Charles Duffy,

2
A proposito, se volessi escludere in qualche modo in modo più completo il caso del personaggio della pipa che interferisce con una fase precedente di analisi (che sono d'accordo non sta accadendo, ma non è così ovvio per il lettore come potrebbe essere), avresti utilizzare pattern='a|b'e quindi espandere $patternnon quotato su RHS.
Charles Duffy,

@CharlesDuffy, era questo il punto sollevato nelle domande e risposte a cui questa domanda fa seguito.
Stéphane Chazelas,

Ahh - il contesto ha senso; e la tua risposta qui è eccezionale. Grazie per entrambi.
Charles Duffy,

Tim, hai mai risposto a una delle seguenti domande? Si prega di considerare di accettarne uno in tal caso. Grazie!
Jeff Schaller

Risposte:


13

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.


zsh sta inoltre segnalando un errore [[ $a = a|b ]].
Isaac,

@isaac, sì, questo è il punto che sto facendo qui. a|bNon è un guscio PAROLA qui, è il a, |e btoken. Come echo a|bse non emettesse a|bo non espandesse un a|bglob, è necessario citarlo in |quanto è un carattere shell speciale che non è valido in quel contesto. [[ $a = (a|b) ]]funzionerebbe come echo (a|b)funzionerebbe come (a|b)è un operatore jolly zsh.
Stéphane Chazelas,

La formulazione e la spiegazione sulla tua risposta indicano solo bash. Questa non è tutta la verità.
Isaac,

11

Globs standard ( "espansione filename") sono: *, ?, e [ ... ]. |non è un operatore glob valido nelle impostazioni standard (non extglob).

Provare:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
Grazie. Ma perché non è |integrato letteralmente? Perché c'è un errore di sintassi?
Tim

1
Non è stato citato.
Jeff Schaller

3
Nelle impostazioni standard, |non è un operatore glob, quindi non viene |interpretato letteralmente senza essere citato? Quindi perché c'è un errore di sintassi?
Tim

1
|è un personaggio di controllo; non viene mai trattato come un personaggio letterale come una lettera o un numero.
Chepner,

3
Perché in quella modalità la shell non si aspettava un carattere di reindirizzamento della pipe nel mezzo di un non ancora chiuso [[]]. [[ $a = anon è un comando valido il cui output può essere reindirizzato a un altro processo (almeno è quello che la shell pensava che stavi cercando di fare).
Jason C,

5

Se si desidera una corrispondenza regex il test sarebbe:

[[ "$a" =~ a|b ]]

@Tim Dovresti aprire nuove domande, non modificare continuamente la tua domanda attuale.
gardenhead,

@gardenhead: Il mio aggiornamento è di chiarire le mie domande, invece di cambiarle, nel caso in cui ti manchi. La seconda parte che ho aggiunto è di mostrare una spiegazione della pipe di un commento sulla mia domanda originale (perché l'errore di sintassi) non è corretta.
Tim
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.