Bash'te dosya tanımlayıcısı (file descriptor), kabuğun açık bir dosyaya veya akışa verdiği numaradır: 0 standart girdi, 1 standart çıktı, 2 standart hatadır, 3 ve sonrası sizin açacağınız akışlar içindir. exec 3> dosya yazarak 3 numarayı bir dosyaya yazmak için açar, echo ... >&3 ile oraya yazar, exec 3>&- ile kapatırsınız. exec komutu tek başına yönlendirmeyle kullanıldığında yönlendirme bir komutla sınırlı kalmaz, betiğin geri kalanına uygulanır. {fd}> biçimi ise boş bir numarayı kabuğun kendisinin seçmesini sağlar.
Bu yazı GNU Bash kılavuzunun Redirections bölümündeki tanımlayıcı kopyalama, taşıma ve okuma-yazma için açma başlıklarını ve exec yerleşik komutunu çalıştırılmış örneklerle anlatıyor. Betiğin tüm çıktısını bir günlüğe yönlendirip sonra ekrana geri dönmek, iki dosyayı aynı döngüde satır satır okumak ve standart çıktı ile standart hatanın yerini değiştirmek gibi pratik kalıpları da gösteriyoruz. Temel yönlendirme operatörleri (>, >>, 2>&1) için önce yönlendirme yazısını okumanızı öneririz.
exec ile kalıcı yönlendirme

Kılavuza göre exec yerleşik komutu iki farklı iş yapar. Ona bir komut verirseniz kabuk yeni bir süreç oluşturmadan o komutla yer değiştirir; exec satırından sonraki satırlar hiç çalışmaz. Komut vermeden yalnızca yönlendirme yazarsanız yönlendirmeler mevcut kabuğun ortamına uygulanır ve sonraki tüm komutlar bundan etkilenir. Yönlendirme hatasız yapılırsa dönüş durumu sıfırdır.
exec echo "son komut"
echo "bu satır hiç çalışmaz"
# çıktı: son komutBu yazıdaki örneklerin çoğu ikinci kullanımla ilgili. Birinci kullanım, bir betiğin son adımında başka bir programı başlatırken kabuk sürecini gereksiz yere açık tutmamak için tercih edilir. Kılavuz, komut çalıştırılamazsa etkileşimli olmayan bir kabuğun çıktığını, execfail seçeneği açıksa ise sıfırdan farklı bir durumla devam ettiğini de belirtir.
Yeni bir tanımlayıcı açmak ve kapatmak
exec 3> dosya 3 numaralı tanımlayıcıyı dosyaya yazmak için açar; tıpkı > gibi dosya varsa sıfırlanır. Sonrasında >&3 ile yazdığınız her satır aynı açık dosyaya, kaldığı yerden eklenir. Dosya her yazmada yeniden açılmadığı için çok sayıda satır yazan döngülerde bu yöntem >> kullanmaktan daha verimlidir. İşiniz bitince exec 3>&- ile tanımlayıcıyı kapatırsınız. Kılavuz, kopyalama operatörlerinde hedef sözcük - olduğunda tanımlayıcının kapatıldığını söyler.
exec 3> gunluk.txt
echo "ilk kayıt" >&3
echo "ikinci kayıt" >&3
exec 3>&-
cat gunluk.txt
# çıktı:
# ilk kayıt
# ikinci kayıtOkuma için de aynısı geçerlidir: exec 4< dosya 4 numarayı okumak için açar. read komutu varsayılan olarak standart girdiden okur; başka bir tanımlayıcıdan okumak için ya read ... <&4 biçiminde yönlendirme yaparsınız ya da read komutunun -u seçeneğini kullanırsınız. Kılavuza göre -u fd, okumayı standart girdi yerine verilen tanımlayıcıdan yapar. İki yöntem de aynı açık dosyadan okuduğu için her read bir sonraki satıra geçer.
printf '%s\n' "ilk kayıt" "ikinci kayıt" > gunluk.txt
exec 4< gunluk.txt
read -r s1 <&4
read -r -u 4 s2
exec 4<&-
echo "$s1 / $s2"
# çıktı: ilk kayıt / ikinci kayıtread seçeneklerinin tamamı read ile kullanıcı girdisi yazısında anlatılıyor. Burada önemli olan, açık bir tanımlayıcının bir okuma konumu tutmasıdır: dosyayı kapatıp yeniden açmadığınız sürece okuma kaldığı yerden devam eder.
Kopyalama: n>&m ve n<&m
Kılavuzdaki [n]>&sözcük operatörü çıktı tanımlayıcılarını, [n]<&sözcük girdi tanımlayıcılarını kopyalar. Sözcük bir sayıya genişlerse n o tanımlayıcının kopyası yapılır. n yazılmazsa çıktı için 1, girdi için 0 varsayılır. Kopyalamanın anlamı, iki numaranın aynı açık dosyayı göstermesidir; birine yazılan diğerinin konumunu da ilerletir. Kılavuz, sayının çıktı için açık olmayan bir tanımlayıcıyı göstermesinin yönlendirme hatası olduğunu belirtir. Açık olmayan bir tanımlayıcıya yazmaya çalıştığınızda aldığınız hata şudur:
echo "deneme" >&7
echo "durum: $?"
# çıktı:
# ornek.sh: line 1: 7: Bad file descriptor
# durum: 1Kopyalamanın en bilinen örneği 2>&1'dir, ama aynı mekanizma kendi açtığınız tanımlayıcılarla da çalışır. Aşağıda betik boyunca açık kalan bir hata dosyası 3 numaraya bağlanıyor ve tek bir komutun hata akışı 2>&3 ile oraya yönlendiriliyor. Standart çıktı etkilenmediği için ekranda kalıyor.
f() { echo "normal çıktı"; echo "hata mesajı" >&2; }
exec 3> sessiz.txt
f 2>&3
exec 3>&-
echo "dosyada: $(cat sessiz.txt)"
# çıktı:
# normal çıktı
# dosyada: hata mesajıOtomatik numara: {değişken}>

