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

117

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

IFS=$'\n'

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

    
por Abdul Al Hazred 14.02.2015 / 17:08

5 respostas

155

IFS significa "separador de campo interno". Ele é usado pelo shell para determinar como fazer a divisão de palavras, i. e. como reconhecer limites de palavras.

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

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

O valor padrão para IFS consiste em caracteres de espaço em branco (para ser 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 o espaço em branco é um limite de palavra.

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

Word: foo
Word: bar baz rab

Agora, o shell também divide mystring em palavras - mas agora ele 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 obtido do Guia avançado de script Bash , 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

Compare com:

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

Observe que, nos dois exemplos, o shell ainda tratará todos os caracteres : , - e ; como limites de palavras. 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 IFS incluir caracteres de espaço em branco, os espaços em branco iniciais e finais são removidos da string para serem divididos antes do processamento e uma seqüência de caracteres consecutivos de espaços em branco também delimita os campos. No entanto, isso só se aplica aos caracteres de espaço em branco que estão realmente presentes em IFS .

Por exemplo, vamos ver a string "a:b:: c d " (espaço à direita e dois caracteres de espaço entre c e d ).

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

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

Quanto a IFS=$'\n' , essa é uma sintaxe ksh93 também suportada por bash , zsh , mksh e FreeBSD sh (com variações entre todos os shells). Citando o bash manpage:

Words of the form $'string' are treated specially. The word expands to "string", with backslash-escaped characters replaced as specified by the ANSI C standard.

\n é a sequência de escape para uma nova linha, então IFS acaba sendo definido para um único caractere de nova linha.

    
por 14.02.2015 / 17:31
19

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 bash diz,

is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is <space><tab><newline>.

    
por 14.02.2015 / 17:16
13

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

$'string' construct é um mecanismo de cotação que usa para decodificar ANSI C como sequências de escape. Essa sintaxe vem de ksh93 e era portável para o shell moderno como bash , zsh , pdksh , busybox sh .

Esta sintaxe não é definida pelo POSIX, mas foi aceita para questão 7 do SUS .

    
por 14.02.2015 / 17:35
0

Truque com IFS=$'\n' (e o comportamento Readline na conclusão ambígua) usado para emular a conclusão do menu com descrições:

_xxx() {
    local IFS=$'\n'
    local args="--AAA  - AAA to aaa
--BBB   - BBB for bbb"
    COMPREPLY=( $(compgen -W "$args" -- "${COMP_WORDS[COMP_CWORD]}") )
    if [[ ${#COMPREPLY[*]} -eq 1 ]]; then
        COMPREPLY=( ${COMPREPLY[0]%%  *} )
    fi
} && complete -F _xxx xxx
    
por 13.02.2017 / 20:42
-1

Eu preferi explicar $IFS pelo exemplo:
Suppsoe você quer cp ou mv ou outro processo de arquivo, IFS está vazio por defualt, Quando seus arquivos têm meta-char ou espaço como:
Linux Administration.pdf ou Free Software Fundation.ogg , Claro que você terá probelm, Porque: Linux considere um parâmetro separado e Administartion considere um parâmetro separado.So bash tem built-in variable , Então você pode inicializar para IFS==$(echo -en "\n\b") , Em seguida, bash descartar qualquer meta-espaço 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
    
por 15.02.2015 / 22:42