Como posso fazer com que o operador do bash backtick mantenha novas linhas na saída?


36

Existe uma maneira de fazer com que o bash não coma novas linhas na substituição do backtick?

Por exemplo:

var=`echo line one && echo line two`
echo $var

line one line two

O que eu quero é

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

Respostas:


54

Não é um problema com a substituição de backticks, mas com echo; você precisa citar a variável para que os caracteres de controle funcionem:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

Então, o bash remove novas linhas enquanto expande uma variável na garantia de comando? E esse comportamento pode ser desativado citando a variável?
slowpoison

7
bashexpansão executa parâmetros antes de executar um comando, citando é necessário informar bashque você deseja imprimir uma única seqüência e não 4 palavras ( line, one, linee two). Tente: echo a <many spaces> be echo "a <many spaces> b". Veja também esta resposta .
Cyrus

5

Mesmo que o @cyrus esteja correto - ele não responde a toda a pergunta e não há explicação do que está acontecendo.

Então, vamos percorrer isso.

Novas linhas na string

Primeiro, determine a sequência de bytes esperada:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Agora, use Substituição de Comando (Seção 3.5.4) para tentar capturar esta sequência de bytes:

$ x=$( echo a; echo b; )

Em seguida, faça um eco simples para verificar a sequência de bytes:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Parece que a primeira nova linha foi substituída por um espaço e a segunda nova linha foi deixada intacta. Mas por que ?

Vamos analisar o que realmente está acontecendo aqui:

Primeiro, o bash fará a expansão dos parâmetros do shell (Seção 3.5.3)

O caractere '$' introduz expansão de parâmetro, substituição de comando ou expansão aritmética. O nome ou símbolo do parâmetro a ser expandido pode ser colocado entre chaves, que são opcionais, mas servem para proteger a variável a ser expandida dos caracteres imediatamente a seguir, que podem ser interpretados como parte do nome.

Então o bash fará a Divisão de Palavras (Seção 3.5.7)

O shell varre os resultados de expansão de parâmetro, substituição de comando e expansão aritmética que não ocorreram entre aspas duplas para divisão de palavras.

O shell trata cada caractere do $ IFS como um delimitador e divide os resultados das outras expansões em palavras nesses caracteres. Se o IFS não estiver definido ou seu valor for exatamente, ...

Em seguida, o bash o tratará como um comando simples (Seção 3.2.1)

Um comando simples é o tipo de comando encontrado com mais frequência. É apenas uma sequência de palavras separadas por espaços em branco, terminadas por um dos operadores de controle do shell (consulte Definições). A primeira palavra geralmente especifica um comando a ser executado, com o restante das palavras sendo os argumentos desse comando.

A definição de espaços em branco (Seção 2 - Definições)

em branco Um caractere de espaço ou tabulação.

Por fim, o bash chama o comando interno echo (Seção 4.2 - Comandos internos do bash)

... Gera os argumentos, separados por espaços, terminados com uma nova linha. ...

Então, para resumir, as novas linhas são removidas pela Divisão de Palavras e, em seguida, o echo obtém 2 argumentos, "a" e "b" e os gera separados por espaços e terminando com uma nova linha.

Fazendo o que o @cyrus sugeriu (e suprima a nova linha do eco com -n), o resultado é melhor:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Novas linhas no final da string

Ainda não é perfeito, a nova linha se foi. Olhando mais de perto a Substituição de Comando (Seção 3.5.4) :

O Bash executa a expansão executando o comando e substituindo a substituição do comando pela saída padrão do comando, com as novas linhas finais excluídas.

Agora que está claro por que a nova linha vai embora, o bash pode ser enganado para mantê-la. Para fazer isso, adicione uma sequência adicional ao final e remova-a ao usar a variável:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Adicione parte extra ao final da saída e cite variáveis:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46

0

Eu colocaria minha pequena adição em um comentário sobre a resposta do cYrus, mas ainda não posso comentar sobre as respostas que acho. Então, duplicarei parte da resposta de CYrus por completude.

O que acontece na primeira linha do seu código é que, de fato, a substituição de backticks consome as novas linhas finais da saída do comando substituído, conforme documentado . Mas não consome a nova linha entre suas duas linhas.

O que acontece na segunda linha de código é que o eco é executado com dois argumentos, a primeira e a segunda linha. A citação da variável expandida corrigirá isso: echo "$var"Isso preservará a nova linha entre a linha um e a linha dois. E echoadicionará uma nova linha à direita por padrão, para que tudo esteja bem.

Agora, se você deseja preservar todas as novas linhas à direita na saída de alguma substituição de comando, uma solução alternativa é adicionar outra linha à saída, que você pode remover após a substituição do comando, veja aqui .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.