“Cat << EOF” bash'da nasıl çalışır?


629

Bir programa ( psql) çok satırlı girdi girmek için bir komut dosyası yazmam gerekiyordu .

Biraz googling yaptıktan sonra, aşağıdaki sözdiziminin işe yaradığını buldum:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Bu, doğru (gelen çoklu dizesini yapıları BEGIN;için END;bir girdi olarak ve boru da dahil) psql.

Ama nasıl / neden işe yaradığına dair hiçbir fikrim yok, bazıları açıklayabilir mi?

Ben esas olarak cat << EOF, >bir dosyaya çıktıları biliyorum , bir dosyaya >>ekler, dosyadan <girdi okur.

<<Tam olarak ne yapar ?

Bunun için bir sayfa var mı?


26
Bu muhtemelen işe yaramaz cat. Deneyin psql ... << EOF ... da "Burada dizeleri" See. mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
sonraki duyuruya kadar duraklatıldı.

1
Kedi ile çalıştığına şaşırdım ama yankı ile değil. cat bir dosya adını char dizesi olarak değil, stdin olarak beklemelidir. psql << EOF kulağa mantıklı geliyor, ancak başka türlü değil. Kedi ile çalışır ancak yankı ile çalışmaz. Garip davranış. Bunun hakkında bir ipucu var mı?
Alex

Kendime cevap veriyorum: Parametresiz kedi girdi (stdin) üzerinden ne gönderirse gönderir ve çıktıyı çoğaltır, bu nedenle çıktıyı> ile doldurmak için kullanır. Aslında parametre olarak okunan bir dosya adı stdin akışı değildir.
Alex

@Alex echo sadece stding'i catokurken komut satırı argümanlarını yazdırır (buna bağlandığında) veya komut satırı argümanlarına karşılık gelen bir dosyayı okur
The-null-Pointer-

Yanıtlar:


517

Bu, stdin'e bir dize sağlamak için heredoc formatı olarak adlandırılır . Daha fazla ayrıntı için bkz. Https://en.wikipedia.org/wiki/Here_document#Unix_shells .


Gönderen man bash:

İşte Belgeler

Bu tür yeniden yönlendirme, kabuğa yalnızca sözcük içeren (izleyen boşluk olmadan) bir satır görünene kadar geçerli kaynaktan girişi okumasını söyler.

Bu noktaya kadar okunan tüm satırlar daha sonra bir komut için standart girdi olarak kullanılır.

Buradaki belgelerin formatı:

          <<[-]word
                  here-document
          delimiter

