Bash: σωστά quotes, filenames και διαχείριση αποτυχιών — DedSec Project
Τα ονόματα αρχείων είναι δεδομένα, όχι σύνταξη της shell Αντιμετώπισε κάθε filename ως αυθαίρετο κείμενο Ένα path μπορεί να έχει κενά, αστερίσκους ή να αρχίζει με παύλα. Η μη περικλεισμένη…
Τα ονόματα αρχείων είναι δεδομένα, όχι σύνταξη της shell
Αντιμετώπισε κάθε filename ως αυθαίρετο κείμενο
Ένα path μπορεί να έχει κενά, αστερίσκους ή να αρχίζει με παύλα. Η μη περικλεισμένη μεταβλητή στη shell μπορεί να προκαλέσει word splitting και wildcard expansion, αλλάζοντας απρόβλεπτα τα αρχεία που θα επεξεργαστεί μια εντολή. Βάλε quotes γύρω από μεταβλητές path και χρησιμοποίησε -- όπου υποστηρίζεται για ονόματα που μοιάζουν με options.
#!/usr/bin/env bash
set -euo pipefail
root="${1:?provide an existing directory}"
if [[ ! -d "$root" ]]; then
printf 'not a directory: %s\n' "$root" >&2
exit 2
fi
root="$(cd -- "$root" && pwd -P)"
find "$root" -maxdepth 1 -type f -print0 |
while IFS= read -r -d '' file; do
printf 'file: %q\n' "$file"
done
Γιατί χρειαζόμαστε NUL delimiter
Σε Unix ακόμη και newline επιτρέπεται μέσα σε filename. Η επεξεργασία ανά γραμμή μπορεί επομένως να κόβει ένα όνομα στη μέση. Τα -print0 και read -d '' διατηρούν ακριβή όρια. Το παράδειγμα απλώς εμφανίζει paths και δεν τα τροποποιεί.
Τα όρια του strict mode
Το set -e δεν συμπεριφέρεται ίδια σε conditionals, pipelines και command substitutions· δεν είναι μηχανισμός rollback. Το pipefail βοηθά σε αποτυχίες pipeline, αλλά διεργασίες background χρειάζονται explicit wait. Φτιάξε δοκιμές με κενό directory, όνομα με spaces και όνομα που περιέχει newline.
Τι πρέπει να αποδειχθεί
Κάθε πραγματικό αρχείο πρέπει να αναφέρεται ακριβώς μία φορά. Σημείωσε Bash version και OS, επειδή όλα τα shells δεν υποστηρίζουν [[...]] ή read -d. Μην εκτελείς Bash-specific κώδικα ως απλό POSIX sh χωρίς έλεγχο.
Δοκίμασε directory arguments που μοιάζουν με options
Τα quotes κρατούν ένα argument ενιαίο, αλλά δεν εμποδίζουν το πρόγραμμα να ερμηνεύσει αρχική παύλα ως option. Το starting point του find κανονικοποιείται παρακάτω σε απόλυτη διαδρομή, ώστε να δέχεται directory που αρχίζει με -. Το -- στο cd ανήκει στο Bash builtin· άλλα εργαλεία χρειάζονται ξεχωριστό έλεγχο υποστήριξης.
Σε άδειο προσωρινό directory δημιούργησε αρχεία two words.txt, star*.txt και όνομα που περιέχει newline. Η έξοδος %q είναι shell-escaped παρουσίαση και δεν είναι γενικό line-based format ανταλλαγής. Μην τη δίνεις σε eval. Αν άλλο πρόγραμμα χρειάζεται πραγματικά filenames, διατήρησε NUL delimiters αντί για την οπτική αναπαράσταση.
Το pipeline δεν είναι συναλλαγή
Αν ο consumer σταματήσει νωρίς, ο producer μπορεί να αποτύχει με SIGPIPE. Με pipefail αποτυγχάνει τότε ολόκληρο το pipeline, ακόμη κι αν καταναλώθηκε το αναμενόμενο πρώτο αποτέλεσμα. Εξέτασε τι απαιτεί η σύμβαση της εφαρμογής αντί για γενικό || true. Ήδη γραμμένα δεδομένα παραμένουν μετά από επόμενο error. Προσωρινό output και τελικό rename είναι χωριστός σχεδιασμός, όχι λειτουργία που παρέχει το set -e.
Πηγή: Bash word splitting.
Κατάσταση pipeline και σκόπιμος χειρισμός σφαλμάτων
Ένα pipeline κανονικά αναφέρει την έξοδο της τελευταίας εντολής. Προηγούμενο στάδιο μπορεί να αποτύχει ενώ ο τελικός μορφοποιητής πετυχαίνει. Το pipefail αλλάζει την κατάσταση αλλά δεν αποφασίζει ποιοι κωδικοί αναμένονται. Εργαλεία όπως grep διαχωρίζουν «χωρίς αντιστοίχιση» από πραγματικό σφάλμα, οπότε μια καθολική αποτυχία απορρίπτει έγκυρο κενό αποτέλεσμα.
Το τοπικό δείγμα έχει αποτυχημένο παραγωγό και επιτυχημένο καταναλωτή. Εξετάζει άμεσα την κατάσταση αντί να θεωρεί το set -e πλήρες σύστημα εξαιρέσεων. Χρειάζεται ιδιαίτερη σαφήνεια σε συνθήκες, υποκελύφη και παγίδες καθαρισμού.
set -u
set +o pipefail
(exit 7) | cat >/dev/null
normal=$?
set -o pipefail
(exit 7) | cat >/dev/null
strict=$?
[[ "$normal" == 0 && "$strict" == 7 ]] || exit 1
printf 'pipeline statuses: %s %s\n' "$normal" "$strict"
Δοκίμασε χωριστά κενή αναζήτηση και μη αναγνώσιμο αρχείο. Αποθήκευσε αμέσως την κατάσταση πριν την αντικαταστήσει άλλη εντολή. Σε ονόματα από διεργασία διατήρησε διαχωρισμό NUL όπου υποστηρίζεται· οι αλλαγές γραμμής δεν είναι σαφείς όταν ανήκουν στο όνομα.