10

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

28

Я столкнулся с необходимостью написать скрипт для ввода многострочного текста в программу (psql).

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

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

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

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

Меня в первую очередь интересует конструкция cat << EOF. Я знаю, что > выводит в файл, >> добавляет в файл, а < читает ввод из файла.

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

И есть ли руководство (man page) по этому поводу?

5 ответ(ов)

8

Синтаксис 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.

7

Это называется формат heredoc для передачи строки во входной поток стандартного ввода (stdin). Более подробную информацию можно найти по ссылке: https://en.wikipedia.org/wiki/Here_document#Unix_shells.


Из документации man bash:

Here Documents

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

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

Формат here-documents выглядит так:

          <<[-]word
                  here-document
          delimiter

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

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

5

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

Некоторые правила о Here Tag:

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

Пример:

$ cat >> test <<HERE
> Hello world HERE <-- Не на отдельной строке -> не считается концом строки
> This is a test
>  HERE <-- Ведущий пробел, так что не считается концом строки
> and a new line
> HERE <-- Теперь у нас есть конец строки
1

Цитаты предотвращают расширение параметров

Без кавычек:

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 предпочел здесь табуляции. Увы.

POSIX 7

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

Операторы перенаправления << и <<- позволяют перенаправление строк, содержащихся в файле ввода оболочки, известном как "here-document", на ввод команды.

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

[n]<<word
    here-document
delimiter

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

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

Если в word нет символов, заключенных в кавычки, все строки here-document будут расширены для расширения параметров, подстановки команд и арифметического расширения. В этом случае <обратная косая черта> во вводе ведет себя как <обратная косая черта> внутри двойных кавычек (см. Двойные кавычки). Однако символ двойных кавычек (") не должен обрабатываться особенно в рамках here-document, за исключением случаев, когда двойная кавычка появляется внутри "\(()", "``" или "\)".

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

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

0

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

Следующий способ в этом случае не сработает:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# мой конфигурационный файл
foo=bar
EOF

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

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

$ sudo tee /etc/somedir/foo.conf >/dev/null <<EOF
# мой конфигурационный файл
foo=bar
EOF

Таким образом, tee правильно работает с правами root.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь