Bash'te bir komutu arka planda çalıştırmak için sonuna & eklersiniz; kabuk [1] 25647 gibi bir satırla iş numarasını ve sürecin kimliğini yazar ve hemen yeni komut beklemeye döner. Çalışan işleri jobs listeler. Ön plandaki bir komutu Ctrl-Z ile durdurursunuz; durdurulan işi bg arka planda, fg ön planda sürdürür. İşlere %1, %+ gibi iş belirteçleriyle başvurulur; wait işlerin bitmesini bekler, disown bir işi kabuğun iş tablosundan çıkarır.
Bu yazıda iş denetiminin (job control) temel kavramlarını, ön plan ile arka plan arasındaki farkı, iş belirteçlerini, jobs, fg, bg, wait, kill ve disown komutlarını, kabuk kapanırken işlerin başına gelenleri ve betiklerde iş denetiminin neden kapalı olduğunu göreceksiniz. Ctrl-Z gibi terminal davranışlarını etkileşimli oturum örnekleriyle anlatıyor, betikte çalışabilen kısımları gerçek çıktılarıyla gösteriyoruz. Kaynak, GNU Bash Reference Manual'ın 7. bölümü (Job Control) ve sinyallerle ilgili 3.7.6 bölümüdür.
İş, süreç grubu, ön plan ve arka plan
Kabuk her boru hattı (pipeline) için bir iş oluşturur; komut1 | komut2 iki süreçten oluşsa da tek bir iştir. Kabuk çalışan işlerin bir tablosunu tutar ve her işe köşeli parantez içinde gösterilen, 1'den başlayan bir iş numarası verir. Arka planda başlattığınız bir işin ardından basılan [1] 25647 satırında 1 iş numarası, 25647 ise boru hattındaki son sürecin kimliğidir (PID). Tablo boşaldığında numaralar yeniden 1'den başlar.
İş denetimi işletim sistemi çekirdeğinin terminal sürücüsüyle birlikte çalışır. Her sürecin bir süreç grubu kimliği vardır ve terminalin o anki bir ön plan süreç grubu bulunur. Ön plan grubundaki süreçler klavyeden üretilen SIGINT (Ctrl-C) gibi sinyalleri alır; arka plan süreçleri bunlardan etkilenmez. Terminalden yalnızca ön plan süreçleri okuyabilir; arka plandaki bir süreç terminalden okumaya kalkarsa sistem ona SIGTTIN gönderir ve süreç, sinyali yakalamadıysa durur. Terminale yazma için de aynı kural, stty tostop ayarı açıksa SIGTTOU ile geçerlidir.
Pratik sonucu şudur: girdi bekleyen bir programı & ile arka plana atarsanız, ilk okumada durdurulur ve jobs onu Stopped olarak gösterir. Arka planda çalışacak komutların girdisini bir dosyadan yönlendirin; yönlendirme kuralları stdout ve stderr yönlendirme yazısında.
Ctrl-Z, bg ve fg ile bir işi yönetmek
Uzun süren bir komutu ön planda başlattınız ve terminali geri istiyorsunuz. Askıya alma karakterine (genellikle Ctrl-Z) bastığınızda süreç durur ve kontrol kabuğa döner. Sonra bg ile işi arka planda sürdürebilir, işiniz bitince fg ile ön plana alabilirsiniz. Askıya alma karakteri hemen etki eder ve bekleyen çıktıyla önceden yazılmış girdiyi de atar. Gecikmeli askıya alma karakteri (genellikle Ctrl-Y) ise süreci hemen değil, terminalden okumaya çalıştığı anda durdurur. Etkileşimli bir oturumda akış aşağı yukarı şöyle görünür; ^Z ve ^C basılan tuşları, diğer satırlar kabuğun iş bildirimlerini gösterir:
$ sleep 300
^Z
[1]+ Stopped sleep 300
$ bg
[1]+ sleep 300 &
$ jobs
[1]+ Running sleep 300 &
$ fg %1
sleep 300
^C
$bg her verilen işi, sanki baştan & ile başlatılmış gibi arka planda sürdürür; iş belirtilmezse kabuğun o anki işini kullanır. fg verilen işi ön plana alır ve onu o anki iş yapar; dönüş durumu ön plana alınan komutun durumudur. İkisi de iş denetimi kapalıyken ya da iş denetimi olmadan başlatılmış bir iş verildiğinde başarısız olur. Kısayollar da vardır: yalnızca %1 yazmak fg %1 ile, %1 & yazmak bg %1 ile aynıdır.
İş belirteçleri: %1, %+, %- ve %?metin

