Από μονόγραμμο σε ψευδώνυμο κελύφους#

Ένα μονόγραμμο είναι εκ σχεδιασμού αναλώσιμο. Τη στιγμή που πληκτρολογείτε το ίδιο δύο φορές, κάτι έχει κερδίσει θέση στη ρύθμιση του κελύφους σας. Το κεφάλαιο αυτό καλύπτει τη μηχανική του πώς ανυψώνεται μια συνταγή pperl σε συνάρτηση κελύφους bash ή zsh, πώς να την παραμετροποιήσετε, και πότε να σταματήσετε και να γράψετε ένα πραγματικό σενάριο αντί γι” αυτό.

Υποτιθέμενη αφετηρία: έχετε κέλυφος bash ή zsh, ένα ~/.bashrc ή ~/.zshrc (ή έναν κατάλογο rc.d / functions τον οποίο φορτώνει το κέλυφός σας), και την υπομονή να κλείσετε και να ξανανοίξετε ένα τερματικό μετά την επεξεργασία του.

Ψευδώνυμο έναντι συνάρτησης#

Το bash και το zsh έχουν δύο μηχανισμούς. Δεν είναι εναλλάξιμοι.

  • Το alias είναι κειμενική αντικατάσταση. Εκτελείται στην αρχή μιας γραμμής εντολών, αντικαθιστώντας το όνομα του ψευδώνυμου με την τιμή του πριν το κέλυφος ξανα-αναλύσει. Δεν δέχεται ορίσματα, δεν παρουσιάζει ιδιαιτερότητες σωλήνωσης πέρα από αυτό που σας δίνει η αντικατάσταση.

  • Μια συνάρτηση κελύφους είναι ένα ονοματισμένο κομμάτι κώδικα κελύφους. Δέχεται θεσικά ορίσματα ($1, $2, $@), διαβάζει το stdin όπως κάθε άλλη εντολή και μπορεί να σωληνωθεί προς και από άλλες εντολές.

Εμπειρικός κανόνας. Αν η συνταγή δεν δέχεται ορίσματα, αρκεί το ψευδώνυμο. Αν χρειάζεται οποιοδήποτε όρισμα (αριθμό στήλης, regex, όνομα αρχείου), χρησιμοποιήστε συνάρτηση.

Ψευδώνυμο - η συνταγή χωρίς ορίσματα#

# In ~/.bashrc
alias sum-stdin='pperl -MList::Util=sum -lane '"'"'print sum @F'"'"
alias count-lines='pperl -lne '"'"'END { print $. }'"'"

Τα εισαγωγικά είναι άσχημα γιατί μια συμβολοσειρά μονών εισαγωγικών στο bash δεν μπορεί να περιέχει μονό εισαγωγικό· το ιδίωμα '"'"' κλείνει και ξανανοίγει. Αυτός είναι ο κύριος λόγος που προτιμώνται οι συναρτήσεις: σας επιτρέπουν να εισαγάγετε τον κώδικα Perl μία φορά, καθαρά.

Συνάρτηση - η συνηθισμένη περίπτωση#

# In ~/.bashrc
sum-stdin() { pperl -MList::Util=sum -lane 'print sum @F'; }
count-lines() { pperl -lne 'END { print $. }'; }

Η χρήση είναι η ίδια:

$ printf '1 2 3\n4 5 6\n' | sum-stdin
6
15

Παραμετροποιημένες συναρτήσεις#

Το κέλυφος παρεμβάλλει τα $1, $@, "$1" στο σώμα της συνάρτησης πριν την εκτελέσει. Μέσα στο πρόγραμμα Perl, το $1 σε επίπεδο κελύφους εμφανίζεται ως κυριολεκτική τιμή - το ίδιο το $1 της Perl (η σύλληψη regex) μένει ανέπαφο από την παρεμβολή του κελύφους μόνο αν χρησιμοποιήσετε μονά εισαγωγικά.

