Comm – chudobný príbuzný diff-u, čo neraz postačuje

Kde je môj diff?

Pri skriptovaní v shelli sa neraz stáva, že chceme porovnať obsah dvoch súborov. Ak pracujete s nejakým systémom pre udržiavanie verzií (SVN a pod.) a chcete porovnať lokálnu kópiu s kópiu na serveri, viete, že použijete starý známy diff.

Žiaľ, diff nemusí byť nainštalovaný na každom systéme, a môžete sa diviť, či ešte má zmysel uvažovať o alternatívach. Určite má, pretože napr. na BusyBoxe (minimalistickom shelli použiteľnom napr. na Androide) diff nie je.

Nepodliehajte však panike.

Máme tu comm.

comm

Nápad porovnávať porovnávacie nástroje je celkom veselý, ale venovať sa mu nebudeme. V skratke: comm je chudobný príbuzný diffu, ale

  1. je súčasťou POSIXového štandardu pre unixovské systémy (na rozdiel od diffu)
  2. pre mnohé jednoduché úlohy stačí.

Ukážme si fungovanie tohto nástroja na príklade s…

Tiobe

Tiobe.com je zábavný index udávajúci popularitu programovacích jazykov vo svete. Áno, prvá je Java, druhé je C. Skôr než sa rozčúlite nad oboma jazykmi, treba povedať, že tento index neudáva kvalitu jednotlivých jazykov (tu by Java nevyhrala) a dokonca ani poradie jazykov podľa množstva kódu, ktorý je v nich napísaný (tu by vyhral zrejme COBOL). Do úvahy sa berie množstvo dokumentov indexovaných webovými vyhľadávačmi (Google, Bing, Yahoo, Wikipedia a pod.). Hlavným účelom indexu je sledovať trendy (vedeli ste, že Objective-C letí nahor?) a napr. zistiť, či sa na tom-ktorom jazyku oplatí vo všeobecnosti postaviť nový systém.

Na webe indexu nájdete len dáta z posledného prepočtu, ale ak chcete obetovať 5000 dolárov, môžete získať kompletnú historickú sadu dát od roku 2001. Alebo pogooglite a nájdete niektoré archívne záznamy rozhádzané po blogoch a článkoch. Zoberme si napríklad stav z roku 2007 a porovnajme ho so súčasnosťou.

Pre jednoduchosť zoberme len prvých 10 jazykov, budeme ich musieť ručne prepísať do texťáku. Vyrobme si dva súbory: 2007.txt a 2012.txt.

Rok 2007:

1 java
2 c
3 basic
4 cpp
5 php
6 perl
7 python
8 csharp
9 ruby
10 javascript

Rok 2012:

1 java
2 c
3 csharp
4 cpp
5 objectivec
6 php
7 basic
8 python
9 perl
10 javascript

Ako zistiť, ktoré jazyky pribudli?

Ak chceme zistiť, ktoré jazyky pribudli a ktoré zmizli z prvej desiatky, vieme použiť horeuvedený comm

Skúsme prvý pokus. Vezmime oba súbory a rovno ich porovnajme.

novotnyr@x64:~/$ comm 2007.txt 2012.txt
            1 java
            2 c
3 basic
    3 csharp
            4 cpp
    5 objectivec
5 php
6 perl
    6 php
    7 basic
7 python
8 csharp
    8 python
    9 perl
comm: file 2 is not in sorted order
    10 javascript
9 ruby
comm: file 1 is not in sorted order
10 javascript

Uvidíme zmäť riadkov preloženú varovaniami File is not in sorted order. Beda, čo sa stalo?

Zabudli sme prečítať man.

Príkaz comm vie porovnať obsahy dvoch súborov, ale predpokladá, že oba súbory mať zotriedené riadky. Napriek tomu, že z ľudského hľadiska sú riadky zotriedené, nie je to tak — triedenie v comm totiž funguje na reťazcoch a predpokladá, že riadok 10 javascript je pred 1 java a nie na konci.

Zotriediť súbory však nie je problém: máme sort! Ale počkajme ešte chvíľu.

Zrušenie poradových čísiel

Je tu ešte jeden problém: poradia jazykov. Každý riadok obsahuje číselné poradie, ale to nás v tejto chvíli až tak nezaujíma. Nie je dôležité, či bolo Ruby deviate alebo siedme, dôležité je, že v roku 2012 vypadlo z prvej desiatky.

Odstrániť čísla môžeme napr. cutom. Keďže riadok pozostáva z dvoch „stĺpcov“ oddelených medzerou (v prvom je poradové číslo, v druhom meno jazyka), stačí z neho vyseknúť len druhý stĺpec.

cut 2007.txt -f2 -d' '

Parameter -f indikuje, že nás zaujíma len druhý stĺpec. Parameter -d zase nastaví medzeru ako oddeľovač stĺpcov, pretože v štandardnom móde cut predpokladá, že oddeľovačom je tabulátor. (Medzeru uvedieme do apostrofov, indikujeme tým, že oddeľovačom je reťazec s jediným znakom medzery).

Sekanie a triedenie

Výstup z cut môžete utriediť, aby sme nerozosmutnili comm a výsledok uložme do bočného súboru.

cut 2007.txt -f2 -d' ' | sort > 2007s.txt
cut 2012.txt -f2 -d' ' | sort > 2012s.txt

Výstup je o niečo prijateľnejší, i keď stále chaotický:

        basic
        c
        cpp
        csharp
        java
        javascript
    objectivec
        perl
        php
        python
ruby

Co śie stao?

Výstupom comm sú dáta o riadkových rozdieloch zobrazené v troch stĺpcoch oddelených tabulátormi. Prvý stĺpec obsahuje riadky, ktoré sú prítomné v prvom súbore, ale nie sú v druhom súbore (inak povedané, riadky, ktoré sa zmazali z prvého súboru). Druhý stĺpec obsahuje riadky prítomné v druhom súbore, ale chýbajúce v súbore č. 1. (riadky, ktoré pribudli). A iste si už domyslíte, že tretí stĺpec obsahuje riadky spoločné pre oba súbory.

Ak chcete vidieť len rozdielne riadky, použite parameter -3, ktorým potlačíte tretí stĺpec.

novotnyr@x64:~/$ comm -3 2007s.txt 2012s.txt
    objectivec
ruby

A ak chcete primeranejší tvar používajúci pluská a mínuská, môžete použiť sed s dvoma príkazmi. V prvom kroku pred každý riadok dodáme [-] a v ďalšom kroku každý riadok s kombináciou [-]\t nahradíme kombináciou [+].

comm -3 2007s.txt 2012s.txt | sed -e 's/^/[-]/' -e 's/\[-\]\t/[+]/'

Výsledok?

Za päť rokov pribudlo v prvej desiatke Objective-C a ubudlo Ruby.

[+]objectivec
[-]ruby