Printf imprimindo apenas uma única linha

3

Estou tentando obter a saída de env em uma variável do shell e imprimi-la.

#!/bin/sh
ENV_BEFORE=$(env)
printf $ENV_BEFORE

Como resultado, uma única variável da saída env é impressa.

Ao usar echo em vez de printf , toda a saída é impressa, mas sem as novas linhas.

O que estou perdendo aqui?

    
por TheMeaningfulEngineer 14.03.2016 / 13:08

2 respostas

8

O problema é que você não está citando a variável $ENV . Conforme explicado em man bash :

Enclosing characters in double quotes preserves the literal value of all characters within the quotes, with the exception of $, ', \, and, when history expansion is enabled, !. The characters $ and ' retain their special meaning within double quotes. The backslash retains its special meaning only when followed by one of the following characters: $, ', ", \, or .

Portanto, incluir uma sequência como \n nas aspas duplas preserva seu significado. É por isso que, quando não é cotado, \n é apenas um% normaln:

$ printf \n
n$

Enquanto, quando citado:

$ printf "\n"

$

Uma variável sem nome no bash invoca o operador split + glob. Isso significa que a variável é dividida em espaço em branco (ou qualquer que seja a variável especial $IFS definida) e cada palavra resultante é usada como glob (expandirá para corresponder a qualquer nome de arquivo correspondente). Seu problema é com a parte "dividida" disso.

Para ilustrar, vamos pegar uma variável multilinha mais simples:

$ var=$(printf "foo\nbar\n")

Agora, usando o recurso de depuração set -x do shell, você pode ver exatamente o que está acontecendo:

$ echo $var
+ echo foo bar
foo bar

$ echo "$var"
+ echo 'foo
bar'
foo
bar

Como você pode ver acima, echo $var (sem aspas) apresenta $var para dividir + glob. Isso resulta em duas sequências separadas, foo e bar . A nova linha foi comida pela divisão split + glob. Quando a variável foi citada, não foi submetida a split + glob, a nova linha foi mantida e, por ser citada, também é interpretada corretamente e impressa.

O próximo problema é que printf não é como echo . Ele não imprime apenas qualquer coisa que você der, ele espera uma string de formato. Por exemplo, printf "color:%s" "green" imprimirá color:green porque o %s será substituído por green .

Ele também ignora qualquer entrada que não possa caber na string de formato que foi fornecida. Portanto, se você executar printf foo bar , printf tratará foo como sua string de formato e bar como a variável que deve formatar com ela. Como não há %s ou equivalente a ser substituído por bar , bar é ignorado e foo é impresso sozinho:

$ printf $var
+ printf foo bar
foo

Foi o que aconteceu quando você executou printf $ENV_BEFORE . Como a variável não foi citada, a divisão glob substituiu efetivamente as novas linhas por espaços, e printf apenas imprimiu a primeira "palavra" que viu.

Para fazer isso corretamente, use strings de formato e sempre cite suas variáveis:

printf '%s\n' "$ENV_BEFORE"
    
por 14.03.2016 / 13:58
4

printf (ao contrário de echo ) não imprime nova linha por padrão, você precisa explicitamente informar:

printf "${ENV_BEFORE}\n"

Ou melhor:

printf '%s\n' "$ENV_BEFORE"

Após @don_crissti corretamente apontado no comentário e depois que eu li a pergunta novamente, eu acho que o seu caso é diferente do anterior.

No seu caso, o problema é que você não citou a variável $ENV_BEFORE .

Aqui estão os comportamentos de printf e echo se você não citar uma variável:

  • printf :

    1. Sem aspas e qualquer especificador de formato, só será impressa a primeira palavra

    2. Com aspas e sem qualquer especificador de formato, seria considerado que qualquer coisa na expansão tivesse % como especificador de formato e poderia sufocar

    3. Sem aspas e com um especificador de formato, ele estará sujeito à divisão de palavras e à expansão do nome do caminho, portanto, resultaria em resultados inesperados, pois qualquer valor de IFS se tornaria um espaço na saída

    4. Com o especificador de formato e cotação, você obteria o resultado desejado, ou seja, nenhuma expansão de palavras e de nomes de caminho será executada na expansão.

  • echo :

    1. Sem aspas, a expansão da variável estará sujeita à divisão de palavras e à expansão do nome do caminho, portanto, você não obteria a saída desejada

    2. Com quote , a expansão da variável não estará sujeita à divisão de palavras e à expansão do nome de caminho e, portanto, você obterá a saída desejada

por 14.03.2016 / 13:10