Bash'ta <<, <<< ve <<arasındaki fark nedir?


102

Ne arasındaki fark var <<, <<<ve < <bash?


20
En azından bu Google merkezli zamanlarda, bu sembol tabanlı operatörleri aramak zor. "<< <<< <<" takıp kullanabileceğiniz bir arama motoru var mı?
Daniel Griscom

11
@DanielGriscom var SymbolHound .
Dennis,

1
@DanielGriscom Yığın Değişimi arama sembollerini desteklemek için kullanılır, ancak daha sonra bir şey bozuldu ve kimse onu düzeltti.
muru

O zaten orada (ve neredeyse bir yıldır olmuştur): Kabuğun kontrol ve yönlendirme operatörleri nelerdir?
Scott

Yanıtlar:


115

İşte belge

<<here-documentyapı olarak bilinir . Programa bitiş metninin ne olacağını bildirirsiniz ve bu sınırlayıcı göründüğünde, programa verdiğiniz tüm bilgileri giriş olarak okur ve üzerinde bir görev gerçekleştirir.

İşte demek istediğim:

$ wc << EOF
> one two three
> four five
> EOF
 2  5 24

Bu örnekte, wcprogramın EOFstring'i beklemesini , ardından beş kelimeyi EOFyazıp girdi verdiğimizi işaret etmek için yazın . Aslında, wctek başına koşmaya , sözcükleri yazmaya ve ardından tuşuna basmaya benzer.CtrlD

Bash'te bunlar geçici dosyalar aracılığıyla, genellikle formda /tmp/sh-thd.<random string>, tire ise adsız borular olarak uygulanırlar. Bu, sistem çağrıları stracekomut ile izleme yoluyla görülebilir . Bu yönlendirmeyi nasıl gerçekleştirdiğini görmek için bashile değiştirin .sh/bin/sh

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'

İşte dize

<<<olarak bilinir here-string. Metin yazmak yerine, bir programa önceden hazırlanmış bir metin dizesi verirsiniz. Örneğin, sadece bu özel durum için çıktı almak için bcyapabileceğimiz bir programla, bc <<< 5*4etkileşimli olarak bc çalıştırmaya gerek yoktur.

Burada bash dizeleri, genellikle /tmp/sh-thd.<random string>daha sonra bağlantısız olan biçimindeki geçici dosyalar aracılığıyla uygulanır , bu nedenle geçici olarak bir miktar bellek alanı kaplarlar, ancak /tmpdizin girişleri listesinde görünmezler ve yine de isimsiz dosyalar olarak etkin bir şekilde bulunurlar. dosya tanıtıcısı aracılığıyla kabuğun kendisi tarafından referans alınabilir ve bu dosya tanıtıcısı komut tarafından miras alınır ve daha sonra dup2()işlev aracılığıyla dosya tanıtıcısı 0 (stdin) üzerine çoğaltılır . Bu üzerinden gözlemlenebilir

$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd

Ve izleme sistemleri aracılığıyla (okunabilirlik için çıktı kısaltıldı; geçici dosyanın fd 3 olarak nasıl açıldığına, bunun üzerine yazılan verinin nasıl açıldığına dikkat edin, daha sonra O_RDONLYfd 4 olarak bayrakla yeniden açıldı , daha sonra bağlantısı kaldırıldı, sonra dup2()fd 0 üzerine, catsonradan devralındı. ):

$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4)         = 4
[pid 10229] write(3, "\n", 1)           = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0)                  = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072)   = 5
[pid 10229] write(1, "TEST\n", 5TEST
)       = 5
[pid 10229] read(0, "", 131072)         = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

Görüş: potansiyel olarak burada dizgiler geçici metin dosyalarını kullandığı için, burada-dizgilerin her zaman sonunda yeni bir satır eklemesi olası bir nedenidir, çünkü POSIX tanımına göre metin dosyasının satırsonu karakterinde biten satırlara sahip olması gerekir.

İşlem Değiştirme

Gibi tldp.org açıklıyor

Süreç ikame işlemi, bir işlemin (ya da işlemlerin) çıktısını başka bir işlemin başlığına besler.

Yani aslında bu boru benzer stdout'u örneğin diğer bir komuta echo foobar barfoo | wc. Ancak dikkat: bash manpage’de bunun olarak belirtildiğini göreceksiniz <(list). Yani temelde çoklu (!) Komutların çıktısını yönlendirebilirsiniz.

Not: teknik < <olarak tek bir şeye atıfta bulunmadığınızı söylediğinizde , tek <çıkışlı iki yönlendirme ve çıktının işlem yeniden yönlendirmesidir <( . . .).

Şimdi sadece ikame işlemini yaparsak ne olur?

$ echo <(echo bar)
/dev/fd/63

