bash polecenie wieloliniowe z komentarzami po znaku kontynuacji


29

Rozważać

echo \ # this is a comment
foo

To daje:

$ sh foo.sh
 # this is a comment
foo.sh: line 2: foo: command not found

Po kilku poszukiwaniach w Internecie znalazłem rozwiązanie firmy DigitalRoss na siostrzanej stronie Stack Overflow. Więc można to zrobić

echo `: this is a comment` \
foo

lub alternatywnie

echo $(: this is a comment) \
foo

Jednak DigitalRoss nie wyjaśnił, dlaczego te rozwiązania działają. Byłbym wdzięczny za wyjaśnienie. Odpowiedział komentarzem:

Kiedyś było gotopolecenie powłoki, które rozgałęziało się na etykiety określone jak :tutaj. gotoZniknął, ale nadal można używać : whateverskładni ... :jest rodzajem parsed komentarz teraz.

Chciałbym jednak więcej szczegółów i kontekstu, w tym omówienie przenośności.

Oczywiście, jeśli ktoś ma inne rozwiązania, to też byłoby dobrze.

Zobacz także wcześniejsze pytanie Jak komentować polecenia wieloliniowe w skryptach powłoki? .


Weź wiadomość do domu z dyskusji poniżej. To `: this is a comment`tylko podstawienie polecenia. Wynikiem : this is a commentjest nic, a to zostaje zastąpione `: this is a comment`.

Lepszym wyborem jest:

echo `# this is a comment` \
foo

Odpowiedzi:


25

Komentarze kończą się na pierwszym nowym wierszu (patrz reguła 10 rozpoznawania tokena powłoki ), nie zezwalając na wiersze kontynuacji , więc ten kod ma fooosobny wiersz poleceń:

echo # this is a comment \
foo

Jeśli chodzi o twoją pierwszą propozycję, po odwrotnym ukośniku nie ma nowej linii, po prostu cytujesz spację: jest to odpowiednik

echo ' # this is a comment'
foo

$(: this is a comment)zastępuje dane wyjściowe polecenia : this is a comment. Jeśli wynik tego polecenia jest pusty, jest to bardzo mylący sposób wstawiania komentarza na środku wiersza.

Magia się nie dzieje: :to zwykłe polecenie, narzędzie do dwukropka , które nic nie robi. Narzędzie dwukropka jest szczególnie przydatne, gdy składnia powłoki wymaga polecenia, ale zdarza się, że nie masz nic do roboty.

# Sample code to compress files that don't look compressed
case "$1" in
  *.gz|*.tgz|*.bz2|*.zip|*.jar|*.od?) :;; # the file is already compressed
  *) bzip2 -9 "$1";;
esac

Innym przypadkiem użycia jest idiom do ustawiania zmiennej, jeśli nie została jeszcze ustawiona.

: "${foo:=default value}"

Uwaga o goto jest historyczna. Narzędzie dwukropka sięga jeszcze przed powłoką Bourne'a , aż do powłoki Thompson , która miała instrukcję goto . Okrężnica oznaczała wtedy etykietę; dwukropek jest dość powszechną składnią etykiet goto (nadal występuje w sed ).


Dobra, widzę. Czy znasz lepsze sposoby wstawiania komentarzy w tym kontekście?
Faheem Mitha

3
@FaheemMitha Zgodnie z zaleceniami w cytowanym przez Ciebie wątku: podziel swoje polecenie na porcje możliwe do zarządzania i skomentuj każdą część. Jeśli twoje polecenie jest tak skomplikowane, że potrzebujesz komentarza pośrodku, nadszedł czas, aby je uprościć!
Gilles 'SO - przestań być zły'

1
Cóż, to polecenie ma wiele argumentów ... Konwertuje kilka plików wideo do jednego pliku. Nie widzę bezpośredniego sposobu, aby to uprościć. Może stworzysz jakąś listę i przekażesz ją jako argument? To chyba kolejne pytanie.
Faheem Mitha

@FaheemMitha Przykład: szybki make_FINDskrypt, który buduje długą listę argumentów find. Tutaj motywacja do budowania tego fragmentu po kawałku polega na tym, że każdy fragment pochodzi z korpusu pętli, ale ten sam styl pozwala komentować każdy fragment.
Gilles 'SO - przestań być zły'

1
Dzięki za przykład. Mój przykład to po prostu długa lista nazw podanych jako argumenty polecenia. Chcę dołączać komentarze do każdej nazwy, ponieważ jest to dla mnie najłatwiejszy sposób na zachowanie kontekstu. Nie widzę żadnego oczywistego sposobu na rozbicie polecenia na kawałki, a gdybym to zrobił, mogłoby to wydłużyć go i jest już wystarczająco długi.
Faheem Mitha

