Os seguintes tópicos neste site e o StackOverflow foram úteis para entender como IFS
funciona:
- O que é o IFS no contexto de loop?
- Como fazer um loop pelas linhas de um arquivo
- Bash, leia linha por linha do arquivo, com IFS
Mas ainda tenho algumas perguntas curtas. Decidi perguntar a eles no mesmo post, pois acho que pode ajudar melhores futuros leitores:
Q1 IFS
é normalmente discutido no contexto de "divisão de campo". A divisão de campos é igual à divisão de palavras ?
P2: A especificação POSIX diz :
Se o valor do IFS for nulo, nenhuma divisão de campo deve ser realizada.
A configuração é IFS=
igual IFS
a nula? É isso que significa definir também um empty string
?
Q3: na especificação POSIX, li o seguinte:
Se o IFS não estiver definido, o shell deve se comportar como se o valor do IFS fosse
<space>, <tab> and <newline>
Digamos que eu queira restaurar o valor padrão de IFS
. Como faço isso? (mais especificamente, como me refiro <tab>
e <newline>
?)
Q4: Finalmente, 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
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.IFS
, todas elas significamIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Er, o quê? Deve haver vários delimitadores de espaço, o mecanismo SO continua removendo-os).read
; a última variável pega tudo o que resta, exceto o último separador e deixa os separadores internos para dentro.Q1: sim. "Divisão de campo" e "divisão de palavras" são dois termos para o mesmo conceito.
P2: Sim. Se não estiver definido
IFS
(ou seja, depoisunset IFS
), é equivalente aIFS
ser definido como$' \t\n'
(um espaço, uma guia e uma nova linha). SeIFS
estiver definido como um valor vazio (é o que “nulo” significa aqui) (ou seja, depoisIFS=
ouIFS=''
ouIFS=""
), nenhuma divisão de campo é executada (e$*
, que normalmente usa o primeiro caractere$IFS
, usa um caractere de espaço).T3: se você deseja ter o
IFS
comportamento padrão , você pode usarunset IFS
. Se você desejar definirIFS
explicitamente esse valor padrão, poderá colocar os caracteres literais espaço, guia, nova linha entre aspas simples. No ksh93, bash ou zsh, você pode usarIFS=$' \t\n'
. Portably, se você quiser evitar ter um caractere de tabulação literal no arquivo de origem, poderá usarQ4: com
IFS
definido como um valor vazio,read -r line
defineline
como a linha inteira, exceto sua nova linha final. ComIFS=" "
, os espaços no início e no final da linha são cortados. Com o valor padrão deIFS
, tabulações e espaços são cortados.fonte
$@
, existem algumas variações entre shells em contextos não relacionados à listaIFS=; var=$@
). Deve-se observar que, quando o IFS está vazio, nenhuma divisão de palavras é realizada, mas $ var ainda se expande para nenhum argumento, em vez de um argumento vazio quando $ var está vazio, e globbing ainda se aplica, portanto, você ainda precisa citar variáveis (mesmo se desativar globbing)Q1 Divisão de campo.
Sim, ambos apontam para a mesma idéia.
P2: quando o IFS é nulo ?
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.
O que é exatamente equivalente a:
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 varOldIFS
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.
Para zsh, ksh e bash (AFAIK), o IFS pode ser definido como o valor padrão como:
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> :
3.- Um simples? solução:
Se houver scripts filhos que precisam do IFS configurado corretamente, você sempre poderá escrever manualmente:
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
: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:
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:
Em resumo, sh torna a redefinição do IFS como padrão uma aventura bastante estranha.
Q4: no código real:
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 :
Eu recebo:
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,
fonte
unset IFS
limpa o IFS, mesmo que se presuma que o IFS seja "\ t \ n":Testado nas versões 4.2.45 e 3.2.25 do bash com o mesmo comportamento.
fonte
unset
deIFS
, como explicado nos comentários da resposta aceito aqui.