Làm thế nào để mèo mèo << EOF xông hoạt động trong bash?


631

Tôi cần phải viết một tập lệnh để nhập đầu vào nhiều dòng vào một chương trình ( psql).

Sau một chút loay hoay, tôi thấy cú pháp sau hoạt động:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Điều này xây dựng chính xác chuỗi nhiều dòng (từ BEGIN;đến END;, bao gồm) và đặt nó làm đầu vào psql.

Nhưng tôi không biết làm thế nào / tại sao nó hoạt động, ai đó có thể giải thích?

Tôi chủ yếu đề cập đến cat << EOF, tôi biết >đầu ra cho một tệp, >>nối vào một tệp, <đọc đầu vào từ tệp.

Không gì <<làm chính xác?

Và có một trang người đàn ông cho nó?


26
Đó có lẽ là một cách sử dụng vô dụng cat. Hãy thử psql ... << EOF ... Xem thêm "ở đây chuỗi". mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
Tạm dừng cho đến khi có thông báo mới.

1
Tôi ngạc nhiên khi nó hoạt động với mèo nhưng không có tiếng vang. mèo nên mong đợi một tên tập tin là stdin, không phải là một chuỗi char. psql << EOF nghe có vẻ hợp lý, nhưng không được nhắc đến. Hoạt động với mèo nhưng không có tiếng vang. Hành vi kỳ lạ. Có manh mối nào về điều đó không?
Alex

Tự trả lời: mèo không có tham số thực thi và sao chép vào đầu ra bất cứ điều gì gửi qua đầu vào (stdin), do đó sử dụng đầu ra của nó để điền vào tệp qua>. Trong thực tế, tên tệp được đọc dưới dạng tham số không phải là luồng stdin.
Alex

@Alex echo chỉ in các đối số dòng lệnh của nó trong khi catđọc stding (khi được dẫn tới nó) hoặc đọc một tệp tương ứng với dòng lệnh của nó args
The-null-Pulum-

Câu trả lời:


519

Đây được gọi là định dạng heredoc để cung cấp một chuỗi thành stdin. Xem https://en.wikipedia.org/wiki/Here_document#Unix_shells để biết thêm chi tiết.


Từ man bash:

Tài liệu ở đây

Kiểu chuyển hướng này hướng dẫn shell đọc đầu vào từ nguồn hiện tại cho đến khi một dòng chỉ chứa từ (không có khoảng trống ở cuối) được nhìn thấy.

Tất cả các dòng đọc đến thời điểm đó sau đó được sử dụng làm đầu vào tiêu chuẩn cho một lệnh.

Định dạng của tài liệu ở đây là:

          <<[-]word
                  here-document
          delimiter

Không có mở rộng tham số, thay thế lệnh, mở rộng số học hoặc mở rộng tên đường dẫn được thực hiện trên word . Nếu bất kỳ ký tự nào trong từ được trích dẫn, dấu phân cách là kết quả của việc loại bỏ trích dẫn trên từ và các dòng trong tài liệu ở đây không được mở rộng. Nếu từ không được trích dẫn, tất cả các dòng của tài liệu ở đây phải chịu sự mở rộng tham số, thay thế lệnh và mở rộng số học. Trong trường hợp sau, chuỗi ký tự \<newline>được bỏ qua, và \phải được sử dụng để trích dẫn các nhân vật \, $`.

Nếu toán tử chuyển hướng là <<-, thì tất cả các ký tự tab hàng đầu sẽ bị xóa khỏi các dòng đầu vào và dòng chứa dấu phân cách . Điều này cho phép các tài liệu trong tập lệnh shell được thụt vào một cách tự nhiên.


12
Tôi đã có thời gian khó khăn nhất để vô hiệu hóa mở rộng biến / tham số. Tất cả những gì tôi cần làm là sử dụng "dấu ngoặc kép" và nó đã sửa nó! Cảm ơn bạn về thông tin!
Xeoncross

11
Liên quan <<-xin lưu ý rằng chỉ các ký tự tab hàng đầu bị tước - không phải các ký tự tab mềm. Đây là một trong những trường hợp hiếm hoi khi bạn thực sự cần nhân vật tab. Nếu phần còn lại của tài liệu của bạn sử dụng các tab mềm, hãy đảm bảo hiển thị các ký tự vô hình và (ví dụ) sao chép và dán một ký tự tab. Nếu bạn làm đúng, tô sáng cú pháp của bạn sẽ bắt chính xác dấu phân cách kết thúc.
trkoch

1
Tôi không thấy câu trả lời này hữu ích hơn câu trả lời dưới đây. Nó chỉ đơn thuần lấy lại thông tin có thể tìm thấy ở những nơi khác (có khả năng đã được kiểm tra)
BrDaHa

@BrDaHa, có lẽ không phải vậy. Tại sao câu hỏi? vì upvote? nó cái duy nhất trong vài năm nó được nhìn thấy bằng cách so sánh ngày.
Alexei Martianov

501

Các cat <<EOFcú pháp là rất hữu ích khi làm việc với nhiều dòng văn bản trong Bash, ví dụ. khi gán chuỗi nhiều dòng cho biến shell, tệp hoặc đường ống.

Ví dụ về cat <<EOFsử dụng cú pháp trong Bash:

1. Gán chuỗi nhiều dòng cho biến shell

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

Các $sqlbiến hiện nay nắm giữ các nhân vật mới dòng quá. Bạn có thể xác minh với echo -e "$sql".

2. Truyền chuỗi nhiều dòng vào một tệp trong Bash

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

Các print.shtập tin hiện có chứa:

#!/bin/bash
echo $PWD
echo /home/user

3. Truyền chuỗi nhiều dòng vào một đường ống trong Bash

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

Các b.txttập tin chứa barbazdòng. Đầu ra tương tự được in tới stdout.


1. 1 và 3 có thể được thực hiện mà không cần mèo; 2. Ví dụ 1 có thể được thực hiện với một chuỗi nhiều dòng đơn giản
Daniel Alder

269

Trong trường hợp của bạn, "EOF" được gọi là "Thẻ ở đây". Về cơ bản <<Herenói với shell rằng bạn sẽ nhập một chuỗi nhiều dòng cho đến khi "thẻ" Here. Bạn có thể đặt tên cho thẻ này như bạn muốn, nó thường EOFhoặc STOP.

Một số quy tắc về các thẻ ở đây:

  1. Thẻ có thể là bất kỳ chuỗi, chữ hoa hoặc chữ thường, mặc dù hầu hết mọi người sử dụng chữ hoa theo quy ước.
  2. Thẻ sẽ không được coi là thẻ Ở đây nếu có các từ khác trong dòng đó. Trong trường hợp này, nó sẽ chỉ được coi là một phần của chuỗi. Thẻ phải nằm trên một dòng riêng biệt, để được coi là một thẻ.
  3. Thẻ không được có khoảng trắng ở đầu hoặc cuối trong dòng đó để được coi là thẻ. Nếu không, nó sẽ được coi là một phần của chuỗi.

thí dụ:

$ 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

31
đây là câu trả lời thực tế tốt nhất ... bạn xác định cả hai và nêu rõ mục đích chính của việc sử dụng thay vì lý thuyết liên quan ... điều này rất quan trọng nhưng không cần thiết ... cảm ơn - siêu hữu ích
oemb1905

5
@edelans bạn phải thêm rằng khi <<-được sử dụng, tab hàng đầu sẽ không ngăn thẻ được nhận ra
The-null-Pulum-

1
câu trả lời của bạn đã nhấp vào tôi "bạn sẽ nhập một chuỗi nhiều dòng"
Giải tích

79

POSIX 7

kennytm đã trích dẫn man bash, nhưng hầu hết trong số đó cũng là POSIX 7: http://pub.opengroup.org/onlinepub/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

Cả hai toán tử chuyển hướng "<<" và "<< -" đều cho phép chuyển hướng các dòng có trong tệp đầu vào shell, được gọi là "tài liệu ở đây", vào đầu vào của lệnh.

Tài liệu ở đây sẽ được coi là một từ duy nhất bắt đầu sau từ tiếp theo và tiếp tục cho đến khi có một dòng chỉ chứa dấu phân cách và a, không có ký tự ở giữa. Sau đó, tài liệu tiếp theo ở đây bắt đầu, nếu có. Định dạng như sau:

[n]<<word
    here-document
delimiter

Trong đó n tùy chọn đại diện cho số mô tả tập tin. Nếu số bị bỏ qua, tài liệu ở đây đề cập đến đầu vào tiêu chuẩn (mô tả tệp 0).

Nếu bất kỳ ký tự nào trong từ được trích dẫn, dấu phân cách sẽ được hình thành bằng cách thực hiện loại bỏ trích dẫn trên từ và các dòng tài liệu ở đây sẽ không được mở rộng. Nếu không, dấu phân cách sẽ là chính từ đó.

Nếu không có ký tự nào trong từ được trích dẫn, tất cả các dòng của tài liệu ở đây sẽ được mở rộng để mở rộng tham số, thay thế lệnh và mở rộng số học. Trong trường hợp này, trong đầu vào hoạt động như dấu ngoặc kép bên trong (xem Dấu ngoặc kép). Tuy nhiên, ký tự trích dẫn kép ('"') sẽ không được xử lý đặc biệt trong tài liệu ở đây, ngoại trừ khi trích dẫn kép xuất hiện trong" $ () "," `` "hoặc" $ {} ".

Nếu biểu tượng chuyển hướng là "<< -", tất cả các <tab>ký tự đầu sẽ bị xóa khỏi các dòng đầu vào và dòng chứa dấu phân cách. Nếu có nhiều toán tử "<<" hoặc "<< -" được chỉ định trên một dòng, tài liệu ở đây được liên kết với toán tử đầu tiên sẽ được ứng dụng cung cấp trước và phải được đọc trước bởi trình bao.

Khi tài liệu ở đây được đọc từ thiết bị đầu cuối và vỏ tương tác, nó sẽ ghi nội dung của biến PS2, được xử lý như mô tả trong Biến Shell, thành lỗi tiêu chuẩn trước khi đọc từng dòng đầu vào cho đến khi nhận dạng dấu phân cách.

Ví dụ

Một số ví dụ chưa được đưa ra.

Báo giá ngăn chặn mở rộng tham số

Không có dấu ngoặc kép:

a=0
cat <<EOF
$a
EOF

Đầu ra:

0

Với dấu ngoặc kép:

a=0
cat <<'EOF'
$a
EOF

hoặc (xấu nhưng hợp lệ):

a=0
cat <<E"O"F
$a
EOF

Đầu ra:

$a

Dấu gạch nối loại bỏ các tab hàng đầu

Không có dấu gạch nối:

cat <<EOF
<tab>a
EOF

<tab>tab chữ ở đâu và có thể được chèn vàoCtrl + V <tab>

Đầu ra:

<tab>a

Với dấu gạch nối:

cat <<-EOF
<tab>a
<tab>EOF

Đầu ra:

a

Điều này tồn tại tất nhiên để bạn có thể thụt lề catgiống như mã xung quanh, dễ đọc và bảo trì hơn. Ví dụ:

if true; then
    cat <<-EOF
    a
    EOF
fi

Thật không may, điều này không hoạt động cho các ký tự không gian: POSIX được tabthụt lề ở đây. Rất tiếc.


Trong ví dụ cuối cùng của bạn thảo luận <<-<tab>a, cần lưu ý rằng mục đích là cho phép thụt mã bình thường trong tập lệnh trong khi cho phép văn bản heredoc được trình bày cho quá trình nhận bắt đầu trong cột 0. Đây là một tính năng không thường thấy và một chút nhiều bối cảnh hơn có thể ngăn chặn rất nhiều chuyện đau đầu ...
David C. Rankin

1
Làm cách nào để thoát khỏi sự mở rộng nếu một số nội dung ở giữa các thẻ EOF của tôi cần được mở rộng và một số thì không?
Jeanmichel Côte

2
... chỉ cần sử dụng dấu gạch chéo ngược phía trước$
Jeanmichel Côte

@JeanmichelCote Tôi không thấy tùy chọn nào tốt hơn :-) Với các chuỗi thông thường, bạn cũng có thể xem xét trộn các trích dẫn như "$a"'$b'"$c", nhưng không có tương tự ở đây AFAIK.
Ciro Santilli 郝海东 冠状 病 事件

25

Sử dụng tee thay vì mèo

Không chính xác như một câu trả lời cho câu hỏi ban đầu, nhưng dù sao tôi cũng muốn chia sẻ điều này: Tôi có nhu cầu tạo một tệp cấu hình trong một thư mục yêu cầu quyền root.

Sau đây không hoạt động cho trường hợp đó:

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

bởi vì chuyển hướng được xử lý bên ngoài bối cảnh sudo.

Tôi đã kết thúc bằng cách sử dụng này thay thế:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF

trong trường hợp của bạn, hãy sử dụng sudo bash -c 'cat << EOF> /etc/somedir/foo.conf # tệp cấu hình của tôi foo = bar EOF'
likewhoa

5

Một phần mở rộng cho các câu trả lời trên. Các trailing >chỉ đạo đầu vào vào tập tin, ghi đè nội dung hiện có. Tuy nhiên, một cách sử dụng đặc biệt thuận tiện là mũi tên kép >>nối thêm, thêm nội dung mới của bạn vào cuối tệp, như trong:

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

Điều này mở rộng fstabmà không cần bạn phải lo lắng về việc vô tình sửa đổi bất kỳ nội dung nào của nó.


1

Đây không nhất thiết là một câu trả lời cho câu hỏi ban đầu, nhưng chia sẻ một số kết quả từ thử nghiệm của riêng tôi. Điều này:

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

sẽ tạo cùng một tệp như:

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

Vì vậy, tôi không thấy quan điểm của việc sử dụng lệnh mèo.


2
Vỏ nào? Tôi đã thử nghiệm với bash 4.4 trên Ubuntu 18.04 cũng như bash 3.2 trên OSX. Cả hai tạo ra một tập tin trống khi chỉ sử dụng <<testmà không có cat <<test.
wvducky

Điều này làm việc cho tôi trên LInux Mint 19 Tara trong zsh
Geoff Langenderfer

0

Đáng lưu ý rằng ở đây các tài liệu cũng hoạt động trong các vòng lặp bash. Ví dụ này cho thấy cách lấy danh sách cột của bảng:

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 ,

hoặc thậm chí không có dòng mới

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.