İş denetimi komutları işleri % ile başlayan iş belirteçleriyle tanır. Kabuğun o anki iş ve önceki iş kavramları vardır: arka planda yeni başlayan, ön plandayken durdurulan ya da arka planda sürdürülen iş o anki iş olur, eski o anki iş önceki işe düşer. O anki iş bittiğinde önceki iş o anki iş olur. jobs çıktısında o anki iş +, önceki iş - ile işaretlenir.
| Belirteç | Anlamı |
|---|---|
| %n | n numaralı iş |
| %metin | komut adı metin ile başlayan iş |
| %?metin | komut satırında metin geçen iş |
| %% , %+ , % | o anki iş |
| %- | önceki iş |
Önek ya da alt dize birden fazla işle eşleşirse Bash hata bildirir. Aşağıdaki betik üç arka plan işi başlatıp belirteçleri jobs ile deniyor. Betikte iş denetimi kapalı olsa da kabuk iş tablosunu yine tutar, bu yüzden jobs çalışır:
sleep 3 &
( sleep 3; echo yedek >/dev/null ) &
sleep 3 &
jobs
jobs %+
jobs %-
jobs %?yedek
jobs -r | wc -l
kill $(jobs -p)
wait
echo "bitti"
# çıktı:
# [1] Running sleep 3 &
# [2]- Running ( sleep 3; echo yedek > /dev/null ) &
# [3]+ Running sleep 3 &
# [3]+ Running sleep 3 &
# [2]- Running ( sleep 3; echo yedek > /dev/null ) &
# [2]- Running ( sleep 3; echo yedek > /dev/null ) &
# 3
# bittiEn son başlatılan 3 numaralı iş o anki iş (+), ondan önceki önceki iş (-) oldu. %?yedek komut satırında yedek geçen işi buldu. Kabuğun, alt kabuk komutunu listelerken kendi biçiminde yeniden yazdığına dikkat edin (>/dev/null yerine > /dev/null). jobs -p her işin süreç grubu liderinin kimliğini verdi ve kill bu kimliklere varsayılan sinyal olan SIGTERM gönderdi. Alt kabukların ayrıntısı subshell ve çalışma ortamı yazısında.
| jobs seçeneği | Etkisi |
|---|---|
| -l | normal bilgiye ek olarak süreç kimliklerini gösterir |
| -p | yalnızca işin süreç grubu liderinin kimliğini yazar |
| -n | yalnızca son bildirimden bu yana durumu değişen işleri gösterir |
| -r | yalnızca çalışan işleri gösterir |
| -s | yalnızca durdurulmuş işleri gösterir |
| -x komut | komuttaki iş belirteçlerini süreç grubu kimlikleriyle değiştirip komutu çalıştırır |
wait: işlerin bitmesini beklemek
wait argümansız çağrıldığında çalışan tüm arka plan işlerinin bitmesini bekler ve 0 döner. Bir süreç kimliği ya da iş belirteci verirseniz o işin bitmesini bekler ve çıkış durumunu döndürür; iş belirteciyse işteki bütün süreçleri bekler. Verilen kimlik kabuğun bir alt süreci değilse dönüş durumu 127 olur. -n seçeneği verilen işlerden, ya da hiç argüman yoksa herhangi bir işten, ilk bitenini bekler; -p değişken de dönen durumun hangi işe ait olduğunu o değişkene yazar. Bu ikisi birlikte, paralel işleri bittikleri sırayla işlemenin yoludur:
for n in 3 1 2; do
( sleep "$n"; exit "$n" ) &
sureler[$!]=$n
done
for i in 1 2 3; do
wait -n -p kim
d=$?
echo "${sureler[$kim]} saniyelik iş bitti, durum $d"
done
# çıktı:
# 1 saniyelik iş bitti, durum 1
# 2 saniyelik iş bitti, durum 2
# 3 saniyelik iş bitti, durum 3$!, en son arka planda başlatılan sürecin kimliğidir; örnekte bu kimliği indeks olarak kullanıp her işin süresini bir dizide tuttuk. İşler 3, 1, 2 sırasıyla başladı ama wait -n onları bitiş sırasıyla döndürdü. Özel parametreler script argümanları ve özel değişkenler, seyrek indeksli diziler Bash dizi kullanımı yazısında. Bekleme sırasında tuzak kurulmuş bir sinyal gelirse wait 128'den büyük bir durumla hemen döner ve ardından tuzak çalışır; tuzaklar trap ile sinyal ve temizlik yazısında.
İş denetimi açıkken wait bir iş yalnızca durum değiştirdiğinde (örneğin durdurulduğunda) da döner; -f seçeneği gerçekten sonlanana kadar beklemeye zorlar. Aşağıdaki örnek bir sinyalle sonlandırılan işin durumunu ve bulunmayan bir işi beklemenin sonucunu gösteriyor:
sleep 5 &
pid=$!
kill "$pid"
wait "$pid"
durum=$?
echo "durum: $durum" # çıktı: durum: 143
kill -l "$durum" # çıktı: TERM
wait 12345
echo "durum: $?"
# çıktı: ornek.sh: line 8: wait: pid 12345 is not a child of this shell
# çıktı: durum: 127143, 128 ile SIGTERM sinyal numarası olan 15'in toplamıdır: sinyalle sonlanan bir sürecin durumu 128 artı sinyal numarasıdır. kill -l böyle bir durumu alıp sinyalin adını yazar; 128'den küçük sayıları ise doğrudan sinyal numarası sayar. Çıkış durumlarının tamamı exit kodları ve çıkış durumu yazısında.
kill ve iş belirteçleri
kill verilen her kimliğe, sinyal belirtilmezse SIGTERM gönderir. Kimlik bir süreç kimliği ya da iş belirteci olabilir; sinyal adı -s TERM, -TERM ya da -15 biçiminde, SIG öneki olsun olmasın ve büyük küçük harf fark etmeksizin yazılabilir. Etkileşimli kabukta kill %2, 2 numaralı işin bütün süreçlerine sinyal gönderir; süreç kimliğini aramanıza gerek kalmaz. Kılavuz iş denetimi etkin değilken kill ve wait komutlarının iş belirteci kabul etmediğini, süreç kimliği verilmesi gerektiğini söyler; bu yüzden betiklerde $! ile kimliği saklayıp onu kullanmak en güvenli yoldur.
Durdurulmuş bir işi zorla durdurmak ya da terminalinize bağlı olmayan bir süreci askıya almak için SIGSTOP sinyalini kill ile gönderebilirsiniz. Bir işi gerçekten öldürmek için önce SIGTERM deneyin; süreç ona tepki vermiyorsa daha sert sinyallere geçin.
Kabuktan çıkarken işlere ne olur? disown ve huponexit

