Como posso ter uma nova linha em uma string em sh?

338

este

STR="Hello\nWorld"
echo $STR

produz como saída

Hello\nWorld

ao invés de

Hello
World

O que devo fazer para ter uma nova linha em uma string?

Nota: Esta pergunta não é sobre eco . Estou ciente echo -e, mas estou procurando uma solução que permita passar uma string (que inclui uma nova linha) como argumento para outros comandos que não têm uma opção semelhante para interpretar \ncomo novas linhas.

Juan A. Navarro
fonte

Respostas:

353

A solução é usar $'string', por exemplo:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

Aqui está um trecho da página de manual do Bash:

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.
anfetamaquina
fonte
74
Isso é válido, mas não POSIX sh.
jmanning2k
7
+ 1-apenas uma observação: export varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev
3
De fato, isso funciona com cadeias literais como o OP solicitou, mas falha assim que as variáveis ​​são substituídas: STR=$"Hello\nWorld"simplesmente imprime Hello\nWorld.
ssc
3
@ssc aspas simples, não double
miken32
7
@ssc não há variáveis ​​no seu exemplo. Além disso, esse método requer aspas simples. Finalmente, para incluir variáveis ​​interpoladas, você teria que concatenar seqüências de citação dupla e citação única. por exemplo H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}", repetirá "Hello" e "World" em linhas separadas
JDS
166

O eco é tão noventa e tão cheio de perigos que seu uso deve resultar em lixões não inferiores a 4 GB. Sério, os problemas da echo foram a razão pela qual o processo de padronização do Unix finalmente inventou o printfutilitário, eliminando todos os problemas.

Então, para obter uma nova linha em uma string:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

Lá! Sem SYSV vs BSD eco loucura, tudo é impresso com clareza e suporte totalmente portátil para seqüências de escape C. Todo mundo, por favor, use printfagora e nunca olhe para trás.

Jens
fonte
3
Obrigado por esta resposta, acho que fornece realmente bons conselhos. Observe, no entanto, que minha pergunta original não era realmente sobre como obter eco para imprimir novas linhas, mas como obter uma nova linha em uma variável de shell. Você mostra como fazer isso usando printf também, mas acho que a solução da anfetamaquina usando $'string'é ainda mais limpa.
Juan A. Navarro
11
Apenas para uma definição deficiente de 'limpo'. A $'foo'sintaxe não é válida para POSIX a partir de 2013. Muitos shells reclamam.
Jens
5
BAR=$(printf "hello\nworld\n")não imprime a fuga \ n para mim
Jonny
3
@ Jonny Não deveria; a especificação do shell para substituição de comando diz que uma ou mais novas linhas no final da saída são excluídas. Sei que isso é inesperado para o meu exemplo e o editei para incluir uma nova linha no printf.
Jens
5
@ismael Não, $()é especificado pelo POSIX como removendo sequências de um ou mais caracteres <newline> no final da substituição .
Jens
61

O que fiz com base nas outras respostas foi

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight
zvezda
fonte
30

O problema não está no shell. Na verdade, o problema está no echopróprio comando e na falta de aspas duplas em torno da interpolação da variável. Você pode tentar usar, echo -emas isso não é suportado em todas as plataformas, e um dos motivos printfagora é recomendado para portabilidade.

Você também pode tentar inserir a nova linha diretamente no seu script de shell (se um script é o que você está escrevendo), para que pareça ...

#!/bin/sh
echo "Hello
World"
#EOF

ou equivalente

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
Ritmo
fonte
23

Acho a -ebandeira elegante e direta

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

