bash багаторядкова команда з коментарями після символу продовження


29

Розглянемо

echo \ # this is a comment
foo

Це дає:

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

Після деякого пошуку в Інтернеті я знайшов рішення DigitalRoss на сестринському сайті Stack Overflow. Так можна зробити

echo `: this is a comment` \
foo

або альтернативно

echo $(: this is a comment) \
foo

Однак DigitalRoss не пояснив, чому ці рішення працюють. Я буду вдячний за пояснення. Він відповів коментарем:

Раніше була gotoкоманда оболонки, яка розгалужується на вказані :тут мітки . gotoПішов , але ви все ще можете використовувати : whateverсинтаксис ... :це свого роду аналізованої коментар в даний час.

Але я хотів би отримати більше деталей та контексту, включаючи обговорення переносимості.

Звичайно, якщо хтось має інші рішення, це теж було б добре.

Дивіться також попереднє питання Як коментувати багаторядкові команди в скриптах оболонки? .


Візьміть домашнє повідомлення з обговорення нижче. Це `: this is a comment`просто підміна команд. Результатом роботи : this is a commentнічого, і це ставиться на місце `: this is a comment`.

Кращим вибором є наступний:

echo `# this is a comment` \
foo

Відповіді:


25

Коментарі закінчуються на першому новому рядку (див. Правило 10 розпізнавання лексеми оболонки ), не дозволяючи продовжувати рядки , тому цей код має fooокремий командний рядок:

echo # this is a comment \
foo

Що стосується вашої першої пропозиції, за зворотним нахилом не йде новий рядок, ви просто цитуєте пробіл: він еквівалентний

echo ' # this is a comment'
foo

$(: this is a comment)підміняє вихід команди : this is a comment. Якщо вихід цієї команди порожній, це фактично дуже заплутаний спосіб вставити коментар в середину рядка.

Ніякої магії не відбувається: :це звичайна команда, утиліта товстої кишки , яка нічого не робить. Утиліта товстої кишки в основному корисна, коли синтаксис оболонки вимагає команди, але вам трапляється нічого робити.

# 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

Іншим випадком використання є ідіома для встановлення змінної, якщо вона ще не встановлена.

: "${foo:=default value}"

Зауваження про гото є історичним. Утиліта товстої кишки бере початок ще до оболонки Борна , аж до оболонки Томпсона , яка мала інструкцію про гото . Тоді двокрапка означала мітку; двокрапка - досить поширений синтаксис для міток goto (він все ще присутній у sed ).


Добре, я бачу. Чи є якісь кращі способи вставити коментарі в цей контекст, про які вам відомо?
Faheem Mitha

3
@FaheemMitha Як рекомендовано в нитці, яку ви цитуєте: розділіть свою команду на керовані фрагменти та прокоментуйте кожен фрагмент. Якщо ваша команда настільки складна, що вам потрібен коментар посередині, настав час її спростити!
Жил 'SO- перестань бути злим'

1
Що ж, у команди, про яку йдеться, є безліч аргументів ... Це перетворення купу відеофайлів в один файл. Я не бачу прямого способу спростити це. Може, створити якийсь список і передати його як аргумент? Я думаю, це може бути інше питання.
Faheem Mitha

Приклад @FaheemMitha: make_FINDсценарій швидкого доступу , який створює довгий список аргументів find. Тут мотивація побудови цього фрагмента за допомогою шматка полягає в тому, що кожен шматок походить від тіла петлі, але той самий стиль дозволяє коментувати кожен шматок.
Жил 'SO- перестань бути злим'

1
Дякую за приклад. Мій приклад - це лише довгий список імен, наведених як аргументи команди. Я хочу, щоб коментарі додавалися до кожного імені, тому що це найпростіший спосіб зберегти контекст. Я не бачу очевидного способу розбити команду на шматки, і якби я це зробив, це може зробити її ще довшою, і вона вже досить довга.
Faheem Mitha

6

Ви можете досягти цього за допомогою масивів Bash, наприклад

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

"${CMD[@]}"

Це визначає масив $CMD, а потім розширює його. Після розширення отриманий рядок оцінюється, тому в цьому випадку echo fooвиконується.

Текст між масивом (і )визначає масив і підпорядковується звичайному синтаксису bash, тому все в рядку після #цього ігнорується.

Зверніть увагу на збереження процитованого простору

${CMD[@]}розширюється до однієї струни, яка є об'єднанням усіх елементів, розділених пробілом. Після розширення Bash потім буде розбирати рядок у лексеми звичайним чином (cf $ IFS ), що часто не є тим, що ми хочемо.

На противагу цьому, якщо розширення загорнуте у подвійні лапки, тобто "${CMD[@]}"кожен елемент масиву зберігається. Розглянемо різницю між hello world second itemі "hello world" "second item".

Ілюстративний приклад:

# 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

Дякую за відповідь, @RobM. Отже, ваша пропозиція полягає в тому, щоб включити коментарі / метаінформацію щодо елементів у список до самого масиву bash? Це питання, ймовірно, було б кориснішим, якби я включив приклад того типу команди, який я намагався використовувати. Я не знаю, чому я цього не зробив.
Faheem Mitha

Ба, виявляється, я уважно не прочитав ваше запитання. Ви думали, що ви ставите питання, з яким ви пов’язані. На жаль :)
RobM

${CMD[@]}не розширюється на один рядок. - Він розширюється на багато рядків: не просто розділений для кожного елемента масиву, а й на пробіл у кожному елементі. (Тобто: абсолютно марний)
Роберт Сімер

4

Не робіть $(: comment). Це не коментар - це нижня оболонка - ще один цілий новий процес оболонки для більшості оболонок. Ваша мета - зробити менше вводу, не більше, що саме це зробить, навіть якщо це безглуздо.

Ви можете замість цього зробити ...

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>

В основному те, що там відбувається, оболонка робить заміну. Він $-щоразу підміняє значення спеціального параметра оболонки . Це все-таки коротка струна, але вона завжди встановлюється - і тому внутрішня підміна - що інтерпретується як шаблон, який потрібно знімати із зовнішньої, - не розширюється на вміст між дужками, коли я використовую -форму розширення.

Тут:

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

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

Побачити? Тож вона просто розширюється вдвічі. Як тільки оболонка знаходить параметр, встановлюється все, що в необов'язковому полі розширення відкидається, в значній мірі, і він розширюється до цілого значення, яке видаляється з себе і так ні до чого взагалі нічого. У більшості оболонок вам навіть не потрібні цитати, але bashвимагають цього.

Ще краще:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.