Come produrre una stringa multilinea in Bash?


250

Come posso generare una stringa su più righe in Bash senza usare più chiamate eco in questo modo:

echo "usage: up [--level <n>| -n <levels>][--help][--version]"
echo 
echo "Report bugs to: "
echo "up home page: "

Sto cercando un modo portatile per farlo, usando solo i builtin di Bash.


4
Se stai inviando un messaggio di utilizzo in risposta a una chiamata errata, normalmente invierai quel messaggio a un errore standard anziché a un output standard, conecho >&2 ...
Mark Reed,

2
@MarkReed Il messaggio d'uso viene emesso digitando --help(che dovrebbe andare allo standard out).
helpermethod,

Per gli altri che arrivano, maggiori informazioni su "qui documenti" sono disponibili: tldp.org/LDP/abs/html/here-docs.html
Jeffrey Martinez,

Controlla la printfsoluzione basata su Gordon Davidson. Nonostante sia all'ombra degli approcci basati echoo catbasati, sembra essere molto meno di un kludge. Certo, la sintassi `printf 'rappresenta un po' una curva di apprendimento, ma mi piacerebbe
sentire

Risposte:


296

Qui i documenti vengono spesso utilizzati per questo scopo.

cat << EOF
usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page:
EOF

Sono supportati in tutte le shell derivate da Bourne, comprese tutte le versioni di Bash.


4
Sì, ma catnon è integrato.
Mark Reed,

8
@MarkReed: è vero, ma è sempre disponibile (tranne forse in circostanze insolite).
In pausa fino a nuovo avviso.

6
+1 Thx. Ho finito per usare read -d '' help <<- EOF ...per leggere la stringa multilinea in una variabile e quindi ho fatto eco al risultato.
helpermethod,

3
posso salvare un HEREDOC in una variabile?
Chovy

177

oppure puoi farlo:

echo "usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page: "

1
@OliverWeiler: Funzionerà anche nelle shell Bourne come Dash e Heirloom Bourne Shell .
In pausa fino a nuovo avviso.

6
Non eccezionale se hai bisogno di questo in una funzione perché dovrai 1) superare la stringa fino alla sinistra del tuo file o 2) tenerlo rientrato per allinearlo con il resto del codice ma poi stampa con anche i rientri
sg

43

Ispirato dalle risposte perspicaci di questa pagina, ho creato un approccio misto, che considero il più semplice e flessibile. Cosa ne pensi?

Innanzitutto, definisco l'utilizzo in una variabile, che mi consente di riutilizzarlo in contesti diversi. Il formato è molto semplice, quasi WYSIWYG, senza la necessità di aggiungere caratteri di controllo. Questo mi sembra ragionevolmente portatile (l'ho eseguito su MacOS e Ubuntu)

__usage="
Usage: $(basename $0) [OPTIONS]

Options:
  -l, --level <n>              Something something something level
  -n, --nnnnn <levels>         Something something something n
  -h, --help                   Something something something help
  -v, --version                Something something something version
"

Quindi posso semplicemente usarlo come

echo "$__usage"

o ancora meglio, quando analizzo i parametri, posso semplicemente echeggiarlo lì in una riga:

levelN=${2:?"--level: n is required!""${__usage}"}

4
questo ha funzionato per me in uno script in cui la risposta sopra non lo fa (senza modifiche).
David Welch,

3
Questo è molto più pulito che coinvolgere un sacco di caratteri come \ t e \ n che sono difficili da trovare nel testo ed espandersi per rendere l'output molto diverso dalla stringa nella sceneggiatura
sg

1
Per alcuni motivi stampa tutto sulla stessa riga per me: /
Nicolas de Fontenay il

2
@Nicolas: usare le doppie virgolette in echo "$__usage"era necessario per me. echo $__usagenon ha funzionato.
Mario,

24

Utilizzare l' -eopzione, quindi è possibile stampare un nuovo carattere di riga con \nnella stringa.

Campione (ma non sono sicuro che sia buono o no)

La cosa divertente è che l' -eopzione non è documentata nella pagina man di MacOS mentre è ancora utilizzabile. È documentato nella pagina man di Linux .


