Algumas pessoas têm essa noção errônea que readé o comando para ler uma linha. Não é.
readlê palavras de uma linha (possivelmente continuada por barra invertida), na qual as palavras são $IFSdelimitadas e a barra invertida pode ser usada para escapar dos delimitadores (ou continuar linhas).
A sintaxe genérica é:
read word1 word2... remaining_words
readlê stdin um byte de cada vez até encontrar um caractere de nova linha unescaped (ou fim-de-entrada), divide que de acordo com regras complexas e armazena o resultado dessa divisão em $word1, $word2... $remaining_words.
Por exemplo, em uma entrada como:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
e com o valor padrão de $IFS, read a b catribuiria:
$a ⇐ foo
$b ⇐ bar baz
$c ⇐ blah blahwhatever whatever
Agora, se passado apenas um argumento, isso não se torna read line. Ainda está read remaining_words. O processamento de barra invertida ainda está concluído, os caracteres de espaço em branco do IFS ainda são removidos do início e do fim.
A -ropção remove o processamento da barra invertida. Portanto, o mesmo comando acima com -ratribuiria
$a ⇐ foo
$b ⇐ bar\
$c ⇐ baz bl\ah blah\
Agora, para a parte de divisão, é importante perceber que existem duas classes de caracteres para $IFS: os caracteres de espaço em branco do IFS (ou seja, espaço e tab (e nova linha, embora aqui isso não importe, a menos que você use -d), o que também acontece estar no valor padrão de $IFS) e os outros. O tratamento para essas duas classes de personagens é diferente.
Com IFS=:( :não sendo um espaço em branco IFS), uma entrada como :foo::bar::seria dividido em "", "foo", "", bare ""(e um extra ""com algumas implementações embora isso não importa, exceto read -a). Enquanto se substituirmos isso :por espaço, a divisão será feita em somente fooe bar. Os principais e os finais são ignorados e as sequências são tratadas como uma. Existem regras adicionais quando caracteres em branco e não em branco são combinados $IFS. Algumas implementações podem adicionar / remover o tratamento especial dobrando os caracteres no IFS ( IFS=::ou IFS=' ').
Portanto, aqui, se não queremos que os caracteres de espaço em branco à esquerda e à esquerda sejam removidos, precisamos remover esses caracteres de espaço em branco do IFS do IFS.
Mesmo com caracteres IFS que não sejam espaços em branco, se a linha de entrada contiver um (e apenas um) desses caracteres e for o último caractere na linha (como IFS=: read -r wordem uma entrada como foo:) com shells POSIX (não, zshnem em algumas pdkshversões), essa entrada é considerado como uma foopalavra, porque nessas conchas, os caracteres $IFSsão considerados terminadores ; portanto word, conterão foo, não foo:.
Portanto, a maneira canônica de ler uma linha de entrada com o readbuiltin é:
IFS= read -r line
(observe que, na maioria das readimplementações, isso funciona apenas para linhas de texto, pois o caractere NUL não é suportado, exceto em zsh).
O uso da var=value cmdsintaxe garante que IFSsomente seja definido de forma diferente pela duração desse cmdcomando.
Nota do histórico
O readbuiltin foi introduzido pelo shell Bourne e já devia ler palavras , não linhas. Existem algumas diferenças importantes com os shells POSIX modernos.
O shell Bourne readnão suportava uma -ropção (que foi introduzida pelo shell Korn), então não há como desativar o processamento de barra invertida além de pré-processar a entrada com algo parecido sed 's/\\/&&/g'.
O shell Bourne não tinha a noção de duas classes de caracteres (que novamente foram introduzidas pelo ksh). No shell Bourne, todos os caracteres passam pelo mesmo tratamento que os caracteres de espaço em branco do IFS no ksh, ou seja, IFS=: read a b cem uma entrada que foo::barseria atribuída bara $b, e não na sequência vazia.
No shell Bourne, com:
var=value cmd
Se cmdfor um built-in (como readé), varpermanece definido como valueapós a cmdconclusão. Isso é particularmente crítico $IFSporque, no shell Bourne, $IFSé usado para dividir tudo, não apenas as expansões. Além disso, se você remover o caractere de espaço do $IFSshell Bourne, "$@"não funcionará mais.
No shell Bourne, o redirecionamento de um comando composto faz com que ele seja executado em um subshell (nas versões anteriores, até coisas como read var < fileou exec 3< file; read var <&3não funcionavam), portanto, era raro no shell Bourne usar readpara qualquer coisa, exceto a entrada do usuário no terminal (onde esse tratamento de continuação de linha fazia sentido)
Alguns Unices (como HP / UX, também há um util-linux) ainda têm um linecomando para ler uma linha de entrada (que costumava ser um comando UNIX padrão até a Especificação Única do UNIX versão 2 ).
É basicamente o mesmo, head -n 1exceto que ele lê um byte de cada vez para garantir que não leia mais de uma linha. Nesses sistemas, você pode fazer:
line=`line`
Obviamente, isso significa gerar um novo processo, executar um comando e ler sua saída através de um pipe, muito menos eficiente que o ksh IFS= read -r line, mas ainda muito mais intuitivo.