Transformação automática de novas linhas na atribuição de variável de shell

5

Isso veio de um dos meus comentários para essa questão com relação ao uso de bc no shell script. bc coloca quebras de linha em grandes números, por exemplo:

> num=$(echo 6^6^3 | bc)
> echo $num
12041208676482351082020900568572834033367326934574532243581212211450\ 20555710636789704085475234591191603986789604949502079328192358826561\ 895781636115334656050057189523456

Mas observe que eles não são realmente quebra de linha na variável - ou pelo menos não, se for usado sem aspas. Por exemplo, ao brincar com mais pipe na atribuição, por exemplo:

num=$(echo 6^6^3 | bc | perl -pne 's/\\n//g')

Percebi que, embora exista realmente um \n na saída bc , a verificação de echo $num > tmp.txt com hexdump mostra que o \n (ASCII 10) se tornou definitivamente um espaço (ASCII 32) na variável atribuição.

Ou pelo menos, na saída de $num > sem aspas. Por que isso acontece?

Como aponta o fedorqui, se você usa aspas: echo "$num" , você obtém novas linhas novamente. Isso fica evidente ao examinar a diferença entre echo $num > tmp.1 e echo "$num" > tmp.2 com hexdump; o primeiro contém \ (espaço de barra invertida), enquanto o último contém \n (nova linha invertida).

    
por goldilocks 28.02.2014 / 15:39

5 respostas

3

echo coloca um espaço entre cada dois argumentos. O shell considera a nova linha em $num apenas um separador de palavras (assim como o espaço).

lines="a
b
c"
set -x
echo $lines   # several arguments to echo
echo "$lines" # one argument to echo

Veja esta resposta (pelo próprio OP) para uma explicação mais detalhada.

    
por 28.02.2014 / 16:25
7

Nenhuma ideia do porquê de estar lá, mas eis como desativá-lo com a implementação GNU de bc :

echo '6^6^3' | BC_LINE_LENGTH=0 bc

BC_LINE_LENGTH

This should be an integer specifying the number of characters in an output line for numbers. This includes the backslash and newline characters for long numbers. As an extension, the value of zero disables the multi-line feature. Any other value of this variable that is less than 3 sets the line length to 70.

Atualização:

Eu estava confuso sobre esta questão, eu pensei que era sobre as origens do recurso multi-line, parece estranho. De qualquer forma, a resposta real é que, se você não citar a variável, o shell fará dividir palavras antes de passar para echo . A divisão de palavras é o processo em que uma expansão é dividida em 'palavras', dependendo do conteúdo de IFS , essas 'palavras' tornam-se argumentos diferentes. No exemplo da questão, isso cria dois argumentos para ecoar, que echo separa com um espaço (eu sabia disso antes de Stephane comentar, honestamente ...).

Para evitar que isso aconteça, apenas aspas duplas:

num=$(echo '6^6^3' | bc)
echo "$num"

Às vezes, isso é realmente útil como uma maneira de remover caracteres IFS de uma variável (embora printf %s seja mais seguro para sequências arbitrárias). Por exemplo (em bash ):

$ var=$'spaces:    newlines:\n\n\ntabs:\t\t\t end'

$ echo "$var"
spaces:    newlines:


tabs:            end
$ newvar="$(printf '%s ' $var)"
$ echo "$newvar"
spaces: newlines: tabs: end
    
por 28.02.2014 / 15:49
2

Use tr para excluir as continuações de linha e as novas linhas:

$ num=$(echo 6^6^3 | bc)
$ echo "$num"
12041208676482351082020900568572834033367326934574532243581212211450\
20555710636789704085475234591191603986789604949502079328192358826561\
895781636115334656050057189523456
$ num=$(echo "$num" | tr -d '\n\')
$ echo "$num"
12041208676482351082020900568572834033367326934574532243581212211450205557106367897040854752345911916039867896049495020793281923588265618957816361153346560500571895 23456
    
por 28.02.2014 / 15:48
2

Na man page do bc, sob expressões, explica o limite.

Since numbers are of arbitrary precision, some numbers may not be printable on a single output line. These long numbers will be split across lines using the "\" as the last character on a line. The maximum number of characters printed per line is 70.

    
por 28.02.2014 / 16:21
0

De acordo com o comentário de Stephane Chazelas , a questão não é exatamente que o espaço é interpretado como uma nova linha. Ele explicou este outro lugar em relação a um (conceitual) operador "split + glob", e embora eu não siga totalmente a semântica disso, aqui está o que está acontecendo:

  • A variável sem aspas é dividida no espaço em branco pelo shell e estas são passadas como argumentos para echo . A divisão remove o espaço em branco. Isso pode ser um pouco confuso, já que "barra invertida-nova linha" parece ser uma sequência de escape, mas não é equivalente a x=\\n , que é equivalente a x=$'\2' , 2 sendo octal para uma nova linha real - em oposição a a seqüência de escape \n , que resulta na string contendo um literal "\ n" (barra invertida-n), que não seria dividida.

  • echo , em seguida, exibe seus argumentos separados por um espaço.

Para reiterar:

str1=hello\nworld
str2=$'hello2world'

O shell não irá dividir a primeira unquoted uma vez que não contém realmente qualquer espaço em branco - que contém a seqüência de escape \n , enquanto que no segundo caso, será dividido unquoted sobre o caráter real de nova linha (que é espaço em branco).

    
por 28.02.2014 / 17:01