Мне нужно было написать script для ввода многострочного ввода в программу (psql
).
После нескольких поисковых запросов, я нашел следующие синтаксические работы:
cat << EOF | psql ---params
BEGIN;
`pg_dump ----something`
update table .... statement ...;
END;
EOF
Это правильно построит многострочную строку (от BEGIN;
до END;
включительно) и передает ее как вход в psql
.
Но я понятия не имею, как/почему это работает, может кто-нибудь объяснить?
Я имею в виду главным образом cat << EOF
, я знаю >
выходы в файл, >>
присоединяется к файлу, <
читает ввод из файла.
Что делает <<
точно?
И есть ли для него справочная страница?
Это называется heredoc, чтобы предоставить строку в stdin. Подробнее см. https://en.wikipedia.org/wiki/Here_document#Unix_shells.
От man bash
:
Здесь Документы
Этот тип перенаправления инструктирует оболочку читать ввод от текущий источник до линии содержащий только слово (без пробелы).
Все строки, считываемые до этой точки, затем используются как стандартный ввод для команды.
Формат здесь-документов:
<<[-]word here-document delimiter
Нет расширения параметров, подстановки команд, арифметического расширения или расширение имени пути выполняется слово. Если любые символы в wordцитируется, разделитель является результатом удаления цитаты на word, а строки в здесь-документе не расширены. Если слово не указано, все строки здесь-документ подвергаются расширению параметров, команда подстановка и арифметика расширение. В последнем случае символьная последовательность
\<newline>
игнорируется, а\
- для обозначения символов\
,$
и`
.Если оператор перенаправления
<<-
, то все ведущие символы табуляции удаляются из строк ввода и строка, содержащая разделитель. Эта позволяет здесь-документы в сценариях оболочки быть отступом естественным образом.
<<-
обратите внимание, что удаляются только начальные символы табуляции, а не символы программных табуляций. Это один из тех редких случаев, когда вам действительно нужен символ табуляции. Если в остальной части документа используются мягкие вкладки, убедитесь, что отображаются невидимые символы и (например) скопируйте и вставьте символ вкладки. Если вы все сделаете правильно, ваша подсветка синтаксиса должна правильно поймать конечный разделитель.
Синтаксис cat <<EOF
очень полезен при работе с многострочным текстом в Bash, например. при назначении многострочной строки переменной оболочки, файлу или каналу.
cat <<EOF
в Bash:$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)
В переменной $sql
также содержатся символы новой строки. Вы можете проверить с помощью echo -e "$sql"
.
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
Файл print.sh
теперь содержит:
#!/bin/bash
echo $PWD
echo /home/user
$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF
Файл b.txt
содержит строки bar
и baz
. Тот же вывод печатается на stdout
.
В вашем случае "EOF" известен как "Тег здесь". В основном <<Here
говорится оболочке, что вы собираетесь вводить многострочную строку до "тега" Here
. Вы можете назвать этот тег так, как хотите, часто EOF
или STOP
.
Некоторые правила тегов Here:
пример:
$ 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
<<-
ведущая вкладка не помешает распознаванию тега
kennytm цитирует man bash
, но большая часть из них также POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04:
Операторы перенаправления "< и" < - "оба допускают перенаправление строк, содержащихся в входном файле оболочки, называемом" here-document", на вход команды.
Настоящий документ должен рассматриваться как одно слово, которое начинается после следующего и продолжается до тех пор, пока не будет строка, содержащая только разделитель и a, без символов между ними. Затем начинается следующий документ, если он есть. Формат выглядит следующим образом:
[n]<<word
here-document
delimiter
где необязательный n обозначает номер дескриптора файла. Если номер опущен, здесь-документ ссылается на стандартный ввод (дескриптор файла 0).
Если какой-либо символ в слове цитируется, разделитель формируется путем удаления цитаты по слову, а строки здесь-документа не должны расширяться. В противном случае разделителем должно быть само слово.
Если никакие символы в словах не указаны, все строки этого документа должны быть расширены для расширения параметров, подстановки команд и арифметического расширения. В этом случае вход на входе ведет себя как внутренние двойные кавычки (см. Double-Quotes). Однако символ двойной кавычки ('' ') не обрабатывается специально внутри документа здесь, за исключением случаев, когда двойная кавычка отображается внутри "$()", "`" или "${}".
Если символ переадресации равен "< < -", все ведущие символы
<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
отступ здесь. Хлоп.
<<-
и <tab>a
, следует отметить, что цель состояла в том, чтобы обеспечить нормальный отступ кода в скрипте, при этом текст heredoc, представляемый процессу получения, начинается в столбце 0. Это не слишком часто видимая особенность и немного больше контекста могут предотвратить много царапин на голове ...
Использование tee вместо cat
Не совсем как ответ на исходный вопрос, но я все равно хотел бы поделиться этим: у меня возникла необходимость создать файл конфигурации в каталоге, который требует прав 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
Стоит отметить, что здесь документы также работают в баш-петлях. В этом примере показано, как получить список столбцов таблицы:
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
Это не обязательно является ответом на исходный вопрос, но это результат некоторых результатов моего собственного тестирования. Эта:
<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
будет выдавать тот же файл, что и:
cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
Итак, я не вижу смысла использовать команду cat.
cat
. Попробуйтеpsql ... << EOF ...
Смотрите также "здесь строки". mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings