Q1. Divisão de campo.
Is field splitting the same as word splitting ?
Sim, ambos apontam para a mesma ideia.
Is setting IFS=''
the same as null, the same as an empty string too?
Sim, todos os três significam o mesmo: Nenhuma divisão de campo / palavra deve ser executada.
Além disso, isso afeta os campos de impressão (como em echo "$*"
), todos os campos serão concatenados juntos sem espaço.
Q3: (parte a) Desactivar o IFS.
In the POSIX specification, I read the following:
If IFS is not set, the shell shall behave as if the value of IFS is <space><tab><newline>.
Qual é exatamente o equivalente a:
With an unset IFS
, the shell shall behave as if IFS is default.
Isso significa que a "Divisão de campo" será exatamente igual a um valor IFS padrão ou não definida.
Isso NÃO significa que o IFS funcionará da mesma maneira em todas as condições.
Sendo mais específico, a execução de OldIFS=$IFS
definirá o var OldIFS
para nulo , não o padrão. E tentando definir o IFS de volta, pois isso, IFS=OldIFS
irá definir o IFS como nulo, não mantê-lo como antes. Cuidado!.
Q3: (parte b) Restaurar o IFS.
How could I restore the value of IFS to default.
Say I want to restore the default value of IFS. How do I do that? (more specifically, how do I refer to <tab> and <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, pode se tornar complexo.
Vamos dar uma olhada do mais fácil para concluir sem inconvenientes (exceto complexidade).
1.- Desativar o IFS.
Poderíamos apenas unset IFS
(Leia Q3 parte a, acima.).
2.- Troque os caracteres.
Como solução alternativa, a troca do valor de tab e newline torna mais simples definir o valor do IFS e, em seguida, funciona de maneira equivalente.
Defina o IFS como < espaço > < newline > < tab > :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Um simples? solução:
Se houver scripts filhos que precisam definir corretamente o IFS, você sempre poderá escrever manualmente:
IFS='
'
Onde a seqüência digitada manualmente era: IFS=
' espaço guia nova linha ' , seqüência que foi digitada corretamente acima (se você precisar confirmar, edite esta resposta). Mas uma cópia / colagem do seu navegador será interrompida porque o navegador irá apertar / ocultar o espaço em branco. Torna difícil compartilhar o código como descrito acima.
4.- Solução completa.
Para escrever código que pode ser copiado com segurança, geralmente envolve saídas imprimíveis não ambíguas.
Precisamos de algum código que "produz" o valor esperado. Mas, mesmo se conceitualmente correto, este código NÃO irá definir um \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Isso acontece porque, na maioria dos shells, todas as novas linhas de substituição de comandos $(...)
ou '...'
são removidas na expansão.
Precisamos usar os truques 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 do bash (por exemplo) e, em seguida, chamar sh (as versões dele que aceitam o IFS a serem configuradas através do ambiente), como este:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Resumindo, sh faz com que o IFS reconfigure uma aventura bastante estranha.
Q4: no código real:
Finally, how would this code:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
behave if we we change the first line to
while read -r line # Use the default IFS value
or to:
while IFS=' ' read -r line
Primeiro: eu não sei se o echo $line
(com o var NÃO citado) está lá 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 a ambos. :)
Com este código (assim você pode 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=
' espaço guia nova linha '
Próxima linha é todos os valores hexadecimais que o var $a
tem, e uma nova linha '0a' no final, como será dado 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 (como esperado).
As próximas três linhas, como o IFS contém um espaço, removem os espaços iniciais e definem a linha var para o saldo restante.
As últimas quatro linhas mostram o que uma variável sem aspas fará. Os valores serão divididos nos (vários) espaços e serão impressos como: bar,baz,qux,