Как cat ‹< EOF работает в bash?

Мне нужно было написать сценарий для ввода многострочного ввода в программу (psql).

После небольшого поиска в Google я обнаружил, что работает следующий синтаксис:

cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Это правильно создает многострочную строку (от BEGIN; до END; включительно) и передает ее как вход в psql.

Но я понятия не имею, как / почему это работает, может кто-нибудь объяснить?

Я имею в виду в основном cat << EOF, я знаю, что > выводит в файл, >> добавляет в файл, < читает ввод из файла.

Что именно делает <<?

И есть ли для этого справочная страница?


person hasen    schedule 23.03.2010    source источник
comment
Вероятно, это бесполезное использование cat. Попробуйте psql ... << EOF ... См. Также здесь строки. mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings   -  person Dennis Williamson    schedule 23.03.2010
comment
Я удивлен, что он работает с котом, но не с эхом. cat должен ожидать, что имя файла будет stdin, а не символьной строкой. psql ‹< EOF звучит логично, но не иначе. Работает с котом, но не с эхом. Странное поведение. Есть какие-нибудь подсказки об этом?   -  person Alex    schedule 24.03.2015
comment
Отвечая себе: cat без параметров выполняет и реплицирует на вывод все, что отправлено через ввод (stdin), следовательно, используя свой вывод для заполнения файла через ›. Фактически имя файла, считываемое как параметр, не является потоком стандартного ввода.   -  person Alex    schedule 24.03.2015
comment
@Alex echo просто печатает аргументы командной строки, в то время как cat читает stding (когда он передан по конвейеру) или читает файл, соответствующий его аргументам командной строки   -  person The-null-Pointer-    schedule 01.01.2018


Ответы (9)


Это называется форматом heredoc, чтобы предоставить строку в стандартный ввод. Подробнее см. https://en.wikipedia.org/wiki/Here_document#Unix_shells. .


От 1_:

Здесь документы

Этот тип перенаправления инструктирует оболочку читать ввод из текущего источника до тех пор, пока не появится строка, содержащая только слово (без завершающих пробелов).

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

Формат здесь-документов:

          <<[-]word
                  here-document
          delimiter