Το μείγμα επιλύεται με προσοχή. Δύο ιδιώματα καλύπτουν σχεδόν κάθε περίπτωση.

Ιδίωμα 1: στήλη / τιμή με παρεμβολή κελύφους#

Χρησιμοποιήστε διπλά εισαγωγικά γύρω από το πρόγραμμα Perl ώστε το κέλυφος να επεκτείνει το $1, και έπειτα διαφεύγετε από το ίδιο το $ της Perl με \$.

# Sum column N of stdin: sumcol N
sumcol() {
    pperl -MList::Util=sum -lane "\$s += \$F[$1]; END { print \$s }"
}

# Usage
$ cat data.tsv
10 apple 3
15 pear  7
20 plum  2
$ sumcol 2 < data.tsv
12

Κάθε \$ είναι κυριολεκτικό σύμβολο δολαρίου για την Perl· κάθε $1 (χωρίς διαφυγή) είναι το πρώτο θεσικό όρισμα που επεκτείνει το κέλυφος.

Ιδίωμα 2: πρόγραμμα σε μονά εισαγωγικά, δεδομένα μέσω περιβάλλοντος#

Η Perl σε μονά εισαγωγικά παραμένει ευανάγνωστη και οι τιμές φτάνουν στο πρόγραμμα μέσω του %ENV.

# Grep with a pperl regex: pgrep PATTERN [FILE...]
pgrep() {
    local pat=$1
    shift
    pat="$pat" pperl -ne 'print if /$ENV{pat}/' "$@"
}

$ pgrep '\berror\b' app.log

Έτσι το $1 (σύλληψη Perl) παραμένει χρησιμοποιήσιμο και αποφεύγεται ο καταρράκτης ανάστροφων καθέτων.

Ιδίωμα 3: ανάμειξη και των δύο#

# Replace column N's commas with semicolons, in place: csv-fix N FILE...
csv-fix() {
    local col=$1
    shift
    col="$col" pperl -i.bak -F'\t' -lane '
        my $c = $ENV{col};
        $F[$c] =~ tr/,/;/;
        print join("\t", @F);
    ' "$@"
}

Ο αριθμός στήλης φτάνει στο $ENV{col} - χωρίς χορό διαφυγών. Η λίστα αρχείων διαδίδεται μέσω του "$@" ώστε τα κενά στα ονόματα αρχείων να επιβιώνουν.

Πρακτικά ψευδώνυμα που αξίζει να κρατηθούν#

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

sumcol N - άθροισμα στήλης N (χωρισμένης με λευκούς χαρακτήρες)#

sumcol() {
    pperl -MList::Util=sum -lane "\$s += \$F[$1]; END { print \$s }"
}

sumcolf N FS - άθροισμα στήλης N με προσαρμοσμένο διαχωριστή πεδίων#

sumcolf() {
    local col=$1 fs=$2
    col="$col" pperl -MList::Util=sum -F"$fs" -lane '
        $s += $F[$ENV{col}]; END { print $s }
    '
}

$ sumcolf 3 : < /etc/passwd    # sum of UIDs

colstats N - min / max / μέσος όρος / πλήθος για τη στήλη N#

colstats() {
    col="$1" pperl -MList::Util=qw(min max sum) -lane '
        my $c = $ENV{col};
        push @v, $F[$c];
        END {
            printf "n=%d min=%g max=%g mean=%.4f\n",
                scalar @v, min(@v), max(@v), sum(@v) / @v
        }
    '
}

uniq-stream - μοναδικότητα σε ροή, διατηρώντας την πρώτη εμφάνιση#

uniq-stream() { pperl -ne 'print unless $seen{$_}++'; }

Σε αντίθεση με το sort -u, η σειρά διατηρείται. Σε αντίθεση με το uniq, η είσοδος δεν χρειάζεται να είναι ταξινομημένη.

extract PAT - τυπώνει κάθε αντιστοιχία regex, μία ανά γραμμή#

extract() { pat="$1" pperl -lne 'print for /$ENV{pat}/g'; }