6
Quelle pagine man sono per il echocomando fornito dal sistema /bin/echo, che su Mac OS non ha -eopzioni. quando usi bash su questi sistemi, il suo echocomando integrato prende il sopravvento. Puoi vederlo digitando /bin/echo whatevere osservando esplicitamente la differenza di comportamento. Per visualizzare la documentazione per il built-in, digitare help echo.
Mark Reed,

1
/bin/echoè spesso diverso da un sistema operativo all'altro e diverso dall'integrato di Bash echo.
In pausa fino a nuovo avviso.

@MarkReed: ci proverò più tardi, ma grazie per le informazioni. +1. Lascerò semplicemente la mia risposta qui, poiché ci sono molte discussioni in corso.
nhahtdh,

7
echo -enon è portatile - ad esempio, alcune implementazioni di echo stamperanno "-e" come parte dell'output. Se vuoi la portabilità, usa invece printf. Ad esempio, / bin / echo su OS X 10.7.4 fa questo. IIRC l'eco integrata bash era anche strano sotto 10.5.0, ma non ricordo più i dettagli.
Gordon Davisson,

2
echo -emi ha morso prima ... Sicuramente usare printfo catcon una eredità. La <<-variante di questi documenti è particolarmente utile perché puoi
rimuovere il

22

Dato che ho raccomandato printfin un commento, dovrei probabilmente fornire alcuni esempi del suo utilizzo (sebbene per la stampa di un messaggio di utilizzo, avrei più probabilità di usare le risposte di Dennis o Chris). printfè un po 'più complesso da usare di echo. Il suo primo argomento è una stringa di formato, in cui gli escape (like \n) sono sempre interpretati; può anche contenere direttive di formato che iniziano con %, che controllano dove e come eventuali argomenti aggiuntivi sono inclusi in esso. Ecco due diversi approcci per usarlo per un messaggio di utilizzo:

Innanzitutto, potresti includere l'intero messaggio nella stringa di formato:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: \nup home page: \n"

Si noti che echo, diversamente , è necessario includere esplicitamente la nuova riga finale. Inoltre, se il messaggio contiene %caratteri, dovrebbero essere scritti come %%. Se si desidera includere gli indirizzi bugreport e homepage, è possibile aggiungerli in modo abbastanza naturale:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: %s\nup home page: %s\n" "$bugreport" "$homepage"

In secondo luogo, potresti semplicemente usare la stringa di formato per farlo stampare ogni argomento aggiuntivo su una riga separata:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: " "up home page: "

Con questa opzione, l'aggiunta degli indirizzi bugreport e homepage è abbastanza ovvia:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: $bugreport" "up home page: $homepage"

9

Inoltre con il codice sorgente rientrato puoi usare <<-(con un trattino finale) per ignorare le schede iniziali (ma non gli spazi iniziali). Ad esempio questo:

if [ some test ]; then
    cat <<- xx
        line1
        line2
xx
fi

Emette testo indentato senza lo spazio bianco iniziale:

line1
line2

Non ha funzionato per me. Che shell stai usando?
quattro43,

Non ha funzionato in bash 4.4.19 in Ubuntu. Non ha rimosso la spaziatura prima della riga 1 e della riga 2
quattro43

1
@ four43, avevi ragione. Non funziona per rimuovere gli spazi iniziali. Tuttavia, rimuove le schede principali. Quindi ho corretto la mia risposta dalle schede e dagli spazi, alle schede e non agli spazi. Scusa per l'errore. Ho controllato il manuale e chiaramente dice solo che le schede sono state rimosse. Grazie per avermelo fatto notare.
Vista ellittica il

0

Se usi la soluzione di @jorge e scopri che tutto è sulla stessa riga, assicurati di racchiudere la variabile tra virgolette:

echo $__usage

stamperà tutto su una riga mentre

echo "$__usage"

manterrà le nuove righe.


Questa è in realtà l'unica soluzione che ha funzionato per me. Printf fa molte cose e con il mio xml multilinea, probabilmente richiede molta fuga perché rovina completamente il contenuto. Assegno foo = cat <<EOF .... EOF&& echo "$ foo"
Jilles van Gurp
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.