Jak dodawać nowe linie do zmiennych w skrypcie bash

64

Kiedy robię

str="Hello World\n===========\n"

Dostaję też \nwydrukowany. Jak mogę mieć wtedy nowe linie?

Jiew Meng
źródło
1
Chociaż odpowiedzi tutaj są świetne, w rzeczywistości myślę, że lepiej byłoby używać tablicy do tego typu rzeczy przez większość czasu.
evilsoup
Zobacz także pytanie Próba osadzenia nowej linii w zmiennej w bash .
HelloGoodbye

Odpowiedzi:

73

W bashmożesz użyć składni

str=$'Hello World\n===========\n'

Pojedyncze cudzysłowy poprzedzone a $to nowa składnia, która pozwala wstawiać sekwencje specjalne w ciągach znaków.

Również printfwbudowany pozwala zapisać wynikowy wynik w zmiennej

printf -v str 'Hello World\n===========\n'

Oba rozwiązania nie wymagają podpowłoki.

Jeśli poniżej musisz wydrukować ciąg, powinieneś użyć podwójnych cudzysłowów, jak w poniższym przykładzie:

echo "$str"

ponieważ gdy drukujesz ciąg bez cudzysłowów, znak nowej linii jest konwertowany na spacje.

enzotib
źródło
1
Jak nazywa się składnia str=$'Hello World\n===========\n'? zmienne podstawienie?
zengr,
5
@zengr: Nazywa się to cytowaniem ANSI-C , jest także obsługiwane w zshi ksh; jednak NIE jest zgodny z POSIX.
mklement0
4
@ mkelement0, pochodzi z ksh93, jest także obsługiwany przez zsh, bash, mksh i FreeBSD sh, a jego włączenie do kolejnej ważnej wersji POSIX jest dyskutowane
Stéphane Chazelas
1
Wydaje się, że nie działa z podwójnymi cudzysłowami? na przykład str=$"My $PET eats:\n$PET food"? To podejście działa w przypadku podwójnych cytatów
Brad Parks
31

Możesz wstawić dosłowne znaki nowej linii w pojedynczych cudzysłowach (w dowolnej powłoce w stylu Bourne / POSIX).

str='Hello World
===========
'

W przypadku ciągu wielowierszowego dokumenty tutaj są często wygodne. Ciąg jest podawany jako dane wejściowe do polecenia.

mycommand <<'EOF'
Hello World
===========
EOF

Jeśli chcesz zapisać ciąg w zmiennej, użyj catpolecenia w podstawieniu polecenia. Znaki nowego wiersza na końcu ciągu zostaną usunięte przez podstawienie polecenia. Jeśli chcesz zachować ostatnie znaki nowej linii, umieść korek na końcu i zdejmij go później. W powłokach zgodnych z POSIX możesz pisać, str=$(cat <<'EOF'); str=${str%a}a następnie heredoc właściwy, ale bash wymaga, aby heredoc pojawił się przed nawiasem zamykającym.

str=$(cat <<'EOF'
Hello World
===========
a
EOF
); str=${str%a}

W ksh, bash i zsh możesz użyć $'…'cytowanego formularza, aby rozwinąć znaki odwrotnego ukośnika w cudzysłowach.

str=$'Hello World\n===========\n'
Gilles
źródło
1
Korzystam z GNU bash 4.1.5 i str=$(cat <<'EOF')nie działa tak, jak jest. )Musi zostać umieszczony w następnym wierszu po końcu dokumentu EOF... ale mimo to traci końcowy znak nowej linii z powodu polecenia Podstawienie.
Peter.O
@fred Dobre punkty, wyjaśniłem o końcowych znakach nowej linii i pokazałem kod, który działa w bash. Myślę, że to błąd w bashu, choć szczerze mówiąc po ponownym przeczytaniu specyfikacji POSIX, nie jest jasne, czy zachowanie jest wymagane, gdy << znajduje się w podstawieniu polecenia, a heredoc nie.
Gilles
Świetna sprawa; aby zachować końcowe (i wiodące) \ninstancje bashpodczas przechwytywania dokumentu tutaj w zmiennej, rozważ IFS= read -r -d '' str <<'EOF'...jako alternatywę dla podejścia ograniczającego (patrz moja odpowiedź).
mklement0
8

Czy używasz „echa”? Spróbuj „echo -e”.

echo -e "Hello World\n===========\n"
Mikrofon
źródło
2
BTW. Nie potrzebujesz końcowego \ n, ponieważ echoautomatycznie doda jeden, chyba że podasz -n. (Jednak głównym celem pytania jest to, jak przekształcić te nowe wiersze w zmienną).
Peter.O
+1 Ze wszystkich rozwiązań, to jest najbardziej bezpośrednie i najprostsze.
Hai Vu,
echo -e nie działa w systemie OS X
Greg M. Krsak
2
@GregKrsak: W bash, echo -e czyni pracę na OS X - to dlatego, że echojest bash wbudowane (zamiast zewnętrznego wykonywalny) i że wbudowane obsługuje -e. (Jako wbudowany, powinien działać na wszystkich platformach, na których działa bash; nawiasem mówiąc, echo -edziała kshi zshrównież). Jednak zewnętrzne echonarzędzie w systemie OS X - /bin/echo- rzeczywiście nie obsługuje -e.
mklement0
3

Jeśli wiele razy potrzebujesz nowego wiersza w skrypcie, możesz zadeklarować zmienną globalną zawierającą nowy wiersz. W ten sposób możesz używać go w ciągach cudzysłowów (zmienne rozszerzenia).

NL=$'\n'
str="Hello World${NL} and here is a variable $PATH ===========${NL}"
pihentagy
źródło
Dlaczego $''wymagałaby podpowłoki?
Mat.
Przepraszam, źle odczytałem odpowiedź.
pihentagy
Czy mogę prosić o wyjaśnienia od downvoters?
pihentagy
Miły! Można to uwzględnić w zmiennych za pomocą podwójnych cudzysłowów, np."My dog eats:${NL}dog food"
Brad Parks
3

Z całej dyskusji oto dla mnie najprostszy sposób:

bash$ str="Hello World
==========="
bash$ echo "$str"
Hello World
===========

Polecenie echo musi używać podwójnych cudzysłowów .

kholis
źródło
3

Aby uzupełnić wspaniałe istniejące odpowiedzi:

Jeśli używasz bashi wolisz używać rzeczywistych znaków nowej linii dla czytelności , readto kolejna opcja przechwytywania dokumentu tutaj w zmiennej , która (podobnie jak inne rozwiązania tutaj) nie wymaga użycia podpowłoki.

# Reads a here-doc, trimming leading and trailing whitespace.
# Use `IFS= read ...` to preserve it (the trailing \n, here).
read -r -d '' str <<'EOF'   # Use `IFS= read ...` to preserve the trailing \n
Hello World
===========
EOF
# Test: output the variable enclosed in "[...]", to show the value's boundaries.
$ echo "$str"
[Hello World
===========]
  • -rzapewnia, że readnie interpretuje danych wejściowych (domyślnie traktowałby ukośniki specjalne, ale jest to rzadko potrzebne).

  • -d ''ustawia separator „rekordu” na pusty ciąg, powodując readodczytanie całego wejścia naraz (zamiast tylko jednej linii).

Zauważ, że pozostawiając $IFS(wewnętrzny separator pól) na wartości domyślnej $' \t\n'(spację, tabulator, nową linię), wszelkie początkowe i końcowe białe znaki są przycinane od wartości przypisanej do $str, w tym nowej linii tutaj-doc.
(Zauważ, że chociaż treść tutaj-doc zaczyna się od linii po ograniczniku początkowym ( 'EOF'tutaj), nie zawiera wiodącej nowej linii).

Zwykle jest to pożądane zachowanie, ale jeśli chcesz tę końcową linię, użyj IFS= read -r -d ''zamiast po prostu read -r -d '', ale zwróć uwagę, że wszystkie początkowe i końcowe białe znaki są następnie zachowywane.
(Należy pamiętać, że przejście IFS= bezpośrednio do readpolecenia oznacza, że ​​przypisanie działa tylko podczas tego polecenia, więc nie ma potrzeby przywracania poprzedniej wartości).


Użycie dokumentu tutaj pozwala również opcjonalnie użyć wcięcia w celu uruchomienia ciągu wielowierszowego dla zapewnienia czytelności:

# Caveat: indentation must be actual *tab* characters - spaces won't work.
read -r -d '' str <<-'EOF' # NOTE: Only works if the indentation uses actual tab (\t) chars.
    Hello World
    ===========
EOF
# Output the variable enclosed in "[...]", to show the value's boundaries.
# Note how the leading tabs were stripped.
$ echo "$str"
[Hello World
===========]

Umieszczenie -między znakiem <<otwierającym doc tutaj i otwierającym ( 'EOF'tutaj) powoduje, że wiodące znaki tabulacji są usuwane z ciała doc doc, a nawet ogranicznika zamykającego, ale należy pamiętać, że działa to tylko z rzeczywistymi znakami tabulacji , a nie spacjami, więc jeśli edytor tłumaczy naciśnięcia klawiszy tabulacji na spacje, potrzebna jest dodatkowa praca.

mklement0
źródło
-1

musisz to zrobić w ten sposób:

STR=$(echo -ne "Hello World\n===========\n")

Aktualizacja:

Jak zauważył Fred, w ten sposób stracisz końcowe „\ n”. Aby przypisać zmienną z rozwiniętymi sekwencjami odwrotnego ukośnika, wykonaj:

STR=$'Hello World\n===========\n\n'

przetestujmy to:

echo "[[$STR]]"

daje nam teraz:

[[Hello World
===========

]]

Pamiętaj, że $ „” jest inny niż $ „”. Drugi wykonuje tłumaczenie zgodnie z bieżącymi ustawieniami narodowymi. Dla deital patrz sekcja CYTOWANIE w man bash.

Michał Šrajer
źródło
-2
#!/bin/bash

result=""

foo="FOO"
bar="BAR"

result+=$(printf '%s' "$foo")$'\n'
result+=$(printf '%s' "$bar")$'\n'

echo "$result"
printf '%s' "$result"

wynik:

FOO
BAR

FOO
BAR
Vern
źródło
1
Dlaczego nie po prostu result+=$foo$'\n'? $(printf %s "$foo")zmniejszyłby końcowe znaki nowego wiersza, $foojeśli takie istnieją.
Stéphane Chazelas,
Nie ma wyjaśnienia dla kodu, inaczej wygląda dla mnie dobrze.
somethingSomething