Kabuk varsayılan olarak SIGHUP sinyali aldığında, örneğin terminal penceresi kapandığında ya da uzak bağlantı koptuğunda, kapanır. Kapanmadan önce etkileşimli kabuk bu SIGHUP sinyalini çalışan ya da durdurulmuş bütün işlerine yeniden gönderir; durdurulmuş işlerin sinyali alabilmesi için onlara önce SIGCONT gönderir. Sonuç olarak arka planda bıraktığınız işler, sinyali özel olarak ele almıyorlarsa, kabukla birlikte sonlanır. Bunu önlemenin kabuk içi yolu disown komutudur:
disown %1: işi iş tablosundan tamamen çıkarır; kabuk artık onu tanımaz,jobsgöstermez veSIGHUPgöndermez.disown -h %1: işi tabloda bırakır ama kabukSIGHUPaldığında bu işe sinyali iletmemesi için işaretler.disown -avedisown -r: argüman verilmediğinde sırasıyla tüm işleri ve yalnızca çalışan işleri kapsar; hiç seçenek ve argüman yoksa o anki iş kullanılır.
sleep 1 &
sleep 1 &
jobs
disown %1
echo "disown sonrası:"
jobs
wait
echo "bitti"
# çıktı:
# [1]- Running sleep 1 &
# [2]+ Running sleep 1 &
# disown sonrası:
# [2]+ Running sleep 1 &
# bittiBir ayrıntı daha: huponexit kabuk seçeneği açıksa, etkileşimli bir oturum açma kabuğu exit ile normal şekilde kapanırken de bütün işlerine SIGHUP gönderir. Bu seçenek varsayılan olarak kapalıdır; shopt seçenekleri yazısında diğerleriyle birlikte anlatılıyor.
Çıkışta başka bir koruma da vardır. Durdurulmuş işler varken kabuktan çıkmaya çalışırsanız Bash bir uyarı basar; checkjobs seçeneği açıksa çalışan işler için de uyarır ve işleri durumlarıyla listeler. Arada başka bir komut çalıştırmadan hemen yeniden çıkmayı denerseniz Bash ikinci kez uyarmaz ve durdurulmuş işleri sonlandırarak kapanır.
Sunucuda oturumu kapatsanız bile çalışmaya devam etmesi gereken uzun işler için disown bir ilk yardımdır. Bağlantının kopmasına dayanıklı, sonradan yeniden bağlanabileceğiniz oturumlar için tmux ve screen ile terminal oturumları, sürekli çalışması gereken servisler için systemd servis dosyası daha uygun araçlardır.
Betiklerde iş denetimi neden kapalı?
İş denetimi etkileşimli kabuklarda varsayılan olarak açıktır; betik çalıştıran etkileşimsiz kabukta kapalıdır. Betik yine & ile arka plan süreçleri başlatabilir, jobs ve wait kullanabilir; ama süreçler ayrı süreç gruplarına konmaz ve fg, bg çalışmaz:
sleep 1 &
fg
echo "fg durumu: $?"
# çıktı: ornek.sh: line 2: fg: no job control
# çıktı: fg durumu: 1set -m (uzun adıyla set -o monitor) iş denetimini açar: her süreç ayrı bir süreç grubunda çalışır ve arka plandaki bir iş bittiğinde kabuk çıkış durumunu içeren bir satır basar. Bunun gerçekten işe yaraması için kabuğun bir terminale bağlı olması gerekir. set -b (notify) ise biten arka plan işlerinin durumunu bir sonraki istemi beklemeden hemen bildirir; bu da yalnızca iş denetimi açıkken etkilidir. Normalde Bash, başka çıktıyı bölmemek için durum değişikliklerini bir sonraki istemden hemen önce bildirir.
Kılavuz iş denetimi olmayan kabuklarla ilgili bir sinyal ayrıntısı da verir: iş denetimi etkin değilken arka planda başlatılan komutlar, kabuktan devraldıkları ayarlara ek olarak SIGINT ve SIGQUIT sinyallerini de yok sayar. Bu yüzden betiğin başlattığı bir arka plan sürecini Ctrl-C ile durduramazsınız; onu kill ile sonlandırmanız ya da bir trap içinde temizlemeniz gerekir.
auto_resume ve iş sayısını istemde göstermek
auto_resume değişkeni tanımlıysa, yönlendirme içermeyen tek kelimelik bir komut önce durdurulmuş işlerden birini sürdürmek için aday sayılır. Değeri exact ise kelime durdurulmuş işin adıyla tam eşleşmelidir, substring ise adın bir parçası olması yeter (%?metin gibi); başka herhangi bir değerde önek eşleşmesi kullanılır (%metin gibi). Örneğin bir düzenleyiciyi Ctrl-Z ile durdurduysanız, yeniden adını yazmak onu yeni bir kopya açmak yerine ön plana getirir. Kaç işiniz olduğunu sürekli görmek isterseniz istemde \j kaçışını kullanabilirsiniz; bkz. PS1 prompt özelleştirme.
Sık yapılan hatalar

