Configurando opções do bash em um comando composto

Configurando opções do bash em um comando composto

Descobri que definir a extglobopção shell apenas dentro de um composto composto resulta na falha de globs estendidos subsequentes. As opções do shell devem ser definidas fora dos comandos compostos? Não vi nenhuma indicação de tal requisito nas páginas de manual do Bash.

Por exemplo, o seguinte script funciona bem (prints a.0 a.1):

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

Entretanto, se as duas últimas linhas forem executadas como um comando composto, o script falhará com o seguinte erro:

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

Isso foi testado usando versões Bash de 4.2 a 4.4 e com uma variedade decomandos compostos:

(1) condicional -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) chaves -{ }

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

(3) subnível -- ( ):

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

Em todos os casos, se shoptfor movido para fora do comando composto, o script será bem-sucedido.

Responder1

Nenhuma parte de um comando composto é executada antes de todo o comando ser analisado, o que é documentado obliquamente noOperação Shellseção do manual: toda tokenização e análise acontecem antes da execução de "o" comando. A ativação extglobaltera a sintaxe da linguagem adicionando novos operadores de correspondência de padrões, que são reconhecidos durante a tokenização.

Como o shoptcomando não foi executado no momento em que !(foi alcançado, ele é visto como uma tentativa de expansão do histórico ( !) ou como o operador de controle (, em vez de !(ser visto como um único item (e o mesmo para @(, etc, exceto que não há expansão do histórico lá).

Quando a análise atinge esse token, qualquer um dos casos geralmente será um erro, embora !(...)no início de uma linha ou depois timeseja um pipeline de subshell negado. " unexpected token `('" significa que não foi possível aceitar uma expressão subshell naquele local.

Isso é um tanto irritante quando você deseja que o extglob seja ativado apenas temporariamente em um subshell. Uma solução alternativa é definir uma função que use o glob desejado, desabilitar novamente o extglob eentãochame a função do subshell com extglob habilitado:

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

É muito desajeitado.


O mesmo efeito acontece se você criar um alias dentro de um comando composto, porque eles também são processados ​​antes da análise:

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

irá imprimir foo: command not founde então xyz, porque o aliasécriado, mas não disponível até que ifseja concluído.

informação relacionada