Internal ve External Komutlar

CLI’a geçtiğimizde – özellikle de Linux dünyasında – karşımıza sıklıkla çıkan bir konu var: Internal ve external komut farkı.

Shell Script’lerin Çalışma Mantığı yazımda da bu ifadeler geçiyor. Bu yazımda; internal ve external komut farkının ne olduğunu, bu farkın nasıl anlaşılabileceğini, bu ayrımın neye göre yapıldığını anlatmaya çalışacağım. Bunlara ek olarak, bu farkı iliklerinize kadar hissedeceğiz bir de örnek senaryom var 🙂

External Komut Nedir?

External komutlar, adından da anlaşılabileceği üzere, shell’inizin ne olduğunu bilmediği, ancak gidip çalıştırdığı komutlardır.

External komutlar, sisteminizdeki programlardır. Bu programlar derlenmiş kodlar olabileceği gibi, script’ler de olabilir.

Aşağıdaki örnekte “ls” komutunu inceliyoruz. “ls”, “/usr/bin/” dizininde bulunan bir şey. Ama tam olarak ney? Bunu anlamak için öncelikle “file” komutundan yardım alalım. Sonrasında “ls“in ismini değiştirelim. Bakalım ne oluyor:

[root@localhost ~]# file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c8ada1f7095f6b2bb7ddc848e088c2d615c3743e, stripped
[root@localhost ~]# ls
anaconda-ks.cfg  wget-1.14-18.el7_6.1.x86_64.rpm
[root@localhost ~]# mv /usr/bin/ls /usr/bin/listeleyici_program
[root@localhost ~]# listeleyici_program
anaconda-ks.cfg  wget-1.14-18.el7_6.1.x86_64.rpm
[root@localhost ~]# listeleyici_program -l
toplam 552
-rw-------. 1 root root   1638 Eyl  3 09:41 anaconda-ks.cfg
-rw-r--r--. 1 root root 560272 May 16  2019 wget-1.14-18.el7_6.1.x86_64.rpm
[root@localhost ~]# ls
-bash: /usr/bin/ls: Böyle bir dosya ya da dizin yok

“file” ile baktığımızda, bu dosyanın “ELF 64-bit LSB executable” olduğunu görüyoruz. Derlenmiş bir program. Sonrasında “ls” yazıyoruz, bulunduğu dizindeki dosyaları listeliyor. Zaten beklenen de bu.

Ancak bu dosyanın adını değiştirip “listeleyici_program” yaptığımızda ve devamında “listeleyici_program” komutunu girdiğimizde, yine “ls” programının çalıştığını görüyoruz. Hatta “ls -l” ile “listeleyici_program -l” aynı işi yapıyor. Son olarak “ls” çalıştırmak istediğimizde ise, bash bize bu dosyayı bulamadığını söylüyor.

Çok normal bir durum. Sisteminizdeki bir programın adını değiştirdiğinizde, çalışmasında herhangi bir farklılık olmaz ki. Sadece adı değişti. İçerik aynı. Peki bash, bizim “listeleyici_program”ı nereden buldu da çalıştırdı?

PATH Çevre Değişkeni

Shell’imiz, external komutları ararken “PATH” çevre değişkeninden (environment variable) faydalanır:

