Wie ordne ich einer Variablen im eingerückten Zustand einen String-Wert über mehrere Zeilen zu?

54

Die Angelegenheit:

  1. Ich muss einer Variablen einen Wert zuweisen, der anständig lang ist.
  2. Alle Zeilen meines Skripts müssen sich unter einer bestimmten Anzahl von Spalten befinden.

Ich versuche es also mit mehr als einer Zeile zuzuweisen.

Es ist ganz einfach, auf Einzüge zu verzichten:

VAR="This displays without \
any issues."
echo "${VAR}"

Ergebnis:

This displays without any issues.

Jedoch mit Einrückungen:

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"

Ergebnis:

This displays with      extra spaces.

Wie kann ich es ohne diese Leerzeichen elegant zuweisen?

Sman865
quelle

Antworten:

30

Hier ist das Problem ist, dass Sie die Variable mit doppelten Anführungszeichen umgeben ( „“). Entfernen Sie es und die Dinge werden gut funktionieren.

    VAR="This displays with \
    extra spaces."
    echo ${VAR}

Ausgabe

 This displays with extra spaces.

Hier ist das Problem, dass eine doppelte eine Variable unter Angabe erhält alle Leerzeichen. Dies kann verwendet werden, wenn Sie es ausdrücklich benötigen.

Zum Beispiel,

$ echo "Hello     World    ........ ...            ...."

wird gedruckt

Hello     World    ........ ...            ....

Und beim Entfernen von Anführungszeichen ist es anders

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....

Hier entfernt der Bash zusätzliche Leerzeichen im Text, da im ersten Fall der gesamte Text als "einzelnes" Argument interpretiert wird und somit zusätzliche Leerzeichen erhalten bleiben. Im zweiten Fall echoerhält der Befehl den Text als 5 Argumente.

Zitiert eine Variable wird auch hilfreich, während Argumente auf Befehle übergeben.

Im folgenden Befehl wird echonur ein einziges Argument als abgerufen"Hello World"

$ variable="Hello World"
$ echo "$variable"

Aber im Fall des folgenden Szenarios echoerhält man zwei Argumente als HelloundWorld

$ variable="Hello World"
$ echo $variable
Kannan Mohan
quelle
Die meisten Antworten haben gut funktioniert, aber dies war die einfachste. Vielen Dank!
Sman865
12
@ Sman865 - bitte glauben Sie mir, wenn ich Ihnen sage, dass dies tatsächlich mit ziemlicher Sicherheit die komplizierteste Antwort ist, die hier angeboten wird, und sie ist auch in den meisten Punkten falsch - insbesondere in ihrer Eröffnungserklärung. Jede Frage der Wertzuweisung kann in keiner Weise mit ihrer späteren Ausweitung zusammenhängen - das ist nur rückwärts. Es tut mir leid, Kannan, aber diese Antwort ist sowohl falsch als auch falsch. Field-Splitting-Erweiterungen $IFSsind ein leistungsfähiges und universelles Werkzeug - aber so geschriebener Code kann niemals zuverlässige Ergebnisse jeglicher Art liefern.
mikeserv
2
Wenn beim Erweitern einer Variablen keine Anführungszeichen verwendet werden, treten früher oder später Probleme auf, beginnend mit der Dateinamenerweiterung (beliebig * ? []).
mr.spuratic
Ich bin damit einverstanden, dass Variablen in doppelte Anführungszeichen eingeschlossen werden müssen, um eine Bash-Erweiterung zu verhindern. In besonderen Fällen können wir dies jedoch vermeiden, um eine Komplikation des Codes zu vermeiden.
Kannan Mohan
1
Ich bin mit @mikeserv einverstanden, dies ist ein sehr schlechter Rat, wenn er zum Nennwert genommen wird. Dies wird brechen, wenn es ohne die Konsequenzen verwendet wird. Wenn die Variable beispielsweise als Argument an einen Befehl übergeben wird, soll nicht jedes Wort als separates Argument aufgeteilt werden.
Haridsv
28

Die Lösungen von esuoxu und Mickaël Bucas sind die gebräuchlichsten und portableren Methoden, um dies zu erreichen.

