No bash, como eu defino uma variável “escopo de comando”?

6

Estou lendo este livro sobre a arquitetura do bash e um dos mantenedores do bash diz:

There are different variable scopes for shell function calls and temporary scopes for variables set by assignment statements preceding a command. When those assignment statements precede a command that is built into the shell, for instance, the shell has to keep track of the correct order in which to resolve variable references, and the linked scopes allow bash to do that.

Eu não consigo descobrir como fazer isso. Isso imprime uma nova linha em branco:

foo="bar" echo $foo

Isso imprime "barra", mas a variável foo ainda é definida após o término do comando, portanto, não considero que seja "escopo de comando":

foo="bar"; echo $foo

Como defino uma variável com o escopo do comando?

    
por Daniel Kaplan 23.08.2014 / 01:22

2 respostas

6

Seu primeiro palpite estava certo, mas você usou um mau exemplo. Quando o shell lê o comando

foo=bar echo $foo

a primeira coisa que faz (ou, pelo menos, uma das primeiras coisas) é procurar referências de variáveis (como $foo ) e avaliá-las. Então processa o que resta da linha. (Isso pode ser um pouco de simplificação excessiva; veja bash(1) ou o Bash Reference Manual para detalhes.) Então você tem o comando

echo (nothing)

executando com foo=bar ; mas é muito tarde; não resta mais nada para ver o valor de $foo . Você pode demonstrar isso com a seguinte sequência:

$ foo=oldvalue
$ foo=bar echo "$foo"
oldvalue

Mas

foo=bar command

é o caminho a percorrer. Alguns exemplos que funcionam são:

foo=bar sh -c 'echo "$foo"'

e

foo=bar env

(Você pode querer canalizar isso através de grep foo .)

Eu acabei de notar a frase que você citou, “Quando essas instruções de atribuição precedem um comando embutido no shell, por exemplo,…”, sugerindo que comandos internos (como echo ) representam um caso especial. É intuitivo (para mim, pelo menos) que esse "escopo de comando" que você está procurando teria que trabalhar definindo uma variável de ambiente em um processo filho, e não está claro como isso funcionaria para um comando interno, que não é executado em um processo filho. Não há muitos comandos internos que acessam diretamente o ambiente, e sua interação com variáveis shell é, às vezes, arcanizada. Mas eu criei mais alguns exemplos que podem ser mais do que você procura:

foo=bar eval 'echo $foo'

exibirá "barra", porque a avaliação de $foo é adiada. Ele é manipulado pelo comando eval (interno), em vez da passagem de análise inicial do shell. Ou crie um arquivo de texto chamado (por exemplo) showme.sh . Coloque um comando echo $foo (ou echo "$foo" ) nele e diga

foo=bar . showme.sh

Isto é provavelmente o que seu livro está falando quando diz: “… O shell tem que acompanhar a ordem correta na qual resolver referências de variáveis….” De alguma forma, o shell executa os comandos em showme.sh com foo igual a bar , e depois reverte para o valor anterior de foo (se houver) quando voltar para sua entrada principal (o terminal).

    
por 23.08.2014 / 02:04
5

O problema aqui é que a expansão de variável ( $foo → value) acontece antes de avaliar o comando inteiro (coloque foo=bar no ambiente, execute echo ... ).

Funcionaria para comandos que acessam o ambiente:

foo=bar env | grep ^foo

foo=bar declare -p foo

O Bash realmente não tem um escopo adequado para o que você está procurando; você precisará usar o truque "subprocesso" para obter um novo processo para o comando. (Claro que isso significa que as mudanças de somente leitura do escopo, não alcançarão o pai ...) Use ( ... ) para criar um subshell:

(foo=bar; echo $foo)
    
por 23.08.2014 / 02:02

Tags