многострочная команда 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 является историческим. Утилита двоеточия восходит к еще до оболочки Bourne , вплоть до оболочки Thompson , в которой была инструкция goto . Двоеточие тогда означало метку; двоеточие - довольно распространенный синтаксис для меток goto (он все еще присутствует в sed ).

Жиль "ТАК - перестань быть злым"
источник
Да я вижу. Есть ли какие-нибудь более эффективные способы вставки комментариев в этом контексте, о которых вы знаете?
Фахим Митха
3
@FaheemMitha В соответствии с рекомендацией, которую вы цитируете, разбейте свою команду на управляемые фрагменты и прокомментируйте каждый фрагмент. Если ваша команда настолько сложна, что в середине необходим комментарий, самое время упростить ее!
Жиль "ТАК - перестань быть злым"
1
Ну, у рассматриваемой команды много аргументов ... Она конвертирует кучу видеофайлов в один файл. Я не вижу прямого способа упростить это. Может быть, создать какой-то список и передать его в качестве аргумента? Я думаю, это может быть другой вопрос.
Фахим Митха
@FaheemMitha Пример: быстрый make_FINDскрипт, который строит длинный список аргументов find. Здесь, мотивация для создания этого чанка по чанку состоит в том, что каждый чанк происходит из тела цикла, но тот же стиль позволяет комментировать каждый чанк.
Жиль "ТАК - перестань быть злым"
1
Спасибо за пример. Мой пример - это просто длинный список имен, приведенных в качестве аргументов команды. Я хочу, чтобы комментарии были добавлены к каждому имени, потому что это самый простой способ сохранить контекст. Я не вижу очевидного способа разбить команду на части, и если бы я это сделал, это могло бы сделать ее еще длиннее, и уже достаточно долго.
Фахим Митха
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
источник
Спасибо за ответ, @RobM. Итак, вы предлагаете включить комментарии / мета-информацию об элементах в списке в самом массиве bash? Этот вопрос, вероятно, был бы более полезным, если бы я включил пример команды, которую я пытался использовать. Я не знаю, почему я не сделал.
Фахим Митха
Бах, оказывается, я не внимательно прочитал твой вопрос. Думаю, вы задали вопрос, с которым вы связаны. К сожалению. :)
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
mikeserv
источник