Διαδρομές
/
Bash
Bash
/
Ύλη
/
Η σημασία των εισαγωγικών
Η

Η σημασία των εισαγωγικών σε Bash

{one: "1 άσκηση", other: "%{count} ασκήσεις"}

Σχετικά με την έννοια Η σημασία των εισαγωγικών

Μια συμβουλή που θα ακούσουν οι νέοι προγραμματιστές bash ξανά και ξανά:

Βάλε εισαγωγικά στις μεταβλητές σου!

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

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

Τα εισαγωγικά

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

Διπλά εισαγωγικά

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

echo "This is bash version $BASH_VERSION"

Μπορείς να συμπεριλάβεις ένα κυριολεκτικό διπλό εισαγωγικό μέσα σε μια συμβολοσειρά με διπλά εισαγωγικά, βάζοντας πριν από αυτό ένα backslash.

echo "She said \"Let me teach you about bash.\""

Μονά εισαγωγικά

Τα μονά εισαγωγικά δεν επιτρέπουν καμία επέκταση μέσα τους.

echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION

Δεν είναι δυνατόν να ενσωματώσεις ένα μονό εισαγωγικό μέσα σε μια συμβολοσειρά με μονά εισαγωγικά.

Η σημασία των εισαγωγικών

Τι ακριβώς πάει στραβά με το echo $name;

Όταν το shell αναλύει μια γραμμή κώδικα, εκτελεί διάφορα είδη επεκτάσεων, με τη σειρά. Δύο από τις τελευταίες επεκτάσεις είναι ο Διαχωρισμός λέξεων και η Επέκταση ονόματος αρχείου.

Διαχωρισμός λέξεων

Οποιαδήποτε αποτελέσματα της επέκτασης παραμέτρου, της υποκατάστασης εντολής ή της αριθμητικής επέκτασης που δεν συνέβησαν μέσα σε διπλά εισαγωγικά είναι επιλέξιμα για "διαχωρισμό λέξεων". Το shell παίρνει το αποτέλεσμα της επέκτασης και το χωρίζει σε οποιαδήποτε ακολουθία χαρακτήρων υπάρχει στην τιμή του $IFS. Από προεπιλογή, αυτό είναι το κενό, το tab και η νέα γραμμή.

Μπορούμε να το δούμε αυτό στην πράξη με έναν βρόχο shell:

sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^

εμφανίζει

1 The quick brown fox jumps.

Αλλά χωρίς εισαγωγικά:

i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

εμφανίζει

1 The
2 quick
3 brown
4 fox
5 jumps.

Ας το δούμε με μια διαφορετική τιμή του $IFS:

csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done

εμφανίζει

first
second
third
Απενεργοποίηση του διαχωρισμού λέξεων

Μια τεχνική για να απενεργοποιήσεις τον διαχωρισμό λέξεων είναι να θέσεις το $IFS σε μια κενή συμβολοσειρά

sentence="The quick brown fox jumps."
IFS=    # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

εμφανίζει

1 The quick brown fox jumps.

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

Επέκταση ονόματος αρχείου

Τα μοτίβα "glob" χρησιμοποιούνται για να εκφράσουν ένα συνοπτικό μοτίβο που να αντιστοιχίζει ένα σύνολο αρχείων. Για παράδειγμα, μπορείς να αντιστοιχίσεις όλα τα αρχεία Markdown σε έναν κατάλογο με *.md. Τα μοτίβα glob υπάρχουν από την εφεύρεση του Unix.

