Ciąg wieloliniowy z dodatkową spacją (zachowane wcięcie)

409

Chcę zapisać do pliku niektóre wstępnie zdefiniowane teksty:

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

echo -e $text > filename

Spodziewam się czegoś takiego:

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

Ale mam to:

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

Jestem pewien, że po każdym nie ma miejsca \n, ale jak powstaje dodatkowa przestrzeń?

cizixs
źródło
2
Nie jestem pewien, ale ... co jeśli wpiszesz właśnie text="this is line one\nthis is line two\nthis is line three"ten sam wiersz ..? (bez żadnego wpisu)
Yohanes Khosiawan
4
Usuń \nna każdej linii, już nacisnąłeś nową linię, aby przejść do nowej linii
Mark Setchell
Już dałeś \n. Więc dlaczego wstawiłeś następny wiersz w nowym wierszu? Po prostutext="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi
1
Usunięcie znaku \nna końcu każdego wiersza powoduje, że wszystkie dane wyjściowe działają razem w jednym wierszu.
Jonathan Hartley
18
Aha: Umieszczenie podwójnych cudzysłowów wokół "$text"linii echa ma kluczowe znaczenie. Bez nich żadna z nowych linii (zarówno dosłowna, jak i „\ n”) nie działa. Z nimi wszyscy tak robią.
Jonathan Hartley

Odpowiedzi:

662

Heredoc wydaje się w tym celu wygodniejszy. Służy do wysyłania wielu poleceń do programu interpretera poleceń, takiego jak ex lub cat

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

Ciąg po <<wskazuje, gdzie się zatrzymać.

Aby wysłać te linie do pliku, użyj:

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

Możesz również zapisać te linie w zmiennej:

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

To przechowuje linie do zmiennej o nazwie VAR.

Podczas drukowania pamiętaj cudzysłowy wokół zmiennej, w przeciwnym razie nie zobaczysz znaków nowej linii.

echo "$VAR"

Co więcej, możesz użyć wcięcia, aby wyróżnić się bardziej w kodzie. Tym razem dodaj -po, <<aby zatrzymać wyświetlanie kart.

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

Ale musisz użyć tabulacji, a nie spacji, do wcięcia w kodzie.

nowy dzieciak
źródło
38
dlaczego masz << - zamiast << w pierwszym wierszu 2. i 5. bloku kodu? robi - robi coś specjalnego?
lohfu
35
@ sup3rman '-' Ignoruje wiodące zakładki. Zobacz stackoverflow.com/questions/2500436/…
kervin
7
Jak mogę zakończyć odczyt ze statusem 0? Wygląda na to, że nie może znaleźć końca linii (ponieważ jest to -d) i kończy się na 1, co nie pomaga, gdy jesteś w skrypcie.
Misha Slyusarev
6
@MishaSlyusarev użyj -d '\ 0' i dodaj \ 0 na końcu ostatniej linii
Lars Schneider
11
Korzystanie z -dopcji w read -r -d '' VARIABLE <<- EOMnie działało dla mnie.
dron22
186

Jeśli próbujesz przekształcić ciąg w zmienną, innym łatwym sposobem jest coś takiego:

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

Jeśli wciśniesz swój ciąg za pomocą tabulatorów (tj. „\ T”), wcięcie zostanie usunięte. Jeśli wcinasz spacjami, wcięcie pozostanie w.

UWAGA: Znaczące jest to, że ostatni nawias zamykający znajduje się w innej linii. Sam ENDtekst musi pojawić się w wierszu.

Andrew Miner
źródło
1
Czy to jest znaczące? Działa na moim komputerze Mac z )tą samą linią. Wydaje mi się, że dzieje się tak dlatego, że to, co jest pomiędzy, $(i )będzie działać we własnym wszechświecie, a rzeczywiste polecenie i tak nie zobaczy „)”. Zastanawiam się, czy to działa również na innych.
deej
Możliwe, że różne powłoki będą interpretować różne rzeczy. W dokumentacji bash wydaje się, że nie mówi nic w ten czy inny sposób, ale wszystkie przykłady mają to na swój sposób.
Andrew Miner,
Zabawne jest to, że znalazłem odpowiedź podczas próby ustawienia wartości zmiennej USAGE. Twoja odpowiedź pasuje dokładnie. :)
Bunyk
1
@deej Ze specyfikacji POSIX : Dokument tutaj… trwa, dopóki nie będzie linii zawierającej tylko ogranicznik i <nową linię>
Fornost
1
@Fornost Nie jest to sprzeczne z tym, co powiedziałem
deej
84

