Shell builtin ve shell anahtar sözcüğü arasındaki fark nedir?


Yanıtlar:


45

Bash'in kodunuzu ayrıştırmasıyla bir yerleşik ve bir anahtar kelime arasında güçlü bir fark vardır. Fark hakkında konuşmadan önce, tüm anahtar kelimeleri ve yerleşikleri listeleyelim:

yerleşikleri:

$ compgen -b
.         :         [         alias     bg        bind      break     
builtin   caller    cd        command   compgen   complete  compopt   
continue  declare   dirs      disown    echo      enable    eval      
exec      exit      export    false     fc        fg        getopts   
hash      help      history   jobs      kill      let       local     
logout    mapfile   popd      printf    pushd     pwd       read      
readarray readonly  return    set       shift     shopt     source    
suspend   test      times     trap      true      type      typeset   
ulimit    umask     unalias   unset     wait                          

Anahtar Kelimeler:

$ compgen -k
if        then      else      elif      fi        case      
esac      for       select    while     until     do        
done      in        function  time      {         }         
!         [[        ]]        coproc              

Örneğin [, bir yerleşiktir ve bu [[bir anahtar kelimedir. Bu ikisini aşağıdaki farkı göstermek için kullanacağım, çünkü onlar iyi bilinen operatörler: herkes onları tanıyor ve onları düzenli olarak kullanıyor (veya gerekir).

Bir anahtar kelime, ayrıştırma işleminin başlarında Bash tarafından taranıp anlaşılır. Bu, örneğin aşağıdakilere izin verir:

string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
    echo "The string is non-empty"
fi

Bu iyi çalışıyor ve Bash mutlu bir şekilde çıkacak

The string is non-empty

Alıntı yapmadığımı not et $string_with_spaces. Oysa aşağıdakileri:

string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
    echo "The string is non-empty"
fi

Bash'in mutlu olmadığını gösterir:

bash: [: too many arguments

Neden yerleşik kelimelerle değil, anahtar kelimelerle çalışır? Çünkü Bash kodu ayrıştırdığında, [[hangisinin bir anahtar kelime olduğunu görür ve özel olduğunu çok erken anlar. Böylece kapanışı arayacak ]]ve içerisine özel bir şekilde davranacaktır. Bir yerleşik (veya komut), argümanlarla çağrılacak gerçek bir komut olarak değerlendirilir. Bu son örnekte bash, komutu [argümanlarla çalıştırması gerektiğini (her satırda bir tane gösterilir) anlar :

-n
some
spaces
here
]

değişken genişleme, alıntı silme, yol adı genişletme ve sözcük bölme işlemlerinden beri. Komut [kabuğun içine yerleştirildi, bu yüzden hata ile sonuçlanan bu argümanlarla yürütür, bu nedenle şikayet.

Uygulamada, bu ayrımın yerleşik davranışlarla (veya komutlarla) mümkün olmayan karmaşık davranışa izin verdiğini görüyorsunuz.

Hala pratikte, bir yerleşimi anahtar kelimeden nasıl ayırt edebilirsiniz? Bu gerçekleştirmek için eğlenceli bir deney:

$ a='['
$ $a -d . ]
$ echo $?
0

Bash çizgiyi ayrıştırdığında $a -d . ]özel hiçbir şey görmez (yani, diğer ad yok, yönlendirme yok, anahtar kelime yok), bu nedenle yalnızca değişken genişletme gerçekleştirir. Değişken açılımlardan sonra şunu görür:

[ -d . ]

öyleyse komutu (builtin) [argümanları ile çalıştırır -d, .ve ]elbette doğrudur (bu sadece .bir dizin olup olmadığını test eder ).

Bak şimdi:

$ a='[['
$ $a -d . ]]
bash: [[: command not found

Ah. Çünkü Bash bu çizgiyi gördüğünde, özel bir şey görmez ve bu nedenle tüm değişkenleri genişletir ve sonunda şunları görür:

[[ -d . ]]

Şu anda, takma ad genişlemeleri ve anahtar kelime taraması uzun zamandır gerçekleştirildi ve artık gerçekleştirilmeyecek, böylece Bash çağrılan komutu bulmaya çalışıyor, bulamıyor [[ve şikayet ediyor.

Aynı çizgiler boyunca:

$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found

ve

$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found

Takma adın genişlemesi de oldukça özel bir şey. Hepiniz aşağıdakileri en az bir kere yaptınız:

$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found

Sebep aynıdır: takma ad genişlemesi değişken genişleme ve fiyat teklifi kaldırma işleminden çok önce gerçekleşir.


Anahtar Kelime - Diğer Ad

Şimdi, bir takma adı bir anahtar kelime olarak tanımlarsak ne olur?

$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0

Oh, işe yarıyor! bu nedenle takma adlar anahtar kelimeleri takmak için kullanılabilir! bilmek iyi oldu.


Sonuç: builtins gerçekten komutlar gibi davranır: doğrudan değişken genişleme ve kelime bölme ve globbing gibi tartışmalarla yürütülen bir eyleme karşılık gelir. Gerçekten de, herhangi bir yerde harici bir komutun olması /binya da /usr/bindeğişken genişlemeden sonra verilen argümanlarla adlandırılması gibi bir şey. değişken genişleme vb. Bir yerleşik, kabuğun iç durumunu değiştirebilir!

Öte yandan, anahtar kelimeler çok erken taranır ve anlaşılır ve karmaşık kabuk davranışına izin verir: kabuk, sözcük bölmeyi veya yol adı genişlemesini vb. Yasaklayabilir.

Şimdi yerleşiklerin ve anahtar kelimelerin listesine bakın ve neden bazılarının anahtar olması gerektiğini anlamaya çalışın.


!bir anahtar kelimedir. Davranışlarını bir fonksiyonla taklit etmek mümkün olacak gibi görünüyor:

not() {
    if "$@"; then
        return false
    else
        return true
    fi
}

ancak bu, aşağıdaki gibi yapıları yasaklar:

$ ! ! true
$ echo $?
0

veya

$ ! { true; }
echo $?
1

Aynı şekilde time: bir anahtar kelimeye sahip olmak daha güçlüdür, böylece yeniden yönlendirmeli karmaşık bileşik komutları ve boru hatlarını zamanlayabilir:

$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null

Eğer timesadece komut (hatta yerleşik), sadece argümanlar görebileceği yerlerde grep, ^#ve /home/gniourf/.bashrc, zaman bu ve daha sonra çıkış boru hattının geri kalan bölümleri içinden gider. Fakat bir anahtar kelimeyle, Bash her şeyi halledebilir! timeyönlendirmeleri de dahil olmak üzere tam boru hattı olabilir ! timeYalnızca bir komut olsaydı , yapamadık:

$ time { printf 'hello '; echo world; }

Dene:

$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'

Bunu düzeltmeye çalış:

$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory

Umutsuz.


Anahtar kelime vs takma ad?

$ alias mytime=time
$ alias myls=ls
$ mytime myls

Sence ne olur?


Gerçekten, bir yerleşik bir kabuk gibidir, ancak bir anahtar kelime karmaşık davranışa izin veren bir şeydir. Kabuğun gramerinin bir parçası olduğunu söyleyebiliriz.


2
@JohnyTex ile anlaşarak, bu Stack sitelerinde gördüğüm en kapsamlı ve uygun cevaplardan biri. Teşekkür ederim. Belki bazıları alakasız bir soru: Ben bir komut önceki gelen 'geçici olarak devre dışı takma' işlevselliği belgelerine bulmaya çalışıyorum sadece meraktan aşkına =\kullanarak ' man, aproposve helpve hiçbir şansım olmadı ettik. Bu bilgiyi bulma konusunda nereye gideceğim hakkında bir fikrin var mı? Çoğunlukla ileride orada başka neler olduğunu görebiliyorum, çünkü referans kaynağını özlüyorum.
nc.

@nc: açıkça belgelenmiş bulamazsınız. İşe yaramasının nedeni bu cevapta açıklanmıştır. Bulabileceğiniz en yakın şey, Shell Operation bölümündeki referans el kitabında . Takma ad genişlemesinin çok erken yapıldığını göreceksiniz (bu yanıtta vurgulamaya çalıştığım bir şey), 2. adımda. Alıntı kaldırma, parametre genişletme, küreselleşme, vb. Daha sonra gerçekleştirilir. Yani için devre dışı bir takma ad yapmanız alıntı çeşit kullanabilirsiniz korusun bir takma ad olarak bir belirteç anlayış, örneğin, gelen kabuk \ll, "ll"ya da 'll'.
gniourf_gniourf

1
Aslında siparişi aldım, takma adlar ve işten atılan anahtar kelimeler önce olur. [[Verimliliği teklif ettiğimizden beri \[[, takma ad olarak ayrıştırılmaz. Şu ana kadar mı? Kaybolduğum yer ters eğik çizginin Bash'de alıntı bir şey olduğunun farkında değildi ve takma adlar ve anahtar kelimeler altında aramaya çalıştım ve tamamen kayboldum ... Şimdi her şey yolunda. Tırnak işareti bölümünün altında:> Kote olmayan ters eğik çizgi (), kaçış karakteridir.
nc.

1
Bu yüzden (2) Op listesindeki Tokenization olarak düşünebiliriz ve bunun bir OpenTestKeywordToken olacağı ve bunun bir OpenTestKeywordToken olacağı ve oroper derlemesi / doğru bir sözdizimi için kapanması için CloseTestKeywordToken'ı gerektiren \[[tek bir belirteci olduğunu düşünebiliriz . Daha sonra, (4) 'te LiteralQuoteToken, çalıştırılacak bulitin / komutunun adı olarak değerlendirilecek ve (6)' da Bash, herhangi bir yerleşik veya komut olmadığı için çürütecektir . Güzel. Zamanla kesin detayları unutabilirim, ama şu anda Bash'in işleyiş tarzı benim için çok daha net; teşekkür ederim. [[]][[[[
nc.

9

man bashonları çağırır SHELL BUILTIN COMMANDS. Dolayısıyla, bir "shell builtin" normal bir komut gibidir grep, vb., Ancak ayrı bir dosyada bulunmak yerine, bash'ın içine yerleştirilmiştir . Bu, harici komutlardan daha verimli performans göstermelerini sağlar.

Bir anahtar kelime aynı zamanda "Bash'e zor kodlanmıştır, ancak bir yerleşimin aksine, bir anahtar kelime kendi başına bir komut değildir, ancak bir komut yapısının alt birimidir." Bunu, anahtar kelimelerin tek başına bir işlevi olmadığı, ancak bir şey yapması için komut gerektirdiği anlamına gelir. (Bağlantısından, diğer örneklerdir for, while, dove !, ve orada daha fazla cevabım diğer sorunuza.)


1
İlginç gerçek: [[ is a shell keywordama [ is a shell builtin. Neden olduğuna dair hiçbir fikrim yok.
Sparhawk

1
O zamandan bu yana, mümkün olduğunca yakından eski Bourne kabuğuna uymak için tarihsel nedenlerden ve POSIX standardı olasılıkla [bir gün içinde ayrı komut arka olarak mevcuttu. [[standart tarafından belirtilmediğinden, geliştiriciler bunu anahtar kelime veya dahili olarak eklemeyi seçebilir.
Sergiy Kolodyazhnyy,

1

Ubuntu ile birlikte verilen komut satırı kılavuzu, anahtar kelimelerin tanımını vermez, ancak çevrimiçi el kitabı (sidenote'a bakın) ve POSIX Shell Komut Dili standart spesifikasyonları , bunlara "Ayrılmış Kelimeler" olarak bakın ve her ikisinin de listelerini sağlayın. POSIX standardından:

Bu tanıma yalnızca karakterlerden hiçbiri alıntılanmadığında ve kelime şu şekilde kullanıldığında gerçekleşir:

  • Bir komutun ilk sözcüğü

  • Büyük / küçük harf dışında ayrılmış sözcüklerden birini izleyen ilk sözcük

  • Bir vaka komutunda üçüncü kelime (sadece bu durumda geçerlidir)

  • For komutundaki üçüncü kelime (sadece bu durumda ve geçerlidir)

Buradaki anahtar anahtar kelimelerin / ayrılmış kelimelerin özel bir anlama sahip olmalarıdır, çünkü kabuk sözdizimini kolaylaştırırlar, döngüler, bileşik komutlar, dallanma (if / case) ifadeleri gibi belirli kod bloklarını işaret ederler, ancak komut ifadeleri oluşturmaya izin verirler. kendi başlarına - hiçbir şey yapmayın ve aslında for, gibi anahtar kelimeler girerseniz until, case- kabuk tam bir ifade bekler, aksi takdirde - sözdizimi hatası:

$ for
bash: syntax error near unexpected token `newline'
$  

Kaynak kod düzeyinde, bash için ayrılmış sözcükler parese.y'de tanımlanırken , yerleşiklerin kendilerine ayrılmış bir dizini vardır.

Kenar notu

GNU endeksi [ayrılmış sözcük olarak gösterir , ancak aslında yerleşik bir komuttur. [[aksine ayrılmış bir kelimedir.

Ayrıca bkz: Anahtar kelime, ayrılmış kelime ve yerleşik kelime arasındaki farklar?

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.