Stringa multilinea con spazio aggiuntivo (rientro conservato)


410

Voglio scrivere alcuni testi predefiniti in un file con il seguente:

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

echo -e $text > filename

Mi aspetto qualcosa del genere:

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

Ma ottenuto questo:

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

Sono sicuro che non c'è spazio dopo ciascuno \n, ma come viene fuori lo spazio extra?


2
Non sono sicuro ma .. come se avessi appena digitato text="this is line one\nthis is line two\nthis is line three"la stessa riga ..? (senza entrare)
Yohanes Khosiawan 许先汉

4
Rimuovi \nsu ogni riga, hai già colpito Newline per passare alla nuova riga
Mark Setchell,

Hai già dato \n. Quindi perché hai messo la riga successiva in una nuova riga? Semplicementetext="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi

1
La rimozione \ndi alla fine di ogni riga fa sì che l'output venga eseguito insieme su un'unica riga.
Jonathan Hartley,

18
Aha: Mettere le doppie virgolette attorno alla "$text"linea dell'eco è cruciale. Senza di essi, nessuna delle newline (sia letterali che '\ n') funziona. Con loro, lo fanno tutti.
Jonathan Hartley,

Risposte:


663

Heredoc sembra più conveniente per questo scopo. Viene utilizzato per inviare più comandi a un programma di interpretazione comandi come ex o cat

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

La stringa dopo <<indica dove fermarsi.

Per inviare queste righe a un file, utilizzare:

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

È inoltre possibile memorizzare queste righe in una variabile:

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

Questo memorizza le linee nella variabile denominata VAR.

Durante la stampa, ricorda le virgolette attorno alla variabile altrimenti non vedrai i caratteri di nuova riga.

echo "$VAR"

Ancora meglio, puoi usare il rientro per farlo risaltare maggiormente nel tuo codice. Questa volta è sufficiente aggiungere un -after <<per interrompere la visualizzazione delle schede.

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

Ma poi devi usare le schede, non gli spazi, per il rientro nel tuo codice.


38
perché hai << - anziché << sulla prima riga del 2 ° e 5 ° blocco di codice? fa - fare qualcosa di speciale?
lohfu,

35
@ sup3rman '-' Ignora le schede iniziali. Vedere stackoverflow.com/questions/2500436/...
kervin

8
come posso uscire di lettura con lo stato 0? Sembra che non riesca a trovare la fine della linea (dato che è -d '') ed esce con 1 che non aiuta quando sei nello script.
Misha Slyusarev,

7
@MishaSlyusarev usa -d '\ 0' e aggiungi un \ 0 alla fine dell'ultima riga
Lars Schneider,

11
L'uso -ddell'opzione in read -r -d '' VARIABLE <<- EOMnon ha funzionato per me.
dron22,

186

Se stai cercando di inserire la stringa in una variabile, un altro modo semplice è qualcosa del genere:

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

Se indentate la stringa con delle tabulazioni (es. '\ T'), il rientro verrà rimosso. Se rientri con spazi, il rientro verrà lasciato.

NOTA: è significativo che l'ultima parentesi di chiusura sia su un'altra riga. Il ENDtesto deve apparire su una riga da solo.


1
È significativo? Funziona sul mio mac con )sulla stessa linea. Penso che sia perché ciò che si frappone $(e )verrà eseguito nel suo universo, e il comando effettivo non vedrà comunque ')'. Mi chiedo se funziona anche per gli altri.
Deej

È possibile che shell diverse interpretino le cose in modo diverso. Nella documentazione di Bash non sembra dire nulla in un modo o nell'altro, ma tutti gli esempi lo hanno sulla propria linea.
Andrew Miner,

La cosa divertente è che ti ho trovato una risposta quando cercavi di impostare un valore anche per la variabile USAGE. Quindi la tua risposta corrisponde esattamente. :)
Bunyk,

1
@deej Dalle specifiche POSIX : Il documento qui ... continua fino a quando non c'è una riga contenente solo il delimitatore e una <newline>
Fornost

1
@Fornost Non contraddice ciò che ho detto
deej

84

echoaggiunge spazi tra gli argomenti passati ad esso. $textè soggetto a espansione variabile e suddivisione in parole, quindi il tuo echocomando è equivalente a:

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

Puoi vedere che verrà aggiunto uno spazio prima di "questo". Puoi rimuovere i caratteri di nuova riga e citare $textper preservare le nuove righe:

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

echo "$text" > filename

Oppure potresti usare printf, che è più robusto e portatile di echo:

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

In bash, che supporta l'espansione del controvento, potresti persino fare:

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

1
Grazie per aver spiegato perché mod vede spazio extra quando si usa il comando "echo". Questa risposta funziona anche bene quando si usa in script bash (al contrario della sessione di shell interattiva). Senti che questa è la vera risposta e dovrebbe essere una risposta accettata.
onelaview,

1
Questa dovrebbe essere una risposta accettata. Tutte le altre risposte hanno fornito tonnellate di altri modi interessanti per risolvere il dolore di OP, ma tecnicamente la domanda era "come esce lo spazio extra" e nessuno ha risposto a questo :-) !!! Grazie, Josh per aver fatto -1 mistero!
Dmitry Shevkoplyas,

45

in uno script bash le seguenti opere:

#!/bin/sh

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

in alternativa:

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

il nome file cat dà:

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

Sembra che il trucco alternativo funzioni mentre Shell Scripts ma non sembra funzionare per bash.
Macindows,

3
@Macindows Mi sembra di aver dimenticato l' -eopzione per echo. Per favore riprova.
Chris Maes,

41

Ho trovato più soluzioni da quando volevo che ogni riga fosse correttamente indentata:

  1. Puoi usare echo:

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

    Non funziona se si inserisce "\n"poco prima \della fine di una riga.

  2. In alternativa, è possibile utilizzare printfper una migliore portabilità (mi è capitato di avere molti problemi con echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
    
  3. Un'altra soluzione potrebbe essere:

    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
    

    o

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
    
  4. Un'altra soluzione si ottiene miscelando 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
    

    Non è facile riformattare il codice formattato in questo modo poiché si codifica il livello di rientro nel codice.

  5. È possibile utilizzare una funzione di supporto e alcuni trucchi di sostituzione variabile:

    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"

3b è la mia soluzione preferita quando voglio preservare il rientro del codice e il rientro delle stringhe in modo indipendente, almeno quando non riesco a usare 2 nell'assegnazione variabile, ed è più leggibile di 3a. Quando non mi interessa, continuo a tenere aperta la stringa tra le diverse righe.
Pysis,

Risposta molto bella, in particolare 3b
Sumit Trehan,

"Non funziona se si inserisce" \ n "appena prima \ alla fine di una riga." - è un ottimo consiglio quando si utilizza l'eco.
Noam Manos,

6

Quello che segue è il mio modo preferito di assegnare una stringa multilinea a una variabile (penso che sia carino).

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

String1
String2
String3
...
StringN
_______________________________________________________________________________

Il numero di caratteri di sottolineatura è lo stesso (qui 80) in entrambi i casi.


0

Solo per citare una semplice concatenazione di una riga in quanto può essere utile a volte.

# 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" 

Avrai qualcosa del genere

bar 
   foo 
 Guga 
   Puga 

Tutti gli spazi bianchi iniziali e finali verranno conservati correttamente

echo "$v3" > filename

0

Ci sono molti modi per farlo. Per me, convogliare la stringa rientrata in sed funziona bene.

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"

Questa risposta si basava sulla risposta di Mateusz Piotrowski ma si perfezionò un po '.


-1

funzionerà se lo metti come di seguito:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.