Neden `[` bir kabuk yerleşik ve `[[` bir kabuk anahtar kelime?


64

Bildiğim kadarıyla, [[geliştirilmiş bir versiyonu [, ancak [[bir anahtar kelime olarak gördüğümde ve [bir yerleşik olarak gösterildiğinde kafam karıştı .

[root@server ~]# type [
[ is a shell builtin
[root@server ~]# type [[
[[ is a shell keyword

TLDP diyor ki

Bir yerleşik, aynı addaki bir sistem komutunun eş anlamlısı olabilir, ancak Bash bunu dahili olarak yeniden uygular. Örneğin, Bash echo komutu, davranışları neredeyse aynı olsa da, / bin / echo ile aynı değildir.

ve

Bir anahtar kelime ayrılmış bir kelime, belirteç veya operatördür. Anahtar kelimeler kabuk için özel bir anlama sahiptir ve gerçekten de kabuğun sözdiziminin yapı taşlarıdır. Örnek olarak, bir süre için yapın ve! anahtar kelimelerdir. Bir yerleşime benzer şekilde, bir anahtar kelime Bash'e kodlanmıştır, ancak bir yerleşikin aksine, bir anahtar kelime kendi başına bir komut değildir, ancak bir komut yapısının bir alt birimidir. [2]

Her iki yapmamalı [ve [[bir anahtar sözcük? Burada özlediğim bir şey var mı? Ayrıca, bu bağlantıyı de o tazeliyor [ve [[aynı tür ait olmalıdır.



9
Makinemde / bin / [var.
Joshua,

2
İkisi arasındaki tek farkın basit bir gösteri gibi: if "[" $x -eq 3 ](Bash denilen komut için göründüğünden beklendiği gibi çalıştığını [ve bu var), ama if "[[" $x -eq 3 ]]yok değil kez daha Bash uygun ismin bir komutla arar çünkü (çalışır, ancak hiçbir yoktur [[komutu).
Kyle Strand

1
@Joshua Yani /usr/bin/echo, ama bu bir yerleşik olmadığı anlamına gelmez .
Jonathon Reinhart

Dışsal olarak bulunan tüm bulitler, yerleşik değillerse argümanları ayrıştırırlar.
Joshua,

Yanıtlar:


80

Arasındaki fark [ve [[oldukça önemlidir.

  • [bir komuttur. Argümanları, diğer komut argümanlarının işleme alındığı şekilde işlenir. Örneğin, şunları göz önünde bulundurun:

    [ -z $name ]

    Kabuk, herhangi bir diğer komutta olduğu gibi, sonuçta hem sözcük bölme hem de dosya adı oluşturma $nameişlemlerini genişletecek ve gerçekleştirecektir .

    Örnek olarak, aşağıdakiler başarısız olacaktır:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments

    Bu işin doğru yapılması için, alıntı yapılması gerekir:

    $ [ -n "$name" ] && echo not empty
    not empty
  • [[bir kabuk anahtar sözcüğüdür ve argümanları özel kurallara göre işlenir. Örneğin, şunları göz önünde bulundurun:

    [[ -z $name ]]

    Kabuk genişleyecektir $namefakat, başka herhangi bir komuta farklı olarak, yapacak ne sonuca kelime bölme ne de dosya adı nesil. Örneğin, içine yerleştirilen boşluklara rağmen aşağıdakiler başarılı olacaktır name:

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty

özet

[ bir komuttur ve kabuğun yürüttüğü tüm diğer komutlarla aynı kurallara tabidir.

Çünkü [[bir komut değil bir anahtar kelimedir, ancak kabuk özel olarak davranır ve çok farklı kurallar altında çalışır.


+1. Teşekkürler. Bir komut ve bir anahtar kelimenin hangi kurallar altında çalıştığı için kaynak (referans) verebilir misiniz?
Tim

@Tim Komutların işleyiş kuralları , içinde ayrıntılı olarak verilmiştir man bash. Özellikle, "BASİT KOMUTANLIĞI GENİŞLETME" ve "KOMUTANLI YÜRÜTME" başlıklarına bakınız. Ek olarak [[, diğer bash anahtar içerir if, then, whileve 'vaka'. Anahtar kelimeler için genel kurallar yoktur: her anahtar kelime özel bir durumdur. man bash Her biri için detayları içerir.
John1024

62

In V7 Unix Bourne kabuğu kendi yaptı - - [denirdi testve sadece olarak varolmuştur /bin/test. Yani, bugün olarak yazmak istediğiniz kod:

if [ "$foo" = "bar" ] ; then ...

yerine yazmış olacaktın

if test "$foo" = "bar" ; then ...

Bu ikinci gösterim hala var ve bunu ne olup bittiğini hakkında daha net olduğunu bulmak: Eğer adı verilen bir komutu aradığınız testBağımsız değişkenlerini değerlendirir ve bir döner, çıkış durum koduif bundan sonra ne karar vermek kullanır. Bu komut kabuğun içine yerleştirilebilir veya harici bir program olabilir.

[testsonradan geleceğe bir alternatif olarak .² Bunun için yerleşik bir eş anlamlı olabilir test, ancak yerleşik olarak bulunmayan /bin/[kabukları için modern sistemlerde de sağlanmıştır .

[ve testaynı kod kullanılarak uygulanabilir. Buna örnek olarak /bin/[ve /bin/testbunlar OS X üzerinde sabit bağlantılar sonucunda aynı executable.³ için, uygulama tamamen sondaki yok sayar ]sen olarak çağırırsanız bunu gerektirmez: /bin/[ve o şikayet etmiyor Eğer varsa yapmak için bunu sağlamak /bin/test.⁴

Bu tarihin hiçbiri etkilemiyor [[, çünkü hiçbir zaman ilkel bir program olmadı [[. Tamamen POSIX kabuğuna bir uzantı olarak uygulayan kabukları içinde bulunur .

"Yerleşik" ve "anahtar kelime" arasındaki ayrımın bir kısmı bu tarihe bağlıdır. Aynı zamanda ayrıştırma için sözdizimi kuralları gerçeğini yansıtmaktadır [[içinde belirttiği gibi ifadeler, farklı John1024 cevabı .⁵


Dipnotlar:

  1. Buna baktığınızda, [parantez ve parantezlerin çoğu diğer programlama dillerinde çalışma şeklinden farklı olarak, neden kabuk betiklerine boşluk koymak zorunda olduğunuzu açıkça belirtir . Kabuğun komut ayrıştırıcısına izin verilirse if["$x"..., izin vermesi de gerekirdi.iftest"$x"...

  2. O 1980 civarında oldu /bin/[benim kopyasında yok Antik Unix V7 1979 den, ne de yok man testtakma ad olarak belgelemek. Ben bir ön sürüm kopyasında sahip karşılık gelen adam sayfası kaydında Sistem III 1980 den kılavuzda, bu edilmektedir sıraladı.

  3. ls -i /bin/[ /bin/test

  4. Fakat bu davranışa güvenmeyin. Bash yerleşik sürüm [kapanmasını gerektirir ]ve bunun yerleşik testeğer uygulama şikayet yok bunu sağlamak.

  5. Yerleşik ve dış komut ayrımı da başka bir nedenden dolayı önemli olabilir: iki uygulama farklı davranabilir. Bu, echobirçok sistem için geçerlidir. Yalnızca bir uygulama olduğundan, bir anahtar kelime için böyle bir ayrım yapılması gerekmez.


Eğer Young @Warren ederim, ama neden builtinve keywordayrım arasında [ve [[her iki (aslında dışında aynı işlevi sağlayan zaman [[daha fazla özellik daha birlikte gelir [)?
Sree

2
@sree [[bir anahtar kelime olmak, bash ile mümkün olmayan şeyleri yapmanıza izin verir [- örneğin, alıntı yapmak çok zaman gerektirmez, çünkü kabuk bir değişken olduğunun farkındadır. Diğer bir deyişle, bir anahtar kelime kullanıldığında komut satırının işlenmesi etkilenir, ancak bir yerleşik kullanıldığında - bu daha sonra gerçekleşir.
muru

1
V7 Bourne kabuğu [için kod bir yerleşik gösterir , ancak kod yorumlanmıştır.
Stéphane Chazelas

cdbir yerleşiktir, ancak hiçbir şeyi gölgede bırakmaz ( cdharici bir program olarak uygulanamaz).
Palo Ebermann

2
Bu mükemmel bir tarihsel özettir, ancak cevabında, yukarıda bir muru ve John1024 tarafından işaret edilen (IMO) kritik detayını, [[bir anahtar kelime yapmanın kabuğun argümanları için özel ayrıştırma kuralları kullanmasına izin verdiğini gösterir. Ne yazık ki, benim oyum John 1024’e gidiyor.
Ilmari Karonen

3

[Başlangıçta sadece harici bir komut, başka bir isim /bin/test. Ancak , kabuk komut dosyalarında [ve gibi birkaç komut, echokabuk uygulayıcılarının her seferinde başka bir işlemi çalıştırmak yerine, kodu doğrudan kabuğun içine kopyalamaya karar verdikleri kabuk komut dosyalarında kullanılır. Bu komutları "builtins" e çevirdi, ancak harici programı hala tam yolu ile çağırabilirsin.

[[çok sonra geldi. Yerleşik kabuk içinde dahili olarak uygulanmasına rağmen, harici komutlar gibi ayrıştırılır. John1024'ün cevabında açıklandığı gibi, bu, değişken olmayan değişkenlerin kendilerine kelime bölme işlemi yapacağı ve belirteçleri G / Ç yönlendirmesi gibi >ve <işlendiği anlamına gelir . Bu, karmaşık karşılaştırma ifadelerinin yazılmasını uygunsuz hale getirdi. [[Kabuk sözdizimi olarak oluşturulmuş, böylece ideosyncratically ayrıştırılabilir. [[Değişkenler içinde sözcük bölme işlemi yapılmaz <ve >karşılaştırma operatörleri olarak kullanılabilir =, bir sonraki parametrenin kote edilmiş olup olmamasına bağlı olarak farklı davranışlarda bulunabilirler. Bunların hepsi [[, geleneksel [komuttan / yerleşikten daha kolay kullanılmasını sağlayan kolaylıklardır .

[Milyonlarca senaryoda uyumsuz bir değişiklik olacağından, basitçe böyle bir sözdizimi olarak kodlanamazlardı . [[Daha önce bulunmayan yeni sözdizimini kullanarak, yukarı doğru uyumlu bir şekilde kullanılma şeklini tamamen düzeltebilirlerdi.

Bu, $((...))çoğunlukla geleneksel exprkomutun yerini alan aritmetik ifadelerin sözdizimi ile sonuçlanan evrime benzer .


0

Yeni [[yer bashbir olan optimizasyon ait [.

Klasik [önemsiz bir işlem yapmak için sıkça kullanıldığında büyük bir dezavantajı vardır: her seferinde yeni bir süreç ortaya çıkarır:
(Sadece karşılaştırma 0ve 1! Her seferinde yeni bir adres alanı yaratır !)

Bence, eklemenin temel bir [[amacı, içindeki ifadenin değerlendirmesini [fazladan bir işlem yapmamaktır. Ancak [, çalışma nasıl değiştirilemedi - çok fazla kafa karışıklığı ve sorun yaratabilirdi. Bu nedenle, optimizasyon yeni bir adla, daha verimli bir şekilde, yani shell builtin komutu ile gerçekleştirildi.
Yan etki olarak kabuk sözdiziminde anahtar kelime haline geldi.

İlk [kullanıldığında, bunu harici bir işlemle yapmanın doğru yoluydu .


5
Her ne [kadar orjinal bir komut olsa da, muhtemelen Unix System III piyasaya sürüldüğünde ve kesinlikle Unix System V piyasaya sürülmeden önce, kabuğa yerleşik olarak eklenmişti. Bu yüzden, 'ekstra süreç' uzun zamandır sorun olmuyor. Ancak, sözdizimi değişmeden kaldı - harici bir komut olacak gibi ele alındı.
Jonathan Leffler

@JonathanLeffler Oh, teşekkürler, [her ikisini de özledim - bu bazı değişiklikler anlamına gelir ...
Volker Siegel
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.