$ extract '\b\d{3}-\d{4}\b' < phonebook.txt
555-1212
555-9876

between A B - τυπώνει τα πάντα μεταξύ regex A και regex B, συμπεριλαμβανομένων#

between() {
    a="$1" b="$2" pperl -ne 'print if /$ENV{a}/ .. /$ENV{b}/'
}

$ between 'BEGIN cert' 'END cert' < bundle.pem

json-pretty - όμορφη εκτύπωση ενός εγγράφου JSON#

json-pretty() {
    pperl -MJSON::PP -0777 -ne '
        print JSON::PP->new->pretty->canonical->encode(
            JSON::PP->new->decode($_)
        )
    '
}

passwd-json - το /etc/passwd ως NDJSON#

passwd-json() {
    pperl -MJSON::PP -F: -lane '
        print JSON::PP->new->encode({
            user => $F[0], uid => 0+$F[2], gid => 0+$F[3],
            home => $F[5], shell => $F[-1]
        })
    ' < /etc/passwd
}

words-by-freq - μέτρηση συχνοτήτων λέξεων, ταξινομημένη#

words-by-freq() {
    pperl -ne '
        $w{lc $_}++ for /\b[[:alpha:]]+\b/g;
        END { print "$w{$_} $_\n" for sort { $w{$b} <=> $w{$a} } keys %w }
    '
}

$ words-by-freq < essay.txt | head -20

strip-comments LANG - αφαίρεση σχολίων για μερικές κοινές γλώσσες#

strip-comments() {
    case "$1" in
        sh|py|rb|pl) pperl -pe 's/(?<!\\)#.*//' ;;
        c|cpp|rs|java|js) pperl -0777 -pe 's{//[^\n]*|/\*.*?\*/}{}gs' ;;
        sql|lua) pperl -pe 's/--[^\n]*//' ;;
        *) echo "unknown language: $1" >&2; return 1 ;;
    esac
}

Πότε να σταματήσετε και να γράψετε σενάριο#

Η μετάβαση μονόγραμμο → ψευδώνυμο τελειώνει όταν:

  • Το πρόγραμμα χρειάζεται κλάδο if / else μεγαλύτερο από έναν μεμονωμένο υπό συνθήκη τελεστή ? :.

  • Υπάρχουν πάνω από δύο-τρία αρθρώματα -M.

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

  • Το πρόγραμμα είναι το μόνο ψευδώνυμο στη ρύθμισή σας με σχόλιο από πάνω που εξηγεί τι κάνει.

  • Ένας συνάδελφος πρόκειται να το τρέξει.

Μεταφερθείτε σε αρχείο σεναρίου. Βάλτε τις σημαίες στη γραμμή shebang και αφήστε το πρόγραμμα να διαβάζεται φυσικά:

#!/usr/bin/env pperl
use strict;
use warnings;
use List::Util qw(sum);

my $col = shift @ARGV // die "usage: $0 COLUMN [FILE...]\n";
my $total = 0;
while (<>) {
    my @f = split;
    $total += $f[$col];
}
print "$total\n";

Δώστε του ένα λογικό όνομα, chmod +x, και προχωρήστε. Το μονόγραμμο έκανε τη δουλειά του: ήταν η συντομότερη διαδρομή από το «έχω μια ιδέα» στο «ξέρω ότι αυτό δουλεύει»· το σενάριο είναι η διαδρομή από το «ξέρω ότι αυτό δουλεύει» στο «ο συνάδελφός μου μπορεί να το διαβάσει».

Μάθετε περισσότερα#

  • progression - οι συνταγές που τυλίγουν αυτά τα ψευδώνυμα.

  • gotchas - εισαγωγικά μέσα σε συναρτήσεις κελύφους και ψευδώνυμα.

  • numeric - περισσότεροι υποψήφιοι για τη λίστα ψευδωνύμων, ειδικά αυτοί της αριθμητικής ημερομηνιών.