A idéia básica é que os VAR=VALUE some-command
conjuntos VAR
para VALUE
a execução de some-command
quando some-command
é um comando externo, e ele não fica mais chique do que isso. Se você combinar essa intuição com algum conhecimento de como um shell funciona, na maioria dos casos, você encontrará a resposta certa. A referência do POSIX é "Comandos simples" no capítulo "Linguagem de comando do shell" .
Se some-command
é um comando externo , VAR=VALUE some-command
é equivalente a env VAR=VALUE some-command
. VAR
é exportado no ambiente de some-command
e seu valor (ou falta de valor) no shell não muda.
Se some-command
é uma função , então VAR=VALUE some-command
é equivalente a VAR=VALUE; some-command
, ou seja, a atribuição permanece em vigor após o retorno da função e a variável não é exportada para o ambiente. A razão disso tem a ver com o design do shell Bourne (e posteriormente com a compatibilidade com versões anteriores): ele não tinha capacidade para salvar e restaurar valores variáveis em torno da execução de uma função. Não exportar a variável faz sentido, pois uma função é executada no próprio shell. No entanto, ksh (incluindo ATT ksh93 e pdksh / mksh), bash e zsh implementam o comportamento mais útil, onde VAR
é definido apenas durante a execução da função (também é exportado). No ksh , isso é feito se a função for definida com a sintaxe kshfunction NAME …
, não se for definido com a sintaxe padrão NAME ()
. No bash , isso é feito apenas no modo bash, não no modo POSIX (quando executado com POSIXLY_CORRECT=1
). No zsh , isso é feito se a posix_builtins
opção não estiver configurada; esta opção não está definida por padrão, mas está ativada por emulate sh
ou emulate ksh
.
Se some-command
for um builtin, o comportamento depende do tipo de builtin. Construções especiais se comportam como funções. Built-ins especiais são os que precisam ser implementados dentro do shell porque afetam o estado do shell (por exemplo break
, cd
afeta o fluxo de controle, afeta o diretório atual, set
afeta parâmetros e opções posicionais ...). Outros componentes internos são integrados apenas para desempenho e conveniência (principalmente - por exemplo, o recurso bash printf -v
pode ser implementado apenas por um componente interno) e se comportam como um comando externo.
A atribuição ocorre após a expansão do alias; portanto, se some-command
for um alias , expanda-o primeiro para descobrir o que acontece.
Observe que, em todos os casos, a atribuição é executada após a análise da linha de comando, incluindo qualquer substituição de variável na própria linha de comando. Então , var=a; var=b echo $var
imprime a
, porque $var
é avaliado antes da atribuição. E, assim, IFS=. printf "%s\n" $var
usa o IFS
valor antigo para dividir $var
.
Eu cobri todos os tipos de comandos, mas há mais um caso: quando não há comando para executar , ou seja, se o comando consiste apenas em atribuições (e possivelmente redirecionamentos). Nesse caso, a atribuição permanece no local . VAR=VALUE OTHERVAR=OTHERVALUE
é equivalente a VAR=VALUE; OTHERVAR=OTHERVALUE
. Então IFS=. arr=($var)
, depois , IFS
permanece definido como .
. Como você pode usar $IFS
na atribuição arr
com a expectativa de que ele já tenha seu novo valor, faz sentido que o novo valor de IFS
seja usado para a expansão de $var
.
Em resumo, você pode usar apenas IFS
para divisão temporária de campos:
- iniciando um novo shell ou um subshell (por exemplo,
third=$(IFS=.; set -f; set -- $var; echo "$3")
é uma maneira complicada de fazer, third=${var#*.*.}
exceto que eles se comportam de maneira diferente quando o valor de var
contém menos de dois .
caracteres);
- em ksh, com
IFS=. some-function
onde some-function
é definido com a sintaxe ksh function some-function …
;
- no bash e zsh,
IFS=. some-function
desde que estejam operando no modo nativo em oposição ao modo de compatibilidade.
IFS
permanece definido como.
" Eek. Depois de ler a primeira parte, isso faz sentido, mas antes de postar este Q, eu não esperava isso.A resposta do @Gilles é realmente ótima, ele explica (em detalhes) uma questão complexa.
No entanto, acredito que a resposta para o porquê deste comando:
funciona como é a ideia simples de que toda a linha de comando é analisada antes de ser executada. E que cada "palavra" é processada uma vez pelo shell.
As atribuições, como
IFS=.
, estão atrasadas (a etapa 4 é a última):até pouco antes do comando ser executado e todas as expansões nos argumentos serem processadas primeiro para criar esta linha executável:
O valor de
$var
é expandido com o IFS "antigo" paraa.b.c
antes que o comandoprintf
receba os argumentos"%s\n"
ea.b.c
.Eval
Um nível de atraso pode ser introduzido por
eval
:A linha é analisada (1ª vez) e 'IFS =.' está definido no ambiente da seguinte maneira:
Em seguida, é analisado novamente para isso:
E executado para isso:
O valor de
$var
(abc) é dividida com o valor de FI em uso:.
.Meio Ambiente
A parte complexa e complicada é o que é válido no ambiente quando !!!
Isso é explicado muito bem na primeira parte da resposta de Gilles.
Com um detalhe adicional.
Quando este comando é executado:
O valor do IFS é retido no ambiente atual, sim:
IFS para uma única declaração.
Mas poderia ser evitado: Configurando o IFS para uma única instrução
fonte
Sua pergunta sobre
é uma caixa de canto.
Isso ocorre porque o
macro expansion
comando acontece antes da variável shellIFS=.
ser configurada.Em outras palavras: quando
$var
é expandido, oIFS
valor anterior ativo e, em seguida,IFS
é definido como'.'
.fonte