Πως να αφαιρέσετε τις διπλές γραμμές ενός αρχείο σε Linux χωρίς να αλλάξει η σειρά τους – επεξήγηση awk one-liner κώδικα.
Δημοσιεύτηκε στις 11/11/2019.
Το άρθρο είναι διαθέσιμο και στα αγγλικά.
Υποθέστε ότι έχετε ένα αρχείο κειμένου και θέλετε να αφαιρέσετε όλες τις διπλές γραμμές.
TL;DR
Για να αφαιρεθούν οι γραμμές διατηρώντας τη σειρά τους στο αρχείο χρησιμοποιήστε:
awk '!visited[$0]++' your_file > deduplicated_file
Πως δουλεύει
Το script διατηρεί έναν πίνακα συσχετισμών (associative array) με κλειδιά την κάθε μοναδική γραμμή του αρχείου και τιμές τον αριθμό των εμφανίσεων τους. Για κάθε γραμμή του αρχείου, αν οι εμφανίσεις της γραμμής είναι 0 τότε αυξάνονται κατά 1 και εκτυπώνεται η γραμμή, διαφορετικά απλά αυξάνονται οι εμφανίσεις χωρίς να πραγματοποιείται εκτύπωση της γραμμής.
Δεν ήμουν εξοικειωμένος με την awk
και ήθελα να καταλάβω πως επιτυγχάνεται αυτή η λειτουργικότητα με ένα
τόσο μικρawk script. Έκανα την έρευνά μου και ιδού τι συμβαίνει:
- το script της awk
!visited[$0]++
εκτελείται για κάθε γραμμή του αρχείου εισόδου - η
visited[]
είναι μια μεταβλητή τύπου Πίνακα συσχετισμών (associative array) (δηλαδή Map). Δε χρειάζεται να την αρχικοποιήσουμε, ηawk
θα το κάνει αυτό για μας την πρώτη φορά που θα την προσπελάσουμε. - η μεταβλητή
$0
κρατά τα περιεχόμενα της γραμμής που βρίσκεται κάθε φορά υπό επεξεργασία - ο κώδικας
visited[$0]
προσπελαύνει την τιμή που είναι αποθηκευμένη στον πίνακα συσχετισμών με κλειδί ίσο με$0
(που όπως είπαμε, είναι η υπό επεξεργασία γραμμή του αρχείου), δηλαδή τις εμφανίσεις (που εκχωρούμε παρακάτων) - ο τελεστής
!
δημιουργεί την άρνηση της τιμής των εμφανίσεων:- Στην awk, οποιαδήποτε μη μηδενική αριθμητική τιμή ή οποιαδήποτε μη άδεια λεκτική τιμή θεωρείται αληθής – true
- Εξ ορισμού, οι μεταβλητές αρχικοποιούνται σε κενά λεκτικά, τα οποία αν μετατραπούν σε αριθμό γίνονται μηδέν – 0
- Έχοντας πει αυτά λοιπόν:
- αν η εκτέλεση του
visited[$0]
επιστρέψει αριθμό μεγαλύτερο του μηδενός, η άρνησή του θα ισούται μεfalse
. - αν η εκτέλεση του
visited[$0]
επιστρέψει αριθμό ίσο με το μηδέν ή το κενό λεκτικό, η άρνησή του θα ισούται μεtrue
.
- αν η εκτέλεση του
- ο τελεστής
++
αυξάνει την τιμή της μεταβλητής (visited[$0]
) κατά ένα – 1.- Αν η τιμή είναι κενή, η
awk
τη μετατρέπει σε αριθμό άρα σε μηδέν – 0 αυτόματα και κατόπιν την αυξάνει. - Σημείωση: η πράξη εκτελείται αφού προσπελάσουμε πρώτα την τιμή της μεταβλητής.
- Αν η τιμή είναι κενή, η
Συνοψίζοντας, ολόκληρη η έκφραση εκτιμάται ως:
- true αν οι εμφανίσεις είναι 0 ή το κενό λεκτικό
- false αν οι εμφανίσεις είναι περισσότερες από μηδέν – 0
Οι δηλώσεις (statements) στην awk
αποτελούνται από ένα μοτίβο (pattern) ή έκφραση (expression) και μια συσχετισμένη ενέργεια (associated action).
<pattern/expression> { <action> }
Αν το μοτίβο/έκφραση επιτύχει τότε εκτελείται η συσχετισμένη ενέργεια. Αν δεν δηλώσουμε την συσχετισμένη ενέργεια, η awk
εξ ορισμού θα εκτυπώσει (print
) τα δεδομένα εισόδου.
An omitted action is equivalent to
{ print $0 }
Το script μας αποτελείται από μια awk
δήλωση με μία έκφραση, παραλείποντας την συσχετισμένη ενέργεια.
Οπότε αυτό:
awk '!visited[$0]++' your_file > deduplicated_file
είναι ισοδύναμο με αυτό:
awk '!visited[$0]++ { print $0 }' your_file > deduplicated_file
Για κάθε γραμμή του αρχείου, αν η έκφραση επιτύχει τότε η γραμμή εκτυπώνεται στην έξοδο (output). Διαφορετικά, η συσχετισμένη ενέργεια δεν εκτελείται, τίποτα δεν εκτυπώνεται.
Γιατί να μη χρησιμοποιήσω την εντολή uniq
;
Η εντολή uniq
αφαιρεί μόνο τις γειτονικές διπλές γραμμές. Επίδειξη:
$ cat test.txt
A
A
A
B
B
B
A
A
C
C
C
B
B
A
$ uniq < test.txt
A
B
A
C
B
A
Άλλες προσεγγίσεις
Χρησιμοποιώντας την εντολή sort
Μπορούμε να χρησιμοποιήσουμε την παρακάτω sort
εντολή για να αφαιρέσουμε τις διπλές γραμμές αλλά η σειρά των γραμμών δεν διατηρείται.
sort -u your_file > sorted_deduplicated_file
Χρησιμοποιώντας τις εντολές cat
, sort
και cut
Στην προηγούμενη προσέγγιση θα παραγόταν ένα αποδιπλοποιημένο αρχείο οι γραμμές του οποίου θα ήταν ταξινομημένες βάσει του περιεχομένου τους. Χρησιμοποιώντας σωληνώσεις με ένα σύνολο εντολών μπορούμε να διατηρήσουμε την αρχική σειρά των γραμμών:
cat -n your_file | sort -uk2 | sort -nk1 | cut -f2-
Πως λειτουργεί
Υποθέστε ότι έχουμε το ακόλουθο αρχείο κειμένου:
abc
ghi
abc
def
xyz
def
ghi
klm
Η εντολή cat -n test.txt
τοποθετεί τον αριθμό της θέσης κάθε σειράς στην αρχή της.
1 abc
2 ghi
3 abc
4 def
5 xyz
6 def
7 ghi
8 klm
Η εντολή sort -uk2
ταξινομεί τις γραμμές βάσει της δεύτερης στήλης (επιλογή k2
) και κρατά μόνο την πρώτη εμφάνιση των γραμμών που έχουν την ίδια τιμή στη δεύτερη στήλη (επιλογή u
)
1 abc
4 def
2 ghi
8 klm
5 xyz
Η εντολή sort -nk1
ταξινομεί τις γραμμές βάσει της πρώτης στήλης (επιλογή k1
) αντιμετωπίζοντας την τιμή της στήλης ως αριθμό (επιλογή n
)
1 abc
2 ghi
4 def
5 xyz
8 klm
Τέλος, η εντολή cut -f2-
εκτυπώνει κάθε γραμμή ξεκινώντας από τη δεύτερη στήλη και φτάνοντας ως το τέλος της (επιλογή -f2-
: προσέξτε την παύλα -
στο τέλος που δηλώνει να συμπεριληφθεί το υπόλοιπο της γραμμής)
abc
ghi
def
xyz
klm
References
- The GNU Awk User’s Guide
- Arrays in Awk
- Awk – Truth values
- Awk expressions
- How can I delete duplicate lines in a file in Unix?
- Remove duplicate lines without sorting [duplicate]
- How does awk ‘!a[$0]++’ work?
Αυτά. Να μια γάτα.
Πηγή άρθρου: https://iridakos.com