[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

Yukarıda sıralanan dizinlere sırasıyla bakar. Eğer bu dizinlerden birinde yazılan komutu bulamazsa, “komut bulunamadı” hatasını döner. Tabii verilen komut, internal değilse. Oraya geleceğiz.

PATH neden önemli? FHS’e (Filesystem Hierarchy Standard) uymayan dizinlere kurulum yaptığınızda, doğru komutları yazmanıza rağmen “komut bulunamadı” hatası alabilirsiniz. Bu durumlarda, çalıştırılabilir programların bulunduğu dizini PATH değişkeninize eklemeniz gerekir. Dikkat edin! “Eklemeniz” gerekir. Üzerine yazarsanız vay hâlinize 🙂

Benzer durumlarla Windows ortamında da karşılaşmışsınızdır. Java ya da Python kurulumunda mesela. “Bunu PATH’e ekleyeyim mi?” diye sorar size. Eklemezseniz, sabaha kadar “python” yazarsınız, komut bulunamaz.

Internal Komut Nedir?

Internal komutlar ise, shell’inize eklenmiş komutlardır. Harici program değillerdir. Bu komutlar, shell’inize ait işlevlerdir. Unutmayın, shell’iniz de bir program. Programlara uygun şeyleri yazdığınızda size yanıt verirler. Shell’iniz de öyle. Önce bir bakar, “Ben bu yazılanı anlıyor muyum? Benim böyle bir özelliğim var mı?”. Baktı ki yok, gider PATH’teki dizinlerde arar komutu. O da yoksa, yapacak bir şey yok der ve “bulamadım” der.

“type”, “set”, “cd” gibi komutlar; bash’in internal komutlarına (internal commands, built-in) örnek verilebilir.

Internal ve External Komut Farkı Nasıl Anlaşılır?

Bu ayrımı yapabilmemiz için bize yardımcı olabilecek bazı araçlar var. Şimdi bunlardan bahsedelim.

type

“type” komutu, bir komutun tipiyle ilgili size bilgi verir. Örneklere bakalım:

[root@localhost ~]# type env
env /usr/bin/env'dir
[root@localhost ~]# type set
set bir kabuk yerleşiğidir
[root@localhost ~]# type type
type bir kabuk yerleşiğidir
[root@localhost ~]# type cd
cd bir kabuk yerleşiğidir

“type” şu işimize yarıyor. “Kıymetli yol arkadaşım bash. Sana birazdan bir komut yazacağım ama sen onu ne şekilde algılayacaksın, bana gösterir misin?”

Yukarıdaki çıktıdan şunu anlıyoruz. “env” komutu external. “env” yazdığımızda, “/usr/bin/env” çalıştırılacak. Kalan komutlar ise bash’in içine yerleştirilmiş fonksiyonlar.

which

“Komut mu? Hangi komut?” dedirtmenizi sağlayacak bir araçtır. Girdiğiniz komutun full path’ini verir.

[root@localhost ~]# which cp
alias cp='cp -i'
	/usr/bin/cp
[root@localhost ~]# which tar
/usr/bin/tar
[root@localhost ~]# which type
/usr/bin/which: no type in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
[root@localhost ~]# which help
/usr/bin/which: no help in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)

Yukarıdaki çıktıyı inceleyelim. “which cp” sorusuna, bunun bir alias olduğu yanıtı gelmiş. “cp -i” çalıştıracak. O alias içindeki “cp” de “/usr/bin/cp” yolunda. “tar” ise “/usr/bin/tar” yolundaki program. “type” ve “help” için ise “şu şu şu dizinlere baktım, böyle bir şey yok” yanıtı gelmiş. O baktığı dizinler bizim PATH değişkenindeki dizinlerdi hatırlarsanız.

Bulunamayan komutlar, belki de gerçekten external komutlar, ancak bulundukları dizinler PATH’e eklenmemiş. Bu bir ihtimal. Ya da belki de internal komutlardır. Bu da bir ihtimal. Internal olup olmadıklarından emin olmak için “type” kullanabilirsiniz ki bunların internal komut olduğunu zaten görmüştük.

Internal Komutlarla İlgili Yardım Alma

External komutların kullanımı ile ilgili yardım almak için muazzam bir aracımız zaten var: “man

Ancak söz konusu internal komutlar olduğunda ya bir man sayfası bulamazsınız ya da shell’inizin tüm man sayfasını taramak zorunda kalırsınız. Burada yardımımıza koşan komut ise “help“. Kendisi, internal komutlarla ilgili yardım gösteren bir internal komut.

[root@localhost ~]# type help
help bir kabuk yerleşiğidir
[root@localhost ~]# help help
help: help [-dms] [pattern ...]
    Display information about builtin commands.
    
    Displays brief summaries of builtin commands.  If PATTERN is
    specified, gives detailed help on all commands matching PATTERN,
    otherwise the list of help topics is printed.
    
    Options:
      -d	output short description for each topic
      -m	display usage in pseudo-manpage format
      -s	output only a short usage synopsis for each topic matching
    	PATTERN
    
    Arguments:
      PATTERN	Pattern specifiying a help topic
    
    Exit Status:
    Returns success unless PATTERN is not found or an invalid option is given.

Örnek Senaryo

echo Örneği

“echo” komutunun man sayfasında “–version” opsiyonu ile programın sürümünü görebileceğiniz yazıyor. Deneyelim bakalım:

root@localhost ~]# man echo
[root@localhost ~]# echo --version
--version