6

Możesz to osiągnąć za pomocą tablic Bash, np

#!/bin/bash
CMD=(
  echo  # this is a comment
  foo
  )

"${CMD[@]}"

Definiuje tablicę $CMD, a następnie ją rozwija. Po rozwinięciu linia wynikowa jest oceniana, więc w tym przypadku echo foojest wykonywana.

Tekst pomiędzy (i )definiuje tablicę i podlega zwykłej składni bash, więc wszystko w linii po #jest ignorowane.

Uwaga na temat zachowania cytowanych białych znaków

${CMD[@]}rozwija się do pojedynczego łańcucha, który jest konkatenacją wszystkich elementów, oddzielonych spacją. Po rozwinięciu Bash następnie parsował łańcuch na tokeny w zwykły sposób (por. $ IFS ), co często nie jest tym, czego chcemy.

Natomiast jeśli rozwinięcie jest zawinięte w podwójne cudzysłowy, tzn. "${CMD[@]}"Każdy element w tablicy zostaje zachowany. Rozważ różnicę między hello world second itemi "hello world" "second item".

Obrazowy przykład:

# LIST=("hello world" "second item")

# for ITEM in ${LIST[@]}; do echo $ITEM; done
hello
world
second
item

# for ITEM in "${LIST[@]}"; do echo $ITEM; done
hello world
second item

Dziękuję za odpowiedź, @RobM. Czy sugerujesz więc umieszczenie komentarzy / meta-informacji o pozycjach na liście w samej tablicy bash? To pytanie byłoby prawdopodobnie bardziej przydatne, gdybym podał przykład polecenia, którego próbowałem użyć. Nie wiem dlaczego tego nie zrobiłem.
Faheem Mitha

Okazuje się, że nie przeczytałem dokładnie twojego pytania. Myślałem, że zadajesz pytanie, z którym się łączysz. Ups :)
RobM

${CMD[@]}nie rozwija się do pojedynczego ciągu. - Rozwija się do wielu ciągów: nie tylko dla każdego elementu tablicy, ale także dla białych znaków w każdym elemencie. (Tj .: całkowicie bezużyteczne)
Robert Siemer

4

Nie rób $(: comment). To nie jest komentarz - to podpowłoka - kolejny zupełnie nowy proces powłoki dla większości powłok. Twoim celem jest zrobienie mniej w / w twój wkład, a nie więcej, co by to zrobił - nawet jeśli jest to bezcelowe.

Możesz zamiast tego zrobić ...

printf '<%s>\n' some args here ${-##*"${--

                my long comment block

                }"}  and "more ${-##*"${--

                and another one in the
                middle of the quoted string
                there shouldn\'t b\e any special &
                (character) `echo issues here i hope >&2`
                basically anything that isn\'t a close \}
                $(echo the shell is looking for one >&2)
                }$(echo "}'"\" need backslash escaping >&2
                                )${-##*${--

                nesting is cool though

             }}"}here too"

}'" need backslash escaping
<some>
<args>
<here>
<and>
<more here too>

Zasadniczo to, co się tam dzieje, polega na tym, że powłoka dokonuje substytucji. Za $-każdym razem zastępuje wartość specjalnego parametru powłoki . W każdym razie jest to krótki ciąg, ale zawsze jest ustawiony - a więc podstawienie wewnętrzne - które jest interpretowane jako wzór do usuwania z zewnętrznego - nie rozszerza się do treści między nawiasami, gdy używam -formy rozwinięcia.

Tutaj:

bash -x <<""
printf %s\\n '${-##*"'${-- a set param doesn\'t expand to this optional text }'"}'

+ printf '%s\n' '${-##*"hxB"}'
${-##*"hxB"}

Widzieć? Więc jest tylko dwukrotnie rozszerzony. Gdy tylko powłoka odkryje, że parametr jest ustawiony, wszystko w opcjonalnym polu ekspansji jest odrzucane, i rozwija się do całej wartości, która jest usuwana z siebie, a więc do zera. W większości pocisków nie potrzebujesz nawet znaków ucieczki, ale bashwymaga tego.

Jeszcze lepiej jest:

COMMENT=
echo      ${COMMENT-"
           this will work the same way
           but the stripping isn\'t
           necessary because, while
           the variable is set, it is also
           empty, and so it will expand to
           its value - which is nothing
           "} this you\'ll see

this you'll see
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.