Se a string é a saída de outro comando, eu apenas uso aspas

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
Simon Ndunda
fonte
2
Isso já foi mencionado em outras respostas. Além disso, embora já estava lá, eu apenas editado a questão de frisar o fato de que esta pergunta é não sobre echo.
Juan A. Navarro
Esta não é uma resposta para a pergunta
Rohit Gupta
Isso responde totalmente à pergunta que eu estava procurando! Obrigado!
Kieveli 17/11/2015
11
Funcionou bem para mim aqui. Eu costumava criar um arquivo com base nesse eco e ele gerava novas linhas na saída corretamente.
Mateus Leon
echo -eé notório por seus problemas de portabilidade. Esta é menos uma situação do Velho Oeste agora do que antes do POSIX, mas ainda não há razão para preferir echo -e. Veja também stackoverflow.com/questions/11530203/…
tripleee
21
  1. A única alternativa simples é realmente digitar uma nova linha na variável:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line
    

    Sim, isso significa escrever Enteronde necessário no código.

  2. Existem vários equivalentes a um new linepersonagem.

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.
    

    Mas tudo isso exige "uma interpretação" de alguma ferramenta ( POSIX printf ):

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    E, portanto, a ferramenta é necessária para criar uma string com uma nova linha:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. Em alguns shells, a sequência $'é uma expansão especial do shell. Conhecido por funcionar em ksh93, bash e zsh:

    $ STR=$'new\nline'
  4. Obviamente, soluções mais complexas também são possíveis:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    Ou

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

fonte
6

Um $ antes das aspas simples '... \ n ...' da seguinte maneira, no entanto, aspas duplas não funcionam.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
caot
fonte
4

Não sou especialista no bash, mas este funcionou para mim:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

Achei isso mais fácil de formatar os textos.

Jason
fonte
2
Bom e velho heredoc . E provavelmente também é compatível com POSIX!
PYB
As aspas em torno $NEWSTRde echo "$NEWSTR"são importantes aqui
shadi
0

No meu sistema (Ubuntu 17.10), seu exemplo funciona como desejado, quando digitado na linha de comando (para sh) e quando executado como um shscript:

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

Acho que isso responde à sua pergunta: simplesmente funciona. (Não tentei descobrir detalhes como em que momento exatamente \nocorre a substituição do caractere de nova linha sh).

No entanto, notei que esse mesmo script se comportaria de maneira diferente quando executado combash e seria impresso Hello\nWorld:

[bash bash test-str.sh
Hello\nWorld

Consegui obter a saída desejada com basho seguinte:

[bash STR="Hello
> World"
[bash echo "$STR"

Observe as aspas duplas ao redor $STR. Isso se comporta de forma idêntica se salvo e executado como um bashscript.

O seguinte também fornece a saída desejada:

[bash echo "Hello
> World"
Alexey
fonte
0

Aqueles exigentes que precisam apenas da nova linha e desprezam o código multilinha que quebra o recuo, poderiam fazer:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

O Bash (e provavelmente outros shells) devoram todas as novas linhas à direita após a substituição do comando; portanto, você precisa finalizar a printfstring com um caractere que não seja da nova linha e excluí-la posteriormente. Isso também pode facilmente se tornar um oneliner.

IFS="$(printf '\nx')" IFS="${IFS%x}"

Sei que são duas ações em vez de uma, mas meu TOC de recuo e portabilidade está em paz agora: eu originalmente desenvolvi isso para poder dividir a saída separada por nova linha e acabei usando uma modificação usada \rcomo caractere final. Isso faz com que a divisão da nova linha funcione mesmo para a saída dos que termina com \r\n.

IFS="$(printf '\n\r')"
tlwhitec
fonte
0

Eu não estava realmente feliz com nenhuma das opções aqui. Isto é o que funcionou para mim.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
Adam K Dean
fonte
Essa é uma maneira muito tediosa de escrever str=$(printf '%s\n' "first line" "another line" "and another line")(o shell irá convenientemente (ou não) aparar qualquer nova linha final da substituição de comando). Várias respostas aqui já são promovidas printfde várias formas, por isso não tenho certeza se isso agrega valor como resposta separada.
tripleee
É para strings curtas, mas se você quiser manter as coisas organizadas e cada linha tiver 60 caracteres, é uma maneira organizada de fazê-lo. Dado que é a solução com a qual eu acabei optando, ela acrescenta algo, se não for para você, e para mim mesmo no futuro quando, sem dúvida, retornar.
Adam K Dean
11
Mesmo para seqüências longas, eu prefiro printf '%s\n' "first line" \ (nova linha, recuo) 'second line'etc. Consulte também printf -v strpara atribuir a uma variável sem gerar um subshell.
Tripleee 28/03/19