Bash: Προσθέτοντας tab συμπλήρωση στα script

Ένα βοήθημα σχετικά με το πως μπορείτε να προσθέσετε tab συμπλήρωση στα script σας σε περιβάλλον Bash
This article is also available in english.
I just released stup, a tool for easily keeping organized daily notes in the terminal. You can find it on GitHub here.

Πρόσφατα χρειάστηκε να προσθέσω tab συμπλήρωση σε ένα script μου και βρήκα τη λειτουργικότητα πολύ ενδιαφέρουσα.

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

Τι είναι η tab συμπλήρωση (bash completion);

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

$ git<tab><tab>
git                 git-receive-pack    git-upload-archive  
gitk                git-shell           git-upload-pack     
$ git-s<tab>
$ git-shell

Πως δουλεύει;

Το script συμπλήρωσης είναι κώδικας που χρησιμοποιεί την συστημική bash εντολή complete για να ορίσει ποιες προτάσεις συμπλήρωσης θα εμφανιστούν για ένα δεδομένο εκτελέσιμο πρόγραμμα. Η φύση των προτάσεων αυτών ποικίλει από απλές στατικές έως περίπλοκες και δυναμικές επιλογές.

Γιατί να ασχοληθώ;

Είναι καλό να παρέχεται αυτή η λειτουργικότητα στους χρήστες των script:

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

Ας ξεκινήσουμε

Να τι θα κάνουμε σε αυτό το βοήθημα.

Αρχικά, θα δημιουργήσουμε ένα πολύ απλό εκτελέσιμο script που θα το πούμε dothis. Αυτό θα είναι το script που στη συνέχεια θα προσθέσουμε τη λειτουργικότητα για την tab συμπλήρωση. Το μόνο που θα κάνει το script είναι να εκτελεί την εντολή που βρίσκεται σε μια συγκεκριμένη θέση στο ιστορικό των εντολών του χρήστη. Τη θέση της εντολής στο ιστορικό θα την δέχεται σαν παράμετρο κατά την εκτέλεση. Για παράδειγμα, η παρακάτω εντολή θα εκτελέσει την εντολή ls -a δεδομένου ότι η τελευταία βρισκεται στη θέση 235 στο ιστορικό εντολών του χρήστη:

dothis 235

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

$ dothis <tab><tab>
215 ls
216 ls -la
217 cd ~
218 man history
219 git status
220 history | cut -c 8-

Επίδειξη της tab συμπλήρωσης

Συγκεντρωθείτε bash και τα καταφέρουμε με την πρώτη.

Δημιουργία του εκτελέσιμου προγράμματος

Δημιουργήστε ένα αρχείο με το όνομα dothis στο φάκελο που επιθυμείτε και προσθέστε τον παρακάτω κώδικα:

if [ -z "$1" ]; then
  echo "No command number passed"
  exit 2
fi

exists=$(fc -l -1000 | grep ^$1 -- 2>/dev/null)

if [ -n "$exists" ]; then
  fc -s -- "$1"
else
  echo "Command with number $1 was not found in recent history"
  exit 2
fi

Σημειώσεις:

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

Μετατρέψτε το script σε εκτελέσιμο με την παρακάτω εντολή:

chmod +x ./dothis

Θα εκτελούμε συχνά αυτό το script σε αυτό το βοήθημα οπότε συνιστώ να το μεταφέρετε σε ένα φάκελο που ανήκει στην μεταβλητή PATH του περιβάλλοντός σας ωτε να μπορούμε να το προσπελάσουμε από παντού απλά πληκτρολογώντας dothis.

Το δικό μου το έβαλα στο home bin πληκτρολογώντας:

install ./dothis ~/bin/dothis

Μπορείτε να κάνετε το ίδιο δεδομένου ότι έχετε φάκελο ~/bin ο οποίος εμπεριέχεται στην PATH μεταβλητή σας.

Δοκιμάστε να δείτε ότι δουλεύει:

dothis