Οι χαρακτήρες μπαλαντέρ του glob είναι:

  • * αντιστοιχίζει μηδέν ή περισσότερους οποιουσδήποτε χαρακτήρες,

  • ? αντιστοιχίζει ακριβώς έναν οποιονδήποτε χαρακτήρα,

  • [...] αντιστοιχίζει ακριβώς έναν χαρακτήρα από το σύνολο των χαρακτήρων που παρατίθενται μέσα στις αγκύλες.

    Advanced
    Υπάρχουν πολλές λεπτομέρειες σχετικά με τα σύνολα χαρακτήρων. Κάνε κλικ εδώ για να τις δεις.
    • [abc] αντιστοιχίζει ακριβώς έναν χαρακτήρα που είναι είτε a είτε b είτε c.
      • για να συμπεριλάβεις μια κυριολεκτική αγκύλη κλεισίματος στην έκφραση, κάνε την πρώτο χαρακτήρα μετά την αγκύλη ανοίγματος (μετά από ένα προαιρετικό ^)
        • το [][] είναι ένα σύνολο χαρακτήρων που θα αντιστοιχίσει είτε μια αγκύλη κλεισίματος είτε μια αγκύλη ανοίγματος.
        • ή κάνε escape με backslash: [[\]]
    • [x-y] αντιστοιχίζει ακριβώς έναν χαρακτήρα που βρίσκεται στο εύρος από το x έως το y
      • για να συμπεριλάβεις έναν κυριολεκτικό χαρακτήρα παύλας σε ένα σύνολο χαρακτήρων, πρέπει να είναι είτε ο πρώτος χαρακτήρας μετά την αγκύλη ανοίγματος (μετά από ένα προαιρετικό ^) είτε ο τελευταίος χαρακτήρας πριν από την αγκύλη κλεισίματος.
        • τα [-xy] ή [xy-] αντιστοιχίζουν το καθένα ακριβώς έναν χαρακτήρα που είναι - ή x ή y
        • ή κάνε escape με backslash: [x\-y]
      • μπορούν να οριστούν πολλά εύρη, για παράδειγμα το [0-9A-Fa-f] αντιστοιχίζει ένα δεκαεξαδικό ψηφίο
    • [^abc] αντιστοιχίζει ακριβώς έναν χαρακτήρα που ΔΕΝ είναι a ή b ή c,
    • [^x-y] αντιστοιχίζει ακριβώς έναν χαρακτήρα που ΔΕΝ βρίσκεται στο εύρος από το x έως το y
    • [[:character_class:]] αντιστοιχίζει έναν χαρακτήρα που ανήκει στο ονομαζόμενο "character_class"
      • οι διαθέσιμες κλάσεις χαρακτήρων είναι:
        • lower: πεζά γράμματα
        • upper: κεφαλαία γράμματα
        • alpha: γράμματα
        • digit: ψηφία
        • alnum: γράμματα και ψηφία
        • space: κενά διαστήματα
        • blank: οριζόντια κενά διαστήματα (κενό και tab)
        • xdigit: δεκαεξαδικά ψηφία
        • word: χαρακτήρες που επιτρέπονται σε ένα αναγνωριστικό (γράμματα, αριθμοί, κάτω παύλα)
        • punct: στίξη
        • cntrl: χαρακτήρες ελέγχου
        • graph: "ορατοί" χαρακτήρες
        • print: ορατοί χαρακτήρες συν το κενό
      • μπορούν να συνδυαστούν:
        • η κλάση [[:alpha:]] αποτελείται από [[:lower:][:upper:]]
        • η κλάση [[:alnum:]] αποτελείται από [[:alpha:][:digit:]]
        • η κλάση [[:word:]] αποτελείται από [[:alnum:]_]
        • η κάτω παύλα (οκταδικό 137) είναι και [[:word:]] και [[:punct:]]
        • η κλάση [[:graph:]] αποτελείται από [[:alnum:][:punct:]]
        • η κλάση [[:print:]] αποτελείται από [[:graph:] ], δηλαδή μόνο το κενό (οκταδικό 040) και όχι οποιοδήποτε άλλο κενό διάστημα.
Επανάληψη πάνω σε μια λίστα αρχείων

Μπορείς να επαναλάβεις πάνω σε μια λίστα αρχείων με έναν βρόχο for

for file in *.csv; do
    do_something_with "$file"
done

Πρόσεξε ότι η μεταβλητή "$file" είναι μέσα σε εισαγωγικά. Το μοτίβο glob μπορεί να επιστρέψει αρχεία που περιέχουν κενά στο όνομά τους. Πρέπει να βάλουμε εισαγωγικά στη μεταβλητή, ώστε η εντολή do_something_with να λάβει ακριβώς ένα όρισμα, το όνομα του αρχείου.

Τα μοτίβα glob δεν είναι κανονικές εκφράσεις

