Qual é o significado de IFS = $ '\ n' no script bash?

163

No início de um script do shell bash, está a seguinte linha:

IFS=$'\n'

Qual é o significado por trás dessa coleção de símbolos?

Abdul Al Hazred
fonte
3
Veja também unix.stackexchange.com/questions/26784/understanding-ifs e as perguntas que ele cita.
Gilles
A sintaxe do vim destaca isso como um erro para mim; consertar?
theonlygusti
IFS=$'\n'é um basismo (+ outras conchas, use a cotação ANSI-C , para obter soluções alternativas, consulte stackoverflow.com/questions/10748703/…
pevik

Respostas:

199

IFSsignifica "separador de campo interno". É usado pelo shell para determinar como fazer a divisão de palavras, ou seja, como reconhecer os limites das palavras.

Tente isso em um shell como o bash (outros shells podem lidar com isso de maneira diferente, por exemplo, zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

O valor padrão para IFSconsiste em caracteres de espaço em branco (para ser mais preciso: espaço, tabulação e nova linha). Cada caractere pode ser um limite de palavra. Portanto, com o valor padrão de IFS, o loop acima será impresso:

Word: foo:bar
Word: baz
Word: rab

Em outras palavras, o shell pensa que espaço em branco é um limite de palavra.

Agora, tente configurar IFS=:antes de executar o loop. Desta vez, o resultado é:

Word: foo
Word: bar baz rab

Agora, o shell se divide mystringem palavras também - mas agora, trata apenas dois pontos como o limite da palavra.

O primeiro caractere de IFSé especial: é usado para delimitar palavras na saída ao usar a $*variável especial (exemplo extraído do Advanced Bash Scripting Guide , onde você também pode encontrar mais informações sobre variáveis ​​especiais como essa):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

Comparado a:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Note-se que em ambos os exemplos, o shell ainda irá tratar todos os personagens :, -e ;como limites de palavra. A única coisa que muda é o comportamento de $*.

Outra coisa importante a saber é como o chamado "espaço em branco do IFS" é tratado . Basicamente, assim que IFSinclui caracteres de espaço em branco, os espaços em branco à esquerda e à direita são removidos da sequência a ser dividida antes de processá-la e uma sequência de caracteres consecutivos em branco também delimita os campos. No entanto, isso se aplica apenas aos caracteres de espaço em branco que estão realmente presentes no IFS.

Por exemplo, vejamos a sequência "a:b:: c d "(espaço à direita e dois caracteres de espaço entre ce d).

  1. Com IFS=:isso seria dividido em quatro campos: "a", "b", ""(string vazia) e " c d "(novamente, dois espaços entre ce d). Observe o espaço em branco à esquerda e à direita no último campo.
  2. Com IFS=' :', que seria dividido em cinco campos: "a", "b", ""(string vazia), "c"e "d". Nenhum espaço em branco à esquerda e à esquerda em qualquer lugar.

Observe como vários caracteres de espaço em branco consecutivos delimitam dois campos no segundo exemplo, enquanto vários dois pontos consecutivos não o fazem (já que não são caracteres de espaço em branco).

Quanto a IFS=$'\n', que é uma ksh93sintaxe também suportado por bash, zsh, mkshe FreeBSD sh(com variações entre todas as conchas). Citando a página de manual do bash:

Palavras da forma $ 'string' são tratadas especialmente. A palavra se expande para "string", com caracteres de escape com barra invertida substituídos conforme especificado pelo padrão ANSI C.

\né a sequência de escape de uma nova linha e, portanto, IFSacaba sendo definida como um único caractere de nova linha.

Tblue
fonte
3
Isso é bom, mas, na minha opinião, você faria muito melhor para ler e entender a especificação POSIX, em vez do bashguia de script, ou qualquer outra coisa. Em geral, as informações disponíveis em links como esse estão ausentes de maneiras importantes. De qualquer forma, faltam, portanto, dois pontos cruciais em relação à divisão de shell - globbing e espaço em branco do IFS.
mikeserv
@ mikeserv Obrigado, adicionei algumas informações sobre o espaço em branco do IFS. Não sabia disso. :)
Tblue
4
Não é tão relevante, mas se você estiver curioso, talvez queira ver como o unset IFSshell se comporta de maneira muito diferente IFS=. Além disso, o primeiro byte no IFS também é especial "${named_array[*]}"- mas também não importa quando a expansão não está entre
aspas
Mais alguns pontos: a divisão de uma palavra, governada por, $IFSé uma das duas principais ações realizadas ao expandir uma variável não citada no contexto da lista (é a splitparte do split+globoperador). O outro está brilhando. Ao usar a divisão de trabalho, geralmente é necessário emitir set -fpara desativar a globpeça.
Stéphane Chazelas
3
3- $IFStambém é usado pelo readcomando embutido
Stéphane Chazelas
22

Dentro de aspas simples, alguns caracteres são avaliados especialmente. Por exemplo, \né traduzido para nova linha.

Portanto, essa linha específica atribui nova linha à variável IFS. O IFS, por sua vez, é uma variável especial no bash: Internal Field Separator. Como man bashdiz,

é usado para divisão de palavras após a expansão e para dividir linhas em palavras com o readcomando interno. O valor padrão é <space><tab><newline>.

choroba
fonte
5
+1 para mencionar dollared single quotesque é diferente de aspas simples.
Snowcrash
2
@Snowcrash +1 por dizer +1 por mencionar aspas simples em dólar, diferente de aspas simples . Desculpe, não pude evitar :) Mas, na verdade, é uma coisa muito boa de se destacar, porque é importante!
Pryftan
1
@Pryftan +1 para +1 para +1 ... você sabe ... é realmente importante.
0xc0de 13/06
@ 0xc0de Definitivamente concordo! Obrigado por isso! :)
Pryftan 16/07
15

Para resumir, IFS=$'\n'atribua nova linha \nà variável IFS.

$'string'construct é um mecanismo de citação usado para decodificar ANSI C como seqüências de escape. Essa sintaxe vem ksh93, e foi portátil para shell moderno, como bash, zsh, pdksh, busybox sh.

Essa sintaxe não é definida pelo POSIX, mas foi aceita na edição 7 do SUS .

cuonglm
fonte
-1

Eu preferi explicar $IFSpor exemplo:
Suponha que você deseja cp ou mv ou outro processo de arquivo, o IFS está vazio por padrão. Quando seus arquivos têm meta char ou espaço como:
Linux Administration.pdfou Free Software Fundation.ogg, claro, você terá problemas, porque: o Linux considera um param separado e administartion considerar uma festança param.So separado tem built-in variable, então você pode inicializar para IFS==$(echo -en "\n\b"), então o bash descartar qualquer caractere meta e espaço entre o nome do arquivo, por exemplo:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
PersianGulf
fonte