Как: << "END" работает в bash для создания многострочного блока комментариев?

Я нашел отличный ответ для комментария в bash script ( @sunny256):

#!/bin/bash
echo before comment
: <<'END'
bla bla
blurfl
END
echo after comment

' и ' вокруг разделителя END важны, иначе вещи внутри блока, такие как, например, $(command), будут проанализированы и выполнены.

Это может быть уродливо, но оно работает, и я очень хочу знать, что это значит. Может ли кто-нибудь объяснить это просто? Я уже нашел объяснение для :, что это не-op или true. Но мне не имеет смысла называть no-op или true в любом случае....

Ответ 1

Я боюсь, что это объяснение менее "простое" и более "тщательное", но здесь мы идем.

Целью комментария является текст, который не интерпретируется и не выполняется как код.

Первоначально оболочка UNIX не имела синтаксиса комментария как такового. Однако он имел нулевую команду : (после фактической двоичной программы на диске, /bin/:), которая игнорирует свои аргументы и возвращает true. Таким образом, вы можете поместить такую ​​строку в свой script:

: This is a comment

В командной оболочке будет выполняться команда, но ничего не произойдет. Миссия выполнена, правильно?

Проблема заключается в том, что строка по-прежнему рассматривается как команда. Лексический анализ - замещение параметров, разбиение слов и т.д. - все еще происходит на тех аргументах, которые должны быть проигнорированы. Эта обработка означает, что вы рискуете синтаксической ошибкой в ​​ "комментарии" сбой всего вашего script:

 : Now let see what happens next
 echo "Hello, world!"
 #=> hello.sh: line 1: unexpected EOF while looking for matching `''

Эта проблема привела к введению подлинного синтаксиса комментария: знакомого теперь #. Все от # до конца строки полностью игнорируется оболочкой, поэтому вы можете поместить туда что угодно, не беспокоясь о синтаксической действительности:

 # Now let see what happens next
 echo "Hello, world!"
 #=> Hello, world!

Однако вы искали многострочный (блок) комментарий, сортировки, введенной /* (и завершенной */) в C или Java. Оболочка не имеет такого синтаксиса, и обычной практикой является просто поставить # перед каждой строкой, которую вы хотите прокомментировать. Эта практика тоже моя рекомендация... но это, по общему признанию, не очень "многострочный".

Решение, которое вы нашли, использует то, что называется здесь-документ. Синтаксис some-command <<whatever вызывает следующие строки текста - из строки сразу после команды, вплоть до следующей строки, содержащей только текст whatever - для чтения и подачи в качестве стандартного ввода в some-command. Здесь альтернативная реализация оболочки "Hello, world", которая использует эту функцию:

cat <<EOF
Hello, world
EOF

Если вы замените cat на наш друг :, вы обнаружите, что он игнорирует не только его аргументы, но и его ввод: вы можете подавать все, что хотите, и он все равно ничего не сделает.

Однако содержимое этого документа обрабатывается строкой. Так же, как и при использовании исходного текста :, версия здесь-документа подвержена риску синтаксических ошибок внутри того, что должно быть комментарием:

#!/bin/sh -e 
: <<EOF
This is a backtick: `
EOF
echo 'In modern shells, $(...) is preferred over backticks.'
#=> ./demo.sh: line 2: bad substitution: no closing "`" in `

Решение, как видно из кода, который вы нашли, состоит в том, чтобы указать дозорный пункт конца документа (например, с помощью <<'EOF'). Это приводит к тому, что весь текст документа здесь рассматривается как буквальный текст - не происходит расширение параметра или другая обработка. Вместо этого текст передается команде без изменений, как если бы она считывалась из файла. Таким образом, кроме строки, состоящей только из строки терминатора (EOF или END или любого другого), здесь-документ может содержать любые символы:

#!/bin/sh -e
: <<'EOF'
This is a backtick: `
EOF
echo 'In modern shells, $(...) is preferred over backticks.'
#=> In modern shells, $(...) is preferred over backticks.

(Стоит отметить, что то, как вы цитируете часового, не имеет значения - вы можете использовать <<'EOF', <<E"OF" или даже <<EO\F; все имеют одинаковый результат. Это отличается от способа здесь -документы работают на некоторых других языках, таких как Perl, где контент обрабатывается по-разному в зависимости от способа цитирования часового.)

Несмотря ни на что из вышеперечисленного, я настоятельно рекомендую вместо этого просто поместить # в начало каждой строки, которую вы хотите прокомментировать. Любой достойный редактор кода сделает эту операцию простой - даже простой старый vi - и преимущество в том, что никто, кто читает ваш код, не будет тратить энергию на то, что происходит с чем-то, что, в конце концов, предназначено для документации выгода.

Ответ 2

Он называется Здесь Documen t. Это кодовый блок, который позволяет отправлять список команд другой команде или программе

Строка, следующая за <<, является маркером, определяющим конец блока. Если вы отправляете команды в no-op, ничего не происходит, поэтому вы можете использовать его как блок комментариев.

Ответ 3

Этот heredoc синтаксис. Это способ определения многострочных строковых литералов.

Как объясняет ответ в вашей ссылке, одиночные кавычки вокруг END отключают интерполяцию, подобно тому, как одиночные кавычки отключают интерполяцию в обычных строках bash.