Q1 Divisão de campo.
A divisão de campos é igual à divisão de palavras?
Sim, ambos apontam para a mesma idéia.
P2: quando o IFS é nulo ?
A configuração é IFS=''
igual a null, o mesmo que uma string vazia também?
Sim, todos os três significam o mesmo: Nenhuma divisão de campo / palavra deve ser realizada. Além disso, isso afeta os campos de impressão (como em echo "$*"
) todos os campos serão concatenados, sem espaço.
Q3: (parte a) Desativar IFS.
Na especificação POSIX, li o seguinte :
Se o IFS não estiver definido, o shell deverá se comportar como se o valor do IFS fosse <espaço> <guia> <nova linha> .
O que é exatamente equivalente a:
Com um unset IFS
, o shell deve se comportar como se o IFS fosse o padrão.
Isso significa que a 'Divisão do campo' será exatamente a mesma com um valor padrão do IFS ou não definido.
Isso NÃO significa que o IFS funcionará da mesma maneira em todas as condições. Sendo mais específico, a execução OldIFS=$IFS
definirá o var OldIFS
como nulo , não o padrão. E tentar definir o IFS de volta, como esse, IFS=OldIFS
definirá o IFS para nulo, não o manterá desativado como antes. Cuidado !!.
Q3: (parte b) Restaurar o IFS.
Como eu poderia restaurar o valor do IFS para o padrão. Digamos que eu queira restaurar o valor padrão do IFS. Como faço isso? (mais especificamente, como me refiro a <tab> e <newline> ?)
Para zsh, ksh e bash (AFAIK), o IFS pode ser definido como o valor padrão como:
IFS=$' \t\n' # works with zsh, ksh, bash.
Feito, você não precisa ler mais nada.
Mas se você precisar redefinir o IFS para sh, ele poderá se tornar complexo.
Vamos dar uma olhada do mais fácil ao completo, sem inconvenientes (exceto a complexidade).
1.- Desative o IFS.
Poderíamos apenas unset IFS
(Leia Q3 parte a, acima.).
2.- Trocar caracteres.
Como solução alternativa, a troca do valor de tab e newline facilita a configuração do valor do IFS e, em seguida, funciona de maneira equivalente.
Defina IFS para <espaço> <nova linha> <guia> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Um simples? solução:
Se houver scripts filhos que precisam do IFS configurado corretamente, você sempre poderá escrever manualmente:
IFS = '
"
Onde a sequência digitada manualmente foi:, IFS=
'spacetabnewline'sequência que foi realmente digitada corretamente acima (se você precisar confirmar, edite esta resposta). Mas uma cópia / pasta do seu navegador será interrompida porque o navegador comprime / oculta o espaço em branco. Torna difícil compartilhar o código conforme escrito acima.
4.- Solução completa.
Escrever código que pode ser copiado com segurança geralmente envolve escapes imprimíveis e inequívocos.
Precisamos de algum código que "produz" o valor esperado. Mas, mesmo que conceitualmente correto, esse código NÃO definirá um final \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Isso acontece porque, na maioria dos shells, todas as novas linhas finais $(...)
ou `...`
substituições de comandos são removidas na expansão.
Precisamos usar um truque para sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Uma maneira alternativa pode ser definir o IFS como um valor de ambiente a partir do bash (por exemplo) e, em seguida, chamar sh (as versões dele que aceitam o IFS a serem definidas pelo ambiente), da seguinte forma:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Em resumo, sh torna a redefinição do IFS como padrão uma aventura bastante estranha.
Q4: no código real:
Por fim, como esse código:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
se comportar se mudarmos a primeira linha para
while read -r line # Use the default IFS value
ou para:
while IFS=' ' read -r line
Primeiro: eu não sei se o echo $line
(com o var NÃO citado) existe no porpouse, ou não. Introduz um segundo nível de 'divisão de campo' que a leitura não possui. Então eu vou responder as duas. :)
Com este código (para que você possa confirmar). Você precisará do xxd útil :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Eu recebo:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
O primeiro valor é apenas o valor correto de IFS=
'spacetabnewline'
A próxima linha é todos os valores hexadecimais que a var $a
possui e uma nova linha '0a' no final, conforme será atribuída a cada comando de leitura.
A próxima linha, para a qual o IFS é nulo, não executa nenhuma 'divisão de campo', mas a nova linha é removida (conforme o esperado).
As próximas três linhas, como o IFS contém um espaço, remova os espaços iniciais e defina a linha var para o saldo restante.
As últimas quatro linhas mostram o que uma variável não citada fará. Os valores serão divididos nos (vários) espaços e serão impressos como:bar,baz,qux,
IFS
e um não configuradoIFS
são muito diferentes. A resposta para o quarto trimestre está parcialmente errada: os separadores internos não são tocados aqui, apenas os iniciais e os finais.