Θα έπρεπε να δείτε αυτό:

$ dothis
No command number passed

Done.

Δημιουργία του script tab συμπλήρωσης

Δημιουργήστε ένα αρχείο με όνομα dothis-completion.bash. Από εδώ και στο εξής θα αναφέρομαι στο αρχείο αυτό με τον όρο script συμπλήρωσης.

Αφού του προσθέσουμε κώδικα, θα εκτελέσουμε την εντολή source για να το “ενεργοποιήσουμε”. Θα εκτελούμε την source σε αυτό το αρχείο κάθε φορά που αλλάζουμε το περιεχόμενό του ώστε να ισχύουν οι αλλαγές.

Θα συζητήσουμε επιλογές ώστε να να γίνεται η ενεργοποίηση αυτή κάθε φορά που ανοίγει νέα bash κονσόλα αργότερα.

Στατική συμπλήρωση

Υποθέστε ότι το dothis υποστήριζε μια στατική λίστα από παραμέτρους, για παράδειγμα:

  • τώρα
  • αύριο
  • ποτέ

Θα χρησιμοποιήσουμε την εντολή complete για να καταχωρήσουμε αυτή τη λίστα ως επιλογές για tab συμπλήρωση. (Για να χρησιμοποιήσουμε τη σωστή ορολογία, θα χρησιμοποιήσουμε την εντολή complete για να ορίσουμε τις προδιαγραφές συμπλήρωσης (compspec) για το πρόγραμμά μας.)

Προσθέστε αυτά στο script συμπλήρωσης.

#/usr/bin/env bash
complete -W "τώρα αύριο ποτέ" dothis

Τι κάναμε με τη χρήση της παραπάνω εντολής complete:

  • χρησιμοποιήσαμε την επιλογή -W (wordlist) για να παρέχουμε τη λίστα που θα χρησιμοποιηθεί για συμπλήρωση
  • ορίσαμε σε ποιο πρόγραμμα αυτή η λίστα θα παρέχεται για tab συμπλήρωση (η τελευταία παράμετρος: dothis)

“Ενεργοποιήστε” το αρχείο (δεν βρήκα πιο δόκιμο όρο για να περιγράψω τη σημασία της εκτέλεσης της εντολής “source”):

source ./dothis-completion.bash

Τώρα προσπαθήστε να πατήσετε το πλήκτρο tab δύο φορές όπως παρακάτω:

$ dothis <tab><tab>
αύριο ποτέ τώρα

Προσπαθήστε ξανά αφού βάλετε και ένα τ:

$ dothis τ<tab><tab>
τώρα

Magic! Οι επιλογές συμπλήρωσης φιλτράρονται αυτόματα ώστε να εμφανιστούν μόνο αυτές που ξεκινούν από τ.

Σημείωση: Οι επιλογές συμπλήρωσης δεν εμφανίζονται με τη σειρά που τις ορίσαμε στη λίστα λέξεων, ταξινομούνται αυτόματα.

Υπάρχουν πολλές άλλες επιλογές πέραν της -W που χρησιμοποιήσαμε προηγουμένως. Οι περισσότερες παράγουν συμπληρώσεις με στατικό τρόπο, εννοώντας ότι δεν επεμβαίνουμε δυναμικά στο φιλτράρισμα τους.

Για παράδειγμα, αν θέλαμε να έχουμε ως tab συμπληρώσεις για το εκτελέσιμο dothis τους φακέλους που βρίσκονται στον τρέχων φάκελο, θα αλλάζαμε την εντολή ως εξής:

complete -A directory dothis

Πατώντας το tab μετά το dothis, θα λαμβάναμε μια λίστα σαν την παρακάτω:

$ dothis <tab><tab>
dir1/ dir2/ dir3/

Βρείτε ολόκληρη τη λίστα με τις διαθέσιμες επιλογές εδώ.

Δυναμική tab συμπλήρωση