Sabit numaralarla çalışmanın sorunu, bir kütüphane fonksiyonunun veya başka bir betiğin aynı numarayı kullanabilmesidir. Kılavuz bunun için {değişken} biçimini tanımlar: numara yerine süslü parantez içinde bir değişken adı yazarsanız kabuk 10 veya daha büyük, boş bir tanımlayıcı seçer ve numarasını o değişkene atar. Kapatma operatörleriyle (>&- ve <&-) kullanıldığında ise değişkenin değeri kapatılacak tanımlayıcıyı belirtir.
exec {kayit}> otomatik.txt
echo "fd 10 veya üstü mü: $(( kayit >= 10 ))"
echo "veri" >&"$kayit"
exec {kayit}>&-
cat otomatik.txt
# çıktı:
# fd 10 veya üstü mü: 1
# veriBu biçimin bir özelliği daha vardır: kılavuza göre {değişken} verildiğinde yönlendirme, exec kullanılmasa bile komutun kapsamının ötesinde kalıcı olur. Böylece tanımlayıcının ömrünü kendiniz yönetirsiniz. Aşağıda echo satırı bir dosya açıyor; tanımlayıcı satır bittikten sonra da açık kaldığı için ikinci echo aynı dosyaya devam ediyor. shopt -s varredir_close açıldığında ise kabuk bu tanımlayıcıları komut bitince kendiliğinden kapatır.
echo "ilk" {fd}> kalici.txt
echo "ikinci" >&"$fd"
exec {fd}>&-
cat kalici.txt
# çıktı:
# ilk
# ikinciBetiğin tüm çıktısını günlüğe almak ve geri dönmek
Uzun bir bakım betiğinde, belirli bir bölümün tüm çıktısının ve hatalarının bir günlük dosyasına gitmesini, sonra ekrana geri dönülmesini isteyebilirsiniz. Bunun için önce mevcut standart çıktı ve standart hata yedek numaralara kopyalanır, ardından exec > dosya 2>&1 ile ikisi de dosyaya yönlendirilir. Bölüm bitince yedekler geri taşınır.
exec 5>&1 6>&2
exec > betik.log 2>&1
echo "bu satır dosyaya gider"
echo "bu hata da dosyaya gider" >&2
exec 1>&5- 2>&6-
echo "ekrana döndük"
cat betik.log
# çıktı:
# ekrana döndük
# bu satır dosyaya gider
# bu hata da dosyaya giderGeri dönüş satırındaki 1>&5- kılavuzdaki taşıma operatörüdür: [n]>&rakam- önce rakam tanımlayıcısını n numarasına kopyalar, sonra rakam numarasını kapatır. Yani exec 1>&5 5>&- ile aynı işi tek adımda yapar. Girdi tarafındaki karşılığı [n]<&rakam- biçimidir. Yedek tanımlayıcıyı kapatmayı unutursanız, betiğin başlattığı her program bu fazladan açık tanımlayıcıyı devralır; kapatmak düzenli bir alışkanlıktır.
Standart çıktı ile standart hatanın yerini değiştirmek
Bazen bir komutun hata akışını boruya vermek, normal çıktısını ise ekranda bırakmak istersiniz. Bunun için üçüncü bir tanımlayıcı geçici bir kap olarak kullanılır: 3>&1 1>&2 2>&3 3>&-. Sırayla okunursa: 3 numara, standart çıktının o anki hedefini saklar; 1 numara, standart hatanın hedefine yönlendirilir; 2 numara, 3'te saklanan eski standart çıktıya bağlanır; son olarak 3 kapatılır. Yönlendirmeler soldan sağa işlendiği için bu sıra önemlidir.
f() { echo "normal çıktı"; echo "hata mesajı" >&2; }
{ f 3>&1 1>&2 2>&3 3>&- ; } 2> /dev/null
# çıktı: hata mesajıÖrnekte dıştaki 2> /dev/null önce uygulandığı için takas sonrasında normal çıktı /dev/null dosyasına gidiyor, hata mesajı ise standart çıktıya geçip ekrana basılıyor. Yalnızca hata akışını boruya vermek için daha kısa bir yol da vardır: komut 2>&1 > /dev/null | diğer. Bu kalıbın açıklaması pipe yazısında var.
Okuma ve yazma için açmak: <>
[n]<>sözcük operatörü dosyayı hem okumak hem yazmak için açar; n verilmezse 0 numara kullanılır. Kılavuza göre dosya yoksa oluşturulur. > operatöründen farkı, dosyanın sıfırlanmamasıdır: yazma, dosyanın başından başlar ve var olan baytların üzerine yazar, geri kalanına dokunmaz. Aşağıdaki örnekte ilk üç harf değişiyor, satırın geri kalanı yerinde duruyor.
printf 'merhaba dünya\n' > rw.txt
exec 3<> rw.txt
printf 'MER' >&3
exec 3>&-
cat rw.txt
# çıktı: MERhaba dünyaBu davranış, sabit uzunluklu bir kaydı yerinde güncellemek gibi özel durumlar dışında pek gerekmez; çoğu betikte dosyayı okumak için <, yazmak için > veya >> kullanmak daha anlaşılırdır. <> operatörünün bir kullanım alanı da okuma ve yazmanın aynı tanımlayıcıdan yapılması gereken özel dosyalardır.
İki dosyayı aynı döngüde okumak
Standart girdi tek olduğu için iki dosyayı aynı anda satır satır okumak boruyla mümkün değildir. Döngünün sonuna iki ayrı girdi yönlendirmesi yazıp her read komutunu farklı bir tanımlayıcıdan okutursanız iki dosya paralel ilerler. Döngü, dosyalardan biri bittiğinde && sayesinde sona erer.
printf '%s\n' a b c > harf.txt
printf '%s\n' 1 2 3 > sayi.txt
while read -r h <&3 && read -r s <&4; do
echo "$h$s"
done 3< harf.txt 4< sayi.txt
# çıktı:
# a1
# b2
# c3Bu kalıbın bir yararı daha vardır: döngünün standart girdisi serbest kaldığı için döngü içindeki bir komut kullanıcıdan girdi bekliyorsa veya kendi standart girdisini okuyorsa, okunacak dosyanın satırlarını yanlışlıkla tüketmez. Dosyalar yerine komut çıktılarını okumak için 3< <(komut) biçiminde süreç ikamesi kullanılabilir.
Tanımlayıcılar alt süreçlere geçer
Kılavuz, kabuğun çalışma ortamının bir parçası olarak exec ile değiştirilmiş açık dosyaları sayar ve harici bir komut çalıştırıldığında kabuğun açık dosyalarının, komuta özgü yönlendirmelerle birlikte o komuta geçtiğini belirtir. Alt kabuklar da aynı tanımlayıcıları devralır. Aşağıda parantezle açılan alt kabuk, dışarıda açılan 3 numaraya yazabiliyor.
( echo "alt kabuktan" >&3 ) 3> alt.txt
cat alt.txt
# çıktı: alt kabuktanBunun pratik bir sonucu vardır: bir betikte açık bıraktığınız tanımlayıcılar, betiğin başlattığı uzun ömürlü arka plan programlarına da geçer. Kapatılması gereken bir kilit dosyası veya günlük dosyası, betik bittiği halde o program çalıştığı sürece açık kalabilir. Bu nedenle işi biten tanımlayıcıyı kapatmak iyi bir alışkanlıktır. Alt kabuk ve çalışma ortamı kuralları subshell yazısında toplu olarak anlatılıyor.
Operatör özeti
| Operatör | Anlamı |
|---|---|
| exec 3> dosya | 3 numarayı yazmak için aç (dosya sıfırlanır) |
| exec 3>> dosya | 3 numarayı sona eklemek için aç |
| exec 4< dosya | 4 numarayı okumak için aç |
| exec 5<> dosya | 5 numarayı okuma ve yazma için aç, sıfırlama yok |
| komut >&3 | komutun standart çıktısını 3 numaraya yaz |
| read -u 4 x | 4 numaradan bir satır oku |
| exec 3>&- | 3 numarayı kapat |
| exec 1>&5- | 5 numarayı 1 numaraya taşı ve 5 numarayı kapat |
| exec {fd}> dosya | boş bir numara seç, fd değişkenine yaz |
| exec {fd}>&- | fd değişkenindeki numarayı kapat |
Sık yapılan hatalar

