comm Komutu ile Sıralı Dosyaları Karşılaştırma ve Fark Bulma
Dosya karşılaştırma işlemleri söz konusu olduğunda aklımıza genellikle diff komutu gelir. Oysa Linux dünyasında çok daha spesifik bir iş için tasarlanmış, ama bir o kadar az tanınan bir araç daha var: comm. Bu komut, sıralı iki dosyayı satır satır karşılaştırır ve üç farklı çıktı sütunuyla size tam olarak neyin nerede olduğunu gösterir. Günlük sysadmin hayatında log analizi, paket listesi karşılaştırması, kullanıcı yönetimi gibi pek çok senaryoda comm gerçek bir zaman kurtarıcı olabilir. Gelin bu komutu her açıdan inceleyelim.
comm Komutu Nedir?
comm, adını “common” kelimesinden alan bir GNU coreutils aracıdır. İki sıralı dosyayı karşılaştırır ve üç sütunlu bir çıktı üretir:
- Sütun 1: Sadece birinci dosyada bulunan satırlar
- Sütun 2: Sadece ikinci dosyada bulunan satırlar
- Sütun 3: Her iki dosyada da bulunan satırlar (ortak satırlar)
diff komutundan temel farkı şudur: diff değişiklikleri hunk’lar halinde gösterir ve metin editleme odaklıdır. comm ise daha matematiksel bir yaklaşımla küme işlemleri yapar. İki listenin kesişimini, farkını ve birleşimini bulmak istediğinizde comm çok daha temiz bir araçtır.
Önemli bir uyarı: comm çalışmadan önce her iki dosyanın da sıralı olması gerekir. Sırasız dosyalarla kullandığınızda ya yanlış sonuçlar alırsınız ya da uyarı mesajı görürsünüz. Bu yüzden pratikte genellikle sort komutuyla birlikte kullanılır.
Temel Kullanım
Önce iki örnek dosya oluşturalım:
# Birinci dosya: Pazartesi sunucuda yüklü paketler
cat > liste_pazartesi.txt << EOF
apache2
curl
git
mysql-server
nginx
php8.1
python3
wget
EOF
# İkinci dosya: Salı sunucuda yüklü paketler
cat > liste_sali.txt << EOF
apache2
curl
docker
git
nginx
nodejs
php8.1
redis
EOF
Dosyalar zaten sıralı. Şimdi comm çalıştıralım:
comm liste_pazartesi.txt liste_sali.txt
Çıktı şöyle görünür:
apache2
curl
docker
git
mysql-server
nginx
nodejs
php8.1
python3
redis
wget
İlk bakışta biraz kafa karıştırıcı gelebilir. Girintilere dikkat edin:
- Girintisiz satırlar (mysql-server, python3, wget): Sadece birinci dosyada var
- Tek sekme girintili satırlar (docker, nodejs, redis): Sadece ikinci dosyada var
- Çift sekme girintili satırlar (apache2, curl, git…): Her iki dosyada da var
Parametreler ve Seçenekler
comm komutunun parametreleri son derece sade ama güçlüdür:
-1: Birinci sütunu bastırma (sadece birinci dosyada olanları gizle)
-2: İkinci sütunu bastırma (sadece ikinci dosyada olanları gizle)
-3: Üçüncü sütunu bastırma (her iki dosyada olanları gizle)
–nocheck-order: Dosyaların sıralı olup olmadığını kontrol etme
–check-order: Sıra kontrolünü zorla (varsayılan davranış)
–output-delimiter=STR: Sütunlar arasında sekme yerine özel ayraç kullan
-z veya –zero-terminated: Satır sonu olarak newline yerine null karakter kullan (find -print0 ile uyumlu çalışmak için)
Bu parametrelerin kombinasyonları gerçek gücü ortaya çıkarır. En sık kullanılan kombinasyonlara bakalım.
Pratik Kombinasyonlar
Sadece Ortak Satırları Bulmak (Kesişim)
# Her iki dosyada da olan paketler
comm -12 liste_pazartesi.txt liste_sali.txt
Çıktı:
apache2
curl
git
nginx
php8.1
-12 parametresi birinci ve ikinci sütunu gizler, yani sadece ortak olanları gösterir. Bu küme teorisindeki kesişim (intersection) işlemine karşılık gelir.
Sadece Birinci Dosyada Olanlar
# Pazartesi'den Salı'ya kaldırılan paketler
comm -23 liste_pazartesi.txt liste_sali.txt
Çıktı:
mysql-server
python3
wget
Sadece İkinci Dosyada Olanlar
# Salı günü yeni eklenen paketler
comm -13 liste_pazartesi.txt liste_sali.txt
Çıktı:
docker
nodejs
redis
Sıralama Zorunluluğu ve sort ile Kullanım
comm‘un en kritik gereksinimi dosyaların sıralı olmasıdır. Gerçek dünyada dosyalar her zaman sıralı gelmez. Bu durumu process substitution ile çözebilirsiniz:
# Sırasız dosyaları anında sıralayarak karşılaştırma
comm <(sort dosya1.txt) <(sort dosya2.txt)
# Büyük/küçük harf duyarsız karşılaştırma
comm <(sort -f dosya1.txt) <(sort -f dosya2.txt)
# Sadece ortak olanlar, sırasız dosyalardan
comm -12 <(sort paketler_prod.txt) <(sort paketler_staging.txt)
Bu yapı özellikle komut çıktılarını doğrudan karşılaştırırken çok işe yarar:
# İki sunucunun aktif servislerini karşılaştır
comm -23
<(ssh sunucu1 "systemctl list-units --type=service --state=active --no-legend | awk '{print $1}' | sort")
<(ssh sunucu2 "systemctl list-units --type=service --state=active --no-legend | awk '{print $1}' | sort")
Gerçek Dünya Senaryoları
Senaryo 1: Sunucu Paket Tutarsızlığı Tespiti
Prod ve staging ortamınızın paket listelerini karşılaştırmak istiyorsunuz. Bir ortamda olmayan paket, potansiyel bir sorun kaynağı olabilir.
# Prod sunucuda yüklü paketleri al
ssh prod-server "dpkg --get-selections | grep -v deinstall | awk '{print $1}' | sort" > prod_paketler.txt
# Staging sunucuda yüklü paketleri al
ssh staging-server "dpkg --get-selections | grep -v deinstall | awk '{print $1}' | sort" > staging_paketler.txt
# Prod'da olup staging'de olmayan paketler (kritik!)
echo "=== PROD'DA VAR, STAGING'DE YOK ==="
comm -23 prod_paketler.txt staging_paketler.txt
# Staging'de olup prod'da olmayan paketler
echo "=== STAGING'DE VAR, PROD'DA YOK ==="
comm -13 prod_paketler.txt staging_paketler.txt
# Her ikisinde de olan paketler
echo "=== ORTAK PAKETLER ==="
comm -12 prod_paketler.txt staging_paketler.txt | wc -l
echo "adet ortak paket mevcut"
Senaryo 2: Log Analizi ile Başarısız Login Tespiti
İki farklı zaman dilimindeki başarısız SSH denemelerini karşılaştırarak kalıcı saldırı kaynaklarını tespit edin:
# Dünkü başarısız SSH IP'lerini çıkar
grep "Failed password" /var/log/auth.log.1 |
grep -oE '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' |
sort -u > dunkü_saldirganlar.txt
# Bugünkü başarısız SSH IP'lerini çıkar
grep "Failed password" /var/log/auth.log |
grep -oE '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' |
sort -u > bugünkü_saldirganlar.txt
# Her iki günde de deneme yapan kalıcı saldırganlar
echo "=== KALICI SALDIRGANLАР (Her iki günde de aktif) ==="
comm -12 dunkü_saldirganlar.txt bugünkü_saldirganlar.txt
# Sadece bugün görünen yeni saldırganlar
echo "=== YENİ SALDIRGANLАР ==="
comm -13 dunkü_saldirganlar.txt bugünkü_saldirganlar.txt
Senaryo 3: Kullanıcı Yönetimi ve Grup Karşılaştırması
Bir LDAP ya da lokal kullanıcı listesini beklenen kullanıcı listesiyle karşılaştırmak, özellikle compliance açısından önemlidir:
# Sistemdeki aktif kullanıcılar (UID 1000 ve üzeri)
awk -F: '$3 >= 1000 && $3 != 65534 {print $1}' /etc/passwd | sort > mevcut_kullanicilar.txt
# İnsan kaynakları sisteminden gelen onaylı kullanıcı listesi
# (Bu dosyanın dışarıdan geldiğini varsayalım)
sort onaylı_kullanicilar.txt -o onaylı_kullanicilar.txt
# Sistemde olup onaylı listede olmayan kullanıcılar (potansiyel güvenlik riski!)
echo "=== SİSTEMDE VAR AMA ONAYLANMAMIŞ ==="
comm -23 mevcut_kullanicilar.txt onaylı_kullanicilar.txt
# Onaylı listede olup henüz oluşturulmamış kullanıcılar
echo "=== OLUŞTURULMASI GEREKEN KULLANICILAR ==="
comm -13 mevcut_kullanicilar.txt onaylı_kullanicilar.txt
Senaryo 4: Cron Job Tutarlılığı Kontrolü
Birden fazla sunucuda aynı cron jobların çalışması gerekiyorsa tutarlılık kontrolü yapabilirsiniz:
#!/bin/bash
# Tüm web sunucularında cron tutarlılığını kontrol et
SUNUCULAR=("web1" "web2" "web3")
REFERANS_SUNUCU="web1"
# Referans sunucudan cron listesini al
ssh $REFERANS_SUNUCU "crontab -l 2>/dev/null | grep -v '^#' | sort" > /tmp/cron_referans.txt
for sunucu in "${SUNUCULAR[@]}"; do
if [ "$sunucu" == "$REFERANS_SUNUCU" ]; then
continue
fi
ssh $sunucu "crontab -l 2>/dev/null | grep -v '^#' | sort" > /tmp/cron_${sunucu}.txt
FARK=$(comm -3 /tmp/cron_referans.txt /tmp/cron_${sunucu}.txt | wc -l)
if [ "$FARK" -gt 0 ]; then
echo "UYARI: $sunucu referanstan farklı!"
echo "Sadece $REFERANS_SUNUCU'da olan:"
comm -23 /tmp/cron_referans.txt /tmp/cron_${sunucu}.txt
echo "Sadece $sunucu'da olan:"
comm -13 /tmp/cron_referans.txt /tmp/cron_${sunucu}.txt
else
echo "OK: $sunucu tutarlı"
fi
done
Özel Ayraç Kullanımı
Varsayılan sütun ayracı sekme karakteridir. Ancak bazen çıktıyı işlerken özel bir ayraç kullanmak işleri kolaylaştırır:
# Pipe karakteriyle ayırma
comm --output-delimiter="|" dosya1.txt dosya2.txt
# Daha okunabilir çıktı için
comm --output-delimiter=" <> " <(sort liste1.txt) <(sort liste2.txt)
Bu özellikle çıktıyı başka araçlarla parse etmek istediğinizde kullanışlıdır.
comm ile Awk ve Sed Kombinasyonu
comm‘un çıktısını daha ileri işlemler için awk ile birleştirebilirsiniz:
# comm çıktısını etiketli formata dönüştür
comm dosya1.txt dosya2.txt | awk '{
if (/^tt/) {
gsub(/^tt/, "")
print "ORTAK: " $0
} else if (/^t/) {
gsub(/^t/, "")
print "SADECE_2: " $0
} else {
print "SADECE_1: " $0
}
}'
Bu tür bir çıktı loglama veya raporlama scriptlerinde çok işe yarar.
Büyük Dosyalarda Performans
comm satır satır çalışır ve sıralı dosyalar üzerinde çalıştığında oldukça verimlidir. Milyonlarca satırlık log dosyalarında bile makul sürelerde sonuç verir. Ancak birkaç ipucu:
- Önce filtrele, sonra karşılaştır: Büyük log dosyalarını karşılaştırmadan önce
grepile ilgili satırları çekin. - sort’un LC_ALL etkisine dikkat edin: Locale ayarları sıralama davranışını değiştirir. Tutarlı sonuçlar için
LC_ALL=C sortkullanın. - Geçici dosya yerine process substitution tercih edin: Disk I/O’yu azaltır.
# Büyük dosyalar için optimize edilmiş kullanım
LC_ALL=C comm -12
<(LC_ALL=C sort büyük_dosya1.txt)
<(LC_ALL=C sort büyük_dosya2.txt) | wc -l
Yaygın Hatalar ve Çözümleri
“comm: file 1 is not in sorted order” hatası: Dosyalarınız sıralı değil. Çözüm: <(sort dosya.txt) kullanın.
Boş satırlar sonucu bozuyor: Karşılaştırmadan önce boş satırları temizleyin:
comm <(grep -v '^$' dosya1.txt | sort) <(grep -v '^$' dosya2.txt | sort)
Büyük/küçük harf farkı: sort -f ile case-insensitive sıralama yapın ama dikkatli olun, comm karşılaştırmayı yine de case-sensitive yapar. En temiz çözüm:
comm <(tr '[:upper:]' '[:lower:]' < dosya1.txt | sort)
<(tr '[:upper:]' '[:lower:]' < dosya2.txt | sort)
Trailing whitespace sorunları: Satır sonundaki boşluklar farklılık yaratabilir:
comm <(sed 's/[[:space:]]*$//' dosya1.txt | sort)
<(sed 's/[[:space:]]*$//' dosya2.txt | sort)
comm ile diff Arasında Seçim Yapmak
İkisi de karşılaştırma araçları ama farklı ihtiyaçlar için:
- comm kullanın eğer: İki liste arasındaki küme işlemlerini (kesişim, fark) yapmak istiyorsanız, satır sırası önemli değilse, çıktıyı script içinde kolayca işlemek istiyorsanız.
- diff kullanın eğer: Metin dosyalarındaki değişiklikleri context’ıyla görmek istiyorsanız, hangi satırın değiştiğini konumsal olarak görmek istiyorsanız, patch oluşturmak istiyorsanız.
Bir sysadmin olarak ben şunu söyleyebilirim: Paket listeleri, kullanıcı listeleri, IP listeleri, domain listeleri gibi liste bazlı karşılaştırmalarda her zaman comm‘a öncelik verin. Çok daha temiz ve doğrudan bir çıktı alırsınız.
Bonus: comm ile Hızlı Rapor Scripti
Aşağıdaki script, iki dizindeki dosya listelerini karşılaştıran basit ama kullanışlı bir araçtır:
#!/bin/bash
# dizin_karsilastir.sh - İki dizinin içeriğini karşılaştır
DIR1="$1"
DIR2="$2"
if [ -z "$DIR1" ] || [ -z "$DIR2" ]; then
echo "Kullanım: $0 <dizin1> <dizin2>"
exit 1
fi
DOSYA1=$(mktemp)
DOSYA2=$(mktemp)
# Her dizindeki dosyaları listele (sadece dosya adları)
find "$DIR1" -type f -printf '%Pn' | sort > "$DOSYA1"
find "$DIR2" -type f -printf '%Pn' | sort > "$DOSYA2"
echo "============================================"
echo "KARSILASTIRMA: $DIR1 vs $DIR2"
echo "============================================"
SADECE_1=$(comm -23 "$DOSYA1" "$DOSYA2" | wc -l)
SADECE_2=$(comm -13 "$DOSYA1" "$DOSYA2" | wc -l)
ORTAK=$(comm -12 "$DOSYA1" "$DOSYA2" | wc -l)
echo "Sadece $DIR1'de: $SADECE_1 dosya"
echo "Sadece $DIR2'de: $SADECE_2 dosya"
echo "Her ikisinde de: $ORTAK dosya"
echo ""
if [ "$SADECE_1" -gt 0 ]; then
echo "--- Sadece $DIR1'de olanlar ---"
comm -23 "$DOSYA1" "$DOSYA2"
echo ""
fi
if [ "$SADECE_2" -gt 0 ]; then
echo "--- Sadece $DIR2'de olanlar ---"
comm -13 "$DOSYA1" "$DOSYA2"
fi
rm -f "$DOSYA1" "$DOSYA2"
Kullanımı:
chmod +x dizin_karsilastir.sh
./dizin_karsilastir.sh /etc/nginx/conf.d /backup/nginx/conf.d
Sonuç
comm komutu, Linux araç kutusunun az bilinen ama son derece değerli bir üyesidir. Sıralı listeler üzerinde küme işlemleri yapmak için özel olarak tasarlanmış bu araç, doğru kullanıldığında diff veya el ile yazılmış scriptlere göre çok daha temiz ve hızlı sonuçlar verir.
Özellikle şu alanlarda comm‘u araç kutunuzun ön cebine koymanızı öneririm: sunucu tutarlılığı kontrolleri, güvenlik denetimleri, log analizi, paket yönetimi ve kullanıcı yönetimi. sort ile olan doğal birlikteliği ve process substitution desteğiyle birleşince, gerçekten güçlü ve okunabilir one-liner’lar yazabilirsiniz.
Bir sonraki sefere iki liste karşılaştırmanız gerektiğinde diff‘e uzanmadan önce bir saniye durun ve comm‘un işinizi daha iyi çözüp çözmeyeceğini düşünün. Çoğu zaman cevap evet olacaktır.
