it-roy-ru.com

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

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

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

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

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

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

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

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

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

467
hasen

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


От man bash:

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

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

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

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

          <<[-]Word
                  here-document
          delimiter

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

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

417
kennytm

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

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

1. Присвойте многострочную строку переменной Shell

$ 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.

380
Vojtech Vitek

В вашем случае «EOF» известен как «Здесь тег». В основном <<Here сообщает Shell, что вы собираетесь вводить многострочную строку до «тега» 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
179
edelans

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).

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

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

Если символ перенаправления равен «<< -», все начальные символы <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 здесь. Хлоп.

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

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

Следующее не работает для этого случая:

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

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

Я закончил тем, что использовал это вместо этого:

$ Sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
19
Andreas Maier

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

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

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

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

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

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

0
user9048395