Quando posso usar um IFS temporário para divisão de campo?

18

No bash, digamos que você tenha var=a.b.c. , então:

$ IFS=. printf "%s\n" $var
a.b.c

No entanto, esse uso de IFS tem efeito ao criar uma matriz:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Isso é muito conveniente, claro, mas onde isso é documentado? Uma rápida leitura das seções em Arrays ou Word Splitting na documentação do Bash não fornece nenhuma indicação de qualquer forma. Uma pesquisa por IFS por meio da documentação de página única não fornece nenhuma indicação sobre isso qualquer efeito.

Não sei quando posso fazer de forma confiável:

IFS=x do something

E espere que IFS afetará a divisão de campos.

    
por muru 20.02.2016 / 21:27

3 respostas

27

A idéia básica é que VAR=VALUE some-command define VAR to VALUE para a execução de some-command quando some-command é um comando externo e não fica mais chique do que isso. Se você combinar essa intuição com algum conhecimento de como um shell funciona, você deve encontrar a resposta certa na maioria dos casos. A referência POSIX é “Comandos simples” no capítulo “Linguagem de comando da Shell” .

Se some-command for um comando externo , VAR=VALUE some-command será 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 é alterado.

Se some-command for uma função , VAR=VALUE some-command será equivalente a VAR=VALUE; some-command , ou seja, a atribuição permanecerá em vigor após a função ter retornado e variável não é exportada para o ambiente. A razão para isso tem a ver com o design do shell Bourne (e subsequentemente com compatibilidade retroativa): ele não tinha recursos para salvar e restaurar valores de variáveis em torno da execução de uma função. Não exportar a variável faz sentido, já que 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, em que VAR é definido apenas durante a execução da função (ela também é exportada). Em ksh , isso é feito se a função for definida com a sintaxe ksh function NAME … , e não se for definida com a sintaxe padrão NAME () . Em bash , isso é feito apenas no modo bash, não no modo POSIX (quando executado com POSIXLY_CORRECT=1 ). Em zsh , isso é feito se a opção posix_builtins não estiver definida; essa opção não é definida por padrão, mas é ativada por emulate sh ou emulate ksh .

Se some-command for embutido, o comportamento dependerá do tipo de builtin. Particulares especiais se comportam como funções. Os built-ins especiais são aqueles que precisam ser implementados dentro do shell, pois afetam o shell de estado (por exemplo, break afeta o fluxo de controle, cd afeta o diretório atual, set afeta os parâmetros posicionais e opções…). Outros recursos internos são incorporados apenas para desempenho e conveniência (principalmente - por exemplo, o recurso bash printf -v pode ser implementado apenas por um builtin), e eles 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 linha de comando ser analisada, incluindo qualquer substituição de variável na própria linha de comando. Portanto, var=a; var=b echo $var imprime a , porque $var é avaliado antes que a atribuição ocorra. E assim IFS=. printf "%s\n" $var usa o antigo valor IFS para dividir $var .

Cobri todos os tipos de comandos, mas há mais um caso: quando não há comando para executar , ou seja, se o comando consistir apenas em atribuições (e possivelmente redirecionamentos). Nesse caso, a atribuição permanece em vigor . VAR=VALUE OTHERVAR=OTHERVALUE é equivalente a VAR=VALUE; OTHERVAR=OTHERVALUE . Então, depois que IFS=. arr=($var) , IFS permanece definido como . . Como você pode usar $IFS na atribuição a arr com a expectativa de que 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 a divisão de campo temporária :

  • 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 em que some-function é definido com a sintaxe ksh function some-function … ;
  • no bash e zsh, com IFS=. some-function , desde que estejam operando no modo nativo, em oposição ao modo de compatibilidade.
por 20.02.2016 / 22:32
6

A resposta do @Gilles é realmente ótima, ele explica (em detalhes) uma questão complexa.

No entanto, acredito que a resposta para o porquê desse comando:

$ IFS=. printf "%s\n" $var
a.b.c

funciona como a idéia 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) :

4.- Each variable assignment shall be expanded ...

até pouco antes de o comando ser executado e todas as expansões nos argumentos serem processadas primeiro para construir esta linha executável:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

O valor de $var é expandido com o "antigo" IFS para a.b.c antes que o comando printf receba os argumentos "%s\n" e a.b.c .

Eval

Um nível de atraso pode ser introduzido por eval :

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

A linha é analisada (1ª vez) e 'IFS =.' está definido para o ambiente como este:

$ printf '%s\n' $var

Em seguida, é analisado novamente para isso:

$ printf '%s\n' a b c

E executado para isso:

a
b
c

O valor de $var (a.b.c) é dividido com o valor do IFS em uso: . .

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:

$ IFS=. arr=($var)

O valor do IFS é retido no ambiente atual, sim:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS para uma única instrução.

Mas isso poderia ser evitado: Definindo o IFS para uma única instrução

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 
    
por 21.02.2016 / 05:18
2

Sua pergunta sobre

var=a.b.c
IFS=. printf "%s\n" $var

é um caso de canto.

Isso ocorre porque o macro expansion no comando acontece antes da variável do shell IFS=. ser definida.

Em outras palavras: quando $var for expandido, o valor IFS anterior ativo, então IFS será definido como '.' .

    
por 06.03.2016 / 22:38

Tags