Qual é o significado exato de IFS = $ '\ n'?

123

Se o exemplo a seguir, que define a IFSvariável de ambiente como um caractere de avanço de linha ...

IFS=$'\n'
  • O que significa exatamente o cifrão ?
  • O que faz neste caso específico?
  • Onde posso ler mais sobre esse uso específico (o Google não permite caracteres especiais nas pesquisas e não sei o que procurar)?

Eu sei qual é a IFSvariável de ambiente e qual é o \ncaractere (avanço de linha), mas por que não usar apenas o seguinte formulário: IFS="\n"(que não funciona)?

Por exemplo, se eu quiser percorrer todas as linhas de um arquivo e usar um loop for, poderia fazer o seguinte:

for line in (< /path/to/file); do
    echo "Line: $line"
done

No entanto, isso não funcionará direito, a menos que IFSesteja definido como um caractere de avanço de linha. Para fazê-lo funcionar, eu teria que fazer o seguinte:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Nota: Eu não preciso de outra maneira de fazer a mesma coisa, já conheço muitas outras ... Só estou curioso sobre isso $'\n'e me perguntei se alguém poderia me dar uma explicação sobre isso.

Yanick Girouard
fonte

Respostas:

161

Normalmente bashnão interpreta seqüências de escape em literais de string. Portanto, se você escrever \nou "\n"ou '\n', não será uma quebra de linha - é a letra n(no primeiro caso) ou uma barra invertida seguida pela letra n(nos outros dois casos).

$'somestring'é uma sintaxe para literais de seqüência de caracteres com seqüências de escape . Tão diferente '\n', $'\n'na verdade é uma quebra de linha.

sepp2k
fonte
2
Não é exatamente assim - \né apenas uma letra (escapada) n. Você está certo '\n'e "\n"é folgado, seguido por n.
Roman Cheplyaka
15
Observe que $'\n'é específico do bash - não funcionará em um shell POSIX ( /bin/sh). Para obter o mesmo efeito de uma maneira compatível com POSIX, você pode digitar IFS=', em seguida, bateu retorno ao digitar um caractere real nova linha, em seguida, digite o fechamento'
Richard Hansen
23
IFS=$(echo -e '\n')também deve fazê-lo de maneira compatível com POSIX.
Vineet
12
@ Vinine - me deu uma pausa para contestar um comentário votado. Embora isso esteja correto no Posix, ele não funciona - os operadores de substituição de comando no bash removem todos os caracteres de nova linha à direita. Veja isso para mais detalhes .
Digital Trauma
9
@DigitalTrauma Acho que nem é POSIX: -enão está definido e \nsem -efunciona como uma extensão XSI: pubs.opengroup.org/onlinepubs/9699919799/utilities/… . printf '\n'rocks;)
Ciro Santilli # 10/14
20

Apenas para dar à construção seu nome oficial : as seqüências de caracteres do formulário $'...'são chamadas de seqüências de caracteres C-C ANSI .

Ou seja, como nas seqüências de caracteres [ANSI] C, as seqüências de escape de folga são reconhecidas e expandidas para seu equivalente literal (veja abaixo a lista completa de seqüências de escape suportadas).

Após essa expansão, as $'...'strings se comportam da mesma maneira que as '...'strings - ou seja, são tratadas como literais, NÃO sujeitas a nenhuma expansão adicional da shell .

Por exemplo, $'\n'expande para um caractere literal de nova linha - que é algo que uma string regular do bash literal (se '...'ou "...") não pode fazer. [1]

Outro recurso interessante é que as seqüências de caracteres com citação ANSI C podem escapar '(aspas simples) como\' , as quais '...'(seqüências de caracteres com aspas simples regulares) não podem:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Lista de sequências de escape suportadas :

Seqüências de escape de barra invertida, se presentes, são decodificadas da seguinte maneira:

\ um alerta (sino)

\ b backspace

\ e \ E um caractere de escape (não ANSI C)

\ f feed de formulário

\ n nova linha

retorno de carro

guia horizontal

\ v guia vertical

\ barra invertida

\' citação única

\" citação dupla

\ nnn o caractere de oito bits cujo valor é o valor octal nnn (um a três dígitos)

\ xHH o caractere de oito bits cujo valor é o valor hexadecimal HH (um ou dois dígitos hexadecimais)

\ uHHHH o caractere Unicode (ISO / IEC 10646) cujo valor é o valor hexadecimal HHHH (um a quatro dígitos hexadecimais)

\ UHHHHHHHH o caractere Unicode (ISO / IEC 10646) cujo valor é o valor hexadecimal HHHHHHHH (um a oito dígitos hexadecimais)

\ cx um caractere controle-x

O resultado expandido é de aspas simples, como se o cifrão não estivesse presente.


[1] Você pode, no entanto, incorporar novas linhas reais nas strings '...' e "..."; ou seja, você pode definir cadeias que abrangem várias linhas.

mklement0
fonte
16

Em http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html :

As palavras no formato "$ 'STRING'" são tratadas de maneira especial. A palavra se expande para uma seqüência de caracteres, com caracteres de escape com barra invertida substituídos conforme especificado pelo padrão ANSI-C. As seqüências de escape de barra invertida podem ser encontradas na documentação do Bash.

Eu acho que está forçando o script a escapar do feed de linha para o padrão ANSI-C apropriado.

Brad Swerdfeger
fonte
8

Recuperando o IFS padrão - isso OLDIFS=$IFSnão é necessário. Execute o novo IFS no subshell para evitar a substituição do IFS padrão:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

Além disso, eu realmente não acredito que você recupere completamente o antigo IFS. Você deve aspas duplas para evitar quebras de linha como OLDIFS="$IFS".

Marek
fonte
2
Esta é uma técnica realmente útil. Eu apenas o usei para um shell mais limpo args=$(IFS='&'; echo "$*"). restaurar IFSde $' \t\n'uma maneira amigável para o shell Bourne não é tarefa fácil.
jeberle
Re Besides I don't really believe you recover the old IFS fully: a divisão de palavras não é realizada no RHS de atribuições de variáveis ​​(mas a remoção de cotações é), portanto, OLDIFS=$IFSe OLDIFS="$IFS"se comporta da mesma maneira.
mklement0
3

As seqüências de caracteres C-C ANSI são um ponto-chave. Graças a @ mklement0.

Você pode testar seqüências de caracteres citadas em ANSI C com o comando od.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Saídas:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Você pode conhecer claramente o significado pelas saídas.

Escudo Grande
fonte
-7

É como recuperar o valor de uma variável:

VAR='test'
echo VAR
echo $VAR

são diferentes, então o cifrão avalia basicamente o conteúdo.

Pieter
fonte
6
Isso não tem nada a ver com variáveis. $'FOO'(ao contrário$FOO que esta questão não tratava) é uma string literal. Se você executar echo $'VAR', verá que ele imprime a sequência VAR, não test.
sepp2k