Jak podzielić długi ciąg na wiele wierszy w wierszu polecenia odczytu -p w kodzie źródłowym?

19

Piszę skrypt instalacyjny, który będzie uruchamiany jako /bin/sh.

Jest wiersz z pytaniem o plik:

read -p "goat can try change directory if cd fails to do so. Would you like to add this feature? [Y|n] " REPLY

Chciałbym podzielić tę długą linię na wiele linii, aby żaden z nich nie przekraczał 80 znaków. Mówię o wierszach w kodzie źródłowym skryptu; nie chodzi o wiersze, które mają być faktycznie wydrukowane na ekranie podczas wykonywania skryptu!

Co próbowałem:

  • Pierwsze podejście:

    read -p "goat can try change directory if cd fails to do so. " \
        "Would you like to add this feature? [Y|n] " REPLY
    

    To nie działa, ponieważ nie jest drukowane Would you like to add this feature? [Y|n].

  • Drugie podejście:

    echo "goat can try change directory if cd fails to do so. " \
        "Would you like to add this feature? [Y|n] "
    read REPLY
    

    Nie działa tak dobrze. Drukuje znak nowej linii po znaku zachęty. Dodanie -nopcji do echonie pomaga: po prostu drukuje:

    -n goat can try change directory if cd fails to do so. Would you like to add this feature? [Y|n]
    # empty line here
    
  • Moje obecne obejście to

    printf '%s %s ' \
        "goat can try change directory if cd fails to do so." \
        "Would you like to add this feature? [Y|n] "
    read REPLY
    

i zastanawiam się, czy jest lepszy sposób.

Pamiętaj, że szukam /bin/shkompatybilnego rozwiązania.

Mateusz Piotrowski
źródło
1
Sugeruję, abyś 1. użył szerszego okna terminala i / lub edytora (np. Używam terminala 192 kolumna na 51 linii na moim monitorze 1440p - świetny do dostosowywania plików dziennika i edycji kodu źródłowego w vimie) oraz 2. uczysz się akceptować fakt, że czasami wiersze kodu źródłowego będą miały więcej niż 80 znaków - jest to nieuniknione. Gdybym użył mniejszej czcionki, mógłbym mieć jeszcze szerszy terminal, ale moja obecna konfiguracja jest dobrym kompromisem między szerokością a czytelnością.
cas

Odpowiedzi:

17

Przede wszystkim oddzielmy odczyt od wiersza tekstu za pomocą zmiennej:

text="line-1 line-2"             ### Just an example.
read -p "$text" REPLY

W ten sposób pojawia się problem: Jak przypisać dwie linie do zmiennej.

Oczywiście pierwszą próbą zrobienia tego jest:

a="line-1 \
line-2"

W ten sposób var aotrzymuje wartość line-1 line-2.

Ale nie podoba ci się brak wcięcia, które to powoduje, więc możemy spróbować odczytać wiersze do var z dokumentu doc ​​(pamiętaj, że wcięte linie wewnątrz dokumentu doc ​​wymagają tabulacji, a nie spacji, działać poprawnie):

    a="$(cat <<-_set_a_variable_
        line-1
        line-2
        _set_a_variable_
    )"
echo "test1 <$a>"

Ale to by się nie powiodło, ponieważ tak naprawdę napisane są dwa wiersze $a. Obejściem uzyskania tylko jednej linii może być:

    a="$( echo $(cat <<-_set_a_variable_
        line 1
        line 2
        _set_a_variable_
        ) )"
echo "test2 <$a>"

To blisko, ale stwarza inne dodatkowe problemy.

Prawidłowe rozwiązanie

Wszystkie powyższe próby sprawią, że problem będzie bardziej złożony, niż powinien.

Bardzo podstawowym i prostym podejściem jest:

a="line-1"
a="$a line-2"
read -p "$a" REPLY

Kod dla Twojego konkretnego przykładu to (dla dowolnej powłoki, która readobsługuje -p):

#!/bin/dash
    a="goat can try change directory if cd fails to do so."
    a="$a Would you like to add this feature? [Y|n] "
# absolute freedom to indent as you see fit.
        read -p "$a" REPLY

Dla wszystkich innych powłok użyj:

#!/bin/dash
    a="goat can try change directory if cd fails to do so."
    a="$a Would you like to add this feature? [Y|n] "
# absolute freedom to indent as you see fit.
        printf '%s' "$a"; read REPLY

źródło
@BinaryZebra Wiem. Właśnie poprawiłem „dla dowolnej powłoki”, ponieważ nie wszystkie powłoki przeczytały i nie wszystkie, które mają -p.
terdon
@terdon Oboje jesteśmy zgodni To jest powód podziękowania :-). Dzięki jeszcze raz.
1

Odwrotny ukośnik na końcu linii pozwala na kontynuację polecenia na wielu liniach, a nie na rzeczywiste podziały linii na wyjściu.

Na przykład twoje pierwsze podejście staje się rozkazem

read -p "goat can try change directory if cd fails to do so. " "Would you like to add this feature? [Y|n] " REPLY

Nie jestem pewien, dlaczego chcesz czytać, aby wyprowadzić wiele wierszy, ale po prostu readużyłbym linii wiersza i echopoprzednich linii.

Aby kod był bardziej czytelny w wielu wierszach, nie zamykaj / nie otwieraj cudzysłowów między wierszami.

Spróbuj tego:

read -p "goat can try change directory if cd fails to do so. \
Would you like to add this feature? [Y|n] " REPLY
Ian Petts
źródło
Nie chcę, żeby drukowało dwie linie! Chcę tylko, aby kod zawierał 80 znaków w wierszu w kodzie źródłowym skryptu ! Dziękuję za odpowiedź :)
Mateusz Piotrowski
Nie podoba mi się zaproponowane przez ciebie podejście, ponieważ nie ma wcięć. Jeśli moje readjest wcięte w funkcji, twoje podejście wydaje się popsuć czytelność.
Mateusz Piotrowski
1

Uważam, że podejście @ BinaryZebra jest oparte na zmiennej, aby być czystszym, ale możesz także zrobić to tak, jak próbowałeś. Musisz tylko zachować podział wiersza w cudzysłowie:

read -p "goat can try change directory if cd fails to do so. \
Would you like to add this feature? [Y|n] " REPLY
terdon
źródło
Czy uważasz, że dodanie nowego wiersza po każdym wierszu zwiększy czytelność? Ponieważ nie szukałem sposobu na wstawienie \ndo mojego monitu. Chciałem tylko podzielić kod na wiele linii, aby każda linia miała maksymalnie 80 znaków.
Mateusz Piotrowski
1
@MateuszPiotrowski oh, to ma o wiele więcej sensu. Zatem moja odpowiedź nie jest zbyt przydatna. Edytuj swoje pytanie i wyjaśnij, że chcesz podzielić kod, a nie wynik. Jestem teraz na telefonie komórkowym, ale zobaczę, czy jutro coś wymyślę.
terdon
1
@MateuszPiotrowski zrobiłem, dodałem działającą wersję tego, czego początkowo próbowałeś.
terdon
0

A co z tym właśnie:

#! / bin / bash
czytaj -p "Cześć
świat
to jest
wieloliniowy
monit: „PROMPT
echo $ PROMPT

Desmond
źródło
Jak to wcinasz?
Mateusz Piotrowski