कैसे "बिल्ली << EOF" काम करता है?


629

मुझे एक प्रोग्राम ( psql) में मल्टी-लाइन इनपुट दर्ज करने के लिए एक स्क्रिप्ट लिखने की आवश्यकता थी ।

जरा सी गुगली करने के बाद, मुझे निम्नलिखित वाक्य रचनाएँ मिलीं:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

यह सही ढंग से (से बहु लाइन स्ट्रिंग का निर्माण BEGIN;करने के लिए END;और के लिए एक इनपुट के रूप में यह पाइप, सहित) psql

लेकिन मुझे नहीं पता कि यह कैसे / क्यों काम करता है, क्या कोई इसे समझा सकता है?

मैं मुख्य रूप से करने के लिए बात कर रहा हूँ cat << EOF, मैं >एक फ़ाइल के लिए outputs पता है , >>एक फ़ाइल के लिए भेजता है, <फ़ाइल से इनपुट पढ़ता है।

<<वास्तव में क्या करता है?

और क्या इसके लिए एक आदमी पृष्ठ है?


26
शायद इसका एक बेकार उपयोग है cat। प्रयास करें psql ... << EOF ... भी देखें "यहाँ तार"। mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
अगली सूचना तक

1
मुझे आश्चर्य है कि यह बिल्ली के साथ काम करता है लेकिन गूंज के साथ नहीं। बिल्ली को एक स्टड के रूप में एक फ़ाइल नाम की उम्मीद करनी चाहिए, न कि चार स्ट्रिंग की। psql << EOF तार्किक लगता है, लेकिन othewise नहीं। बिल्ली के साथ काम करता है लेकिन गूंज के साथ नहीं। अजीब व्यवहार। उस बारे में कोई सुराग?
एलेक्स

अपने आप को जवाब देना: मापदंडों के बिना बिल्ली इनपुट (स्टडिन) के माध्यम से जो कुछ भी भेजती है, आउटपुट को निष्पादित और प्रतिकृति करती है, इसलिए फ़ाइल के माध्यम से भरने के लिए इसके आउटपुट का उपयोग करना>। वास्तव में एक पैरामीटर के रूप में पढ़ा जाने वाला फ़ाइल नाम एक स्टडिन स्ट्रीम नहीं है।
एलेक्स

@ अलेक्स इको केवल कमांड लाइन की दलीलें प्रिंट करता है, जबकि catस्टेंडिंग (जब इसे पाइप किया जाता है) पढ़ता है या एक फाइल पढ़ता है जो इसे कमांड लाइन आर्ग से मेल खाती है
The-null-Pointer-

जवाबों:


517

इसे स्टर्ड में स्ट्रिंग प्रदान करने के लिए हेरेडोक प्रारूप कहा जाता है । अधिक जानकारी के लिए https://en.wikipedia.org/wiki/Here_document#Unix_shell देखें ।


से man bash:

यहाँ दस्तावेज़

इस प्रकार के पुनर्निर्देशन से शेल को वर्तमान स्रोत से इनपुट पढ़ने का निर्देश मिलता है, जब तक कि केवल शब्द (कोई अनुगामी कंबल के साथ) एक पंक्ति नहीं देखी जाती है।

उस बिंदु तक पढ़ी गई सभी लाइनें तब कमांड के लिए मानक इनपुट के रूप में उपयोग की जाती हैं।

यहाँ दस्तावेजों का प्रारूप है:

          <<[-]word
                  here-document
          delimiter

