Como posso fazer o operador backtick bash manter novas linhas na saída?

36

Existe uma maneira de fazer o bash não comer novas linhas na substituição de 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
    
por slowpoison 23.03.2012 / 00:27

3 respostas

52

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

$ var='echo line one && echo line two'
$ echo "$var"
line one
line two
    
por 23.03.2012 / 00:38
5

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

Então, vamos passear por ele.

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 para 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.

Portanto, parece que a primeira nova linha foi substituída por um espaço, e a segunda nova linha foi deixada intacta. Mas por quê?

Vamos ver o que realmente está acontecendo aqui:

Primeiro, o bash fará Expansão do Parâmetro da Shell (Seção 3.5.3 )

The ‘$’ character introduces parameter expansion, command substitution, or arithmetic expansion. The parameter name or symbol to be expanded may be enclosed in braces, which are optional but serve to protect the variable to be expanded from characters immediately following it which could be interpreted as part of the name.

Em seguida, o bash fará o Separação de palavras (Seção 3.5.7)

The shell scans the results of parameter expansion, command substitution, and arithmetic expansion that did not occur within double quotes for word splitting.

The shell treats each character of $IFS as a delimiter, and splits the results of the other expansions into words on these characters. If IFS is unset, or its value is exactly , ...

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

A simple command is the kind of command encountered most often. It’s just a sequence of words separated by blanks, terminated by one of the shell’s control operators (see Definitions). The first word generally specifies a command to be executed, with the rest of the words being that command’s arguments.

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

blank A space or tab character.

Finalmente, o bash invoca o echo (Seção 4.2 - Comandos Bash Builtin) comando interno

... Output the args, separated by spaces, terminated with a newline. ...

Para resumir, as novas linhas são removidas pelo Word Splitting e, em seguida, o echo obtém 2 args, "a" e "b" e, em seguida, envia-as separadas por espaços e terminando com uma nova linha.

Fazendo o que @cyrus sugeriu (e suprime a nova linha do echo 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 foi embora. Olhando mais de perto Substituição de Comando (Seção 3.5.4) :

Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

Agora que está claro por que a nova linha vai embora, o bash pode ser enganado em mantê-la. Para fazer isso, adicione uma string 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 uma peça extra ao final da saída e cite as 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
    
por 17.10.2014 / 19:39
0

Eu teria colocado minha pequena adição em um comentário sobre a resposta do cYrus, mas ainda não posso comentar as respostas que penso. Então vou duplicar parte do cyrus sua resposta para completar.

O que acontece na primeira linha do seu código é que, de fato, a substituição de backticks come as novas linhas finais da saída do comando substituído, como documentado . Mas não come 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 sua linha um e a linha dois. E echo adicionará uma nova linha à direita para que tudo esteja bem.

Agora, se você quiser preservar todas as novas linhas finais 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, consulte aqui .

    
por 21.11.2013 / 10:56

Tags