Descobri que definir a extglob
opçã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 shopt
for 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 extglob
altera a sintaxe da linguagem adicionando novos operadores de correspondência de padrões, que são reconhecidos durante a tokenização.
Como o shopt
comando 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 time
seja 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 found
e então xyz
, porque o aliasécriado, mas não disponível até que if
seja concluído.