Nova linha nas variáveis bash

6

Estou tentando armazenar várias linhas em uma variável bash, mas parece que não funciona.

Por exemplo, se eu listar /bin um arquivo por linha e armazená-lo em $LS , então passo $LS como stdin para wc , ele sempre retorna 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

Se eu tentar enviar para a tela, obtenho vários resultados: echo imprime todas as linhas em uma única linha, enquanto printf imprime apenas a primeira linha:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

Então, uma variável bash pode conter várias linhas?

    
por Wizard79 09.07.2015 / 14:28

3 respostas

12

Você precisa fazer uma citação dupla (e você deve variáveis de aspas duplas maioria dos casos):

echo "$LS"

Mas não use echo para imprimir conteúdo de variáveis, usando printf :

printf '%s\n' "$LS"
    
por 09.07.2015 / 14:32
6

As novas linhas estão na variável. LS=$(ls -1) define a variável LS como a saída de ls -1 (que produz a mesma saída que ls , a propósito, exceto quando a saída vai para um terminal), menos novas linhas à direita.

O problema é que você está removendo as novas linhas quando imprime o valor. Em um script de shell, $LS não significa “o valor da variável LS ”, isso significa “pegue o valor de LS , divida-o em palavras de acordo com IFS e interprete cada palavra como um padrão glob " Para obter o valor de LS , você precisa escrever "$LS" ou, mais geralmente, colocar $LS entre aspas duplas.

echo "$LS" imprime o valor de LS , exceto em alguns shells que interpretam caracteres de barra invertida e com exceção de alguns valores que começam com - .

printf "$LS" imprime o valor de LS desde que não contenha nenhum caractere de porcentagem ou barra invertida e (com a maioria das implementações) não comece com - .

Para imprimir exatamente o valor de LS , use printf %s "$LS" . Se você quiser uma nova linha no final, use printf '%s\n' "$LS" .

Observe que $(ls) não é, em geral, a lista de arquivos no diretório atual. Isso só funciona quando você tem nomes de arquivo suficientemente controlados. Para obter a lista de nomes de arquivos (exceto arquivos de ponto), você precisa usar um caractere curinga: * . O resultado é uma lista de strings, não uma string, então você não pode atribuí-lo a uma variável de string; você pode usar uma variável de array files=(*) em shells que os suportam (ksh93, bash, zsh).

Para mais informações, consulte Por que meu script de shell sufoca em espaços em branco ou outros caracteres especiais?

    
por 10.07.2015 / 01:41
0

O acima mencionado

printf '%s\n' "$LS"

é a única solução correta.

Deixe-me demonstrar que as outras soluções propostas, ambas com echo e printf , simplesmente não funciona corretamente:

$ mkdir t
$ cd t
$ touch \n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \n
$ cd ..
$ rmdir t
$

(Também é possível testar com V=$(printf 'line0:\n\n\nline1.\n') printf '%s\n' "$V" e variações.)

Considerando que \n nos nomes de arquivos pode não ser tão comum, armazene git diff em uma variável e suas chances de encontrando literal \n aumentar dramaticamente.

    
por 05.11.2015 / 15:19