Hier sind einige bashLösungen (von denen einige auch in anderen Shells funktionieren sollten, wie z zsh. B. ). Erstens mit dem +=Append-Operator (der für eine Ganzzahlvariable, eine reguläre Variable und ein Array jeweils auf etwas andere Weise funktioniert).

text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
text+="tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
text+="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." 

Wenn Sie Zeilenumbrüche (oder andere Leerzeichen / Escapezeichen) im Text verwenden möchten, verwenden Sie $''stattdessen Anführungszeichen :

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'

Als Nächstes printf -vweisen Sie einer Variablen mit einen formatierten Wert zu

printf -v text "%s" "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
                    "do eiusmod empor incididunt ut labore et dolore magna aliqua. "\
                    "Ut enim ad minim veniam ..."

Der Trick dabei ist, dass es mehr Argumente als Formatbezeichner gibt. Im Gegensatz zu den meisten printfFunktionen verwendet die Bash die Formatzeichenfolge so lange, bis sie leer ist. Sie können eine \nZeichenfolge in das Format einfügen oder $ '' (oder beide) verwenden, um mit Leerzeichen umzugehen.

Als nächstes verwenden Sie ein Array:

text=("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
      "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." )

Sie können +=den Text auch zeilenweise aufbauen (beachten Sie das ()):

text+=("post script")

Hier müssen Sie jedoch daran denken, das Array zu "verflachen", wenn Sie den gesamten Textinhalt auf einmal benötigen

echo "$text"      # only outputs index [0], the first line
echo "${text[*]}" # output complete text (joined by first character of IFS)

(Ganzzahlige indizierte Arrays werden im Gegensatz zu assoziativen Arrays implizit sortiert.) Dies gibt Ihnen etwas mehr Flexibilität, da Sie Linien manipulieren und bei Bedarf sogar schneiden und würfeln können.

Zum Schluss mit readoder readarrayund einem "hier-Dokument":

read -r -d '' text <<-"EOT"
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ...
EOT

readarray -t textarray <<-"EOT"
        Lorem [...]
EOT  

Die Form "Hier-Dokument" <<-bedeutet, dass alle führenden harten Tabulatoren aus der Eingabe entfernt werden. Daher müssen Sie Tabulatoren verwenden, um Ihren Text einzurücken. Anführungszeichen "EOT"verhindern Shell-Erweiterungsfunktionen, sodass die Eingabe wörtlich verwendet wird. Dabei readwird eine durch NUL-Bytes begrenzte Eingabe verwendet, sodass der durch Zeilenumbrüche begrenzte Text auf einmal gelesen wird. Mit readarray(aka mapfile, verfügbar seit Bash-4.0) liest es in ein Array und entfernt -tZeilenumbrüche in jeder Zeile.

mr.spuratic
quelle
1
Die readOption mit here documentist sehr schön! Nützlich, um Python-Skripte einzubetten und mit auszuführen python -c. Früher habe ich das gemacht script=$(cat <here document>), aber es read -r -d '' script <here document>ist viel besser.
Haridsv
9

Es gibt eine spezielle Heredoc-Syntax, mit der Tabulatoren am Anfang aller Zeilen entfernt werden: "<< -" (beachten Sie den hinzugefügten Bindestrich)

http://tldp.org/LDP/abs/html/here-docs.html

Beispiel 19-4. Mehrzeilige Nachricht mit unterdrückten Tabulatoren

Du kannst es so benutzen:

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"

Ergebnis:

A
B
C

Es funktioniert nur mit Tabulatoren, nicht mit Leerzeichen.

Mickaël Bucas
quelle
Hier auf Ubuntu ist das Gegenteil der Fall - Leerzeichen funktionieren, keine Tabulatoren. \tFunktioniert jedoch hervorragend, wenn Sie Registerkarten benötigen. Verwenden Sie einfach, echo -e "$v"anstatt echo "$v"Backslash-Zeichen zu aktivieren
will-ob
5

Lassen Sie die Shell die unerwünschten Zeilenvorschübe und die folgenden Leerzeichen essen:

$ cat weird.sh 
#!/bin/sh

        var1="A weird(?) $(
             )multi line $(
             )text idea. $(
             )PID=$$"

        var2='You can '$(
            )'avoid expansion '$(
            )'too: PID=$$'

        var3='Or mix it: '$(
            )'To insert the PID use $$. '$(
            )"It expands to e.g. $$."

        echo "$var1"
        echo "$var2"
        echo "$var3"
