novas linhas e variável bash

0

Eu tenho um arquivo de texto (ou saída de pipe, não importa aqui)

memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
memcached.cmd_set 17641364
memcached.cmd_flush 0

Se eu usar o comando cat test.txt | while read i; do echo $i; done , ele produzirá uma saída bastante esperada:

memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
etc

Mas se eu fizer um loop usando for i in $(cat test.txt); do echo $i; done , vejo algo diferente:

memcached.uptime
1061374
memcached.curr_connections
480
memcached.cmd_get
478962548
etc

A pergunta é: POR QUE ???

    
por Putnik 07.12.2016 / 22:30

2 respostas

1

A resposta é que $(command) se expande para a saída bruta do comando, na qual seu shell executará a separação usual de palavras. Essa separação consiste em qualquer espaço em branco sendo considerado um separador de palavras.

Você também está fazendo duas coisas diferentes com o texto; em um você está analisando através de read (que funciona na entrada line e não em palavra , e no outro, você está iterando sobre a saída de $(cat) em um loop for . Você provavelmente poderia obter resultados semelhantes com IFS='\n' for i in $(cat test.txt); do echo "$i"; done .

    
por 07.12.2016 / 22:34
4

Em:

cat test.txt | while read i; do echo $i; done

Você conseguiu encaixar algumas práticas ruins de script de shell:

Embora eu deva ter mencionado pela primeira vez Por que usar um loop de shell para processar texto é considerado uma prática ruim? .

Tente, por exemplo, em uma entrada como:

-n
/*/*/*/../../../*/*/*
  foo\
bar

Se você realmente precisou usar um loop de shell, provavelmente teria que ser algo como:

{
while IFS= read <&3 -r i; do
  printf '%s\n' "$i" || exit
done
[ -z "$i" ] || printf %s "$i" || exit
} 3< test.txt

Em

for i in $(cat test.txt); do echo $i; done

Isso substitui uma prática ruim por outra. Aqui, você tem uma boa razão para deixar $(cat test.txt) sem aspas: você quer a parte dividida do operador split + glob, mas esqueceu de especificar o que deseja dividir e desativar a parte glob.

IFS='
' # split on newline only. The default value of $IFS
  # contains space, tab and newline which explains why you see
  # one word per line
set -o noglob # disable glob
for i in $(cat test.txt); do
  printf '%s\n' "$i" || exit
done

Observe que ainda seria possível pular as linhas vazias, e que lê e armazena o conteúdo do arquivo na memória (várias vezes) antes de iniciar o loop.

    
por 07.12.2016 / 22:53