Définition des options bash dans une commande composée

9

J'ai trouvé que la définition de l' extgloboption shell dans un composé composé entraîne l'échec des anti-globes ultérieurs. Les options du shell doivent-elles être définies en dehors des commandes composées? Je n'ai vu aucune indication d'une telle exigence dans les pages de manuel de bash.

Par exemple, le script suivant fonctionne correctement (impressions a.0 a.1):

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

Cependant, si les deux dernières lignes sont exécutées en tant que commande composée, le script échoue avec l'erreur suivante:

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

Cela a été testé en utilisant les versions bash de 4.2 à 4.4 et avec une variété de commandes composées :

(1) conditionnel - 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) bretelles - { }

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

(3) sous-coque - ( ):

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

Dans tous les cas, si le shoptest déplacé en dehors de la commande composée, le script réussit.

user001
la source

Réponses:

14

Aucune partie d'une commande composée ne s'exécute avant que toute la commande ne soit analysée, ce qui est documenté de manière oblique dans la section Opération shell du manuel: toutes les opérations de tokenisation et d'analyse ont lieu avant l'exécution de «la» commande. L'activation extglobmodifie la syntaxe du langage en ajoutant de nouveaux opérateurs de mise en correspondance de modèles, qui sont reconnus lors de la tokenisation.

Étant donné que la shoptcommande n'a pas été exécutée au moment où elle !(est atteinte, elle est considérée comme une tentative d'extension de l'historique ( !) ou l'opérateur de contrôle (, plutôt que d' !(être vue comme un seul élément (et la même chose pour @(, etc., sauf qu'il n'y a pas d'expansion de l'historique Là).

Lorsque l'analyse atteint ce jeton, l'un ou l'autre cas sera généralement une erreur, bien !(...)qu'au début d'une ligne ou après, timeun pipeline de sous-shell soit annulé. " unexpected token `('" signifie qu'il ne pouvait pas accepter une expression de sous-shell à cet endroit.

C'est un peu gênant lorsque vous souhaitez que l'extglob ne soit activé que temporairement dans un sous-shell. Une solution de contournement consiste à définir une fonction qui utilise le glob souhaité, puis à désactiver à nouveau extglob, puis à appeler la fonction à partir du sous-shell avec extglob activé:

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

C'est très maladroit.


Le même effet se produit si vous créez un alias dans une commande composée, car ils sont également traités avant l'analyse:

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

s'affichera foo: command not found, puis xyz, parce que l'alias est créé, mais n'est pas disponible tant que l'opération ifn'est pas terminée.

Michael Homer
la source
Merci - je ne savais pas que les commandes composées étaient analysées en tant qu'unité avant l'exécution. Le comportement est désormais logique.
user001
3
@ user001 Je ne serais pas aussi charitable de dire que cela a du sens. Changer le mode du lexer pendant l'analyse n'est rien de inconnu, et d'autres langues aiment Cou perlsont capables de le faire correctement. Notez que ne pas faire partie d'une commande composée ne suffit pas, shopt -s extglobdoit également être sur une ligne distincte. Voir aussi la discussion et les exemples ici
mosvy
@mosvy: Je voulais dire que le comportement observé a du sens une fois que l'on se rend compte que bash analyse une commande composée comme une unité (pas que les limitations de l'analyseur soient nécessairement raisonnables). Merci d'avoir lié à la discussion dans l'autre post.
user001