$ sh weird.sh 
A weird(?) multi line text idea. PID=13960
You can avoid expansion too: PID=$$
Or mix it: To insert the PID use $$. It expands to e.g. 13960.

Es ist also möglich ... aber sicher ist es Geschmackssache, diese Lösung zu mögen oder nicht zu mögen ...

Yeti
quelle
3
Jedes einzelne davon ist eine Gabel.
mikeserv
5

Vielleicht kannst du es versuchen.

          echo "Test" \
               "Test2" \
               "Test3"
esuoxu
quelle
5

So schlage ich vor, Sie sollten es tun, und ich werde erklären, warum, aber zuerst möchte ich über etwas anderes sprechen ...

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"

Viele der anderen hier angebotenen Lösungen scheinen darauf hinzudeuten, dass Sie den Inhalt einer Shell-Variablen irgendwie beeinflussen können, indem Sie ihre Erweiterungsmethoden ändern. Ich kann Ihnen versichern, dass dies nicht der Fall ist.

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"

AUSGABE

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53

Was Sie oben sehen, ist zuerst eine feldgeteilte Erweiterung, dann ein Bericht über die Byteanzahl für die Quellvariable der Erweiterung, dann eine durch Anführungszeichen getrennte Erweiterung und dieselbe Byteanzahl. Während die Ausgabe unterschiedlich sein kann, $stringändert sich der Inhalt der Shell-Variablen nur bei Zuweisung.

Was mehr ist, wenn Sie nicht verstehen, warum dies so ist, werden Sie früher als später auf einige sehr böse Überraschungen stoßen. Versuchen wir es noch einmal, aber unter etwas anderen Bedingungen.

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"

Gleich $string- andere Umgebung.

AUSGABE

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53

Die Feldaufteilung erfolgt anhand der in definierten Feldbegrenzer $IFS. Es gibt zwei Arten von Begrenzern - $IFSLeerzeichen und $IFSalles andere. Standardmäßig $IFSwird der Wertebereichsregisterkarte Zeilenumbruch zugewiesen - dies sind die drei möglichen $IFSLeerzeichenwerte. Sie lässt sich jedoch leicht ändern, wie Sie oben sehen, und kann drastische Auswirkungen auf die Field-Split-Erweiterungen haben.

$IFSWhitespace wird durch Sequenzierung in ein einzelnes Feld zerlegt - und aus diesem Grund wird echoeine Erweiterung, die eine beliebige Folge von Leerzeichen $IFSenthält, wenn sie ein Leerzeichen enthält, nur zu einem einzelnen Leerzeichen ausgewertet, da echodie Argumente für Leerzeichen verkettet werden. Nicht-Whitespace-Werte werden jedoch nicht auf die gleiche Weise entfernt, und jeder vorkommende Begrenzer erhält immer ein Feld für sich - wie aus der obigen Stuff- Erweiterung hervorgeht.

Das ist nicht das Schlimmste. Betrachten Sie diesen anderen $string.

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    

AUSGABE

* * * * * * 30
 * * *                  * * *  30

Sieht ok aus, oder? Nun, lassen Sie uns die Umgebung noch einmal ändern.

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    

AUSGABE

file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 30
 * * *                  * * *  30

Woah.

Standardmäßig erweitert die Shell Dateinamen-Globs, wenn sie mit ihnen übereinstimmen können. Dies geschieht nach der Parametererweiterung und der Aufteilung der Felder in der angegebenen Reihenfolge. Daher ist jede nicht in Anführungszeichen gesetzte Zeichenfolge auf diese Weise anfällig. Sie können dieses Verhalten mit set -fdeaktivieren, wenn Sie möchten, aber jede POSIX-kompatible Shell wird standardmäßig immer glob sein.

Dies ist die Art von Dingen, mit denen Sie konfrontiert sind, wenn Sie Erweiterungen in Anführungszeichen setzen, um sie Ihren Einzugspräferenzen anzupassen. Und $stringtrotzdem ist der tatsächliche Wert für unabhängig von seinem Expansionsverhalten immer noch der Wert, den Sie zuletzt zugewiesen haben. Kommen wir also zum ersten Punkt zurück.

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
echo "$var" "${#var}"

