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ı /bin
ya da /usr/bin
değ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 time
sadece 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! time
yönlendirmeleri de dahil olmak üzere tam boru hattı olabilir ! time
Yalnı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.