Olmadı… Neden?

man sayfası, “echo programının” man sayfası. Peki biz “echo” yazdığımızda ne çalışıyor?:

[root@localhost ~]# type echo
echo bir kabuk yerleşiğidir

Internal komut… Sistemde iki tane echo var. Bir tanesi, bash’in yerleşik özelliği. Diğeri ise external bir komut.

[root@localhost ~]# which echo
/usr/bin/echo
[root@localhost ~]# /usr/bin/echo --version
echo (GNU coreutils) 8.22
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Brian Fox ve Chet Ramey tarafından yazıldı.
[root@localhost ~]# type echo
echo bir kabuk yerleşiğidir

echo’nun yolunu sorduk. “/usr/bin/echo” yanıtı geldi. “type” ile sorduğumuzda ise “kabuk yerleşiği” yanıtı geliyor. Bash bize şunu söylüyor: “Sen echo yazıyorsun ama bende de böyle bir fonksiyon var. External komutu gidip çalıştıracağıma kendi internal komutumu çalıştırırım.”

External komut olan “echo”yu çalıştırmak için full path veriyoruz. Böylece “–version” beklendiği gibi çalışabiliyor.

pwd Örneği

Bulunduğumuz dizinde “test” adında bir dizin oluşturalım. Sonrasında bu dizine “link” adında bir sembolik link ekleyelim:

[root@localhost ~]# mkdir test
[root@localhost ~]# ln -s test link
[root@localhost ~]# ls -l
toplam 556
-rw-------. 1 root root   1638 Eyl  3 09:41 anaconda-ks.cfg
lrwxrwxrwx. 1 root root      4 Ara 21 01:13 link -> test
drwxr-xr-x. 2 root root   4096 Ara 21 01:13 test
-rw-r--r--. 1 root root 560272 May 16  2019 wget-1.14-18.el7_6.1.x86_64.rpm
[root@localhost ~]# 

Görüldüğü üzere “test” dizini var. “link” adında bir sembolik link de “test” dizinini gösteriyor. Şimdi “link”in içine gidip “pwd” komutu ile hangi dizinde olduğumuzu soralım:

[root@localhost ~]# cd link
[root@localhost link]# pwd
/root/link

/root/link” yanıtı geldi. “echo” ile ilgili yaşadığımız durumun aynısı, “pwd” için de geçerli. “pwd” yazdığımızda, internal komut olan “pwd” çalışıyor. “pwd”nin yardımına baktığımızda ise şunu görüyoruz:

[root@localhost link]# help pwd
pwd: pwd [-LP]
    Print the name of the current working directory.
    
    Options:
      -L	print the value of $PWD if it names the current working
    	directory
      -P	print the physical directory, without any symbolic links
    
    By default, `pwd' behaves as if `-L' were specified.
    
    Exit Status:
    Returns 0 unless an invalid option is given or the current directory

“-L” opsiyonuyla “$PWD” değişkenindeki değeri yazarım. “-P” opsiyonuyla ise sembolik linki göz ardı ederim. Varsayılan olarak -L yazılmış gibi çalışırım. Yani sembolik linkte misin, değil misin, bakmam diyor 🙂

[root@localhost link]# pwd
/root/link
[root@localhost link]# pwd -P
/root/test

Peki external pwd’yi çalıştırınca ne oluyor?:

[root@localhost link]# which pwd
/usr/bin/pwd
[root@localhost link]# pwd
/root/link
[root@localhost link]# /usr/bin/pwd
/root/test

Oops… Anlaşılan external komut olan pwd, sembolik linki varsayılan olarak göz ardı ediyor. Man sayfasına bir göz atalım:

DESCRIPTION
       Print the full filename of the current working directory.

       -L, --logical
              use PWD from environment, even if it contains symlinks

       -P, --physical
              avoid all symlinks

/usr/bin/pwd” external komutuna “-L” eklerseniz, sembolik linki görürüm diyor. “-P” ile de linkleri göz ardı ederim demiş.

Aynı komut, aynı opsiyonlar. Opsiyonların yaptığı işler de aynı. Ancak varsayılan davranışları farklı.

Shell script’lerinizde echo ve pwd’ler havada uçuşuyorsa ve bir yerlerde patlıyorsanız, internal ve external komut farkına dikkat etmenizi öneririm 🙂

Internal ve External Komutlar
Internal ve External Komutlar