exec 3> dosyasatırını bir döngünün içine koymak. Her turda dosya yeniden açılır ve sıfırlanır; açma işlemini döngünün dışına alın.- İşi biten tanımlayıcıyı kapatmamak. Açık kalan numara, betiğin sonradan başlattığı her programa geçer;
exec 3>&-satırını unutmayın. >&3yerine> &3yazmak. Boşluk yüzünden&işareti ayrı bir kontrol operatörü olarak okunur ve Bash "syntax error near unexpected token" hatası verir.- Değişkendeki numarayı dolar işareti olmadan yazmak.
>&"$fd"doğru;>&fdise kılavuzdaki kurala göre standart çıktıyı ve standart hatayıfdadlı bir dosyaya yönlendirir. - Sabit numara seçip bir kütüphanenin de aynı numarayı kullanması. Paylaşılan kodda
{değişken}biçimini tercih edin.
Sık Sorulan Sorular
Kaç tanımlayıcı açabilirim?
Sınır Bash'ten değil işletim sisteminden gelir; kılavuz bir sayı vermez. Elle numara seçerken 3 ile 9 arasında kalmanız, daha fazlası için kabuğa numara seçtirmeniz önerilir.
exec ile yaptığım yönlendirme fonksiyonun içinde de geçerli mi?
Evet. exec mevcut kabuğun ortamını değiştirir ve fonksiyonlar aynı kabukta çalışır. Etkiyi bir bölümle sınırlamak istiyorsanız yönlendirmeyi süslü parantezle gruplanmış bir bloğa veya fonksiyon çağrısının kendisine yazın: { ...; } > dosya ya da fonksiyon > dosya.
/dev/fd/3 yazmak ile >&3 aynı şey mi?
Kılavuz /dev/fd/N dosya adının yönlendirmelerde N numaralı tanımlayıcıyı kopyaladığını, sistem bu dosyaları sağlamıyorsa Bash'in aynı davranışı kendi içinde taklit ettiğini söyler. Bash içinde iki yazım aynı sonucu hedefler; >&3 daha kısa ve sistemden bağımsızdır.
set -x çıktısını ayrı bir tanımlayıcıya yönlendirebilir miyim?
Evet. Kılavuzdaki BASH_XTRACEFD değişkenine açık bir tanımlayıcının numarasını atarsanız iz çıktısı standart hata yerine oraya yazılır. Ayrıntılar hata ayıklama ve set -x yazısında.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Bash öğrenme rehberi · Yönlendirme: stdout ve stderr · Here document ve here string · Process substitution · read ile kullanıcı girdisi · Hata ayıklama ve set -x