Ana içeriğe geç
Kesit Python
Temel · Bilgisayarına geç

Terminal ve komut satırı

Çalışma dizini, göreli/mutlak yol, python betik.py ve -m, sys.argv ile argüman, sys.exit ile çıkış kodu, stdout/stderr, yönlendirme ve ortam değişkenleriyle betiklerini terminalden profesyonelce çalıştırırsın.

Uygulamada aç, 11 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

Neden terminal?

Anadolu Market'in gece 02.00'de çalışan stok raporu bir sunucuda, kimsenin ekranına bakmadığı bir makinede koşar. Orada VS Code yok, "Çalıştır" düğmesi yok; yalnızca terminal (terminal, komut satırı, shell) var. Veri ekipleri betiklerini terminalden çalıştırır, zamanlayıcılar (cron, Windows Görev Zamanlayıcı) betiği terminal komutuyla başlatır, CI sistemleri testleri terminalde koşturur. Terminali rahat kullanmak, "kendi bilgisayarımda çalışıyor" ile "şirkette her gece çalışıyor" arasındaki köprüdür.

Bu derste terminalin dört temel fikrini öğreneceksin: neredesin (çalışma dizini), neyi çalıştırıyorsun (komut ve argümanlar), sonuç ne oldu (çıkış kodu) ve veri nereye akıyor (standart girdi/çıktı/hata). Python tarafında bunların karşılığı os.getcwd(), sys.argv, sys.exit(), sys.stdin/stdout/stderr ve os.environ'dur. Kurulum dersinde terminali açıp python --version yazmıştın; şimdi derine iniyoruz.

Neredesin? Çalışma dizini

Terminal her an bir klasörün "içinde" durur; buna çalışma dizini (working directory, cwd) denir. Yazdığın göreli dosya adları bu klasöre göre çözülür. Nerede olduğunu pwd (macOS/Linux) ya da cd (Windows'ta tek başına) söyler; başka klasöre cd klasor ile geçersin.

$ pwd
/home/ayse
$ mkdir anadolu-rapor
$ cd anadolu-rapor
$ ls
$ cd ..
$ pwd
/home/ayse

Python içinden çalışma dizinini os.getcwd() ya da pathlib.Path.cwd() verir. Tarayıcıdaki Python da sanal bir dosya sisteminde çalışır; aşağıdaki kodu çalıştırıp kendi "konumunu" gör:

import os
from pathlib import Path

print("Çalışma dizini:", os.getcwd())
print("Path ile:", Path.cwd())
os.makedirs("rapor_deneme", exist_ok=True)   # mkdir'in Python karşılığı
os.chdir("rapor_deneme")                     # cd'nin Python karşılığı
print("Şimdi:", os.getcwd())
os.chdir("..")
print("Geri döndük:", os.getcwd())

Windows ve macOS/Linux komutları

macOS ve Linux aynı aileden (Unix) gelir, komutları neredeyse aynıdır. Windows'ta iki terminal vardır: eski cmd ve modern PowerShell. PowerShell, ls, pwd, cat gibi Unix adlarını da takma ad (alias) olarak tanır; bu yüzden yeni başlayanlara PowerShell'i öneririz.

Ne yaparmacOS / LinuxWindows cmdPowerShell
Bulunduğun klasörpwdcdpwd / Get-Location
Klasör içeriğils, ls -ladirls / dir
Klasöre gir / üst klasörcd veri / cd ..cd veri / cd ..aynı
Klasör oluşturmkdir rapormkdir rapormkdir rapor
Dosya içeriğini göstercat satis.csvtype satis.csvcat / Get-Content
Kopyala / taşı / silcp / mv / rmcopy / move / delcp / mv / rm
Ekranı temizleclearclscls / clear
Metin içinde aragrep HATA log.txtfindstr HATA log.txtSelect-String HATA log.txt
Python'ı çağırpython3py ya da pythonpy ya da python
Ortam değişkeni okuecho $HOMEecho %USERPROFILE%$env:USERPROFILE
Son çıkış koduecho $?echo %ERRORLEVEL%$LASTEXITCODE
Tab tuşu senin dostunKlasör ya da dosya adının ilk birkaç harfini yazıp Tab'a bas; terminal gerisini tamamlar. Yukarı ok tuşu önceki komutu geri getirir. Bu iki alışkanlık yazım hatalarının çoğunu önler.

Göreli ve mutlak yol

Mutlak yol (absolute path) kökten başlar: /home/ayse/veri/satis.csv ya da C:\Users\ayse\veri\satis.csv. Nerede olursan ol aynı dosyayı gösterir. Göreli yol (relative path) çalışma dizinine göre yorumlanır: veri/satis.csv, ./satis.csv (bu klasör), ../arsiv/eski.csv (bir üst klasör). ~ ise macOS/Linux'ta ev klasörünün kısaltmasıdır.

import posixpath
from pathlib import PurePosixPath

cwd = PurePosixPath("/home/ayse/anadolu-rapor")
goreli = "../veri/./satis.csv"

birlesik = cwd / goreli
print("Birleştirilmiş:", birlesik)
print("Sadeleştirilmiş:", posixpath.normpath(str(birlesik)))
print("Mutlak mı?", PurePosixPath(goreli).is_absolute(), PurePosixPath("/etc/hosts").is_absolute())
print("Dosya adı:", birlesik.name, "| uzantı:", birlesik.suffix, "| gövde:", birlesik.stem)

PurePosixPath diske dokunmadan yalnızca yol metniyle çalışır; bu yüzden her bilgisayarda aynı sonucu verir ve test yazmak için idealdir. / operatörü yolları birleştirir. .. parçalarını sadeleştirmek için posixpath.normpath kullanılır (gerçek diskte Path.resolve() aynı işi yapar).

Betik nerede, sen neredesin?Betiğin open("veri/satis.csv") diyorsa dosya, betiğin klasörüne göre değil, terminalin bulunduğu klasöre göre aranır. cd yapmadan başka yerden çalıştırırsan FileNotFoundError alırsın. Sağlam çözüm: Path(__file__).parent / "veri" / "satis.csv".

Python'ı terminalden çalıştırmak

python rapor.py               # betiği çalıştır (macOS/Linux'ta çoğu zaman python3)
py rapor.py                   # Windows'ta Python başlatıcısı
python -m pip install rich    # pip'i "bu Python'ın" modülü olarak çalıştır
python -m venv .venv          # sanal ortam oluştur
python -m json.tool veri.json # JSON'u güzel biçimde yazdır
python -c "print(2 ** 10)"    # tek satırlık kod

python betik.py bir dosyayı, python -m modul ise Python'ın bulduğu bir modülü betik gibi çalıştırır. pip yerine python -m pip yazmak, paketin tam olarak o Python'a kurulmasını garanti eder; bilgisayarda birden fazla Python varken en sık karışıklığı böyle önlersin.

Betiğe argüman vermek: sys.argv

Terminalde betik adından sonra yazdığın her kelime argüman (argument) olur ve sys.argv listesine metin (str) olarak düşer. İlk eleman (sys.argv[0]) her zaman betiğin adıdır.

$ python rapor.py İzmir 7 --detay
# sys.argv == ["rapor.py", "İzmir", "7", "--detay"]

Tarayıcıda terminal olmadığı için aynı mantığı bir liste ile taklit ediyoruz. Profesyonel kodda da yaygın kalıp budur: argümanları main(argv) fonksiyonuna parametre olarak vermek; böylece fonksiyonu test edebilirsin.

def main(argv):
    if len(argv) < 2:
        print("Kullanım: python rapor.py <şehir> [gün]")
        return 2
    sehir = argv[1]
    gun = int(argv[2]) if len(argv) > 2 else 7     # argümanlar hep str!
    print(f"{sehir} için son {gun} günün raporu hazırlanıyor")
    return 0

print("Kod:", main(["rapor.py", "İzmir", "30"]))
print("Kod:", main(["rapor.py", "Ankara"]))
print("Kod:", main(["rapor.py"]))
Argümanlar her zaman metindir"7" ile 7 farklıdır. Sayı bekliyorsan int() ile dönüştür. Çok seçenekli araçlar için standart kütüphanedeki argparse modülünü orta seviyede göreceksin; sys.argv onun altında yatan temeldir.

Çıkış kodu: işin sonucu bir sayı

Her program bittiğinde işletim sistemine bir çıkış kodu (exit code) bırakır. Kural basittir: 0 başarı, 0 dışındaki her şey hata. Gelenek olarak 1 genel hata, 2 hatalı kullanım (eksik/yanlış argüman) demektir. Zamanlayıcılar ve CI sistemleri ekrana değil bu sayıya bakar: kod 0 değilse alarm üretir.

Python'da sys.exit(kod) çağırınca SystemExit istisnası fırlatılır ve program o kodla biter. Hiç çağırmazsan ve hata da olmazsa kod 0'dır; yakalanmamış bir istisnada kod 1 olur.

import sys

def kontrol(satir_sayisi):
    if satir_sayisi == 0:
        print("Rapor boş, çıkılıyor", file=sys.stderr)
        sys.exit(1)
    print("Rapor tamam:", satir_sayisi, "satır")

for n in [120, 0]:
    try:
        kontrol(n)
    except SystemExit as e:
        print("Program şu kodla bitecekti:", e.code)

Standart girdi, çıktı ve hata

Her program üç "boru" ile doğar: standart girdi (stdin, klavyeden ya da başka programdan gelen veri), standart çıktı (stdout, asıl sonuç) ve standart hata (stderr, uyarı ve hata mesajları). print() varsayılan olarak stdout'a yazar; print(..., file=sys.stderr) ise stderr'e. İkisi ekranda aynı görünür ama ayrı borulardır ve ayrı yönlendirilebilir.

import sys, io

def ozetle(tutarlar, out=sys.stdout, err=sys.stderr):
    gecerli = []
    for t in tutarlar:
        if t < 0:
            print(f"UYARI: negatif tutar atlandı: {t}", file=err)
        else:
            gecerli.append(t)
    print(f"toplam={sum(gecerli)}", file=out)

hata_kutusu = io.StringIO()          # stderr'i bir metin kutusunda yakala
ozetle([250, -40, 1200], err=hata_kutusu)
print("Yakalanan uyarılar:", hata_kutusu.getvalue().strip())

Akışı parametre yapmak (out, err) fonksiyonu test edilebilir kılar: testte io.StringIO verirsin, gerçekte sys.stdout. Kullanıcıdan girdi almayı (input()) "Kullanıcı girdisi ve tür dönüşümü" dersinde derinlemesine göreceksin.

Yönlendirme ve boru hattı

python rapor.py İzmir > rapor.txt        # stdout'u dosyaya yaz (üzerine)
python rapor.py Bursa >> rapor.txt       # dosyanın sonuna ekle
python rapor.py İzmir 2> hatalar.log      # yalnız stderr'i dosyaya
python rapor.py İzmir > out.txt 2>&1     # ikisini aynı dosyaya
cat stok.log | grep HATA | wc -l        # boru: birinin çıktısı diğerinin girdisi
type stok.log | findstr HATA            # Windows cmd karşılığı
python temizle.py < ham.csv > temiz.csv  # stdin'i dosyadan oku

| (boru, pipe) Unix felsefesinin kalbidir: her araç tek işi iyi yapar, borularla birleşir. Senin betiğin de iyi bir boru vatandaşı olsun: veriyi stdout'a, mesajları stderr'e yaz. Böylece > sonuc.csv yapan kişi dosyasında uyarı satırı bulmaz.

Ortam değişkenleri

Ortam değişkenleri (environment variables), işletim sisteminin programa verdiği anahtar-değer ayarlarıdır: PATH, HOME, veritabanı adresi, ortam adı (geliştirme/canlı). Python'da os.environ bir sözlük gibi davranır. Değer her zaman metindir ve olmayabilir; bu yüzden .get(ad, varsayilan) kullan.

export ANADOLU_ORTAM=canli        # macOS/Linux (bu oturum için)
set ANADOLU_ORTAM=canli           # Windows cmd
$env:ANADOLU_ORTAM = "canli"      # PowerShell
import os

ortam = os.environ.get("ANADOLU_ORTAM", "gelistirme")
zaman_asimi = int(os.environ.get("ZAMAN_ASIMI", "30"))
print("Ortam:", ortam, "| zaman aşımı:", zaman_asimi, "sn")
print("PATH tanımlı mı?", "PATH" in os.environ)

os.environ["ANADOLU_ORTAM"] = "test"     # yalnız bu süreç ve alt süreçleri görür
print("Yeni değer:", os.environ["ANADOLU_ORTAM"])
Parolayı koda yazmaVeritabanı parolası, API anahtarı gibi sırlar kod dosyasına değil ortam değişkenine (ya da gizli kasa/vault aracına) konur. Kod Git'e gider; ortam değişkeni gitmez. os.environ["SIFRE"] değişken yoksa KeyError verir, .get() ise None ya da varsayılanı döndürür.
Gerçek işteIlgaz Bank'ın gün sonu mutabakat betiği şöyle çağrılır: python -m mutabakat 2026-10-27 --sube 0412 > mutabakat.csv 2> mutabakat.log. Zamanlayıcı her gece bu komutu koşturur; veritabanı adresi BANKA_DB_URL ortam değişkeninden gelir. Betik çıkış kodu 0 dönerse CSV muhasebeye gider; 1 dönerse nöbetçi geliştiriciye otomatik bildirim düşer ve log dosyası incelenir. Hiç kimse ekrana bakmaz; her şeyi çıkış kodu, stdout ve stderr anlatır.
Derinleş

Bu bölüm, bir sunucuya bağlandığında ya da büyük bir log dosyasıyla karşılaştığında işini görecek Linux kabuk (shell) araçlarını tanıtıyor. Amaç her bayrağı ezberlemek değil; hangi aracın hangi işe yaradığını bilip gerektiğinde man grep ya da grep --help ile ayrıntıya bakabilmek. Her aracın yanında Python karşılığını da göreceksin: ikisini birlikte bilmek, ne zaman tek satırlık komutun ne zaman betiğin daha uygun olduğunu seçmeni sağlar. Windows'ta bu komutları WSL (Windows Subsystem for Linux) ya da Git Bash ile kullanabilirsin.

Metin araçları: grep, sed, awk, jq

KomutNe yapar?Python karşılığı
grep -i "hata" app.logDeseni içeren satırları yazar (-i büyük/küçük harf duyarsız, -v tersini, -c sayısını, -r klasörde özyinelemeli)[s for s in satirlar if "hata" in s.lower()]
sed 's/eski/yeni/g' dosyaSatır satır bul-değiştir (g olmadan satırdaki yalnız ilk eşleşme)re.sub("eski", "yeni", satir)
awk -F, '{t+=$10} END {print t}'Satırları sütunlara böler ($1, $2... 1'den başlar), hesap yaparsum(float(s.split(",")[9]) ...)
jq '.items[].fiyat' veri.jsonJSON içinden alan seçer, süzer[x["fiyat"] for x in d["items"]]
sort | uniq -c | sort -rnSırala, ardışık tekrarları say, sayıya göre azalan dizCounter(...).most_common()
cut -d, -f2, head, tail -f, wc -lSütun kes, ilk/son satırlar, canlı izle, satır saysplit, dilim, len
# En çok 404 alan 5 yol (erişim logunda 7. alan yol, 9. alan durum kodu)
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn | head -5
# Son 1 saatteki hataları canlı izle
tail -f app.log | grep --line-buffered ERROR
# Lezzet Kurye siparişlerinde duruma göre teslimat ücreti toplamı
awk -F, 'NR > 1 {t[$8] += $10} END {for (k in t) print k, t[k]}' lk_orders.csv
# API yanıtından yalnız adlar
curl -s https://api.example.com/urunler | jq -r '.[] | select(.stok == 0) | .ad'

Aynı awk satırının Python karşılığı aşağıda; gerçek lk_orders dosyasında çalışıyor. Kısa ve tek seferlik sorularda kabuk daha hızlıdır; mantık büyüyünce, test gerekince ya da Windows'ta da çalışması gerekince Python kazanır.

import veri
yol = veri.yol("lezzet_kurye", "lk_orders")
toplam = {}
with open(yol, encoding="utf-8") as f:
    next(f)                                   # NR > 1: başlığı atla
    for satir in f:
        alan = satir.rstrip("\n").split(",")  # -F,
        toplam[alan[7]] = toplam.get(alan[7], 0) + float(alan[9])   # $8, $10
for durum, tutar in sorted(toplam.items()):
    print(durum, round(tutar, 2))

Dosya bulmak ve toplu işlem: find ve xargs

find . -name "*.py" -mtime -1              # son 1 günde değişen .py dosyaları
find /var/log -name "*.log" -size +100M    # 100 MB'tan büyük loglar
find . -name "__pycache__" -type d -prune -exec rm -rf {} +
grep -rl "TODO" src/ | xargs wc -l         # TODO içeren dosyaların satır sayısı
find . -name "*.csv" -print0 | xargs -0 -n 1 python temizle.py   # boşluklu adlara dayanıklı

xargs, girdideki her satırı bir komutun argümanına çevirir. Dosya adlarında boşluk olabileceği için -print0 ve -0 çiftini alışkanlık edin. Python'da karşılığı Path(".").rglob("*.py") ve döngüdür.

İzinler ve chmod

ls -l çıktısındaki -rwxr-x--- üç gruba ayrılır: sahip (user), grup (group), diğerleri (other). Her grupta okuma (r=4), yazma (w=2), çalıştırma (x=1) vardır; toplamları sayısal izni verir: rwx=7, r-x=5, ---=0 → 750.

import stat
for izin in (0o755, 0o644, 0o600, 0o750):
    print(oct(izin)[2:], stat.filemode(stat.S_IFREG | izin))
chmod 755 deploy.sh        # sahibi her şey, diğerleri oku+çalıştır
chmod u+x betik.py         # sembolik: sahibine çalıştırma ekle
chmod 600 ~/.ssh/id_ed25519   # özel anahtar yalnız sahibine açık olmalı
chown app:app /srv/market -R
chmod 777 çözüm değildir"Permission denied" alınca 777 vermek herkese yazma izni açar. Önce dosyanın sahibini (ls -l) ve hangi kullanıcıyla çalıştığını (whoami) kontrol et; gerekeni ver. SSH özel anahtarının izni geniş olursa ssh onu kullanmayı reddeder.

Süreçler, uzak sunucu ve tmux

Komutİş
ps aux | grep gunicorn, top / htopÇalışan süreçleri, CPU ve bellek kullanımını gör
kill 4321 / kill -9 4321SIGTERM: "düzgün kapan" ricası; SIGKILL: anında öldür (temizlik yapılamaz, son çare)
ssh-keygen -t ed25519, ssh-copy-id kul@sunucuAnahtar çifti üret, açık anahtarı sunucuya koy; parolasız ve güvenli giriş
ssh kul@sunucu, scp rapor.csv kul@sunucu:/srv/veri/Uzak kabuk; dosya kopyala (büyük ve tekrarlı kopyada rsync -avz)
tmux new -s etl, Ctrl+b d, tmux attach -t etlOturum aç, ayrıl (bağlantı kopsa da iş sürer), geri bağlan

Uzun süren bir eğitim ya da ETL işini SSH üzerinden başlatıp bağlantın koparsa iş ölür; tmux (ya da screen) içinde başlatırsan oturum sunucuda yaşamaya devam eder. Python tarafında SIGTERM'i yakalayıp yarım işi kaydetmek docker-deploy dersindeki "düzgün kapanma" konusudur.

Gerçek işteÜretimde bir sorun çıktığında ilk dakikalar genellikle şöyle geçer: ssh ile bağlan, df -h ile disk, free -m ile bellek, ps/top ile süreçlere bak, tail -n 200 app.log | grep -i error ile son hataları oku. Bu akışı rahat yapabilmek mühendis, backend ve ML rollerinde mülakatlarda da sorulur.
Uzmanlaş

Test edilebilir komut satırı kalıbı

Profesyonel Python betiklerinin neredeyse hepsi aynı iskeleti kullanır: tüm iş main(argv) içinde yapılır, fonksiyon çıkış kodunu döndürür, sys.exit yalnızca en dışta bir kez çağrılır.

import sys

def main(argv, env=None, out=sys.stdout, err=sys.stderr):
    env = env if env is not None else {}
    if len(argv) != 2:
        print("Kullanım: stok.py <depo>", file=err)
        return 2
    esik = int(env.get("STOK_ESIK", "10"))
    print(f"{argv[1]} deposu, eşik {esik}", file=out)
    return 0

kod = main(["stok.py", "Gebze"], env={"STOK_ESIK": "25"})
print("Çıkış kodu:", kod)
# Gerçek betikte en altta:
# if __name__ == "__main__":
#     sys.exit(main(sys.argv, dict(os.environ)))

Bu kalıbın üç faydası var: testte argv, ortam ve akışları istediğin gibi verirsin; çıkış kodu tek yerden yönetilir; modül içe aktarıldığında (import) hiçbir şey kendiliğinden çalışmaz.

Tırnaklar ve boşluklu argümanlar

Terminal argümanları boşluktan böler: python rapor.py Kahraman Maraş iki argüman verir. Tek argüman için tırnak kullan: "Kahraman Maraş". Python'da bir komut satırını terminal gibi bölmek için shlex.split('rapor.py "Kahraman Maraş" 7') kullanılır. Alt süreç (subprocess) çalıştırırken komutu tek metin yerine liste olarak vermek de aynı nedenle güvenlidir.

Çıkış kodu sözleşmesi

Ekibinle bir tablo üzerinde anlaş: 0 başarı, 1 iş hatası (veri bozuk), 2 kullanım hatası, 3 dış sistem erişilemiyor gibi. Zamanlayıcı kuralları ("kod 3 ise 10 dakika sonra yeniden dene") bu sözleşmeye dayanır. Unix'te 126-255 arası kodlar kabuğa ayrılmıştır; kendi kodlarını küçük tut.

Alıştırmalara geç (11)