Nova linha nas variáveis ​​bash

9

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

Por exemplo, se eu listar /binum arquivo por linha e armazená-lo $LS, passo $LScomo stdin para wc, ele sempre retornará 1:

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

Se eu tentar imprimir na tela, obtenho vários resultados: echoimprime todas as linhas em uma única linha, enquanto printfimprime 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?

Wizard79
fonte

Respostas:

15

Você precisa aspas duplas (e na maioria dos casos você deve aspas duplas ):

echo "$LS"

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

printf '%s\n' "$LS"
cuonglm
fonte
1
Apenas para enfatizar a resposta geral: printf espera que uma string de formato seja seu primeiro parâmetro, e é por isso que eles não viram o que esperavam. Se eles querem ver novas linhas na saída de printf, arbitrariamente divididas em espaços, o printf "%s\n" $LSfaria.
Jeff Schaller
LS% deve ser de US $ LS, btw (eu não poderia fazer uma pequena edição tal)
Jeff Schaller
Obrigado! Obviamente, também funciona com wce com outros comandos:wc -l <<< "$LS"
Wizard79 /
1
@ JeffSchaller: Não, você não deve desmarcar variáveis ​​neste caso, apenas use printf '%s\n' "$LS"se você quiser ver uma nova linha. Isso é digitação incorreta, consertou!
cuonglm
Por que você não deve usar echo para imprimir variáveis?
123
9

As novas linhas estão na variável LS=$(ls -1)define a variável LSpara 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 finais.

O problema é que você está removendo as novas linhas quando imprime o valor. Em um script de shell, $LSnão significa "o valor da variável LS", significa "tome o valor deLS , divida-o em palavras de acordo com IFSe interprete cada palavra como um padrão global”. Para obter o valor de LS, você precisa escrever "$LS", ou mais geralmente, colocar $LSentre aspas duplas.

echo "$LS"imprime o valor de LS, exceto em algumas conchas que interpretam caracteres de barra invertida e, exceto por 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 começa -.

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

Observe que $(ls)geralmente não é a lista de arquivos no diretório atual. Isso funciona apenas quando você tem nomes de arquivos domados o suficiente. Para obter a lista de nomes de arquivos (exceto arquivos de ponto), você precisa usar um curinga: *. O resultado é uma lista de cadeias, não uma cadeia, portanto, você não pode atribuí-la a uma variável de cadeia; você pode usar uma variável de matriz files=(*)em shells que os suportam (ksh93, bash, zsh).

Para obter mais informações, consulte Por que meu script de shell é bloqueado em espaço em branco ou em outros caracteres especiais?

Gilles 'SO- parar de ser mau'
fonte
printf "$ LS" seria também sucumbem à análise barra invertida, semelhante ao eco
CNST
0

O mencionado acima

printf '%s\n' "$LS"

é a única solução correta.

Deixe-me demonstrar que as outras soluções propostas, com echoe printf, simplesmente não funcionam 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.)

Enquanto \nos nomes dos arquivos podem não ser tão comuns, armazene-os git diffem uma variável e suas chances de encontrar literal \naumentam drasticamente.

cnst
fonte
Note que kshe zshtambém suporta print -r -- "$LS"e zshtambém possui echo -E - $LS. cshno echo $LS:qentanto, isso não gera o caractere de nova linha se $ LS estiver vazio, e obter um valor de múltiplas linhas em $ LS em primeiro lugar é bastante complicado no csh.
Stéphane Chazelas 5/11