Як працює «кішка << EOF» в башті?


629

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

Трохи погуглившись, я виявив такі синтаксичні роботи:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Це правильно будує багаторядковий рядок (від включно BEGIN;до END;включно) і передає його як вхід до psql.

Але я поняття не маю, як / чому це працює, може хтось, будь ласка, пояснить?

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

Що <<саме робить?

А чи є сторінка для нього?


26
Це, мабуть, марне використання cat. Спробуйте psql ... << EOF ... також див. "Тут рядки". mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
Призупинено до подальшого повідомлення.

1
Я здивований, що це працює з котом, але не з відлунням. cat повинен очікувати назви файлу як stdin, а не символу рядка. psql << EOF звучить логічно, але не в іншому порядку. Працює з котом, але не з відлунням. Дивна поведінка. Якась підказка про це?
Алекс

Відповідаючи на себе: cat без параметрів виконує та реплікує на висновок, що надсилається через input (stdin), отже, використовуючи його вихід, щоб заповнити файл через>. Насправді ім'я файлу, прочитане як параметр, не є потоком stdin.
Алекс

@ Алекс ехо просто роздруковує аргументи командного рядка під час catчитання stding (при передачі на нього) або читає файл, який відповідає його аргументам командного рядка
The-null-Pointer -

Відповіді:


517

Це називається Heredoc формат , щоб забезпечити рядок в стандартне введення. Докладнішу інформацію див. На веб-сайті https://en.wikipedia.org/wiki/Here_document#Unix_shells .


Від man bash:

Ось документи

Цей тип перенаправлення дає команду оболонці зчитувати введення з джерела струму, поки не буде показано рядок, що містить лише слово (без пробілів).

Усі рядки, прочитані до цього моменту, потім використовуються як стандартний вхід для команди.

Формат тут-документів:

          <<[-]word
                  here-document
          delimiter

У слові не виконується розширення параметрів, підміна команд, арифметичне розширення або розширення імені . Якщо будь-які символи у слові цитуються, роздільник є результатом видалення лапки на слові , а рядки в документі тут не розширюються. Якщо слово не цитується, всі рядки документа тут піддаються розширенню параметрів, заміні команд та арифметичному розширенню. В останньому випадку послідовність символів \<newline>ігноруються, і \повинна бути використана для цитування символів \, $і `.

Якщо оператор перенаправлення є <<-, то всі провідні символи вкладки позбавляються від вхідних рядків та рядка, що містить роздільник . Це дозволяє природним чином відступити документи тут у скриптах оболонки.


12
Мені найскладніше було відключити розширення змінних / параметрів. Все, що мені потрібно було, - це використовувати "подвійні котирування", і це виправлено! Дякую за інформацію!
Xeoncross

11
Стосовно, <<-будь ласка, зверніть увагу, що позбавлені лише провідні символи вкладки - не м'які символи. Це один з тих рідкісних випадків, коли вам справді потрібен символ вкладки. Якщо в іншому документі використовуються м'які вкладки, обов'язково покажіть невидимі символи та (наприклад) скопіюйте та вставте символ вкладки. Якщо ви зробите це правильно, підсвітка синтаксису повинна правильно зафіксувати розмежувач кінця.
trkoch

1
Я не бачу, наскільки ця відповідь корисніша, ніж наведена нижче. Він просто відновлює інформацію, яку можна знайти в інших місцях (які, ймовірно, вже перевірені)
BrDaHa

@BrDaHa, можливо, це не так. Чому питання? через нагороди? це було єдине протягом кількох років. це помічається шляхом порівняння дат.
Олексій Мартьянов

501

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.


1. 1 і 3 можна зробити без кота; 2. Приклад 1 можна виконати за допомогою простої рядкової багаторядкової лінії
Даніель Альдер

269

У вашому випадку "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

30
це найкраща реальна відповідь ... Ви визначаєте і те, і чітко
заявляєте

5
@edelans Ви повинні додати, що при <<-використанні провідна вкладка не завадить розпізнавати тег
The-null-Pointer -

1
Ваша відповідь натиснула на мене: "Ви збираєтеся ввести рядок багаторядкових"
Обчислення

79

POSIX 7

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

Оператори переадресації "<<" та "<< -" дозволяють перенаправляти рядки, що містяться у вхідному файлі оболонки, відомому як "тут-документ", на вхід команди.

Документ тут розглядається як одне слово, яке починається після наступного і продовжується, поки не буде рядка, що містить лише роздільники та а, без символів між ними. Тоді починається наступний документ тут, якщо такий є. Формат такий:

[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відступу тут. Yikes.


У вашому останньому приклад обговорення <<-і <tab>a, слід зазначити , що мета полягала в тому, щоб забезпечити нормальний відступ коду в скрипті, дозволяючи Heredoc текст , представлений отримує процес , щоб почати в колонці 0. Це не дуже часто зустрічаються функція і трохи більше контексту може завадити гарній справі з вичісуванням голови ...
Девід К. Ранкін

1
Як я можу уникнути розширення, якщо частина вмісту між моїми тегами EOF потрібно розширити, а частина - ні?
Jeanmichel Cote

2
... просто використовуйте зворотний $
нахил

@JeanmichelCote Я не бачу кращого варіанту :-) За допомогою звичайних рядків ви також можете розглянути можливість змішування цитат "$a"'$b'"$c", але аналога тут немає AFAIK.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

25

Використання трійника замість кота

Не зовсім як відповідь на оригінальне запитання, але я хотів поділитися цим у будь-якому випадку: мені виникла потреба створити файл конфігурації в каталозі, який вимагав права 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

у вашому випадку використовуйте sudo bash -c 'cat << EOF> /etc/somedir/foo.conf # my config file foo = bar EOF'
likewhoa

5

Невелике розширення до вищезазначених відповідей. Трейлінг >направляє вхід у файл, переписуючи наявний вміст. Однак одне особливо зручне використання - це подвійна стрілка, >>яка додається, додаючи новий вміст у кінець файлу, як у:

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проблему, не турбуючись про те, щоб випадково змінити будь-який її вміст.


1

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

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

створить той самий файл, що і:

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

Отже, я не бачу сенсу використовувати команду cat.


2
яка оболонка? Я тестував bash 4.4 на Ubuntu 18.04, а також bash 3.2 на OSX. Обидва створили порожній файл, лише використовуючи <<testбез cat <<test.
wisbucky

Це працювало для мене на монетному дворі LInux 19 Tara in zsh
Джефф Лангенферфер

0

Варто зазначити, що і тут документи працюють у баш-петлях. Цей приклад показує, як отримати список стовпців таблиці:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.