Dosyaları karşılaştırmak söz konusu olduğunda aklımıza genellikle diff komutu gelir. Oysa comm komutu, özellikle iki sıralı dosya arasındaki farkları ve benzerlikleri bulmak için çok daha temiz ve pratik bir çözüm sunar. Yıllarca sysadmin olarak çalışırken comm komutunu gerçekten işe yarar bulduğum durumlar oldu: sunucu paket listeleri karşılaştırma, kullanıcı listelerini senkronize etme, log dosyalarındaki farklılıkları bulma gibi günlük operasyonlarda bu komut hayat kurtarır.
comm Komutu Nedir?
comm (communicate ya da compare kelimesinden geldiği düşünülür), iki sıralanmış dosyayı satır satır karşılaştıran ve çıktıyı üç sütun halinde sunan bir komut satırı aracıdır. POSIX standardının bir parçasıdır, yani neredeyse tüm Unix/Linux sistemlerinde hazır olarak gelir, ayrıca kurulum gerektirmez.
Çıktının üç sütunu şu anlama gelir:
- Sütun 1: Yalnızca birinci dosyada olan satırlar
- Sütun 2: Yalnızca ikinci dosyada olan satırlar
- Sütun 3: Her iki dosyada da ortak olan satırlar
diff komutunun aksine comm, daha minimal ve betik dostu bir çıktı üretir. Eğer amacınız “iki liste arasındaki farkı bul” veya “ortak elemanları çek” gibi basit sorulara cevap vermekse, comm sizin için biçilmiş kaftandır.
Temel Kullanım ve Sözdizimi
Temel sözdizimi oldukça basittir:
comm [SEÇENEKLER] dosya1 dosya2
Hemen somut bir örneğe bakalım. Elimizde iki dosya olduğunu düşünelim:
# dosya1.txt
cat dosya1.txt
apache2
curl
git
nginx
vim
wget
# dosya2.txt
cat dosya2.txt
apache2
curl
htop
nano
nginx
tmux
Şimdi comm komutuyla bu iki dosyayı karşılaştıralım:
comm dosya1.txt dosya2.txt
Çıktı şöyle görünür:
apache2
curl
git
htop
nano
nginx
tmux
vim
wget
Sütun girintilerine dikkat edin. Birinci sütun (yalnızca dosya1’de olanlar): git, vim, wget. İkinci sütun (yalnızca dosya2’de olanlar): htop, nano, tmux. Üçüncü sütun (her ikisinde de olanlar): apache2, curl, nginx.
Seçenekler ve Parametreler
comm komutunun parametreleri, hangi sütunları gizlemek istediğinizi belirler. Bu biraz alışılmadık bir mantık gibi gelebilir ama pratikte çok kullanışlıdır:
- -1: Birinci sütunu gizler (yalnızca dosya1’de olan satırları göstermez)
- -2: İkinci sütunu gizler (yalnızca dosya2’de olan satırları göstermez)
- -3: Üçüncü sütunu gizler (her iki dosyada da ortak olan satırları göstermez)
- –nocheck-order: Sıralama kontrolünü devre dışı bırakır
- –output-delimiter=STR: Sütunlar arasındaki ayırıcıyı değiştirir
- -z veya –zero-terminated: Satır sonu olarak newline yerine null karakter kullanır
Bu parametreleri birleştirerek istediğiniz sütonu izole edebilirsiniz.
Pratik Kullanım Örnekleri
Yalnızca Ortak Satırları Bulmak
İki dosyadaki ortak satırları bulmak için 1. ve 2. sütunları gizlemeniz yeterli:
comm -12 dosya1.txt dosya2.txt
Çıktı:
apache2
curl
nginx
Bu, “iki listede de bulunan paketler neler?” gibi soruların cevabını saniyeler içinde verir. Örneğin iki farklı sunucuda ortak kurulu paketleri bulmak için birebir kullanabileceğiniz bir yöntemdir.
Yalnızca Birinci Dosyaya Özgü Satırları Bulmak
comm -23 dosya1.txt dosya2.txt
Çıktı:
git
vim
wget
Bu komut “dosya1’de olup dosya2’de olmayan satırlar neler?” sorusunu yanıtlar. Pratikte “birinci sunucuda kurulu olup ikinci sunucuda eksik olan paketler” gibi senaryolarda kullanabilirsiniz.
Yalnızca İkinci Dosyaya Özgü Satırları Bulmak
comm -13 dosya1.txt dosya2.txt
Çıktı:
htop
nano
tmux
Kritik Nokta: Dosyaların Sıralı Olması Zorunluluğu
comm komutunun en önemli gereksinimi, her iki dosyanın da önceden sıralanmış olmasıdır. Sırasız dosyalarla çalışırsanız ya yanlış sonuçlar alırsınız ya da komut uyarı verir.
Dosyalarınızı önce sıralamak için sort komutunu kullanabilirsiniz:
sort dosya1.txt -o dosya1.txt
sort dosya2.txt -o dosya2.txt
Daha iyi bir yaklaşım, sort ve comm komutlarını process substitution ile birleştirmektir. Bu sayede orijinal dosyaları değiştirmeden karşılaştırma yapabilirsiniz:
comm -12 <(sort dosya1.txt) <(sort dosya2.txt)
Bu yaklaşım hem güvenli hem de tek satırda kullanışlıdır. Aşağıdaki örneklerin büyük çoğunluğunda bu yöntemi kullanacağım.
Gerçek Dünya Senaryoları
Senaryo 1: İki Sunucu Arasında Paket Farklılıklarını Bulma
Diyelim ki iki web sunucunuzun aynı paket setine sahip olması gerekiyor. Sunucu konfigürasyonlarının sürüklenmesi (configuration drift) sysadminlerin baş belasıdır. İşte bunu comm ile nasıl yönetirsiniz:
# Birinci sunucudaki paketleri listele
ssh sunucu1 "dpkg --get-selections | awk '{print $1}'" | sort > sunucu1_paketler.txt
# İkinci sunucudaki paketleri listele
ssh sunucu2 "dpkg --get-selections | awk '{print $1}'" | sort > sunucu2_paketler.txt
# Yalnızca sunucu1'de olup sunucu2'de olmayan paketler
echo "=== Sunucu1'de olup Sunucu2'de olmayan paketler ==="
comm -23 sunucu1_paketler.txt sunucu2_paketler.txt
# Yalnızca sunucu2'de olup sunucu1'de olmayan paketler
echo "=== Sunucu2'de olup Sunucu1'de olmayan paketler ==="
comm -13 sunucu1_paketler.txt sunucu2_paketler.txt
Bu basit betik sayesinde iki sunucu arasındaki paket farklılıklarını saniyeler içinde görebilirsiniz.
Senaryo 2: Kullanıcı Listelerini Karşılaştırma
Active Directory veya LDAP entegrasyonunda, sistem kullanıcıları ile yetkili kullanıcı listenizi karşılaştırmanız gerekebilir:
# Sistemdeki kullanıcıları çek (UID 1000 ve üzeri, gerçek kullanıcılar)
awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd | sort > sistem_kullanicilari.txt
# Yetkili kullanıcılar listesi (örneğin HR'dan gelen CSV'den)
sort yetkili_kullanicilar.txt -o yetkili_kullanicilar.txt
# Sistemde olup yetkili listede olmayan kullanıcılar (şüpheli hesaplar!)
echo "=== DİKKAT: Yetkisiz kullanıcı hesapları ==="
comm -23 sistem_kullanicilari.txt yetkili_kullanicilar.txt
# Yetkili listede olup sistemde olmayan kullanıcılar (oluşturulması gerekenler)
echo "=== Oluşturulması gereken hesaplar ==="
comm -13 sistem_kullanicilari.txt yetkili_kullanicilar.txt
Bu tür bir kontrol güvenlik denetimlerinde son derece değerlidir.
Senaryo 3: Log Dosyası Analizi
İki farklı tarihten gelen erişim loglarındaki IP adreslerini karşılaştırmak için comm kullanabilirsiniz:
# Dünkü erişim loglarından IP'leri çek
grep "28/Nov/2024" /var/log/nginx/access.log |
awk '{print $1}' | sort -u > dunku_ipler.txt
# Bugünkü erişim loglarından IP'leri çek
grep "29/Nov/2024" /var/log/nginx/access.log |
awk '{print $1}' | sort -u > bugunku_ipler.txt
# Her iki günde de erişen IP'ler (düzenli ziyaretçiler/botlar)
echo "=== Her iki günde de erişen IP'ler ==="
comm -12 dunku_ipler.txt bugunku_ipler.txt
# Yalnızca bugün görülen yeni IP'ler
echo "=== Bugün ilk kez görülen IP'ler ==="
comm -13 dunku_ipler.txt bugunku_ipler.txt
Senaryo 4: Yedekleme Doğrulama
Bir dizindeki dosyaların yedeğe düzgün alınıp alınmadığını kontrol etmek için:
# Kaynak dizindeki dosyaları listele
find /var/www/html -type f | sed 's|/var/www/html/||' | sort > kaynak_dosyalar.txt
# Yedek dizinindeki dosyaları listele
find /backup/www -type f | sed 's|/backup/www/||' | sort > yedek_dosyalar.txt
# Kaynak'ta olup yedekte olmayan dosyalar (yedeklenmemiş dosyalar!)
echo "=== UYARI: Yedeklenmeyen dosyalar ==="
comm -23 kaynak_dosyalar.txt yedek_dosyalar.txt
# Yedekte olup kaynakta olmayan dosyalar (silinmiş ama yedekte kalan)
echo "=== Kaynakta silinmiş ama yedekte kalan dosyalar ==="
comm -13 kaynak_dosyalar.txt yedek_dosyalar.txt
Büyük/Küçük Harf Duyarlılığı Sorunu
comm komutu varsayılan olarak büyük/küçük harfe duyarlıdır. Eğer büyük/küçük harf farkını görmezden gelmek istiyorsanız, dosyaları sort -f ile sıralayabilirsiniz ancak bu tam anlamıyla çözüm değildir. En temiz yaklaşım verileri normalize etmektir:
# Her iki dosyayı da küçük harfe çevirip karşılaştır
comm -12 <(sort < <(tr '[:upper:]' '[:lower:]' < dosya1.txt))
<(sort < <(tr '[:upper:]' '[:lower:]' < dosya2.txt))
Çıktıyı Özelleştirme
Varsayılan tab girintisi yerine özel bir ayırıcı kullanmak isteyebilirsiniz. --output-delimiter seçeneği bunun için vardır:
comm --output-delimiter='|' dosya1.txt dosya2.txt
Bu özellikle çıktıyı başka araçlarla işleyecekseniz kullanışlıdır.
Bazen sütunları anlamlandırmak için çıktıyı etiketlemek istersiniz. Bunu awk veya paste ile birleştirerek yapabilirsiniz:
# Her satırın hangi kategoride olduğunu göster
comm dosya1.txt dosya2.txt | awk '{
if (/^tt/) {print "ORTAK:tt" $0}
else if (/^t/) {print "SADECE_2:t" $0}
else {print "SADECE_1:t" $0}
}'
comm ile diff Karşılaştırması
Bu iki komutun farklı amaçlar için tasarlandığını anlamak önemlidir:
- comm: Sıralı listeleri karşılaştırmak için idealdir. Satırların sırası önemli değildir, içerik önemlidir. Hangi elemanların paylaşıldığını veya eksik olduğunu bulmak için kullanılır.
- diff: Dosyaların satır bazında nasıl farklılaştığını, hangi satırların değiştirildiğini, eklendiğini veya silindiğini gösterir. Patch oluşturmak ve sıra bağımlı karşılaştırmalar için idealdir.
Eğer iki konfigürasyon dosyasındaki değişiklikleri görmek istiyorsanız diff kullanın. Eğer iki sunucunun kurulu paket listelerini karşılaştırıyorsanız comm kullanın.
Betiklerde comm Kullanımı
comm komutu, betiklerde değişken içinde saklayabileceğiniz temiz çıktılar üretir:
#!/bin/bash
# İki dosyayı karşılaştır ve sonuçlara göre işlem yap
DOSYA1="liste1.txt"
DOSYA2="liste2.txt"
# Yalnızca dosya1'de olanlar
SADECE_1=$(comm -23 <(sort "$DOSYA1") <(sort "$DOSYA2"))
# Yalnızca dosya2'de olanlar
SADECE_2=$(comm -13 <(sort "$DOSYA1") <(sort "$DOSYA2"))
# Her ikisinde de olanlar
ORTAK=$(comm -12 <(sort "$DOSYA1") <(sort "$DOSYA2"))
echo "Toplam ortak eleman sayısı: $(echo "$ORTAK" | wc -l)"
echo "Dosya1'e özgü eleman sayısı: $(echo "$SADECE_1" | wc -l)"
echo "Dosya2'ye özgü eleman sayısı: $(echo "$SADECE_2" | wc -l)"
# Eksik elemanları yeni dosya2'ye ekle
if [ -n "$SADECE_1" ]; then
echo "Dosya2'ye eksik elemanlar ekleniyor..."
echo "$SADECE_1" >> "$DOSYA2"
sort -o "$DOSYA2" "$DOSYA2"
echo "Tamamlandı."
fi
Boş Girdi ile Başa Çıkma
Betiklerde comm kullanırken dosyaların boş olma ihtimalini göz önünde bulundurun:
#!/bin/bash
# Dosya boş mu kontrol et
if [ ! -s dosya1.txt ] || [ ! -s dosya2.txt ]; then
echo "UYARI: Dosyalardan biri boş, karşılaştırma atlanıyor."
exit 1
fi
comm -12 <(sort dosya1.txt) <(sort dosya2.txt)
Ayrıca - karakterini kullanarak standart girdiden okuma yapabilirsiniz, bu da pipeline’larda kullanımı kolaylaştırır:
cat yeni_liste.txt | sort | comm -23 - <(sort mevcut_liste.txt)
Performans Notları
comm komutu son derece hafif ve hızlıdır. Ancak çok büyük dosyalarla çalışırken (milyonlarca satır) sort adımı zaman alabilir. Bu durumda şu optimizasyonları düşünebilirsiniz:
- Eğer dosyalar zaten sıralıysa tekrar sıralamayın,
--nocheck-orderile kontrol adımını atlayabilirsiniz ancak dikkatli kullanın, yanlış sonuçlara yol açabilir. - Büyük log dosyaları için önce
awkveyagrepile ilgili sütunu/satırları filtreleyin, sonracomm‘a verin. - Paralel sıralama için
sort --parallelseçeneğini kullanabilirsiniz.
# Büyük dosyalar için optimize edilmiş kullanım
comm -12
<(sort --parallel=4 buyuk_dosya1.txt)
<(sort --parallel=4 buyuk_dosya2.txt)
Sonuç
comm komutu, sysadmin araç kutusunda hak ettiği yeri her zaman bulamayan ama gerçekten güçlü bir araçtır. Liste karşılaştırma söz konusu olduğunda diff‘e otomatik pilot modunda uzanmak yerine, verilerinizin liste formatında olduğu durumlarda comm‘u değerlendirin.
Özellikle şu senaryolarda comm‘a öncelik vermenizi öneririm: sunucu konfigürasyon denetimleri, kullanıcı hesabı yönetimi, yedekleme doğrulama, güvenlik loglarından anormal aktivite tespiti ve herhangi iki liste arasındaki kesişim veya fark hesaplama işlemleri.
Tek hatırlamanız gereken kritik nokta: dosyaların sıralı olması gerektiği. Process substitution ile sort komutunu birleştirme alışkanlığı edinin, gerisi kendiliğinden gelir. comm -12 <(sort dosya1) <(sort dosya2) kalıbını bir kez ezberlediniz mi, bu komutu çok sık kullandığınızı fark edeceksiniz.