Многострочный синтаксис для передачи heredoc; это портативный?

132

Мне знаком этот синтаксис:

cmd1 << EOF | cmd2
text
EOF

но только что обнаружил, что bash позволяет мне писать:

cmd1 << EOF |
text
EOF
cmd2

(heredoc используется в качестве ввода для cmd1, а вывод cmd1 передается по конвейеру в cmd2). Это кажется очень странным синтаксисом. Он портативный?

Уильям Перселл
источник
Я пришел сюда , чтобы найти хороший способ расщепления этого на несколько строк: big-long-command1 with lots of args << EOF | big-long-command2 with lots of args. «Странный синтаксис» кажется лучшим способом.
PaulC
Один удобный случай использования для этого - когда вы пытаетесь преобразовать таблицу, разделенную пробелами, в таблицу с разделителями-табуляциями, чтобы вы могли вставить ее в электронные таблицы Google. Вам не нужно создавать временный файл.
Шридхар Сарнобат
Первый не работал у меня в z-shell. Мне не нравится второй, потому что он отчуждает | из команды, теряя идиомичность (?) конвейеров оболочки.
Шридхар Сарнобат 05

Ответы:

104

Да, стандарт POSIX позволяет это. По версии 2008 года:

Здесь-документ должен рассматриваться как отдельное слово, которое начинается после следующего <newline>и продолжается до тех пор, пока не появится строка, содержащая только разделитель и a <newline>, без <blank>символов между ними. Затем начинается следующий здесь-документ, если он есть.

И включает этот пример нескольких «здесь-документов» в одну строку:

cat <<eof1; cat <<eof2
Hi,
eof1
Helene.
eof2

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

cat file |
cmd

Грамматика оболочки (далее на связанной странице) включает следующие определения:

pipe_sequence    :                             command
                 | pipe_sequence '|' linebreak command

newline_list     :              NEWLINE
                 | newline_list NEWLINE
                 ;
linebreak        : newline_list
                 | /* empty */

Таким образом, за символом вертикальной черты может следовать конец строки, и он все равно будет считаться частью конвейера.

Нед Дейли
источник
26

Да, это в грамматике оболочки POSIX. У вас также может быть более одного документа для одной команды (в некоторых других примерах используется два catвызова, но это тоже работает):

cat <<EOF1 <<EOF2
first here-doc
EOF1
second here-doc
EOF2

Это надумано (с использованием двух документов здесь для стандартного ввода), но если вы думаете о вводе данных для разных файловых дескрипторов, это сразу имеет смысл.

Также есть возможность полностью отказаться отcat . Почему бы не сделать этот документ напрямую доступным для cmd:

cmd << EOF
input
here
EOF
Jens
источник
`` cat << EOF1 << EOF2 first here-doc EOF1 second here-doc EOF2 '' Вышеупомянутое не работает.
user1424739 01
@ user1424739 Он работает в текущих zsh и bash. Кажется, что ash и ksh93 выводят здесь только второй документ.
Йенс
Почему отрицательный голос? Если что-то неточно, пожалуйста, дайте мне возможность исправить это.
Jens
Это довольно мило при использовании sudo tee /etc/securefile.conf <<EOF.
dragon788
На какой версии bash это работает? Используя bash 4.4.19 (в ubuntu 18.04.02) и bash 5.0 (образ докера), я получил только второй здесь-документ. А может есть конкретный вариант?
huelbois 05
17

Хм, полагаю, что да, согласно тесту в bash в режиме POSIX:

$ bash --posix
$ cat <<EOF |
> ahoj
> nazdar
> EOF
> sed 's/a/b/'
bhoj
nbzdar
TMS
источник
Еще одно маленькое замечание: не ставьте пробелов после закрытия EOF. Подсказка будет вести себя странно, и вы удивитесь, что, черт возьми, не так
Шридхар Сарнобат
2
Запуск bash в режиме POSIX отключает некоторые расширения, но ни в коем случае не все из них. Таким образом, хотя этот ответ верен с точки зрения того, что позволяет POSIX, его рассуждения не очень эффективно поддерживают его.
Чарльз Даффи
3

Привет, проверьте это, например

#!/bin/sh
( base32 -d | base64 -d )<<ENDOFTEXT
KNDWW42DNNSHS5ZXPJCG4MSVM5MVQVT2JFCTK3DELBFDCY2IIJYGE2JUJNHWS22LINVHQMCMNVFD
CWJQIIZVUV2JOVNEOVJLINTW6PIK
ENDOFTEXT

С уважением

BUC
источник