Linee di continuazione di Bash


158

Come si usano le linee di continuazione bash?

Mi rendo conto che puoi farlo:

echo "continuation \
lines"
>continuation lines

Tuttavia, se si dispone di codice rientrato, non funziona così bene:

    echo "continuation \
    lines"
>continuation     lines

5
La Guida allo stile di Bash Shell di Google raccomanda i documenti "qui" per "Se devi scrivere stringhe più lunghe di 80 caratteri". Vedi la risposta di @ tripleee .
Trevor Boyd Smith,

google.github.io/styleguide/… - questo è il link diretto nel nuovo documento
jcollum

Risposte:


161

Questo è ciò che potresti desiderare

$       echo "continuation"\
>       "lines"
continuation lines

Se questo crea due argomenti da echeggiare e ne vuoi solo uno, allora esaminiamo la concatenazione di stringhe. In bash, posizionando due stringhe una accanto all'altra concatenate:

$ echo "continuation""lines"
continuationlines

Quindi una linea di continuazione senza rientro è un modo per spezzare una stringa:

$ echo "continuation"\
> "lines"
continuationlines

Ma quando viene utilizzato un rientro:

$       echo "continuation"\
>       "lines"
continuation lines

Ottieni due argomenti perché questa non è più una concatenazione.

Se desideri una singola stringa che attraversa le linee, mentre rientri ma non ottieni tutti quegli spazi, un approccio che puoi provare è quello di abbandonare la linea di continuazione e usare le variabili:

$ a="continuation"
$ b="lines"
$ echo $a$b
continuationlines

Ciò ti consentirà di avere un codice chiaramente rientrato a scapito di ulteriori variabili. Se rendi le variabili locali non dovrebbe essere troppo male.


1
Grazie per il tuo aiuto, ma mentre questo rimuove gli spazi, ora sono parametri separati (Bash sta interpretando gli spazi sulla seconda riga come separatore di parametri) e ora vengono stampati correttamente solo a causa del comando echo.

1
Oh, vorresti una singola stringa (bash) per estendere le linee! Ora vedo.
Ray Toal,

4
Soluzione con una variabile:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman il

30

Qui i documenti con il <<-HEREterminatore funzionano bene per stringhe di testo a più righe rientrate. Rimuoverà tutte le schede principali dal documento qui. (I terminatori di riga rimarranno comunque, comunque.)

cat <<-____HERE
    continuation
    lines
____HERE

Vedi anche http://ss64.com/bash/syntax-here.html

Se hai bisogno di preservare alcuni, ma non tutti, spazi bianchi di testa, potresti usare qualcosa di simile

sed 's/^  //' <<____HERE
    This has four leading spaces.
    Two of them will be removed by sed.
____HERE

o forse usare trper sbarazzarsi di newline:

tr -d '\012' <<-____
    continuation
     lines
____