Τα μοτίβα glob δεν είναι κανονικές εκφράσεις.

  • αντιστοιχίζουν ακριβώς έναν χαρακτήρα
    • regex .
    • glob ?
  • αντιστοιχίζουν μηδέν ή περισσότερους χαρακτήρες
    • regex .*
    • glob *
      • το μοτίβο glob .* σημαίνει "αντιστοίχισε ένα όνομα αρχείου όπου ο πρώτος χαρακτήρας είναι μια κυριολεκτική τελεία ακολουθούμενη από μηδέν ή περισσότερους οποιουσδήποτε χαρακτήρες".
  • αντιστοιχίζουν έναν ή περισσότερους χαρακτήρες
    • regex .+
    • glob ?*
  • τα σύνολα χαρακτήρων των regex και τα σύνολα χαρακτήρων του glob λειτουργούν με τον ίδιο τρόπο.
Advanced

Το Bash παρέχει εκτεταμένα μοτίβα που είναι πιο κοντά στις κανονικές εκφράσεις.

Εισαγωγικά και επέκταση ονόματος αρχείου

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

read -p "Enter something: " user_input
echo $user_input

Αν ο χρήστης εισάγει έναν αστερίσκο (*), τι θα εμφανιστεί; Η λίστα των αρχείων στον τρέχοντα κατάλογο. Πιθανότατα αυτό δεν είναι αυτό που θέλεις να κάνεις.

Ένα άλλο παράδειγμα είναι να περάσεις μοτίβα ως ορίσματα σε προγράμματα που περιμένουν μοτίβα. Το πρόγραμμα tr χρησιμοποιείται συχνά για χειρισμό κειμένου. Για να μετατρέψεις την τιμή μιας μεταβλητής σε πεζά, μπορείς να κάνεις το εξής

var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'

εμφανίζει

my puppet

Ένα συνηθισμένο λάθος είναι να ξεχάσεις να βάλεις εισαγωγικά στα ορίσματα του tr: tr [:upper:] [:lower:]

Caution

Γιατί είναι πρόβλημα αυτό; Τα μοτίβα glob χωρίς εισαγωγικά θα επεκταθούν στη λίστα των αρχείων που αντιστοιχίζονται. Ας υποθέσουμε ότι το άτομο που χρησιμοποιεί το script σου έχει αρχεία με ονόματα p και u και w στον τρέχοντα κατάλογο:

touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]

Αυτό έχει ως αποτέλεσμα ένα σφάλμα!

Το μοτίβο [:upper:] χωρίς εισαγωγικά αντιστοιχίζει οποιοδήποτε από τα :, u, p, e ή r. Το μοτίβο [:lower:] χωρίς εισαγωγικά αντιστοιχίζει οποιοδήποτε από τα :, l, o, w, e ή r. Το Bash αντιστοιχίζει τα αρχεία p και u για το πρώτο μοτίβο, και το αρχείο w για το δεύτερο μοτίβο, και το tr καλείται έτσι

echo "$var" | tr p u w

Και αυτός είναι ο λάθος αριθμός ορισμάτων για το tr.

Απενεργοποίηση της επέκτασης ονόματος αρχείου

Μπορείς να χρησιμοποιήσεις την εντολή set για να απενεργοποιήσεις την επέκταση ονόματος αρχείου.

echo *      # prints a list of files
set -f      # disable filename expansion
echo *      # prints a literal asterisk
set +f      # enable filename expansion
Έλεγχος της επέκτασης ονόματος αρχείου

Υπάρχουν αρκετές ρυθμίσεις για την ενσωματωμένη εντολή shopt που ελέγχουν πώς λειτουργεί η επέκταση ονόματος αρχείου. Μερικές ενδιαφέρουσες είναι:

  • shopt -s nocaseglob: εκτέλεσε αντιστοίχιση χωρίς διάκριση πεζών/κεφαλαίων
  • shopt -s extglob: ενεργοποίησε τα εκτεταμένα μοτίβα
  • shopt -s nullglob: αν δεν αντιστοιχίζεται κανένα αρχείο, αντικατέστησε το μοτίβο με τίποτα. Η προεπιλεγμένη συμπεριφορά είναι να αφήνει το μοτίβο στη θέση του ως κυριολεκτική συμβολοσειρά.
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα