Εύρεση παλίνδρομων ημερομηνιών στην Linux κονσόλα - επεξήγηση

Ένα άρθρο που εξηγεί πως μπορείτε να βρείτε παλίνδρομες ημερομηνίες χρησιμοποιώντας εντολές στο φλοιό του Linux
This article is also available in english.

Πρόσφατα είδα μια πολύ ωραία εντολή που βρίσκει τις παλίνδρομες ημερομηνίες μεταξύ του τώρα και x ημερών στο παρελθόν.

Η εντολή κοινοποιήθηκε στο Twitter από το λογαριασμό @climagic (ένας πολύ ωραίος λογαριασμός σχετικά με tweets που σχετίζονται με την γραμμή εντολών).

Σε αυτό το άρθρο θα σπάσω την εντολή σε τμήματα και θα εξηγήσω πως λειτουργεί το καθένα.

Τι είναι το παλίνδρομο - Καρκινική γραφή

Καρκινικές επιγραφές ή καρκίνοι ονομάζονται συμμετρικές φράσεις οι οποίες μπορούν να διαβαστούν είτε από την αρχή είτε από το τέλος. Καρκινική γραφή - Wikipedia

Πριν λίγες μέρες, η ημερομηνία ήταν παλίνδρομη: 02/02/2020

Image showing the palindrome 02022020

Η εντολή

printf "now - %d days\n" {1..332044} |date -f- +%Y%_m%_d$'\n'%Y%m%d |tr -d ' ' > alldates.txt; rev alldates.txt >revdates.txt; paste alldates.txt revdates.txt |awk '$1==$2{print $1}'

και παρακάτω μια περισσότερο αναγνώσιμη εκδοχή της, χρησιμοποιώντας νέα γραμμή μετά από κάθε σωλήνωση.

printf "now - %d days\n" {1..332044} |
date -f- +%Y%_m%_d$'\n'%Y%m%d |
tr -d ' ' > alldates.txt; rev alldates.txt >revdates.txt; paste alldates.txt revdates.txt |
awk '$1==$2{print $1}'

Θα εξηγήσω κάθε τμήμα της εντολής χωριστά.

Σημειώστε ότι το αποτέλεσμα (output - έξοδος) του κάθε τμήματος αποτελεί είσοδο (input) για το επόμενο τμήμα (αυτό που ακολουθεί τη σωλήνωση |).

printf "now - %d days\n" {1..332044}

Το πρώτο τμήμα είναι αρκετά απλό αλλά έχει και ένα ενδιαφέρον κομμάτι, το μηχανισμό επέκτασης αγκυλών τον οποίο δε γνώριζα.

Ανοίξτε ένα τερματικό και πληκτρολογήστε τις ακόλουθες εντολές:

# Από το 1 έως το 10
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10

# Από το a έως το z
$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

# Από το 10 έως το 20
$ echo Before-{10..20}-After
Before-10-After Before-11-After Before-12-After Before-13-After Before-14-After Before-15-After Before-16-After Before-17-After Before-18-After Before-19-After Before-20-After

Όπως μπορείτε να δείτε, ο μηχανισμός επέκτασης αγκυλών επιτρέπει την παραγωγή τιμών βασισμένων σε μια ακολουθία - sequence που του ορίζουμε.

Μάθετε περισσότερα για τον μηχανισμό επέκτασης αγκυλών εδώ.

Επεξήγηση εντολής

  • τυπώνουμε τη φράση “now - x days” τόσες φορές όσες το πλήθος των τιμών που προκύπτουν από την ακολουθία - sequence
  • η ακολουθία {1..332044} θα παράξει 332044 τιμές ξεκινώντας από το 1 αυξανόμενη κατά ένα κάθε φορά
  • προσθέτουμε μια νέα γραμμή στο τέλος του παραγόμενου λεκτικού

Αποτελέσματα - Output

now - 1 days
now - 2 days
now - 3 days
now - 4 days
now - 5 days
...
now - 332038 days
now - 332039 days
now - 332040 days
now - 332041 days
now - 332042 days
now - 332043 days
now - 332044 days

date -f- +%Y%\_m%\_d$'\n'%Y%m%d

Σε αυτό το τμήμα της εντολής χρησιμοποιούμε την εντολή date για να μετατρέψουμε σε ημερομηνία την κάθε γραμμή του αποτελέσματος της προηγούμενης εντολής (της εντολής πριν τη σωλήνωση) και να τυπώσουμε δύο γραμμές για κάθε ημερομηνία, μια που θα γεμίσει τις μονοψήφιες ημέρες και τους μονοψήφιους μήνες με ένα μηδενικό και μια που θα γεμίσει τις μονοψήφιες ημέρες και τους μονοψήφιους μήνες με ένα κενό (space).

Εξοικειωθείτε με την εντολή στη γραμμή εντολών:

# Δείξε την τρέχουσα ημερομηνία = now
$ date
Thu Feb  6 07:30:10 EET 2020

# Δείξε τη χθεσινή ημερομηνία
$ date --date="now - 1 days"
Wed Feb  5 07:31:22 EET 2020

# Δείξε τη χθεσινή ημερομηνία ως ηη/μμ/χχχχ (γέμισμα με μηδενικά)
$ date --date="now - 1 days" +%d/%m/%Y
05/02/2020

# Δείξε τη χθεσινή ημερομηνία ως ηη/μμ/χχχχ (γέμισμα με κενά)
$ date --date="now - 1 days" +%_d/%_m/%Y
 5/ 2/2020

# Δείξε τη χθεσινή ημερομηνία ως ηη/μμ/χχχχ (σωρίς γέμισμα)
$ date --date="now - 1 days" +%-d/%-m/%Y
5/2/2020

Επεξήγηση εντολής

  • χρησιμοποιούμε την επιλογή -f για να ορίσουμε ότι η είσοδος της εντολής date θα είναι οι γραμμές ενός αρχείου, ακολουθούμενη από μια παύλα (hyphen) - για να ορίσουμε ότι το αρχείο αυτό είναι η τυπική είσοδος - stdin
  • ορίζουμε τη μορφή των τυπωμένων τιμών (ό,τι ακολουθεί το σύμβολο +) ώστε να είναι η αριθμητική αναπαράσταση του έτους (%Y), του μήνα (γεμισμένου με κενά: %_m, γεμισμένου με μηδενικά: %m) και της ημέρας του μήνα (γεμισμένης με κενά: %_d, γεμισμένης με μηδενικά: %d)
  • η μορφή με τα κενά είναι %Y%\_m%\_d και η μορφή με τα μηδενικά %Y%m%d
  • συνδυάζουμε αυτές τις δύο μορφές για κάθε μία ημερομηνία ενώνοντάς τες με μια νέα γραμμή $'\n' οπότε, για κάθε ημερομηνία παράγουμε δύο γραμμές, μια με μηδενικά και μια με κενά για τους μονοψήφιους μήνες και τις μονοψήφιες ημέρες του μήνα

Σημείωση: αντί να γεμίσουμε με κενά (χρησιμοποιώντας την κάτω παύλα _ στη μορφή), θα μπορούσαμε να χρησιμοποιήσουμε την παύλα - ώστε να μη γεμίζουν με τίποτα οι μονοψήφιες τιμές. Με αυτόν τον τρόπο δε θα χρειαζόταν να αφαιρέσουμε τα κενά όπως περιγράφεται στο επόμενο τμήμα.

Αποτέλεσμα - Output

2020 2 5
20200205
2020 2 4
20200204
2020 2 3
20200203
2020 2 2
20200202
2020 2 1
20200201
2020 131
20200131
2020 130
20200130

tr -d ' ' > alldates.txt; rev alldates.txt >revdates.txt; paste alldates.txt revdates.txt

Σε αυτό το τμήμα αφαιρούμε τα κενά που παρήχθησαν προηγουμένως και δημιουργούμε δύο αρχεία, ένα με όλες τις ημερομηνίες ως έχουν και ένα με όλες τις ημερομηνίες αντεστραμμένες.

Στη συνέχεια συγχωνεύουμε αυτά τα δύο αρχεία, ενώνοντας κάθε γραμμή του πρώτου αρχείου με την αντίστοιχη γραμμή του δεύτερου παρεμβάλλοντας ένα στηλοθέτη - TAB.

Εξοικειωθείτε με τις εντολές στη γραμμή εντολών σας:

# Αφαίρεσε τα κενά
$ echo "Good night " | tr -d " "
Goodnight

# Αφαίρεσε τις παύλες
$ echo "Good-night" | tr -d "-"
Goodnight

# Τύπωσε a-x-z όπου χ ένας αριθμός μεταξύ του 1 και του 9
$ printf "a-%d-z\n" {1..9}
a-1-z
a-2-z
a-3-z
a-4-z
a-5-z
a-6-z
a-7-z
a-8-z
a-9-z

# και αντέστρεψε τη σειρά των χαρακτήρων της κάθε γραμμής
$ printf "a-%d-z\n" {1..9} | rev
z-1-a
z-2-a
z-3-a
z-4-a
z-5-a
z-6-a
z-7-a
z-8-a
z-9-a

Επεξήγηση εντολής

  • tr -d '': αφαιρούμε τα κενά από την είσοδο - input (που είναι η έξοδος - output της προηγούμενης εντολής) και αποθηκεύουμε την έξοδο σε ένα αρχείο με όνομα alldates.txt
  • rev: αντιστρέφουμε του χαρακτήρες κάθε γραμμής του αρχείου alldates.txt και αποθηκεύουμε την έξοδο σε ένα νέο αρχείο με όνομα revdates.txt
  • paste: ενώνουμε τα δύο αρχεία; κάθε γραμμή του αρχείου alldates.txt ενώνεται με την αντίστοιχη γραμμή του αρχείου revdates.txt παρεμβάλλοντας ένα στηλοθέτη - tab (τα δύο αρχεία έχουν τον ίδιο αριθμό γραμμών).

Αποτελέσματα - Output

202025	520202
20200205	50200202
202024	420202
20200204	40200202
202023	320202
20200203	30200202
202022	220202
20200202	20200202
202021	120202
20200201	10200202

awk '$1==$2{print $1}'

Σε αυτό το τελευταίο τμήμα της εντολής, χρησιμοποιούμε την awk για να τυπώσουμε τα παλίνδρομα.

Εξοικειωθείτε με τη σχετική λειτουργικότητα της awk στη γραμμή εντολών σας:

# Τύπωσε ολόκληρη την είσοδο (η προεπιλεγμένη συμπεριφορά της awk) αν τα πεδία είναι ίδια
$ echo "a a" | awk '$1==$2'
a a

# Τύπωσε ολόκληρη την είσοδο (η προεπιλεγμένη συμπεριφορά της awk) αν τα πεδία είναι ίδια
$ echo "a a" | awk '$1!=$2'
# δεν τυπώνεται τίποτα εδώ

# Τύπωσε ολόκληρη την είσοδο (η προεπιλεγμένη συμπεριφορά της awk) αν τα πεδία είναι ίδια
$ echo "a b" | awk '$1!=$2'
a b

# Τύπωσε το πρώτο πεδίο αν τα πεδία δεν είναι ίδια
$ echo "a b" | awk '$1!=$2 {print $1}'
a

# Τύπωσε το δεύτερο πεδίο αν τα πεδία δεν είναι ίδια
$ echo "a b" | awk '$1!=$2 {print $2}'
b

Επεξήγηση εντολής

  • τα αποτελέσματα του προηγούμενου τμήματος της εντολής που είναι η είσοδος αυτού του τμήματος αποτελούνται από γραμμές και κάθε γραμμή έχει δύο τιμές χωρισμένες με στηλοθέτη - TAB. Αυτού του τύπου την είσοδο η awk την διαβάζει ως δύο πεδία για κάθε γραμμή. Μπορούμε να προσπελάσουμε αυτά τα πεδία χρησιμοποιώντας της μεταβλητές $1 και $2 αντίστοιχα.
  • όπως περιγράφεται σε προηγούμενο άρθρο μου, οι δηλώσεις (statements) στην awk αποτελούνται από ένα μοτίβο (pattern) ή έκφραση (expression) και μια συσχετισμένη ενέργεια (associated action).
    <pattern/expression> { <action> }
    

    Αν το μοτίβο/έκφραση επιτύχει τότε εκτελείται η συσχετισμένη ενέργεια. Στην περίπτωσή μας, η έκφραση μεταφράζεται ως “αν τα δύο πεδία είναι ίδια” ($1==$2) και η ενέργεια ως “τύπωσε το πρώτο πεδίο” (printf $1).

  • αν τα πεδία είναι ίδια τότε έχουμε μια παλίνδρομη ημερομηνία αφού κάθε ζεύγος πεδίων είναι η ημερομηνία και η αντίστροφη τιμή της.

Αποτελέσματα - Output

20200202
2019102
2018102
2017102
2016102
2015102
2014102
2013102
2012102
...
11111111
11111111
1111111
1111111
111111

Γατοφωτογραφία

Τελειώσαμε, ευχαριστώ για την ανάγνωση.

Φωτογραφία με την αντανάκλαση της γάτας μου, της Ίριδας

Αναφορές