Por que está configurando uma variável antes de um comando legal no bash?

56

Acabei de encontrar várias respostas, como analisando um arquivo de texto delimitado ... que usa a construção:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

em que a variável IFS está definida antes do comando read .

Eu li na referência do bash , mas não consigo entender por que é legal.

Eu tentei

$ x="once upon" y="a time" echo $x $y

do prompt de comando do bash, mas não obteve nada ecoado. Alguém pode me apontar para onde essa sintaxe é definida na referência que permite que a variável IFS seja definida dessa maneira? É um caso especial ou posso fazer algo parecido com outras variáveis?

    
por Mike Lippert 28.04.2014 / 17:54

6 respostas

57

Está sob a gramática de shell, comandos simples (ênfase adicionada):

A simple command is a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator. The first word specifies the command to be executed, and is passed as argument zero. The remaining words are passed as arguments to the invoked command.

Então você pode passar qualquer variável que quiser. Seu echo exemplo não funciona porque as variáveis são passadas para o comando, não definidas no shell. O shell expande $x e $y antes de invocar o comando. Isso funciona, por exemplo:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time
    
por 28.04.2014 / 18:04
22

As variáveis definidas tornam-se como variáveis de ambiente no processo bifurcado.

Se você correr

A="b" echo $A

então o bash primeiro expande $A para "" e depois executa

A="b" echo

Aqui está a maneira correta:

x="once upon" y="a time" bash -c 'echo $x $y'

Observe as aspas simples em bash -c , caso contrário, você terá o mesmo problema acima.

Portanto, seu exemplo de loop é legal porque o comando bash builtin 'read' procurará o IFS em suas variáveis de ambiente e localizará , . Portanto,

for i in 'TEST=test bash -c 'echo $TEST''
do
  echo "TEST is $TEST and I is $i"
done

imprimirá TEST is and I is test

Por último, quanto à sintaxe, em um loop for, uma string é esperada. Portanto, eu tive que usar backticks para torná-lo um comando. No entanto, enquanto loops esperam a sintaxe de comando, como IFS=, read xx yy zz .

    
por 28.04.2014 / 18:17
5

man bash

ENVIRONMENT

[...] The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described above in PARAMETERS. These assignment statements affect only the environment seen by that command.

As variáveis são expandidas antes da atribuição da variável. Pela razão óbvia de que var=x funcionaria de outra forma também, mas var=$othervar não funcionaria. Ou seja seu $x é necessário antes de estar disponível. Mas esse não é o problema principal. O principal problema é que a linha de comando só pode ser modificada pelo ambiente do shell, mas a atribuição não se torna parte do ambiente do shell.

Você combina recursos: você quer uma substituição de linha de comando, mas coloca a definição da variável no ambiente de comandos. As substituições de linha de comando devem ser feitas pelo shell. O ambiente deve ser explicitamente usado pelo comando chamado. Se e como isso é feito, depende do comando.

A vantagem desse uso é que você pode definir o ambiente para um subprocesso sem afetar o ambiente do shell.

x="once upon" y="a time" bash -c 'echo $x $y'

funciona como esperado porque, nesse caso, os dois recursos são combinados: A substituição da linha de comando não é feita pelo shell de chamada, mas pelo shell do subprocesso.

    
por 28.04.2014 / 18:04
4

O comando que você fornece é diferente porque $x e $y são expandidos antes que o comando echo seja executado, então seus valores no shell atual são usados , não os valores que echo veria em seu ambiente se ele aparecesse.

    
por 28.04.2014 / 18:04
1

Eu estou indo para o quadro maior de " por que é legal"

Resposta: Para que você possa chamar ou invocar um programa e, para essa invocação, use apenas uma variável com uma variável específica.

Como exemplo: Você tem um parâmetro para uma conexão de banco de dados chamado 'db_connection' e normalmente passa em 'test' como o nome da conexão do banco de dados de teste. Na verdade, você pode até configurá-lo como um padrão que não precisa passar explicitamente. Às vezes, no entanto, você quer trabalhar com o banco de dados ci. Então você passa o param como 'ci' e então o programa sendo chamado usa aquele parâmetro do banco de dados como o nome do banco de dados a ser usado para todas as chamadas do banco de dados. Para a próxima execução, se você não repetir a abordagem e apenas chamar o programa, a variável retornará ao seu valor padrão anterior.

    
por 28.04.2014 / 23:52
0

Você também pode usar ; . Será avaliado antes porque é o separador de comandos.

x="once upon" y="a time"; echo $x $y
    
por 28.03.2018 / 15:30