कोई पैरामीटर विस्तार, कमांड प्रतिस्थापन, अंकगणितीय विस्तार या pathname विस्तार शब्द पर नहीं किया जाता है । यदि शब्द के किसी भी अक्षर को उद्धृत किया जाता है, तो सीमांकक शब्द पर उद्धरण हटाने का परिणाम है , और यहां दस्तावेज़ में लाइनों का विस्तार नहीं किया गया है। यदि शब्द को रेखांकित नहीं किया गया है, तो यहां दस्तावेज़ की सभी पंक्तियों को पैरामीटर विस्तार, कमांड प्रतिस्थापन, और अंकगणितीय विस्तार के अधीन किया गया है। उत्तरार्द्ध मामले में, चरित्र अनुक्रम \<newline>नजरअंदाज कर दिया है, और \वर्ण उद्धृत करने के लिए इस्तेमाल किया जाना चाहिए \, $और `

यदि पुनर्निर्देशन ऑपरेटर है <<-, तो सभी प्रमुख टैब वर्ण इनपुट लाइनों और सीमांकक युक्त रेखा से छीन लिए जाते हैं । यह शैल-स्क्रिप्ट के भीतर यहां-दस्तावेजों को एक प्राकृतिक फैशन में शामिल करने की अनुमति देता है।


12
मैं चर / पैरामीटर विस्तार को अक्षम करने में सबसे कठिन समय रहा था। मुझे केवल "डबल-कोट्स" का उपयोग करने की आवश्यकता थी और यह तय हो गया! जानकारी के लिए धन्यवाद!
एक्सोनक्रॉस

11
के संबंध में <<-कृपया ध्यान दें कि केवल प्रमुख टैब नहीं नरम टैब वर्ण - वर्ण हटा दिए जाते हैं। यह उन दुर्लभ मामलों में से एक है जब आपको वास्तव में टैब चरित्र की आवश्यकता होती है। यदि आपके दस्तावेज़ के बाकी हिस्सों में नरम टैब का उपयोग किया गया है, तो अदृश्य वर्णों को दिखाना सुनिश्चित करें और (जैसे) एक टैब वर्ण को कॉपी और पेस्ट करें। यदि आप इसे सही करते हैं, तो आपके सिंटैक्स हाइलाइटिंग को अंत में सीमांकक को पकड़ना चाहिए।
trkoch

1
मैं यह नहीं देखता कि नीचे दिए गए सवालों की तुलना में यह उत्तर कितना उपयोगी है। यह केवल उन
सूचनाओं को पुन: एकत्रित

@BrDaHa, शायद यह नहीं है। सवाल क्यूँ? उतार-चढ़ाव के कारण यह कई वर्षों से एकमात्र था । यह तारीखों की तुलना करके देखा जाता है।
एलेक्सी मार्टिआनोव

501

cat <<EOFवाक्य रचना बहुत उपयोगी है जब बैश में बहु लाइन पाठ, जैसे के साथ काम कर रहा है। शेल चर, फ़ाइल या पाइप में मल्टी-लाइन स्ट्रिंग असाइन करते समय।

cat <<EOFबैश में वाक्य रचना के उदाहरण :

1. शेल चर के लिए मल्टी-लाइन स्ट्रिंग असाइन करें

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

$sqlचर अब नई लाइन पात्रों भी आयोजित करता है। आप के साथ सत्यापित कर सकते हैं echo -e "$sql"

2. बैश में एक फाइल के लिए मल्टी-लाइन स्ट्रिंग पास करें

$ 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


1. 1 और 3 बिल्ली के बिना किया जा सकता है; 2. उदाहरण 1 एक साधारण मल्टी-लाइन स्ट्रिंग के साथ किया जा सकता है
डैनियल एल्डर

269

आपके मामले में, "ईओएफ" को "यहां टैग" के रूप में जाना जाता है। मूल रूप <<Hereसे शेल को बताता है कि आप "टैग" तक एक बहुस्तरीय स्ट्रिंग दर्ज करने जा रहे हैं Here। आप इस टैग को जैसा चाहें, नाम दे सकते हैं, यह अक्सर EOFया STOP

यहाँ टैग के बारे में कुछ नियम:

  1. टैग किसी भी स्ट्रिंग, अपरकेस या लोअरकेस हो सकता है, हालांकि अधिकांश लोग कन्वेंशन द्वारा अपरकेस का उपयोग करते हैं।
  2. यदि उस पंक्ति में अन्य शब्द हैं तो टैग को यहां टैग नहीं माना जाएगा। इस मामले में, यह केवल स्ट्रिंग का हिस्सा माना जाएगा। टैग को एक अलग लाइन पर होना चाहिए, जिसे टैग माना जाए।
  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
यह सबसे अच्छा वास्तविक उत्तर है ... आप दोनों को परिभाषित करते हैं और स्पष्ट रूप से संबंधित सिद्धांत के बजाय उपयोग का प्राथमिक उद्देश्य बताते हैं ... जो महत्वपूर्ण है, लेकिन आवश्यक नहीं है ... धन्यवाद - सुपर उपयोगी
oemb1905

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_04_04 है :

पुनर्निर्देशन ऑपरेटर "<<" और "<< -" दोनों एक शेल इनपुट फ़ाइल में निहित लाइनों के पुनर्निर्देशन की अनुमति देते हैं, जिसे "कमांड-दस्तावेज़" के रूप में जाना जाता है, एक कमांड के इनपुट में।

यहां दस्तावेज़ को एक एकल शब्द के रूप में माना जाएगा जो अगले के बाद शुरू होता है और तब तक जारी रहता है जब तक कि कोई रेखा नहीं होती है जिसमें केवल सीमांकक और ए होता है, जिसके बीच कोई वर्ण नहीं होता है। फिर अगला यहाँ-दस्तावेज़ शुरू होता है, अगर वहाँ एक है। प्रारूप इस प्रकार है:

[n]<<word
    here-document
delimiter

जहाँ वैकल्पिक n फ़ाइल डिस्क्रिप्टर संख्या का प्रतिनिधित्व करता है। यदि संख्या को छोड़ दिया जाता है, तो यहां दस्तावेज़ मानक इनपुट (फाइल डिस्क्रिप्टर 0) को संदर्भित करता है।

यदि शब्द के किसी भी वर्ण को उद्धृत किया जाता है, तो शब्द पर उद्धरण हटाने का प्रदर्शन करके परिसीमन का गठन किया जाएगा, और यहां-दस्तावेज़ लाइनों का विस्तार नहीं किया जाएगा। अन्यथा, परिसीमन शब्द ही होगा।

यदि शब्द में कोई वर्ण उद्धृत नहीं किया गया है, तो यहां दस्तावेज़ की सभी लाइनें पैरामीटर विस्तार, कमांड प्रतिस्थापन और अंकगणितीय विस्तार के लिए विस्तारित की जाएंगी। इस मामले में, इनपुट में डबल-उद्धरण (डबल-उद्धरण देखें) के रूप में व्यवहार किया जाता है। हालाँकि, दोहरे-उद्धरण वर्ण ('' '') को यहाँ-दस्तावेज़ में विशेष रूप से नहीं माना जाएगा, सिवाय इसके कि जब दोहरे-उद्धरण "$ ()", "` `", या "$ {}" के भीतर दिखाई दे।

यदि पुनर्निर्देशन प्रतीक "<< -" है, तो सभी प्रमुख <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

दुर्भाग्य से, यह अंतरिक्ष पात्रों के लिए काम नहीं करता है: पोसिक्स ने tabयहां इंडेंटेशन का पक्ष लिया । ओह।


अपने अंतिम उदाहरण पर चर्चा करते हुए <<-और <tab>a, यह ध्यान दिया जाना चाहिए कि उद्देश्य स्क्रिप्ट के भीतर कोड के सामान्य इंडेंटेशन की अनुमति देना था जबकि स्तंभ में शुरू करने के लिए प्राप्त करने की प्रक्रिया के लिए प्रस्तुत heredoc पाठ की अनुमति देना 0. यह एक बहुत ही सामान्य रूप से देखी गई विशेषता नहीं है और थोड़ा सा अधिक संदर्भ सिर को खरोंचने से बचा सकता है ...
डेविड सी। रंकिन

1
यदि मेरे EOF टैग के बीच की कुछ सामग्री को विस्तारित करने की आवश्यकता है और कुछ नहीं हैं तो मुझे कैसे बचना चाहिए?
1948 में जेनिमिक कोटे

2
... बस बैकस्लैश का उपयोग करें$
जेनेमिकल कोटे

@JeanmichelCote मुझे एक बेहतर विकल्प दिखाई नहीं देता है :-) नियमित तार के साथ आप उद्धरणों को मिलाने पर भी विचार कर सकते हैं "$a"'$b'"$c", लेकिन यहाँ AFAIK का कोई एनालॉग नहीं है।
सिरो सेंटिल्ली 郝海东 i iro i 法轮功 '23

25

बिल्ली के बजाय टी का उपयोग करना

मूल प्रश्न के उत्तर के रूप में बिल्कुल नहीं, लेकिन मैं इसे वैसे भी साझा करना चाहता था: मुझे एक निर्देशिका में एक config फाइल बनाने की आवश्यकता थी जो रूट अधिकारों की आवश्यकता थी।

निम्नलिखित उस मामले के लिए काम नहीं करता है:

$ 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

अपने मामले उपयोग में sudo bash -c 'बिल्ली << EOF> /etc/somedir/foo.conf # मेरी कॉन्फ़िग फ़ाइल foo = बार EOF'
likewhoa

4

उपरोक्त उत्तरों के लिए थोड़ा विस्तार। अनुगामी >मौजूदा सामग्री को अधिलेखित करके फ़ाइल में इनपुट को निर्देशित करता है। हालाँकि, एक विशेष रूप से सुविधाजनक उपयोग डबल तीर >>है, जो आपकी नई सामग्री को फ़ाइल के अंत में जोड़ देता है, जैसे:

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

इसलिए, मुझे कैट कमांड का उपयोग करने की बात नहीं दिखती।


2
कौन सा खोल? मैंने Ubuntu 18.04 पर bash 4.4 के साथ-साथ OSX पर bash 3.2 का परीक्षण किया। <<testबिना उपयोग किए ही दोनों ने एक खाली फ़ाइल बनाई cat <<test
बुद्धिमानबीम

यह मेरे लिए LInux मिंट 19 तारा पर zsh
Geoff Langenderfer

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 Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.