Sözcük üzerinde parametre genişletmesi, komut değiştirme, aritmetik genişletme veya yol adı genişletmesi gerçekleştirilmez . Herhangi karakterler ise kelimenin cinsindendir, sınırlayıcı üzerindeki tırnak kaldırma sonucudur kelime ve çizgiler burada doküman üzerinde genişletilmiş değildir. Eğer kelime tırnak içine almaktadır, tüm hatları burada-belgede parametre genişleme, komut ikamesi ve aritmetik yorumlama tabi tutulur. İkinci durumda, karakter dizisi \<newline>göz ardı edilir ve \karakterlerin kullanılmasında gerekir \, $ve `.

Yeniden yönlendirme operatörü ise <<-, tüm önde gelen sekme karakterleri giriş satırlarından ve sınırlayıcı içeren satırdan çıkarılır . Bu, burada kabuk betikleri içindeki belgelerin doğal bir şekilde girintili olmasına izin verir.


12
Değişken / parametre genişletmeyi devre dışı bırakmanın en zor zamanını yaşıyorum Tek yapmam gereken "çift tırnak" kullanmak ve bu düzeltildi! Bilgi için teşekkürler!
Xeoncross

11
İlgili sekme karakterlerinin değil <<-, yalnızca önde gelen sekme karakterlerinin soyulduğunu unutmayın . Sekme karakterine gerçekten ihtiyacınız olduğunda bu nadir durumlardan biridir. Belgenizin geri kalanı yumuşak sekmeler kullanıyorsa, görünmez karakterleri gösterdiğinizden ve (örneğin) bir sekme karakteri kopyalayıp yapıştırdığınızdan emin olun. Doğru yaparsanız, sözdizimi vurgulamanız bitiş sınırlayıcısını doğru bir şekilde yakalamalıdır.
trkoch

1
Bu cevabın aşağıdaki yanıtlardan daha yararlı olduğunu görmüyorum. Sadece başka yerlerde bulunabilecek (muhtemelen daha önce kontrol edilmiş olan) bilgileri yeniden
düzenler

@BrDaHa, belki de değil. Neden soru? upvotes nedeniyle? o oldu birkaç yıldır tek. tarihleri ​​karşılaştırarak görülür.
Alexei Martianov

501

cat <<EOFBash çok satırlı metin, örneğin çalışırken sözdizimi çok yararlıdır. kabuk değişkenine, dosyaya veya bir boruya çok satırlı dize atarken.

cat <<EOFBash'de sözdizimi kullanımına örnekler :

1. Bir kabuk değişkenine çok satırlı dize atayın

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

$sqlDeğişken artık çok yeni satır karakterleriyle tutar. İle doğrulayabilirsiniz echo -e "$sql".

2. Çok satırlı dizeyi Bash'teki bir dosyaya aktarın

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

print.shDosya artık içerir:

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

3. Çok satırlı dizeyi Bash'teki bir boruya geçirin

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

b.txtDosya içeriyor barve bazçizgiler. Aynı çıktıya yazdırılır stdout.


1. 1 ve 3 kedi olmadan yapılabilir; 2. Örnek 1 basit bir çok satırlı dize ile yapılabilir
Daniel Alder

269

Sizin durumunuzda "EOF", "Burada Etiket" olarak bilinir. Temel olarak <<Herekabuğa "etiket" e kadar çok satırlı bir dize gireceğinizi söyler Here. Bu etiketi istediğiniz gibi adlandırabilirsiniz, genellikle EOFveya STOP.

Burada etiketleri ile ilgili bazı kurallar:

  1. Etiket, herhangi bir dize, büyük harf veya küçük harf olabilir, ancak çoğu insan konvansiyonda büyük harf kullanır.
  2. Bu satırda başka kelimeler varsa, etiket Burada etiketi olarak kabul edilmez. Bu durumda, yalnızca dizenin bir parçası olarak kabul edilecektir. Etiket olarak kabul edilebilmesi için etiket tek başına ayrı bir satırda olmalıdır.
  3. Etiketin, bu satırda etiket olarak kabul edilecek öndeki veya sondaki boşlukları olmamalıdır. Aksi takdirde dizenin bir parçası olarak değerlendirilecektir.

misal:

$ 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
bu en iyi gerçek cevaptır ... ikisini de tanımlarsınız ve ilgili teori yerine kullanımın ana amacını açıkça belirtirsiniz ... bu önemli ama gerekli değildir ... teşekkürler - süper yararlı
oemb1905

5
@edelans <<-, önde gelen sekme kullanıldığında etiketin tanınmasını engellemeyeceğini
eklemelisiniz

1
cevabın beni "çok satırlı bir dize gireceksin" üzerine tıkladı
Matematik

79

POSIX 7

Kennytm alıntı man bash, ancak çoğu da POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

"<<" ve "<< -" yönlendirme işleçlerinin her ikisi de, "burada belge" olarak bilinen kabuk girdi dosyasında bulunan satırların komut girişine yeniden yönlendirilmesine izin verir.

Bu belgede, bir sonrakinden sonra başlayan ve aralarında hiçbir karakter bulunmayan, sadece sınırlayıcı ve a'yı içeren bir çizgi olana kadar devam eden tek bir kelime olarak ele alınacaktır. Sonra bir sonraki belge başlar, eğer varsa. Biçim aşağıdaki gibidir:

[n]<<word
    here-document
delimiter

burada isteğe bağlı n, dosya tanımlayıcı numarasını temsil eder. Numara atlanırsa, buradaki belge standart girdiyi ifade eder (dosya tanımlayıcı 0).

Sözcükteki herhangi bir karakterden alıntı yapılırsa, ayırıcı sözcük üzerinde tırnak işareti kaldırılarak oluşturulur ve buradaki belge satırları genişletilmez. Aksi takdirde, sınırlayıcı kelimenin kendisi olacaktır.

Sözcükteki hiçbir karakter tırnak içine alınmazsa, bu belgedeki tüm satırlar parametre genişletme, komut değiştirme ve aritmetik genişletme için genişletilmelidir. Bu durumda, giriş içindeki iç çift tırnaklar gibi davranır (bkz. Çift Tırnaklar). Ancak, çift tırnak işareti ('"'), çift tırnak işareti" $ () "," `` "veya" $ {} "içinde yer alması haricinde, bu belgede özel olarak ele alınmayacaktır.

Yeniden yönlendirme sembolü "<< -" ise, tüm önde gelen <tab>karakterler giriş satırlarından ve sondaki sınırlayıcıyı içeren satırdan çıkarılır. Bir satırda birden fazla "<<" veya "<< -" operatörü belirtilirse, ilk operatörle ilişkilendirilmiş olan bu doküman ilk olarak uygulama tarafından sağlanacak ve önce kabuk tarafından okunacaktır.

Burada bir belge bir terminal cihazından okunduğunda ve kabuk etkileşimli olduğunda, kabuk değişkenleri tanınana kadar her girdi satırını okumadan önce Kabuk Değişkenleri'nde açıklandığı gibi işlenen PS2 değişkeninin içeriğini standart hataya yazmalıdır.

Örnekler

Bazı örnekler henüz verilmemiştir.

Tırnak işaretleri parametre genişlemesini engeller

Tırnak işareti olmadan:

a=0
cat <<EOF
$a
EOF

Çıktı:

0

Alıntılarla:

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

veya (çirkin fakat geçerli):

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

Çıktılar:

$a

Kısa çizgi, önde gelen sekmeleri kaldırır

Kısa çizgi olmadan:

cat <<EOF
<tab>a
EOF

<tab>gerçek bir sekme nerede ve ile eklenebilirCtrl + V <tab>

Çıktı:

<tab>a

Kısa çizgi ile:

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

Çıktı:

a

Bu elbette var, böylece catokunması ve bakımı daha kolay olan çevredeki kodu girintileyebilirsiniz . Örneğin:

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

Ne yazık ki, bu boşluk karakterleri için işe yaramaz: POSIX tabburada girintiyi tercih etti . Amanın.


Son örneğinizde tartışılıyor <<-ve <tab>aamaç, kodun içinde kodun normal olarak girilmesine izin verirken, alıcı işleme sunulan heredoc metninin 0 sütununda başlamasına izin vermek olduğu unutulmamalıdır. daha fazla bağlam, kafa çizilmelerini iyi engelleyebilir ...
David C. Rankin

1
EOF etiketlerim arasındaki içeriğin bir kısmının genişletilmesi gerekiyor ve bazılarının genişletilmemesi gerekiyorsa, açıklamadan nasıl kaçmalıyım?
Jeanmichel Cote

2
... sadece önündeki ters eğik çizgiyi kullanın$
Jeanmichel Cote

@JeanmichelCote Ben daha iyi bir seçenek görmüyorum :-) Normal dizeleri ile de gibi tırnak karıştırmayı düşünebilirsiniz "$a"'$b'"$c", ancak burada AFAIK analog yok.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

25

Kedi yerine tişört kullanma

Tam olarak orijinal soruya bir cevap olarak değil, ama yine de bunu paylaşmak istedim: Kök hakları gerektiren bir dizinde bir yapılandırma dosyası oluşturmak zorunda kaldı.

Bu durum için aşağıdakiler çalışmaz:

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

çünkü yönlendirme sudo bağlamının dışında işlenir.

Bunun yerine bunu kullanarak sona erdi:

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

senin harfler kullanılırken sudo bash -c 'cat << EOF> /etc/somedir/foo.conf # benim yapılandırma dosyası foo = bar EOF'
likewhoa

5

Yukarıdaki cevapların küçük bir uzantısı. Arka >var olan içeriği üzerine, dosyaya giriş yönlendirir. Bununla birlikte, özellikle kullanışlı bir kullanım, >>yeni içeriğinizi dosyanın sonuna ekleyerek, aşağıdaki gibi ekleyen çift oktur:

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

Bu fstab, içeriğinden herhangi birini yanlışlıkla değiştirmeden endişelenmenize gerek kalmadan genişler .


1

Bu mutlaka orijinal sorunun cevabı değil, kendi testlerimdeki bazı sonuçların paylaşılmasıdır. Bu:

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

ile aynı dosyayı üretecektir:

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

Yani, cat komutunu kullanmanın anlamını göremiyorum.


2
hangi kabuk? Ubuntu 18.04'te bash 4.4 ve OSX'te bash 3.2 ile test ettim. Sadece ikisi de kullanılırken boş bir dosya oluşturdu <<testolmadan cat <<test.
wisbucky

Bu benim için çalıştı LInux Mint 19 Tara zsh
Geoff Langenderfer

0

Burada belgelerin bash döngülerinde de çalıştığını belirtmek gerekir. Bu örnek, tablonun sütun listesinin nasıl alınacağını gösterir:

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 ,

hatta yeni hat olmadan

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.