Sequência de várias linhas com espaço extra (recuo preservado)

410

Quero escrever alguns textos predefinidos em um arquivo com o seguinte:

text="this is line one\n
this is line two\n
this is line three"

echo -e $text > filename

Estou esperando algo assim:

this is line one
this is line two
this is line three

Mas entendi:

this is line one
 this is line two
 this is line three

Tenho certeza de que não há espaço após cada um \n, mas como sai o espaço extra?

cizixs
fonte
2
Não tenho certeza, mas .. como se você digitasse text="this is line one\nthis is line two\nthis is line three"a mesma linha? (sem entrar)
Yohanes Khosiawan 许先汉 29/05
4
Remova o \nem cada linha, você já atingiu a nova linha para ir para a nova linha
Mark Setchell
Você já deu \n. Então, por que você coloca a próxima linha em nova linha? Simplesmentetext="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi
1
A remoção do \nfinal de cada linha faz com que a saída seja executada em conjunto em uma única linha.
Jonathan Hartley
18
Aha: Colocar aspas duplas em volta da "$text"linha de eco é crucial. Sem eles, nenhuma das novas linhas (literais e '\ n') funciona. Com eles, todos eles fazem.
Jonathan Hartley

Respostas:

663

O Heredoc parece mais conveniente para esse fim. É usado para enviar vários comandos para um programa interpretador de comandos como ex ou cat

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

A sequência depois <<indica onde parar.

Para enviar essas linhas para um arquivo, use:

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

Você também pode armazenar essas linhas em uma variável:

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

Isso armazena as linhas na variável denominada VAR.

Ao imprimir, lembre-se das aspas ao redor da variável, caso contrário você não verá os caracteres de nova linha.

echo "$VAR"

Ainda melhor, você pode usar o recuo para destacar mais o seu código. Desta vez, basta adicionar um -depois <<para impedir que as guias apareçam.

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

Mas você deve usar tabulações, não espaços, para o recuo no seu código.

garoto novo
fonte
38
por que você tem << - em vez de << na primeira linha do 2º e 5º bloco de código? faz - faz algo especial?
lohfu
35
@ sup3rman '-' Ignora as guias principais. Veja stackoverflow.com/questions/2500436/…
kervin
8
como posso fazer a leitura sair com o status 0? Parece que ele não consegue encontrar o final da linha (já que é -d '') e sai com 1, o que não ajuda quando você está no script.
Misha Slyusarev
7
@MishaSlyusarev usar -d '\ 0' e adicionar um \ 0 no final da sua última linha
Lars Schneider
11
Usar a -dopção in read -r -d '' VARIABLE <<- EOMnão funcionou para mim.
Dron22
186

Se você está tentando inserir a string em uma variável, outra maneira fácil é algo como isto:

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

Se você recuar sua cadeia de caracteres com tabulações (ou seja, '\ t'), a indentação será removida. Se você recuar com espaços, o recuo será deixado em.

NOTA: é significativo que o último parêntese de fechamento é em outra linha. O ENDtexto deve aparecer em uma linha por si só.

Andrew Miner
fonte
1
Isso é significativo? Funciona no meu mac com )a mesma linha. Eu acho que é porque o que vai entre $(e )será executado em seu próprio universo, e o comando real não verá ')' de qualquer maneira. Gostaria de saber se funciona para outros também.
deej
É possível que conchas diferentes interpretem as coisas de maneira diferente. Na documentação do bash , parece não dizer nada de um jeito ou de outro, mas todos os exemplos têm isso em sua própria linha.
Andrew Miner
O mais engraçado é que achei a resposta ao tentar definir o valor da variável USAGE também. Portanto, sua resposta corresponde exatamente. :) #
22818 Bunyk
1
@deej A partir da especificação POSIX : O documento aqui… continua até que exista uma linha que contenha apenas o delimitador e um <newline> #
Fornost
1
@Fornost Não contradiz o que eu disse #
1212 deej
84

echoadiciona espaços entre os argumentos passados ​​para ele. $textestá sujeito a expansão variável e divisão de palavras, portanto, seu echocomando é equivalente a:

echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n"  ...

Você pode ver que um espaço será adicionado antes de "this". Você pode remover os caracteres da nova linha e citar $textpara preservar as novas linhas:

text="this is line one
this is line two
this is line three"

echo "$text" > filename

Ou você pode usar printf, que é mais robusto e portátil do que echo:

printf "%s\n" "this is line one" "this is line two" "this is line three" > filename

Em bash, que suporta expansão de chaves, você pode até fazer:

printf "%s\n" "this is line "{one,two,three} > filename
Josh Jolly
fonte
1
Obrigado por explicar por que o mod vê espaço extra ao usar o comando "echo". Esta resposta também está funcionando bem ao ser usada no script bash (em oposição à sessão de shell interativa). Sinto que esta é a resposta real e deve ser uma resposta aceita.
Onelaview 18/10/19
1
Essa resposta deve ser aceita. Todas as outras respostas forneceram muitas outras maneiras interessantes de resolver a dor do OP, mas tecnicamente a pergunta era "como sai o espaço extra" e ninguém abordou isso :-) !!! Obrigado, Josh por fazer -1 mistério!
Dmitry Shevkoplyas
45

em um script bash, o seguinte funciona:

#!/bin/sh

text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename

alternativamente:

text="this is line one
this is line two
this is line three"
echo "$text" > filename

nome do arquivo cat fornece:

this is line one
this is line two
this is line three
Chris Maes
fonte
Parece que o truque alternativo funciona enquanto Shell Scripts, mas não parece funcionar para o bash.
Macindows 27/11/19
3
@ Macindows Parece que esqueci a -eopção echo. Por favor, tente novamente.
Chris Maes
41

Encontrei mais soluções desde que queria que cada linha fosse recuada corretamente:

  1. Você pode usar echo:

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename

    Não funciona se você colocar um "\n"pouco antes \no final de uma linha.

  2. Como alternativa, você pode usar printfpara uma melhor portabilidade (eu tive muitos problemas com echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. Ainda outra solução pode ser:

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this is line three\n"
    printf "%b" "$text" > filename

    ou

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
  4. Outra solução é alcançada misturando printfe sed.

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi

    Não é fácil refatorar o código formatado dessa maneira, pois você codifica o nível de indentação no código.

  5. É possível usar uma função auxiliar e alguns truques de substituição de variáveis:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"
Mateusz Piotrowski
fonte
3b é a minha solução preferida quando se deseja preservar a indentação de código e a indentação de cadeias independentemente, pelo menos quando não posso usar 2 na atribuição de variáveis ​​e é mais legível que 3a. Quando não me importo, apenas mantenho a string citada aberta nas várias linhas.
Pysis
Very nice resposta especialmente 3b
Sumit Trehan
"Não funciona se você colocar" \ n "logo antes \ no final de uma linha." - é uma ótima dica ao usar eco.
Noam Manos
6

A seguir, é minha maneira preferida de atribuir uma string de várias linhas a uma variável (acho legal).

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

O número de sublinhados é o mesmo (aqui 80) nos dois casos.

Frank-Rene Schäfer
fonte
0

Apenas para mencionar uma concatenação de uma linha simples, pois às vezes pode ser útil.

# for bash

v=" guga "$'\n'"   puga "

# Just for an example.
v2="bar "$'\n'"   foo "$'\n'"$v"

# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar ${n}   foo ${n}$v"

echo "$v3" 

Você terá algo parecido com isto

Barra 
   foo 
 guga 
   puga 

Todos os espaços em branco iniciais e finais serão preservados

echo "$v3" > filename
it3xl
fonte
0

Existem várias maneiras de fazer isso. Para mim, canalizar a string recuada para o sed funciona muito bem.

printf_strip_indent() {
   printf "%s" "$1" | sed "s/^\s*//g" 
}

printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"

Esta resposta foi baseada na resposta de Mateusz Piotrowski , mas refinou um pouco.

Beni Trainor
fonte
-1

funcionará se você o colocar como abaixo:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
luk
fonte