echododaje spacje między przekazanymi mu argumentami. $textpodlega zmiennej interpretacji i dzieleniu słów, więc twoje echopolecenie jest równoważne z:

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

Możesz zobaczyć, że spacja zostanie dodana przed „tym”. Możesz usunąć znaki nowego wiersza i zacytować, $textaby zachować nowe znaki :

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

echo "$text" > filename

Lub możesz użyć printf, który jest bardziej solidny i przenośny niż echo:

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

W bash, który obsługuje rozwijanie nawiasów, możesz nawet:

printf "%s\n" "this is line "{one,two,three} > filename
Josh Jolly
źródło
1
Dziękujemy za wyjaśnienie, dlaczego mod widzi dodatkowe miejsce podczas używania polecenia „echo”. Ta odpowiedź działa również dobrze, gdy jest używana w skrypcie bash (w przeciwieństwie do interaktywnej sesji powłoki). Wydaje się, że to prawdziwa odpowiedź i powinna być odpowiedzią akceptowaną.
onelaview
1
Należy zaakceptować odpowiedź. Wszystkie pozostałe odpowiedzi dostarczyły mnóstwo interesujących innych sposobów rozwiązania bólu OP, ale technicznie pytanie brzmiało: „jak powstaje dodatkowa przestrzeń” i nikt nie odpowiedział na to :-) !!! Dziękuję Josh za zrobienie tajemnicy -1!
Dmitry Shevkoplyas
45

w skrypcie bash działają następujące elementy:

#!/bin/sh

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

alternatywnie:

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

nazwa pliku kota daje:

this is line one
this is line two
this is line three
Chris Maes
źródło
Wygląda na to, że alternatywna sztuczka działa podczas korzystania ze Skryptów powłoki, ale nie działa na bash.
Macindows
3
@Macindows Wydaje mi się, że zapomniałem -eopcji echo. Proszę spróbuj ponownie.
Chris Maes,
41

Znalazłem więcej rozwiązań, ponieważ chciałem, aby każda linia była odpowiednio wcięta:

  1. Możesz użyć echo:

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

    To nie działa, jeśli umieścisz "\n"tuż przed \końcem linii.

  2. Możesz też użyć printfdla lepszej przenośności (zdarzyło mi się mieć wiele problemów echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. Jeszcze innym rozwiązaniem może być:

    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

    lub

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
  4. Inne rozwiązanie osiąga się przez mieszanie printfi sed.

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

    Refaktoryzacja kodu sformatowanego w ten sposób nie jest łatwa, ponieważ kodujesz poziom wcięcia w kodzie.

  5. Możliwe jest użycie funkcji pomocnika i niektórych sztuczek polegających na zamianie zmiennych:

    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
źródło
3b jest moim preferowanym rozwiązaniem, gdy chcę zachować wcięcie kodu i wcięcie łańcucha niezależnie, przynajmniej wtedy, gdy nie mogę użyć 2 w przypisaniu zmiennej i jest bardziej czytelne niż 3a. Kiedy mnie to nie obchodzi, po prostu trzymam cytowany ciąg w kilku wierszach.
Pysis
Bardzo ładna odpowiedź, szczególnie 3b
Sumit Trehan 10.09.19
„Nie działa, jeśli umieścisz„ \ n ”tuż przed \ na końcu linii.” - jest świetną wskazówką podczas korzystania z echa.
Noam Manos
6

Poniżej znajduje się mój preferowany sposób przypisywania ciągu wieloliniowego do zmiennej (myślę, że wygląda ładnie).

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

String1
String2
String3
...
StringN
_______________________________________________________________________________

Liczba znaków podkreślenia jest taka sama (tutaj 80) w obu przypadkach.

Frank-Rene Schäfer
źródło
0

Wystarczy wspomnieć o prostym łączeniu jednowierszowym, ponieważ może być czasem użyteczne.

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

Dostaniesz coś takiego

bar 
   bla 
 guga 
   puga 

Wszystkie wiodące i kończące się białe spacje zostaną zachowane

echo "$v3" > filename
it3xl
źródło
0

Można to zrobić na wiele sposobów. Dla mnie ładowanie wcięcia sznurka w sed działa dobrze.

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"

Ta odpowiedź była oparta na odpowiedzi Mateusza Piotrowskiego , ale nieco dopracowana.

Beni Trainor
źródło
-1

zadziała, jeśli umieścisz go jak poniżej:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
luk
źródło