(La seconda riga ha una scheda e uno spazio in primo piano; la scheda verrà rimossa dall'operatore trattino prima del terminatore ereditario, mentre lo spazio verrà conservato.)

Per avvolgere stringhe lunghe e complesse su molte linee, mi piace printf:

printf '%s' \
    "This will all be printed on a " \
    "single line (because the format string " \
    "doesn't specify any newline)"

Funziona bene anche in contesti in cui si desidera incorporare parti non banali di script di shell in un'altra lingua in cui la sintassi della lingua host non consente di utilizzare un documento qui, come in un Makefileo Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript

Non funziona per me. Ubuntu 16.04. Ottengo due righe invece di una riga concatenata prevista.
Penghe Geng,

@PengheGeng In effetti, questo risolve il problema di eliminare l'indentazione, non quello di unire le linee. È comunque possibile eseguire il backslash della nuova riga alla fine di una riga per unire due righe insieme.
Tripleee,

(Ma vedi anche il primo printfesempio adesso.)
Tripleee,

12

È possibile utilizzare gli array bash

$ str_array=("continuation"
             "lines")

poi

$ echo "${str_array[*]}"
continuation lines

c'è uno spazio extra, perché (dopo il manuale bash):

Se la parola è racchiusa tra virgolette doppie, si ${name[*]}espande in una singola parola con il valore di ciascun membro dell'array separato dal primo carattere della variabile IFS

Quindi, IFS=''per sbarazzarsi di spazio extra

$ IFS=''
$ echo "${str_array[*]}"
continuationlines

4

In alcuni scenari l'utilizzo dell'abilità di concatenazione di Bash potrebbe essere appropriato.

Esempio:

temp='this string is very long '
temp+='so I will separate it onto multiple lines'
echo $temp
this string is very long so I will separate it onto multiple lines

Dalla sezione PARAMETRI della pagina di Bash Man:

name = [valore] ...

... Nel contesto in cui un'istruzione di assegnazione assegna un valore a una variabile shell o indice di array, l'operatore + = può essere utilizzato per aggiungere o aggiungere al valore precedente della variabile. Quando + = viene applicato a una variabile per la quale è stato impostato l'attributo intero, il valore viene valutato come espressione aritmetica e aggiunto al valore corrente della variabile, che viene anche valutato. Quando + = viene applicato a una variabile di matrice utilizzando l'assegnazione composta (vedere Matrici di seguito), il valore della variabile non viene disattivato (come quando si utilizza =) e i nuovi valori vengono aggiunti alla matrice a partire da uno maggiore dell'indice massimo della matrice (per array indicizzati) o aggiunti come coppie chiave-valore aggiuntive in un array associativo. Se applicato a una variabile con valore di stringa, il valore viene espanso e aggiunto al valore della variabile.


Probabilmente il miglior consiglio in questa pagina. L'uso di un HEREDOC e il piping in una traduzione per <CR> è super intuitivo e fallisce quando la stringa deve effettivamente avere separatori di riga posizionati in modo discreto.
ingyhere il

2

Mi sono imbattuto in una situazione in cui ho dovuto inviare un lungo messaggio come parte di un argomento di comando e ho dovuto aderire alla limitazione della lunghezza della linea. I comandi hanno un aspetto simile al seguente:

somecommand --message="I am a long message" args

Il modo in cui ho risolto questo è di spostare il messaggio come un documento qui (come suggerito da @tripleee). Ma un documento qui diventa uno stdin, quindi deve essere riletto, ho seguito l'approccio seguente:

message=$(
    tr "\n" " " <<- END
        This is a
        long message
END
)
somecommand --message="$message" args

Questo ha il vantaggio che $messagepuò essere utilizzato esattamente come costante di stringa senza spazi bianchi o interruzioni di riga.

Si noti che le righe dei messaggi effettive sopra sono precedute da un tabcarattere ciascuna, che viene rimosso dal documento stesso (a causa dell'uso di <<-). Ci sono ancora interruzioni di riga alla fine, che vengono poi sostituite da ddspazi.

Si noti inoltre che se non si rimuovono le nuove righe, queste appariranno come quando "$message"espanse. In alcuni casi, potresti essere in grado di risolvere il problema rimuovendo le doppie virgolette $message, ma il messaggio non sarà più un singolo argomento.


2

Le continuazioni di linea possono anche essere ottenute attraverso un uso intelligente della sintassi.

Nel caso di echo:

# echo '-n' flag prevents trailing <CR> 
echo -n "This is my one-line statement" ;
echo -n " that I would like to make."
This is my one-line statement that I would like to make.

Nel caso di var:

outp="This is my one-line statement" ; 
outp+=" that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Un altro approccio nel caso di var:

outp="This is my one-line statement" ; 
outp="${outp} that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Ecco!


1

Potresti semplicemente separarlo con le nuove linee (senza usare la barra rovesciata) come richiesto all'interno del rientro come segue e solo una striscia di nuove linee.

Esempio:

echo "continuation
of 
lines" | tr '\n' ' '

Oppure, se si tratta di una definizione variabile, le nuove righe vengono automaticamente convertite in spazi. Quindi, striscia di spazi extra solo se applicabile.

x="continuation
of multiple
lines"
y="red|blue|
green|yellow"

echo $x # This will do as the converted space actually is meaningful
echo $y | tr -d ' ' # Stripping of space may be preferable in this case

1

Questo non è esattamente quello che l'utente ha chiesto, ma un altro modo per creare una lunga stringa che si estende su più righe è costruendolo in modo incrementale, in questo modo:

$ greeting="Hello"
$ greeting="$greeting, World"
$ echo $greeting
Hello, World

Ovviamente in questo caso sarebbe stato più semplice costruirlo in una volta, ma questo stile può essere molto leggero e comprensibile quando si tratta di stringhe più lunghe.


0

Tuttavia, se si dispone di codice rientrato, non funziona così bene:

    echo "continuation \
    lines"
>continuation     lines

Prova con virgolette singole e concatenando le stringhe:

    echo 'continuation' \
    'lines'
>continuation lines

Nota: la concatenazione include uno spazio bianco.


2
Funziona con gli argomenti echo e string, ma non funziona con altre cose, come l'assegnazione delle variabili. Sebbene la domanda non riguardasse le variabili, l'uso dell'eco era solo un esempio. Invece di echo se aveste x=, si otterrebbe l'errore: lines: command not found.
LS

0

A seconda del tipo di rischio che accetterai e della tua conoscenza e affidabilità dei dati, puoi utilizzare l'interpolazione variabile semplicistica.

$: x="
    this
    is
       variably indented
    stuff
   "
$: echo "$x" # preserves the newlines and spacing

    this
    is
       variably indented
    stuff

$: echo $x # no quotes, stacks it "neatly" with minimal spacing
this is variably indented stuff

-2

Questo probabilmente non risponde davvero alla tua domanda ma potresti trovarla utile comunque.

Il primo comando crea lo script visualizzato dal secondo comando.

Il terzo comando rende eseguibile quello script.

Il quarto comando fornisce un esempio di utilizzo.

john@malkovich:~/tmp/so$ echo $'#!/usr/bin/env python\nimport textwrap, sys\n\ndef bash_dedent(text):\n    """Dedent all but the first line in the passed `text`."""\n    try:\n        first, rest = text.split("\\n", 1)\n        return "\\n".join([first, textwrap.dedent(rest)])\n    except ValueError:\n        return text  # single-line string\n\nprint bash_dedent(sys.argv[1])'  > bash_dedent
john@malkovich:~/tmp/so$ cat bash_dedent 
#!/usr/bin/env python
import textwrap, sys

def bash_dedent(text):
    """Dedent all but the first line in the passed `text`."""
    try:
        first, rest = text.split("\n", 1)
        return "\n".join([first, textwrap.dedent(rest)])
    except ValueError:
        return text  # single-line string

print bash_dedent(sys.argv[1])
john@malkovich:~/tmp/so$ chmod a+x bash_dedent
john@malkovich:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

Nota che se vuoi davvero usare questo script, ha più senso spostare lo script eseguibile in ~/binmodo che sia nel tuo percorso.

Controlla il riferimento di Python per i dettagli su come textwrap.dedentfunziona.

Se l'utilizzo di $'...'o "$(...)"ti confonde, fai un'altra domanda (una per costrutto) se non ce n'è già una. Potrebbe essere utile fornire un collegamento alla domanda che trovi / fai in modo che altre persone abbiano un riferimento collegato.


6
Ben intenzionato - e forse anche utile - anche se questo può essere, l'OP ha chiesto consigli sulla sintassi bash di base, e tu gli hai dato una definizione di funzione python che utilizza paradigmi OO, controllo del flusso eccezionale e importazioni. Inoltre, hai chiamato un eseguibile come parte di un'interpolazione di stringhe, qualcosa che una persona che faceva questo tipo di domanda non avrebbe sicuramente visto ancora in bash.
Parthian Shot
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.