Как работает «cat << EOF» в bash? - PullRequest
510 голосов
/ 23 марта 2010

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

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

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

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

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

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

Что конкретно делает <<?

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

Ответы [ 7 ]

441 голосов
/ 23 марта 2010

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


С man bash:

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

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

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

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

          <<[-]word
                  here-document
          delimiter

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

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

409 голосов
/ 04 февраля 2014

Синтаксис 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. Передать многострочную строку в трубу в Баш

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

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

201 голосов
/ 22 августа 2014

В вашем случае «EOF» известен как «Here Tag». В основном <<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
60 голосов

POSIX 7

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

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

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

[n]<<word
    here-document
delimiter

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

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

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

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

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

Примеры

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

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

Без кавычек:

a=0
cat <<EOF
$a
EOF

Вывод:

0

СQuotes:

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 здесь.Хлоп.

20 голосов
/ 13 февраля 2017

Использование тройника вместо кошки

Не совсем как ответ на первоначальный вопрос, но я все равно хотел поделиться этим: мне нужно было создать файл конфигурации в каталоге, который требовал права 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
0 голосов
/ 14 августа 2018

Стоит отметить, что здесь документы также работают в циклах 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
0 голосов
/ 06 июня 2018

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

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

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

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

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

...