Jak wyprowadzić ciąg multilinii w Bash?


250

Jak mogę wyprowadzić wieloliniowy ciąg w Bash bez używania wielu wywołań echa, takich jak:

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

Szukam przenośnego sposobu, aby to zrobić, używając tylko wbudowanych wersji Bash.


4
Jeśli wysyłasz komunikat o użyciu w odpowiedzi na niepoprawne wywołanie, zwykle wysyłasz ten komunikat do standardowego błędu zamiast do standardowego wyjścia, używającecho >&2 ...
Mark Reed

2
@MarkReed Komunikat o użyciu jest generowany przez wpisanie --help(które powinno przejść do standardowego wyjścia).
helpermethod

Dla innych, którzy przyjdą, więcej informacji na temat „tutaj dokumentów” jest dostępnych: tldp.org/LDP/abs/html/here-docs.html
Jeffrey Martinez

Sprawdź printfoparte na Gordon Davidson rozwiązanie. Pomimo tego, że znajduje się w cieniu podejść opartych na echolub cat, wydaje się, że jest to mniej kłopotów. Wprawdzie składnia `printf 'reprezentuje trochę krzywą uczenia się, ale chciałbym poznać inne wady (? Kompatybilność, wydajność? ...)
mjv

Odpowiedzi:


296

Tutaj dokumenty są często wykorzystywane do tego celu.

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

Report bugs to: 
up home page:
EOF

Są obsługiwane we wszystkich powłokach pochodnych Bourne'a, w tym we wszystkich wersjach Bash.


4
Tak - ale catnie jest wbudowany.
Mark Reed,

8
@ MarkReed: To prawda, ale zawsze jest dostępna (z wyjątkiem być może w nietypowych okolicznościach).
Wstrzymano do odwołania.

6
+1 tys. Skończyło się read -d '' help <<- EOF ...na tym, że przeczytałem ciąg multilinii w zmiennej, a następnie powtórzyłem wynik.
helpermethod

3
czy mogę zapisać HEREDOC do zmiennej?
chovy

177

lub możesz to zrobić:

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

Report bugs to: 
up home page: "

1
@OliverWeiler: Działa nawet w powłokach Bourne'a, takich jak Dash i Heirloom Bourne Shell .
Wstrzymano do odwołania.

6
Nie jest to świetne, jeśli potrzebujesz tego w funkcji, ponieważ albo musisz 1) odsunąć ciąg znaków do końca pliku po lewej stronie, albo 2) utrzymać wcięcie, aby wyrównać się z resztą kodu, ale następnie drukuje za pomocą wcięcia również
sg

43

Zainspirowany wnikliwymi odpowiedziami na tej stronie stworzyłem podejście mieszane, które uważam za najprostsze i bardziej elastyczne. Co myślisz?

Po pierwsze, definiuję użycie w zmiennej, co pozwala mi na ponowne użycie w różnych kontekstach. Format jest bardzo prosty, prawie WYSIWYG, bez potrzeby dodawania żadnych znaków kontrolnych. Wydaje mi się to dość przenośne (uruchomiłem to na MacOS i 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
"

Następnie mogę po prostu użyć go jako

echo "$__usage"

lub nawet lepiej, kiedy parsuję parametry, mogę po prostu echo tam w linijce:

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

4
zadziałało to dla mnie w skrypcie, w którym powyższa odpowiedź nie działa (bez modyfikacji).
David Welch,

3
Jest to o wiele czystsze niż angażowanie szeregu znaków, takich jak \ ti \ n, które trudno znaleźć w tekście i rozwinąć, aby wynik był znacznie inny niż ciąg znaków w skrypcie
sg

1
Z niektórych powodów drukuje dla mnie wszystko w tym samym wierszu: /
Nicolas de Fontenay

2
@Nicolas: Używanie podwójnych cudzysłowów echo "$__usage"było dla mnie konieczne. echo $__usagenie działał.
Mario

24

Użyj -eopcji, aby wydrukować nowy znak wiersza ze znakiem \n.

Próbka (ale nie jestem pewien, czy jest dobry czy nie)

Zabawne jest to, że -eopcja ta nie jest udokumentowana na stronie podręcznika MacOS ale nadal jest użyteczna. Jest to udokumentowane na stronie man Linuksa .


6
Te strony podręcznika dotyczą echokomendy dostarczonej przez system /bin/echo, która w systemie Mac OS nie ma -eopcji. kiedy używasz bash na tych systemach, jego wbudowane echopolecenie przejmuje kontrolę. Możesz to zobaczyć, wpisując /bin/echo whateveri obserwując różnicę w zachowaniu. Aby wyświetlić dokumentację wbudowanego, wpisz help echo.
Mark Reed,

1
/bin/echoczęsto różni się w zależności od systemu operacyjnego i różni się od wbudowanego Bash echo.
Wstrzymano do odwołania.

@ MarkReed: Spróbuję później, ale dziękuję za informacje. +1. Zostawię tutaj swoją odpowiedź, ponieważ toczy się całkiem sporo dobrej dyskusji.
nhahtdh

7
echo -enie jest przenośny - na przykład niektóre implementacje echa wypiszą „-e” jako część wyniku. Jeśli chcesz mieć przenośność, użyj printf. Na przykład robi to / bin / echo w OS X 10.7.4. IIRC echo wbudowane w bash również było dziwne pod 10.5.0, ale nie pamiętam już szczegółów.
Gordon Davisson,

2
echo -eugryzł mnie wcześniej ... Zdecydowanie użyj printflub catheredoc. <<-Wariant tutaj docs są szczególnie dobre, bo można rozebrać prowadzące wcięcia na wyjściu, ale dla czytelności tiret w skrypcie
zbeekman

22

Ponieważ zaleciłem printfw komentarzu, prawdopodobnie powinienem podać kilka przykładów jego użycia (chociaż w przypadku drukowania komunikatu o użytkowaniu chętniej skorzystam z odpowiedzi Dennisa lub Chrisa). printfjest nieco bardziej skomplikowany w użyciu niż echo. Pierwszym argumentem jest ciąg formatu, w którym znaki specjalne (jak \n) są zawsze interpretowane; może również zawierać dyrektywy formatujące zaczynające się od %, które kontrolują, gdzie i jak mają być uwzględnione dodatkowe argumenty. Oto dwa różne podejścia do używania go w komunikacie użytkowania:

Po pierwsze, możesz dołączyć całą wiadomość do ciągu formatu:

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

Zauważ, że w przeciwieństwie do tego echomusisz wyraźnie dołączyć ostatni znak nowej linii. Ponadto, jeśli wiadomość zawiera jakieś %znaki, należy je zapisać jako %%. Jeśli chcesz dołączyć raport o błędzie i adresy strony głównej, można je dodać całkiem naturalnie:

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

Po drugie, możesz po prostu użyć ciągu formatu, aby wydrukować każdy dodatkowy argument w osobnym wierszu:

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

Dzięki tej opcji dodanie raportu o błędzie i adresu strony głównej jest dość oczywiste:

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

9

Również z wciętym kodem źródłowym można użyć <<-(z końcowym myślnikiem), aby zignorować wiodące tabulatory (ale nie wiodące spacje). Na przykład to:

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

Wyświetla wcięty tekst bez wiodących białych znaków:

line1
line2

To nie działało dla mnie. Jakiej powłoki używasz?
four43

Nie działał w bash 4.4.19 w Ubuntu. Nie
usunęło

1
@ four43, miałeś rację. Nie działa, aby usunąć spacje wiodące. Usuwa jednak wiodące karty. Poprawiłem więc odpowiedź z tabulatorów i spacji, na tabulatory, a nie spacje. Przepraszam za błąd. Sprawdziłem instrukcję i wyraźnie mówi tylko, że karty zostały usunięte. Dziękuję za zwrócenie mojej uwagi na to.
Eliptyczny widok

0

Jeśli skorzystasz z rozwiązania @jorge i stwierdzisz, że wszystko jest w tym samym wierszu, upewnij się, że umieściłeś zmienną w cudzysłowie:

echo $__usage

wypisze wszystko w jednym wierszu, podczas gdy

echo "$__usage"

zachowa nowe linie.


To jest właściwie jedyne rozwiązanie, które działało dla mnie. Printf robi wiele rzeczy, a mój multiline xml prawdopodobnie wymaga dużo ucieczki, ponieważ całkowicie zniekształca zawartość. cat <<EOF .... EOF
Przypisuję
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.