Impostazione delle opzioni bash in un comando composto


9

Ho scoperto che l'impostazione extglobdell'opzione shell all'interno di un composto composto comporta un fallimento dei successivi anti-globs. Le opzioni della shell devono essere impostate al di fuori dei comandi composti? Non ho visto un'indicazione di tale requisito nelle pagine man di bash.

Ad esempio, il seguente script funziona bene (stampa a.0 a.1):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
shopt -s extglob
ls "a."!(b*)

Tuttavia, se le ultime due righe vengono eseguite come comando composto, lo script non riesce con il seguente errore:

syntax error near unexpected token `('
`    ls "a."!(b*)'

Questo è stato testato usando le versioni bash da 4.2 a 4.4 e con una varietà di comandi composti :

(1) condizionale - if

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
if true; then
    shopt -s extglob
    ls "a."!(b*)
fi

(2) parentesi graffe - { }

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
{
    shopt -s extglob
    ls "a."!(b*)
}

(3) subshell - ( ):

#!/bin/bash
touch a.0   a.1 \
      a.b.0 a.b.1
(
    shopt -s extglob
    ls "a."!(b*)
)

In tutti i casi, se shoptviene spostato all'esterno del comando composto, lo script ha esito positivo.

Risposte:


14

Nessuna parte di un comando composto viene eseguita prima che l'intero comando venga analizzato, che è documentato obliquamente nella sezione Funzionamento della Shell del manuale: tutti i token e le analisi avvengono prima dell'esecuzione del comando "il". L'abilitazione extglobmodifica la sintassi della lingua aggiungendo nuovi operatori di corrispondenza dei modelli, che vengono riconosciuti durante la tokenizzazione.

Poiché il shoptcomando non è stato eseguito al momento del !(raggiungimento, viene visto come un tentativo di espansione della cronologia ( !) o come operatore di controllo (, anziché !(essere visto come un singolo elemento (e lo stesso per @(, ecc., Tranne per il fatto che non esiste alcuna espansione della cronologia Là).

Quando l'analisi raggiunge quel token, in entrambi i casi sarà generalmente un errore, sebbene !(...)all'inizio di una riga o dopo timesia una pipeline subshell negata. " unexpected token `('" significa che non può accettare un'espressione subshell in quel punto.

Questo è alquanto fastidioso quando si desidera che extglob sia abilitato solo temporaneamente in una subshell. Una soluzione alternativa è definire una funzione che utilizza il glob desiderato, quindi disabilitare nuovamente extglob e quindi chiamare la funzione dalla subshell con extglob abilitato:

shopt -s extglob
f() { ls "a."!(b*) ; }
shopt -u extglob
(
    shopt -s extglob
    f
)

È molto goffo.


Lo stesso effetto si verifica se si crea un alias all'interno di un comando composto, poiché vengono anche elaborati prima dell'analisi:

if true
then
    alias foo=echo
    foo bar
fi
foo xyz

stamperà foo: command not founde quindi xyz, poiché l'alias viene creato, ma non disponibile fino a quando non ifviene eseguito.


Grazie - Non mi rendevo conto che i comandi composti venivano analizzati come unità prima dell'esecuzione. Il comportamento ora ha senso.
user001,

3
@ user001 Non sarei così caritatevole dire che ha senso. Cambiare la modalità del lexer durante l'analisi non è nulla di inaudito e altre lingue come Co perlsono in grado di farlo bene. Notare che non essere parte di un comando composto non è abbastanza, shopt -s extglobdeve anche essere su una riga separata. Vedi anche la discussione e gli esempi qui
mosvy

@mosvy: intendevo dire che il comportamento osservato ha senso una volta che ci si rende conto che bash analizza un comando composto come unità (non che i limiti del parser siano necessariamente ragionevoli). Grazie per il collegamento alla discussione nell'altro post.
user001,
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.