Для слова не выполняется раскрытие параметров, подстановка команд, арифметическое раскрытие или раскрытие имени пути. Если какие-либо символы в слове заключены в кавычки, разделитель является результатом удаления кавычек в слове, а строки в здесь- документ не раскрывается. Если слово не заключено в кавычки, все строки здесь-документа подвергаются расширению параметров, подстановке команд и арифметическому расширению. В последнем случае последовательность символов \<newline> игнорируется, и \ необходимо использовать для заключения в кавычки символов \, $ и `.

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

person kennytm    schedule 23.03.2010
comment
Мне было труднее всего отключить расширение переменных / параметров. Все, что мне нужно было сделать, это использовать двойные кавычки, и это исправило! Спасибо за информацию! - person Xeoncross; 27.05.2011
comment
Что касается <<-, обратите внимание, что удаляются только начальные символы табуляции, но не символы мягкой табуляции. Это один из тех редких случаев, когда вам действительно нужен символ табуляции. Если в остальной части документа используются программные вкладки, убедитесь, что отображаются невидимые символы и (например) скопируйте и вставьте символ табуляции. Если вы все сделаете правильно, подсветка синтаксиса должна правильно улавливать конечный разделитель. - person trkoch; 10.11.2015
comment
Я не понимаю, чем этот ответ более полезен, чем приведенные ниже. Он просто извергает информацию, которую можно найти в других местах (которые, вероятно, уже были проверены). - person BrDaHa; 13.07.2017
comment
@BrDaHa, может, и нет. Почему вопрос? из-за голосов? он был единственным за несколько лет. это видно, сравнивая даты. - person Alexei Martianov; 10.12.2019

Синтаксис cat <<EOF очень полезен при работе с многострочным текстом в Bash, например. при назначении многострочной строки переменной оболочки, файлу или каналу.

Примеры использования синтаксиса cat <<EOF в Bash:

1. Назначьте многострочную строку переменной оболочки.

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

Переменная $sql теперь также содержит символы новой строки. Вы можете проверить с помощью echo -e "$sql".

2. Передайте многострочную строку в файл в Bash.

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

Теперь файл print.sh содержит:

#!/bin/bash
echo $PWD
echo /home/user

3. Передайте многострочную строку в канал в Bash.

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

Файл b.txt содержит строки bar и baz. Тот же результат выводится на stdout.

person Vojtech Vitek    schedule 04.02.2014
comment
1. 1 и 3 можно сделать без кота; 2. Пример 1 можно выполнить с помощью простой многострочной строки. - person Daniel Alder; 03.04.2020
comment
Вместо того, чтобы создавать другой процесс с помощью cat, почему бы вместо этого не использовать IFS = '' read -r -d? - person Joseph Van Riper; 22.09.2020
comment
Стоит отметить, что при использовании tee вместо cat, sudo можно использовать для записи в файл в ограниченном месте. Как например sudo tee /etc/somepath/file > /dev/null <<EOF ... - person Wolfson; 26.02.2021

В вашем случае «EOF» известен как «Тег здесь». Обычно <<Here сообщает оболочке, что вы собираетесь ввести многострочную строку до «тега» Here. Вы можете назвать этот тег как хотите, часто это EOF или STOP.

Некоторые правила для тегов Here:

  1. Тег может быть любой строкой, в верхнем или нижнем регистре, хотя большинство людей по соглашению используют прописные буквы.
  2. Тег не будет считаться тегом Here, если в этой строке есть другие слова. В этом случае он будет считаться просто частью строки. Тег должен быть сам по себе на отдельной строке, чтобы считаться тегом.
  3. Тег не должен иметь начальных или конечных пробелов в этой строке, чтобы считаться тегом. В противном случае он будет считаться частью строки.

пример:

$ cat >> test <<HERE
> Hello world HERE <-- Not by itself on a separate line -> not considered end of string
> This is a test
>  HERE <-- Leading space, so not considered end of string
> and a new line
> HERE <-- Now we have the end of the string
person edelans    schedule 22.08.2014
comment
это лучший реальный ответ ... вы определяете оба и четко указываете основную цель использования вместо связанной теории ... что важно, но не обязательно ... спасибо - супер полезно - person oemb1905; 22.02.2017
comment
@edelans, вы должны добавить, что при использовании <<- ведущая вкладка не помешает распознаванию тега - person The-null-Pointer-; 01.01.2018
comment
ваш ответ щелкнул меня вы собираетесь ввести многострочную строку - person AbstProcDo; 28.10.2018

POSIX 7

kennytm цитирует man bash, но большая часть этого также POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04:

Операторы перенаправления «‹<» и «‹< -» позволяют перенаправлять строки, содержащиеся во входном файле оболочки, известном как «здесь-документ», на ввод команды.

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

[n]<<word
    here-document
delimiter

где необязательное n представляет собой номер дескриптора файла. Если номер опущен, здесь-документ относится к стандартному вводу (дескриптор файла 0).

Если какой-либо символ в слове заключен в кавычки, разделитель должен быть сформирован путем удаления кавычек в слове, а строки настоящего документа не должны расширяться. В противном случае разделителем будет само слово.

Если символы в слове не цитируются, все строки настоящего документа должны быть расширены для расширения параметров, подстановки команд и арифметического расширения. В этом случае ввод ведет себя как внутренние двойные кавычки (см. Двойные кавычки). Однако символ двойной кавычки ('"') не должен обрабатываться специально в данном документе, за исключением случаев, когда двойная кавычка появляется внутри" $ () "," `` "или" $ {} ".

Если символ перенаправления - «‹< -», все начальные символы <tab> должны быть удалены из строк ввода и строки, содержащей конечный разделитель. Если в строке указано более одного оператора «‹<» или «‹< -», здесь-документ, связанный с первым оператором, должен быть предоставлен первым приложением и должен быть прочитан оболочкой первым.

Когда здесь-документ читается с терминального устройства и оболочка является интерактивной, она должна записывать содержимое переменной PS2, обработанной, как описано в разделе «Переменные оболочки», в стандартную ошибку перед чтением каждой строки ввода до тех пор, пока не будет распознан разделитель.

Примеры

Некоторые примеры еще не приведены.

Котировки предотвращают расширение параметра

Без кавычек:

a=0
cat <<EOF
$a
EOF

Выход:

0

С цитатами:

a=0
cat <<'EOF'
$a
EOF

или (уродливо, но верно):

a=0
cat <<E"O"F
$a
EOF

Выходы:

$a

Дефис удаляет первые табуляции

Без дефиса:

cat <<EOF
<tab>a
EOF

где <tab> - буквальная вкладка и может быть вставлена ​​с помощью Ctrl + V <tab>

Выход:

<tab>a

С дефисом:

cat <<-EOF
<tab>a
<tab>EOF

Выход:

a

Это, конечно, существует для того, чтобы вы могли делать отступы в вашем cat, как в окружающем коде, который легче читать и поддерживать. Например.:

if true; then
    cat <<-EOF
    a
    EOF
fi

К сожалению, это не работает с пробелами: POSIX одобрил здесь tab отступ. Ой.

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 09.06.2015
comment
В вашем последнем примере, в котором обсуждаются <<- и <tab>a, следует отметить, что цель состояла в том, чтобы разрешить нормальный отступ кода в сценарии, позволяя тексту heredoc, представленному принимающему процессу, начинаться в столбце 0. Это не слишком часто встречающаяся функция и немного больше контекста может предотвратить много головокружений ... - person David C. Rankin; 12.08.2015
comment
Как мне избежать расходов, если часть содержимого между моими тегами EOF нужно расширить, а часть - нет? - person Jeanmichel Cote; 23.09.2015
comment
... просто используйте обратную косую черту перед $ - person Jeanmichel Cote; 23.09.2015
comment
@JeanmichelCote Я не вижу лучшего варианта :-) С обычными строками вы также можете подумать о смешивании кавычек, таких как "$a"'$b'"$c", но здесь нет аналога AFAIK. - person Ciro Santilli 新疆再教育营六四事件ۍ 23.09.2015

Использование футболки вместо кота

Не совсем как ответ на исходный вопрос, но я все равно хотел поделиться этим: мне нужно было создать файл конфигурации в каталоге, для которого требуются права root.

В этом случае не работает следующее:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF

потому что перенаправление обрабатывается вне контекста sudo.

Вместо этого я использовал это:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
person Andreas Maier    schedule 13.02.2017
comment
в вашем случае используйте sudo bash -c 'cat ‹? EOF ›/etc/somedir/foo.conf # мой файл конфигурации foo = bar EOF' - person likewhoa; 19.05.2020

Небольшое расширение к приведенным выше ответам. Завершающий > направляет ввод в файл, перезаписывая существующее содержимое. Однако особенно удобно использовать двойную стрелку >>, которая добавляется, добавляя новый контент в конец файла, как в:

cat <<EOF >> /etc/fstab
data_server:/var/sharedServer/authority/cert /var/sharedFolder/sometin/authority/cert nfs
data_server:/var/sharedServer/cert   /var/sharedFolder/sometin/vsdc/cert nfs
EOF

Это расширяет ваш fstab, не беспокоясь о случайном изменении любого его содержимого.

person Lefty G Balogh    schedule 29.01.2020

Короче говоря, EOF marker (но можно использовать и другой литерал) - это формат heredoc, который позволяет вам вводить данные как многострочные. Большая путаница возникает из-за того, как cat на самом деле это работает. Вы можете использовать cat с >> или > следующим образом:

$ cat >> temp.txt
line 1
line 2

Хотя cat можно использовать таким образом при записи вручную в консоль, это неудобно, если я хочу предоставить ввод более декларативным способом, чтобы его можно было повторно использовать инструментами, а также сохранить отступы, пробелы и т. Д.
Heredoc позволяет определить весь ваш ввод, как если бы вы не работали с stdin, а печатали в отдельном текстовом редакторе. Вот что означает статья в Википедии:

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

person yuranos    schedule 31.08.2020

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

<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

создаст тот же файл, что и:

cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

Итак, я не вижу смысла использовать команду cat.

person Community    schedule 06.06.2018
comment
какая оболочка? Я тестировал bash 4.4 на Ubuntu 18.04, а также bash 3.2 на OSX. Оба создали пустой файл при использовании <<test без cat <<test. - person wisbucky; 15.07.2019

Стоит отметить, что здесь документы работают и в циклах bash. В этом примере показано, как получить список столбцов таблицы:

export postgres_db_name='my_db'
export table_name='my_table_name'

# start copy 
while read -r c; do test -z "$c" || echo $table_name.$c , ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
SELECT column_name
FROM information_schema.columns
WHERE 1=1
AND table_schema = 'public'
AND table_name   =:'table_name'  ;
EOF
)
# stop copy , now paste straight into the bash shell ...

output: 
my_table_name.guid ,
my_table_name.id ,
my_table_name.level ,
my_table_name.seq ,

или даже без новой строки

while read -r c; do test -z "$c" || echo $table_name.$c , | perl -ne 
's/\n//gm;print' ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
 SELECT column_name
 FROM information_schema.columns
 WHERE 1=1
 AND table_schema = 'public'
 AND table_name   =:'table_name'  ;
 EOF
 )

 # output: daily_issues.guid ,daily_issues.id ,daily_issues.level ,daily_issues.seq ,daily_issues.prio ,daily_issues.weight ,daily_issues.status ,daily_issues.category ,daily_issues.name ,daily_issues.description ,daily_issues.type ,daily_issues.owner
person Yordan Georgiev    schedule 14.08.2018