Gördüğünüz gibi, kabuk /dev/fd/63çıktının gittiği geçici bir dosya tanımlayıcı oluşturur ( Gilles'in cevabına göre isimsiz bir boru). Bu, <o dosya tanımlayıcısını bir komuta girdi olarak yönlendirir.

Bu yüzden çok basit bir örnek, iki yankı komutundan çıktının işlem yerine wc'ye işlem yapmasıdır:

$ wc < <(echo bar;echo foo)
      2       2       8

Bu yüzden burada kabuk parantezde gerçekleşen tüm çıktılar için bir dosya tanıtıcısı yaratır ve bunu, wcbeklendiği gibi, wc'nin her biri kelimesi olan iki satır çıktısı alabilen iki eko komutundan bu akımı aldığı için yönlendiririz . ve uygun bir şekilde 2 kelimemiz, 2 satırımız ve 6 karakterimiz var.

Yan Not: Proses ikamesi bir faşizm ( bashPOSIX tarafından belirtilen ancak POSIX gibi gelişmiş kabuklarda kullanılabilecek bir komut veya yapı ) olarak adlandırılabilir, ancak bu, ksh man sayfasıksh olarak bash'ın varlığından önce uygulanmıştır ve bu cevabı göstermektedir. Gibi tcshve mkshancak kabukları işlem ikamesine sahip değildir. Öyleyse, çoklu komutların çıktısını proses ikame edilmeden başka bir komuta yönlendirmeyi nasıl başarabiliriz? Gruplama artı borular!

$ (echo foo;echo bar) | wc
      2       2       8

Etkili olarak bu yukarıdaki örnekte olduğu gibidir, Ancak bu, işlemin yerine kullanılmasından dolayı başlık altında farklıdır, çünkü tüm alt kabuktan stdout wc ve boruya bağlı stdin yaparız . Öte yandan, işlem değiştirme işlemi, komutun geçici bir dosya tanıtıcısını okumasını sağlar.

Peki, borularla gruplama yapabilirsek, neden işlem ikamesine ihtiyacımız var? Çünkü bazen boru kullanamayız. Aşağıdaki örneği ele alalım - iki komutun çıktılarını diff(iki dosyaya ihtiyaç duyar ve bu durumda iki dosya tanımlayıcı veriyoruz) karşılaştırabiliriz.

diff <(ls /bin) <(ls /usr/bin)

7
< <kişi bir süreç ikamesinden stdin yaparken kullanılır . Böyle bir komut gibi görünebilir: cmd1 < <(cmd2). Örneğin,wc < <(date)
John1024 27.05

4
Evet, işlem değişimi bash, zsh ve AT&T ksh {88,93}
John1024

2
< < tek başına bir şey değildir , işlem değiştirme durumunda, bunun <başlaması gereken bir şey daha olur<
immibis

1
@muru Bildiğim kadarıyla <<<, önce Plan 9 rc kabuğunun Unix limanı tarafından, daha sonra zsh, bash ve ksh93 tarafından kabul edildi. O zaman ben buna faşizm demezdim.
jlliagre 27:15

3
Boru kullanılamaz yerlere başka bir örnek: echo 'foo' | read; echo ${REPLY}edecek değil dönmek foo, çünkü read- boru bir alt kabuk başlayan bir alt kabukta başlatılır. Ancak, alt-kabuk olmadığından read < <(echo 'foo'); echo ${REPLY}doğru döner foo.
Paddy Landau

26

< < bir sözdizimi hatası:

$ cat < <
bash: syntax error near unexpected token `<'

< <()bir işlem ikame ( <()(yönlendirme ile bir arada) <):

Kararlı bir örnek:

$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63

İşlem değişikliği ile, dosya tanımlayıcısının yolu dosya adı gibi kullanılır. Bir dosya adını doğrudan kullanmak istemiyorsanız (veya kullanamıyorsanız), işlem değiştirmeyi yeniden yönlendirme ile birleştirirsiniz.

Açık olmak gerekirse, < <operatör yok .


Cevabınızı alıyorum, <<() <() 'den daha yararlıdır?
Solfish

1
@solfish <(), dosya adı benzeri bir şey verir, bu yüzden daha genel olarak kullanışlıdır - stdin'in gerekmeyeceği < <()yeri değiştiriyor. İçinde wc, ikincisi daha faydalı olur. Başka yerde daha az faydalı olabilir
muru

12

< <bir sözdizimi hatasıdır, muhtemelen command1 < <( command2 )bir işlem değiştirme işleminin ardından basit bir giriş yeniden yönlendirmesi olan ve çok benzer, ancak buna eşdeğer olmayan demek

command2 | command1

Çalıştırdığınız varsayarak fark bashedilir command1ilki mevcut kabukta çalıştırılır iken, ikinci durumda bir alt kabukta çalıştırılır. Bu, ayarlanan değişkenlerin command1proses ikame varyantı ile kaybolmayacağı anlamına gelir .


11

< <bir sözdizimi hatası verir. Doğru kullanım aşağıdaki gibidir:

Örneklerin yardımıyla açıklama:

Örnek < <():

while read line;do
   echo $line
done< <(ls)

Yukarıdaki örnekte, while döngüsüne giriş lssatır satır okunabilecek echove döngüde ed komutundan gelecektir .

<()işlem ikamesi için kullanılır. Daha fazla bilgi ve örnek <()bu linkte bulunabilir:

Proses ikamesi ve boru

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.