AUSGABE

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70

Ich glaube, dies ist eine weitaus vernünftigere Möglichkeit, die Shell-Syntax an Ihre Einzugspräferenzen anzupassen. Was ich oben mache, ist die Zuweisung jeder einzelnen Zeichenfolge zu einem Positionsparameter - auf den jeweils durch eine Zahl wie $1oder verwiesen werden kann ${33}- und anschließend die Zuweisung ihrer verketteten Werte zur $varVerwendung des speziellen Shell-Parameters $*.

Dieser Ansatz ist jedoch nicht immun gegen $IFS. Dennoch halte ich seine Beziehung $IFSin dieser Hinsicht für einen zusätzlichen Nutzen. Erwägen:

IFS=\ ;space_split="$*"
IFS=/; slash_split="$*";IFS='
';new_line_split="$*"

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"

AUSGABE

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like.
Arg 1: Line 1./Arg 2: Line 2./and so on for/as long as you might like.
Arg 1: Line 1.
Arg 2: Line 2.
and so on for
as long as you might like.

Wie Sie sehen können, wird $*jedes Argument im "$@"ersten Byte im verkettet $IFS. Wenn Sie also den Wert speichern, während er $IFSunterschiedlich zugewiesen ist, erhalten Sie für jeden gespeicherten Wert unterschiedliche Feldbegrenzer. Was Sie oben sehen, ist übrigens der Literalwert für jede Variable. Wenn Sie überhaupt kein Trennzeichen möchten, würden Sie Folgendes tun:

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"

AUSGABE

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67
mikeserv
quelle
2

Vielleicht möchten Sie versuchen:

echo $VAR | tr -s " "

oder

myArr=($VAL)
VAL=${myArr[@]}
echo "$VAL"

und Sie können dies auch überprüfen .

Vivian Maya
quelle
2

Verwenden Sie eine Bash-Substitution-Erweiterung

Wenn Sie Bash verwenden, können Sie eine Substitutionserweiterung verwenden . Zum Beispiel:

$ echo "${VAR//  /}"
This displays with extra spaces.
CodeGnome
quelle
1

Dies ist eine Variante zum Setzen von pfadähnlichen Variablen:

set -- "${MYDIR}/usr/local/lib" \
      :"${MYDIR}/usr/lib" \
      :"${MYDIR}/lib" \
       "${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
    export LD_LIBRARY_PATH="$*"
    LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)

Verwenden von setÜberschreibungen $@, die wie folgt gespeichert und später verwendet werden können:

ARGV=("$@")
exec foo "${ARGV[@]}"

Die LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)Zeile entfernt Leerzeichen vor Doppelpunkten sowie eventuell nachfolgende Leerzeichen. Verwenden Sie LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }stattdessen, wenn Sie nur nachfolgende Leerzeichen entfernen.

Dieser gesamte Ansatz ist eine Variante der hervorragenden Antwort von mikeserv.

rsanden
quelle
0

Hab keine Angst vor Leerzeichen. Entfernen Sie sie einfach, bevor Sie Ihren mehrzeiligen Text drucken.

$ cat ./t.sh
#!/bin/bash

NEED_HELP=1
if [[ $NEED_HELP -eq 1 ]]; then

    lucky_number=$((1 + RANDOM % 10 + 10))

    read -r -d '' text <<'    EOF'
    NAME says hello

    Look at this helpful text:

                                                 * -**
                                 Eyjafjallajokull        Eyjafjallajokull
                            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

    Don't go away, please rate your experience... It'll just take two minutes.

    EOF
    text=$(echo "$text" | sed -r 's!^\s{4}!!')
    text=$(echo "$text" | sed -r "s!\bNAME\b!$0!") # Bash: text=${text//NAME/$0}
    text=$(echo "$text" | sed -r "s!\btwo\b!$lucky_number!")

    echo "$text"

fi

Ausgabe:

$ ./t.sh
./t.sh says hello

Look at this helpful text:

                                             * -**
                             Eyjafjallajokull        Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

Don't go away, please rate your experience... It'll just take 16 minutes.

Sie müssen keinen <<-Heredoc verwenden und Ihre Einrückung mit vier Leerzeichen nicht durch Tabulatorzeichen trennen.

c0xc
quelle