Θέλουμε να παράγουμε τις προτάσεις συμπλήρωσης για το εκτελέσιμο script μας με την ακόλουθη λογική:

  • αν ο χρήστης πατήσει το tab αμέσως μετά την εντολή, θα δείχνουμε τις τελευταίες 50 εκτελεσθείσες εντολές μαζί με τον αριθμό που αντιστοιχεί στη θέση τους στο ιστορικό
  • αν ο χρήστης πατήσει το tab αφού έχει πληκτρολογήσει και έναν αριθμό τότε θα εμφανίζουμε όλες τις εντολές των οποίων η θέση στο ιστορικό του, είναι ή ξεκινά με αυτόν τον αριθμό
  • αν ο χρήστης πατήσει το tab αφού έχει πληκτρολογήσει και έναν αριθμό και υπάρχει μόνο μια εντολή με θέση που είναι ή ξεκινά με τον αριθμό αυτό τότε θα συμπληρώσουμε απευθείας όλο τον αριθμό (αν έχετε μπερδευτεί μην ανησυχείτε, θα ξεκαθαρίσουν όλα παρακάτω)

Ας ξεκινήσουμε με το να ορίσουμε μια μέθοδο που θα εκτελείται κάθε φορά που ο χρήστης θα “ζητά” προτάσεις συμπλήρωσης για την εντολή dothis (πατώντας το tab). Αλλάξτε το script συμπλήρωσης ως εξής:

#/usr/bin/env bash
_dothis_completions()
{
  COMPREPLY+=("τώρα")
  COMPREPLY+=("αύριο")
  COMPREPLY+=("ποτέ")
}

complete -F _dothis_completions dothis

Σημειώστε τα παρακάτω:

  • χρησιμοποιήσαμε την επιλογή -F στην εντολή complete ορίζοντας ότι η _dothis_completions είναι η μέθοδος που θα παρέχει τις προτάσεις συμπλήρωσης για το dothis εκτελέσιμο
  • η COMPREPLY είναι μια μεταβλητή τύπου πίνακα που χρησιμοποιείται για να αποθηκεύει τις προτάσεις συμπλήρωσης - ο μηχανισμός συμπλήρωσης χρησιμοποιεί τη συγκεκριμένη μεταβλητή για να εμφανίσει τα περιεχόμενά της ως προτάσεις συμπλήρωσης

Τώρα, ενεργοποιήστε το script συμπλήρωσης και κατόπιν δοκιμάστε να πατήσετε tab μετά το εκτελέσιμο dothis:

$ dothis <tab><tab>
αύριο ποτέ τώρα

Τέλεια. Παράγουμε τις ίδιες προτάσεις συμπλήρωσης όπως πριν που χρησιμοποιήσαμε την επιλογή -W. Ή μήπως όχι; Δοκιμάστε το παρακάτω:

$ dothis ποτ<tab><tab>
αύριο τώρα ποτέ

Όπως βλέπετε, παρόλου που έχουμε πληκτρολογήσει ποτ και μετά ζητήσαμε προτάσεις συμπλήρωσης πατώντας το tab, οι διαθέσιμες προτάσεις συμπλήρωσης παραμένουν οι ίδιες, αφιλτράριστες και τίποτα δε συμπληρώνεται αυτόματα (κανονικά θα έπρεπε κατευθείαν να συμπληρωθεί η λέξη ποτέ χωρίς καν να εμφανιστούν προτάσεις συμπλήρωσης αφού είναι η μόνη επιλογή που ξεκινά από ποτ).

Γιατί συμβαίνει αυτό;

  • Τα περιεχόμενα της μεταβλητής COMPREPLY εμφανίζονται πάντα. Η μέθοδος είναι υπεύθυνη να προσθέτει/αφαιρεί προτάσεις από εδώ και στο εξής.
  • Αν η COMPREPLY μεταβλητή είχε μόνο ένα στοιχείο τότε η λέξη θα συμπληρωνόταν αυτόματα στην εντολή dothis. Εφόσον η τρέχουσα υλοποίησή μας πάντα αποθηκεύει στη μεταβλητή τις ίδιες τρεις λέξεις, αυτό δε θα συμβεί.

Θα μας σώσει η compgen: μια συστημική εντολή που παράγει προτάσεις συμπλήρωσης υποστηρίζοντας σχεδόν όλλες τις επιλογές που υποστηρίζει και η συστημική εντολή complete που χρησιμοποιούσαμε ως τώρα (πχ. -W για λίστα λέξεων, -d για φακέλους) και φιλτράρει τις προτάσεις βάσει του τι έχει ήδη πληκτρολογηθεί από το χρήστη.

Και πάλι, μην ανησυχείτε αν έχετε μπερδευτεί, τα πάντα θα ξεκαθαρίσουν παρακάτω.

Πληκτρολογήστε τα παρακάτω στη γραμμή εντολών για να κατανοήσετε καλύτερα τη κάνει η compgen:

$ compgen -W "τώρα αύριο ποτέ"
αύριο
τώρα
ποτέ
$ compgen -W "τώρα αύριο ποτέ" τ
τώρα
$ compgen -W "τώρα αύριο ποτέ" α
αύριο

Οπότε τώρα μπορούμε να χρησιμοποιήσουμε την compgen αλλά πρέπει να βρούμε πρώτα τι έχει πληκτρολογήσει ο χρήστης μετά το dothis. Η λειτουργικότητα tab συμπλήρωσης ευτυχώς παρέχει μεταβλητές που σχετίζονται με την συμπλήρωση που είναι υπό εκτέλεση. Παραθέτω τις πιο σημαντικές:

  • COMP_WORDS: ένας πίνακας με όλες τις λέξεις που έχουν πληκτρολογηθεί μετά από το όνομα του εκτελέσιμου στο οποίο έχει συνδεθεί το compspec (στην περίπτωσή μας: όλες τις λέξεις που έχουν πληκτρολογηθεί μετά το dothis)
  • COMP_CWORD: ένας αριθμός που δείχνει σε ποια θέση του πίνακα COMP_WORDS βρίσκεται ο κέρσορας - με άλλα λόγια, σε ποια λέξη ήταν ο κέρσορας όταν πατήθηκε το tab
  • COMP_LINE: ολόκληρη η τρέχουσα εντολή

Για να προσπελάσουμε την λέξη που πληκτρολογήθηκε αμέσως μετά το dothis αρκεί να χρησιμοποιήσουμε την τιμή του COMP_WORDS[1]

Αλλάξτε το script συμπλήρωσης ξανά:

#/usr/bin/env bash
_dothis_completions()
{
  COMPREPLY=($(compgen -W "τώρα αύριο ποτέ" "${COMP_WORDS[1]}"))
}

complete -F _dothis_completions dothis

Ενεργοποιήστε το και ορίστε:

 $ dothis
αύριο     τώρα       ποτέ
$ dothis α
αύριο

Τώρα, αντί των λέξεων τώρα, αύριο, ποτέ, Θα θέλαμε να δούμε τους αριθμούς που είναι στην ουσία οι θέσεις των εντολών από το ιστορικό του χρήστη.

Η εντολή fc -l ακολουθούμενη από έναν αρνητικό αριθμό -n εμφανίζει τις τελευταίες n εντολές που εκτελέστηκαν. Οπότε θα την χρησιμοποιήσουμε ως εξής:

fc -l -50

εμφανίζοντας τις τελευταίες 50 εντολές μαζί με τον αριθμό της θέσης τους στο ιστορικό. Ο μόνος χειρισμός που πρέπει να κάνουμε είναι να αντικαταστήσουμε τα tabs με κενά διαστήματα ώστε να εμφανίζονται σωστά από τον μηχανισμό των συμπληρώσεων του Bash.

sed βόηθα μας.

Αλλάξτε το script συμπλήρωσης ως εξής:

#/usr/bin/env bash
_dothis_completions()
{
  COMPREPLY=($(compgen -W "$(fc -l -50 | sed 's/\t//')" -- "${COMP_WORDS[1]}"))
}

complete -F _dothis_completions dothis

Ενεργοποιήστε το script συμπλήρωσης και δοκιμάστε στη γραμμή εντολών:

$ dothis <tab><tab>
632 source dothis-completion.bash   649 source dothis-completion.bash   666 cat ~/.bash_profile
633 clear                           650 clear                           667 cat ~/.bashrc
634 source dothis-completion.bash   651 source dothis-completion.bash   668 clear
635 source dothis-completion.bash   652 source dothis-completion.bash   669 install ./dothis ~/bin/dothis
636 clear                           653 source dothis-completion.bash   670 dothis
637 source dothis-completion.bash   654 clear                           671 dothis 6546545646
638 clear                           655 dothis 654                      672 clear
639 source dothis-completion.bash   656 dothis 631                      673 dothis
640 source dothis-completion.bash   657 dothis 150                      674 dothis 651
641 source dothis-completion.bash   658 dothis                          675 source dothis-completion.bash
642 clear                           659 clear                           676 dothis 651
643 dothis 623  ls -la              660 dothis                          677 dothis 659
644 clear                           661 install ./dothis ~/bin/dothis   678 clear
645 source dothis-completion.bash   662 dothis                          679 dothis 665
646 clear                           663 install ./dothis ~/bin/dothis   680 clear
647 source dothis-completion.bash   664 dothis                          681 clear
648 clear                           665 cat ~/.bashrc

Καθόλου κακό.

Έχουμε ένα πρόβλημα ωστόσο. Προσπαθήστε να πληκτρολογήσετε έναν αριθμό όπως τον βλέπετε στις προτάσεις συμπλήρωσης και πατήστε το tab ξανά.

$ dothis 623<tab>
$ dothis 623  ls 623  ls -la
...
$ dothis 623  ls 623  ls 623  ls 623  ls 623  ls -la

Αυτό συμβαίνει γιατί στο script συμπλήρωσής μας χρησιμοποιήσαμε την τιμή της ${COMP_WORDS[1]} ώστε να ελέγχουμε την πρώτη πάντα λέξη που πληκτρολογήθηκε μετά το dothis (ο αριθμός 623 στην παραπάνω απόσπασμα). Οπότε ο μηχανισμός συμπλήρωσης συνεχίζει να προτείνει ξανά και ξανά την ίδια επιλογή πατώντας το tab.

Για να το διορθώσουμε αυτό, δε θα επιτρέπουμε κανένα είδος συμπλήρωσης να λάβει χώρα αν τουλάχιστον μια λέξη έχει ήδη συμπληρωθεί. Θα προσθέσουμε λοιπόν μια συνθήκη ελέγχοντας το μέγεθος της προαναφερθείσας COMP_WORDS μεταβλητής.

#/usr/bin/env bash
_dothis_completions()
{
  if [ "${#COMP_WORDS[@]}" != "2" ]; then
    return
  fi

  COMPREPLY=($(compgen -W "$(fc -l -50 | sed 's/\t//')" -- "${COMP_WORDS[1]}"))
}

complete -F _dothis_completions dothis

Ενεργοποιήστε το script συμπλήρωσης και ξαναδοκιμάστε.

$ dothis 623<tab>
$ dothis 623 ls -la<tab> # Επιτυχία, δεν συμβαίνει συμπλήρωση εδώ

Υπάρχει ωστόσο και κάτι άλλο που δε μας αρέσει. Θέλουμε να εμφανίζουμε τις θέσεις μαζί με τις αντίστοιχες εντολές ώστε να βοηθήσουμε τους χρήστες να αποφασίσουν ποια είναι η εντολή που θέλουν να εκτελέσουν αλλά όταν υπάρχει μόνο μια πρόταση συμπλήρωσης και ο μηχανισμός τη συμπληρώνει αυτόματα, δεν θα έπρεπε να συμπληρώνεται και η εντολή παρά μόνο ο αριθμός.

Με άλλα λόγια, το dothis δέχεται μόνο έναν αριθμό και δεν έχουμε προσθέσει ελέγχους για περισσότερες παραμέτρους. Όταν μέθοδος που υπολογίζει τις προτάσεις συμπληρώσεων βρίσκε μόνο ένα αποτέλεσμα τότε θα πρέπει να πετσοκόψουμε (trim :D) το λεκτικό που περιέχει τον αριθμό και την εντολή ώστε να μείνει μόνο ο αριθμός.

Για να το πετύχουμε αυτό, θα κρατήσουμε το αποτέλεσμα τις εντολής compgen σε μια μεταβλητή τύπου πίνακα, και αν το μέγεθος της είναι 1 τότε θα επεξεργαστούμε το μοναδικό στοιχείο της ώστε να μείνει μόνο ο αριθμός. Διαφορετικά θα αφήσουμε την μεταβλητή ως έχει.

Αλλάξτε το script συμπλήρωσης ως εξής:

#/usr/bin/env bash
_dothis_completions()
{
  if [ "${#COMP_WORDS[@]}" != "2" ]; then
    return
  fi

  # keep the suggestions in a local variable
  local suggestions=($(compgen -W "$(fc -l -50 | sed 's/\t/ /')" -- "${COMP_WORDS[1]}"))

  if [ "${#suggestions[@]}" == "1" ]; then
    # if there's only one match, we remove the command literal
    # to proceed with the automatic completion of the number
    local number=$(echo ${suggestions[0]/%\ */})
    COMPREPLY=("$number")
  else
    # more than one suggestions resolved,
    # respond with the suggestions intact
    COMPREPLY=("${suggestions[@]}")
  fi
}

complete -F _dothis_completions dothis

Αυτόματη ενεργοποίηση του script tab συμπλήρωσης

Αν θέλετε να ενεργοποιήσετε τις συμπληρώσεις για το εκτελέσιμο μόνο για εσάς, το μόνο που χρειάζεται να κάνετε είναι να προσθέσετε της παρακάτω εντολή στο αρχείο σας ~/.bashrc:

source <path-to-your-script>/dothis-completion.bash

Αν θέλετε να τις ενεργοποιήσετε για όλους τους χρήστες τότε μπορείτε να αντιγράψετε το script συμπλήρωσης στο φάκελο /etc/bash_completion.d/ και θα φορτώνεται αυτόματα από το Bash.

Τελειοποίηση του script συμπλήρωσης

Μερικά επιπλέον βήματα για καλύτερα αποτελέσματα.

Εμφανίζοντας κάθε πρόταση συμπλήρωσης σε νέα γραμμή

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

Δυστυχώς, αυτό δεν είναι δυνατόν (τουλάχιστον για την ώρα) γιατί ο μηχανισμός συμπλήρωσης εμφανίζει τις προτάσεις ως καθαρό κείμενο και οι οδηγίες χρώματος δεν εφαρμόζονται (για παράδειγμα, αυτό \e[34mουρανός θα εμφανιζόταν ακριβώς όπως το βλέπετε και όχι η λέξη ουρανός σε μπλε χρώμα).

Αυτό που μπορούμε ωστόσο να κάνουμε για να βελτιώσουμε την εμπειρία των χρηστών (ή όχι :D) είναι να εμφανίζουμε την κάθε πρόταση συμπλήρωσης σε νέα γραμμή. Ο τρόπος για να το κάνουμε αυτό δεν είναι ο προφανής γιατί δεν μπορούμε απλά να προσθέσουμε στο τέλος κάθε πρότασης τον χαρακτήρα αλλαγής γραμμής αφού ο μηχανισμός συμπλήρωσης δεν θα τον μεταφράσει σε αλλαγή γραμμής αλλά θα τον δείξει ως κείμενο (\n).

Θα ακολουθήσουμε ένα μάλλον hackish τρόπο και θα προσθέτουμε κενά στο τέλος κάθε πρότασης συμπλήρωσης, τόσα ώστε το τελικό πλήθος των χαρακτήρων της πρότασης (με τα κενά) να είναι ίσο με το πλήθος των χαρακτήρων που χωρά η κάθε γραμμή της κονσόλας.

printf βόηθα μας.

Αλλάξτε το script συμπλήρωσης ως εξής:

#/usr/bin/env bash
_dothis_completions()
{
  if [ "${#COMP_WORDS[@]}" != "2" ]; then
    return
  fi

  local IFS=$'\n'
  local suggestions=($(compgen -W "$(fc -l -50 | sed 's/\t//')" -- "${COMP_WORDS[1]}"))

  if [ "${#suggestions[@]}" == "1" ]; then
    local number="${suggestions[0]/%\ */}"
    COMPREPLY=("$number")
  else
    for i in "${!suggestions[@]}"; do
      suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
    done

    COMPREPLY=("${suggestions[@]}")
  fi
}

complete -F _dothis_completions dothis

Ενεργοποιήστε το και δοκιμάστε:

dothis <tab><tab>
...
499 source dothis-completion.bash                   
500 clear
...       
503 dothis 500

Done.

Προσαρμόσιμη συμπεριφορά

Στην περίπτωσή μας, ορίσαμε καρφωτά να εμφανίζονται οι τελευταίες 50 εκτελέσιμες εντολές όταν πατιέται το tab χωρίς να έχει πληκτρολογηθεί κάποιος αριθμός. Θα ήταν καλύτερο να αφήνουμε το χρήστη να επιλέγει πόσες θέλει να είναι αυτές οι εντολές (π.χ. οι τελευταίες 100) και αν δεν έχει ορίσει την επιθυμία του τότε εξ ορισμού να δείχνουμε τις τελευταίες 50,

Για να το πετύχουμε αυτό, θα ελέγχουμε την τιμή μιας μεταβλητής από το περιβάλλον του χρήστη, ας πούμε DOTHIS_COMPLETION_COMMANDS_NUMBER.

Αλλάξτε για τελευταία φορά το script συμπλήρωσης:

#/usr/bin/env bash
_dothis_completions()
{
  if [ "${#COMP_WORDS[@]}" != "2" ]; then
    return
  fi

  local commands_number=${DOTHIS_COMPLETION_COMMANDS_NUMBER:-50}
  local IFS=$'\n'
  local suggestions=($(compgen -W "$(fc -l -$commands_number | sed 's/\t//')" -- "${COMP_WORDS[1]}"))

  if [ "${#suggestions[@]}" == "1" ]; then
    local number="${suggestions[0]/%\ */}"
    COMPREPLY=("$number")
  else
    for i in "${!suggestions[@]}"; do
      suggestions[$i]="$(printf '%*s' "-$COLUMNS"  "${suggestions[$i]}")"
    done

    COMPREPLY=("${suggestions[@]}")
  fi
}

complete -F _dothis_completions dothis

Ενεργοποιήστε το και δοκιμάστε:

export DOTHIS_COMPLETION_COMMANDS_NUMBER=5
$ dothis <tab><tab>
505 clear
506 source ./dothis-completion.bash
507 dothis clear
508 clear
509 export DOTHIS_COMPLETION_COMMANDS_NUMBER=5

Χρήσιμοι σύνδεσμοι

Κώδικας και σχόλια

Μπορείτε να βρείτε τον κώδικα από αυτό το άρθρο στο GitHub.

Για feedback, σχόλια, λάθη κ.λπ. παρακαλώ ανοίξτε ένα issue στο αποθετήριο.

Μεγάλο άρθρο, φωτογραφία γάτας

Να σας συστήσω των debugger μου.

My debugger

Αυτό ήταν. Τελειώσαμε. Καλή συνέχεια.