- Girdi bekleyen komutu arka plana atmak: ilk okumada
SIGTTINile durur. Girdiyi bir dosyadan ya da/dev/nullüzerinden verin. - Oturumu kapatınca işin süreceğini sanmak: kabuk kapanırken işlere
SIGHUPgönderebilir;disownkullanın ya da işi tmux veya bir servis içinde çalıştırın. - Betikte fg ve bg kullanmak: iş denetimi kapalı olduğu için
no job controlhatası alırsınız. - Betikte iş belirtecine güvenmek: kılavuza göre iş denetimi yokken
killvewaitsüreç kimliği bekler;$!değerini saklayıp kullanın. - wait sonrası durumu kaybetmek:
$?bir sonraki komutla değişir;waitsonucunu hemen bir değişkene alın. - Belirsiz önek kullanmak:
%slbirden çok işle eşleşirse Bash hata verir; numara kullanın.
Sık Sorulan Sorular
Çalışan bir komutu sonradan arka plana nasıl alırım?
Önce Ctrl-Z ile durdurun, sonra bg yazın. İş arka planda kaldığı yerden devam eder. Oturumu kapattıktan sonra da sürmesi gerekiyorsa ardından disown çalıştırın.
jobs neden hiçbir şey göstermiyor?
jobs yalnızca o kabuğun başlattığı ve henüz bildirimi yapılıp tablodan silinmemiş işleri gösterir. Başka bir terminalde ya da başka bir kabukta başlatılan süreçler bu tabloda yoktur; biten bir iş de durumu size bildirildikten sonra tablodan çıkar.
& ile başlattığım işin süreç kimliğini nasıl alırım?
Hemen ardından $! özel parametresini okuyun ve bir değişkene kaydedin. Daha sonra jobs -l ya da jobs -p da kimlikleri gösterir.
Ctrl-C arka plandaki işi neden durdurmuyor?
Klavyeden üretilen sinyaller yalnızca ön plan süreç grubuna gider. Arka plandaki işi durdurmak için ya fg ile ön plana alıp Ctrl-C basın ya da kill %n kullanın.
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 · trap ile sinyal ve temizlik · exit kodları · subshell ve çalışma ortamı · pipe ve PIPESTATUS · PS1 özelleştirme · tmux ve screen · systemd servis dosyası