Απάντηση 1:

Προγραμματισμός μάθησης: Ποια είναι η διαφορά ανάμεσα στο "#! / usr / bin / env bash "και" #! / usr / bin / bash ";

Η ενέργεια που περιγράφει ο Ondrej Vagner στην απάντησή του (ότι το λειτουργικό του σύστημα μπορεί να βρει το πρόγραμμα bash χωρίς να γνωρίζει πού φυλάσσεται) συμβαίνει να δουλεύει εξαιτίας μιας παρενέργειας του πώς καλεί το σύστημα exec (2) και το πρόγραμμα env (1) αλλά η απάντησή του στην πραγματικότητα δεν είναι εξ ολοκλήρου σωστή στην περιγραφή της διαφοράς.

Πάρα πολύ η πίστη του είναι μια κοινή παρεξήγηση από πολλούς ανθρώπους που ανακαλύπτουν μια ενέργεια / περίπτωση χρήσης και δεν καταλαβαίνουν πλήρως που ήρθε πρώτος (ή όπως θέλουμε να πούμε στο «biz» τι είναι το «bit υψηλής τάξης», καθώς και για να είμαστε ειλικρινείς για αυτό στο Unix, κάποιος που χρησιμοποιεί Unix μόνο από το 2011 είναι λίγο νεοφερμένος στον κόσμο του Unix και ίσως δεν κατανοεί ακόμα πλήρως τον πραγματικό λόγο για ορισμένες από αυτές τις συμβάσεις και λειτουργίες).

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

Πριν συνεχίσω, θα ήθελα να σας υπενθυμίσω ότι από την αρχή της ανάπτυξής της στα τέλη της δεκαετίας του 1960 / αρχές της δεκαετίας του 70, η οικογένεια λειτουργικών συστημάτων Unix περιέχει ένα εξαιρετικά χρήσιμο εργαλείο τεκμηρίωσης που ονομάζεται man pages. με την εντολή man (1). [1] Προτείνω ιδιαίτερα να εξοικειωθείτε με αυτό το υπέροχο εργαλείο, αλλά το παλιό ρητό "Google είναι ο φίλος σας" οι κανόνες ισχύουν πάντα επίσης.

Πρώτον, θα πρέπει να καταλάβετε πώς ξεκινούν τα προγράμματα (δηλ. Exec (2)) και στη συνέχεια θα συζητήσουμε το #! (συχνά, όπως θα αναφερθώ στη συνέχεια ως "shebang"). και τελικά αυτό που κάνει το πρόγραμμα env (1).

Έτσι ... όταν το λειτουργικό σύστημα απαιτείται να φορτώσει ένα πρόγραμμα στη μνήμη, πρέπει να βρει το δυαδικό αρχείο που σχετίζεται με αυτό το πρόγραμμα και να αντιγράψει «αρκετά από αυτό στη μνήμη για να ξεκινήσει το τρέχον πρόγραμμα» και στη συνέχεια να αναγκάσει τον επεξεργαστή να μεταβεί στο πρόγραμμα " ξεκινήστε ". Η κλήση συστήματος Unix exec (2) (η οποία έχει πολλές γεύσεις που δεν θα συζητήσω εδώ) είναι η βασική λειτουργία που χρησιμοποιείται για να κάνει το ίδιο. Μία από τις παραμέτρους της κλήσης λειτουργίας είναι το όνομα του αρχείου προγράμματος που πρέπει να φορτωθεί το λειτουργικό σύστημα.

Δεδομένου ότι τα σύγχρονα λειτουργικά συστήματα όπως το UNIX διαθέτουν ιεραρχικό σύστημα αρχείων [2], μπορούμε να περιγράψουμε την «διαδρομή» που χρειάζεται το σύστημα να ακολουθήσει μέσω του συστήματος αρχείων για να βρει το αρχείο του προγράμματος. Αν το δίνουμε την ακριβή διαδρομή (και υπάρχει σωστό αρχείο προγράμματος σε αυτή τη θέση), το αρχείο φορτώνεται στη μνήμη και ξεκινάει. Εάν όχι, επιστρέφει σφάλμα από το λειτουργικό σύστημα στον καλούντα του exec (2). Αλλά συχνά δεν γνωρίζουμε τι να αποθηκεύουμε εκεί το αρχείο τύπου (είναι ένα πραγματικό 'εκτελέσιμο' πρόγραμμα ή όχι) και πιθανότατα δεν ξέρουμε πού φυλάσσεται το αρχείο, οπότε θέλουμε το λειτουργικό σύστημα να το βρει για μένα και ελέγξτε το.

Τώρα σκεφτείτε για ένα λεπτό για το πώς οι άνθρωποι εργάζονται σε αντίθεση με το πώς οι υπολογιστές λειτουργούν. Σκεφτείτε να πάρετε μια ψωμί ψωμιού στο σούπερ μάρκετ. Γράφετε στη λίστα ψώνια σας «ψωμί» όχι «αγορά Dave's, αρτοποιείο, διάδρομο 2, πρώτο ράφι, 3 πόδια από το τέλος, ψωμί. «Φανταστείτε έναν ρομποτικό αγοραστή, αν βλέπει« ψωμί »πρέπει να του δοθούν όλες οι άλλες πληροφορίες για να βρει το στοιχείο« ψωμί ».

Το ίδιο ισχύει για το αρχείο προγράμματος. Αν πληκτρολογούμε απλά "εντολή" (στην περίπτωσή σας, 'bash' αλλά μπορεί να είναι 'python' ή 'awk' ή 'perl' ή ένα από τα πολλά προγράμματα εντολών που παρέχει το Unix.

Έτσι τίθεται το ερώτημα: Πού είναι αυτό το αρχείο στην ιεραρχία του συστήματος αρχείων; Έτσι δημιουργήθηκε η ιδέα της μεταβλητής PATH, η οποία λέει στη λειτουργία του συστήματος exec (2) να εξετάσει τον πρώτο χαρακτήρα της δοθείσας εντολής, αν δεν είναι μια «κάθετο» (/) και στη συνέχεια να πάρει μια συμβολοσειρά από το PATH, prepend στην εντολή, χωρίζοντάς την με κάθετο και δοκιμάζοντας το ως νέο όνομα αρχείου για να διαπιστώσετε αν το λειτουργικό σύστημα εντοπίζει ένα "εκτελέσιμο αρχείο" στη θέση που ορίζεται από αυτό το νέο όνομα αρχείου. Σημειώστε επίσης ότι το περιβάλλον PATH περιέχει μια σειρά από διαφορετικές διαδρομές (διαχωρισμένες με κόλον) για να προσπαθήσετε να προετοιμαστείτε για να βρείτε ένα "εκτελέσιμο αρχείο" πριν το OS εγκαταλείψει και επιστρέψει ένα σφάλμα.

Τώρα, αυτό το σχήμα εκτελέσιμου αρχείου θέσης έχει κάποια προβλήματα, το μεγαλύτερο είναι αν υπάρχουν περισσότερα από ένα όνομα αρχείου 'bash' στην περίπτωσή σας, που μπορεί να βρεθεί στα διαφορετικά ονόματα διαδρομής που καθορίζονται, ποια έκδοση του αρχείου θα πρέπει να το OS χρήση? Η παραδοσιακή απάντηση Unix ονομάζεται «πρώτη εφαρμογή», ​​ότι είναι το πρώτο μονοπάτι που ταιριάζει με τους δεσμούς OS και χρησιμοποιείται ως εκτελέσιμο αρχείο. Αυτό το σχήμα σημαίνει ότι ταξινομούνται τα διαδρομές που καθορίζονται στη μεταβλητή PATH.

Το δεύτερο ζήτημα είναι ότι αυτό που συμβαίνει εάν το αρχείο που περιγράφουμε με αυτό το νέο όνομα αρχείου δεν είναι ένα "δυαδικό" εκτελέσιμο πρόγραμμα (δηλαδή ένα δυαδικό αρχείο οδηγιών μηχανής για αυτήν την CPU), αλλά αντίθετα κάτι άλλο, όπως λέει ένα κείμενο ASCII αρχείο. Αυτό είναι όπου οι ιδέες ενός "μαγικού αριθμού" Unix και της "shebang" σύμβασης παίζουν ρόλο.

Με το Unix, τα πρώτα μερικά byte που είναι αποθηκευμένα μέσα στο αρχείο προσδιορίζουν τα περιεχόμενα του αρχείου. Αυτό είναι διαφορετικό από το άλλο λειτουργικό σύστημα της ημέρας, το οποίο ακολούθησε μια σύμβαση ότι το περιεχόμενο του αρχείου καθορίστηκε από το όνομα του αρχείου. Για παράδειγμα, τα παλαιά λειτουργικά συστήματα DEC χρησιμοποιούσαν την μεταγενέστερη σύμβαση (δηλαδή VMS, TOP και το πιο σημαντικό RT11, τα οποία η Microsoft συνέχιζε να μιμείται από την κληρονομιά RT11 → CP / M → DOS).

Δεδομένου ότι το Unix κωδικοποίησε τον "τύπο αρχείου" στα περιεχόμενα του αρχείου [3], αυτό σήμαινε ότι η υλοποίηση της κλήσης συστήματος exec (2) μέσα στο λειτουργικό σύστημα θα μπορούσε να φτάσει στα πρώτα δυφία του αρχείου για να καθορίσει τι πρέπει να κάνει . Αρχικά, το λειτουργικό σύστημα Unix έβλεπε να δει αν ήταν ένα αρχείο a.out, το οποίο ήταν η πρώτη μορφή αρχείου UNIX για δυαδικά εκτελέσιμα αρχεία PDP-11. Αλλά ξεκινώντας στα τέλη της δεκαετίας του '70, η ομάδα UC Berkeley υλοποίησε / πρόσθεσε ένα χαρακτηριστικό / σύμβαση στην κλήση exec. Εάν τα πρώτα δύο byte ήταν οι χαρακτήρες, # !, τα επόμενα byte θα ερμηνευτούν από το λειτουργικό σύστημα UNIX ως νέο όνομα διαδρομής του προγράμματος σε exec (2) αντί [4] και το τρέχον αρχείο που μόλις είχε που ανοίχτηκε από το UNIX, θα παραδοθεί ως το πρότυπο του δεύτερου προγράμματος [για περισσότερες πληροφορίες σχετικά με τις εισόδους / εξόδους βλέπε επίσης: Εισαγωγή / έξοδος αρχείου C - Wikipedia].

Αυτή η νέα επέκταση στην κλήση συστήματος UNIX exec (2) επέτρεψε να δημιουργηθούν εκτελέσιμα scripts κελύφους. Χρησιμοποιήθηκε επίσης ως εναλλακτικά κελύφη που εμφανίστηκαν όπως το UCB csh (1) και αργότερα το Gnu's bash (1) και το shebang τελικά χρησιμοποιήθηκε για να υποστηρίξει «μικρές γλώσσες» όπως awk (1), perl (1) και python ) όταν εμφανίστηκαν.

Εντάξει, λοιπόν, τι έχει να κάνει με το env (1) και την παρενέργεια; Όπως περιγράψαμε προηγουμένως, το exec (2) υποστηρίζει μια συμβολοσειρά που ονομάζεται PATH, η οποία χρησιμοποιείται για να αποφασίσει ποια ονόματα διαδρομών να προετοιμαστούν για την εντολή για να αναζητήσουν ένα εκτελέσιμο αρχείο. Αρχικά, ο καθορισμός αυτής της συμβολοσειράς δεν ήταν εύκολο να γίνει, αλλά πολλοί άνθρωποι ήθελαν κάποια ευελιξία να το πράξουν. Ομοίως, το λειτουργικό σύστημα ήθελε κάποιον τρόπο να περάσει πληροφορίες στον τρέχοντα προγραμματισμό, ο οποίος ήταν ορίσιμος από τον χρήστη, αλλά δεν έπρεπε να ξαναγραφεί κάθε φορά που ξεκίνησε ένα πρόγραμμα. Έτσι, ξεκινώντας με την έβδομη έκδοση του Unix, δημιουργήθηκε η ιδέα του προγράμματος "περιβάλλον" και δημιουργήθηκε ένα σχέδιο για να μεταβιβαστούν οι συμβολοσειρές ASCII στο πρόγραμμα που ονομάζονταν μεταβλητές περιβάλλοντος που ορίστηκαν από το χρήστη. με το PATH είναι ένα από τα πιο σημαντικά καθώς επέτρεψε να περάσει τις συμβολοσειρές των οδών του αρχείου για να προσπαθήσει με την κλήση exec (2) (πριν από αυτό, ο PATH καθορίστηκε από το λειτουργικό σύστημα και ήταν πιο δύσκολο να αλλάξει).

Το τότε νέο χαρακτηριστικό «περιβάλλοντος» ήταν αρκετά δημοφιλές, αλλά σχεδόν αμέσως οι άνθρωποι ήθελαν έναν τρόπο να αλλάξουν μία από τις δύο μεταβλητές περιβάλλοντος μόνο για να εκτελέσουν μια συγκεκριμένη εντολή, αλλά δεν πρέπει να θυμούνται / επαναχρησιμοποιούνται με εντολή μετά από αυτό. Έτσι, η εντολή env (1) δημιουργήθηκε με τη σύνταξη:

env EVAR1 = evar1_value .. EVARN = πρόγραμμα evarn_value p1 ... pn

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

Λοιπόν, σκεφτείτε τι συμβαίνει. καθώς η εντολή γίνεται:

env πρόγραμμα 

ή στην περίπτωσή σας: env bash

Η εντολή env δεν αλλάζει το περιβάλλον, αλλά απλά αναγκάζει το πρόγραμμα να καλείται μέσω exec (2) χρησιμοποιώντας τους κανόνες επέκτασης διαδρομής. το οποίο όπως παρατηρεί ο Άνδρεος σημαίνει ότι εάν έχετε μια πλούσια διαδρομή με πολλά προαιρετικά μονοπάτια για να δοκιμάσετε, μπορείτε να τοποθετήσετε bash κάπου αλλού από το / usr / bin [όπου τα κοχύλια όπως bash ζούσαν αρχικά όταν δημιουργήθηκε από το έργο Gnu] και το εκτελέσιμο δυαδικό αρχείο bash θα βρεθεί από το λειτουργικό σύστημα για το πρόγραμμα κλήσης.

Επιπλέον, αυτό είναι συνήθως λιγότερο θέμα, καθώς άλλες μορφές του shebang θα λειτουργήσουν, αλλά το περιβάλλον στο αρχείο script όπως περιγράφηκε παραπάνω δημιουργήθηκε όταν η Python ήρθε στη UNIX σκηνή επειδή η Python ήταν συχνά ιδιωτικά εγκατεστημένη και έτσι δεν στο PATH κάθε χρήστη, καθώς και οι πραγματικοί κατάλογοι αρχείων που εγκατέστησε συχνά η Python συχνά ποικίλλουν. Αυτό είναι ένα λιγότερο θέμα σήμερα, καθώς οι διερμηνείς που χρησιμοποιούνται για εκτελέσιμα αρχεία κειμένου είναι συχνότερα σε στάνταρ θέσεις για εκτελέσιμους καταλόγους προγραμμάτων.

[1] Μια από τις καλύτερες περιγραφές του ποιος λειτουργεί και πώς να το χρησιμοποιήσει προέρχεται από το Πανεπιστήμιο της Ιντιάνα: Χρησιμοποιώντας την εντολή του ανθρώπου Unix για να διαβάσετε τις σελίδες χειρωνακτικής. FWIW: Το Linux είναι μια σύγχρονη εφαρμογή του Unix, όπως περιγράψαμε αλλού, αλλά αυτή η δυνατότητα είναι ακόμα διαθέσιμη όταν το προαιρετικό χαρακτηριστικό Windows Subsystem for Linux των Windows 10 είναι ενεργοποιημένο πληκτρολογώντας στο PowerShell ως Διαχειριστής: Enable-WindowsOptionalFeature -Online -FeatureName Microsoft -Συστήματα Windows-Υποσύστημα-Linux

[2] Μερικοί από μας θυμούνται και έχουν χρησιμοποιήσει συστήματα που δεν είχαν ιεραρχικό σύστημα αρχείων. Ακόμα και τα μικρά λειτουργικά συστήματα που χτίστηκαν στη δεκαετία του 1970, όπως το DOS της Microsoft, ήρθαν στην ιδέα αργά και επίσης ένας από τους λόγους για τους οποίους η Microsoft χρησιμοποίησε την backslash ως διαχωριστικό αρχείου της, όχι τον χαρακτήρα που ήταν ήδη τυποποιημένος στα μεγαλύτερα συστήματα εκείνη την εποχή.

[3] Για περισσότερες λεπτομέρειες σχετικά με πράγματα όπως η ιδέα πίσω από τους τύπους αρχείων και τη μαγεία δείτε τις εντολές: "man 5 magic" και "man 1 file".

[4] Οι χαρακτήρες εκείνοι που επιλέχτηκαν επειδή το κέλυφος Unix, αν ο πρώτος χαρακτήρας είναι ένα "σημάδι αριθμού" (# - ή "σήμα κατατεθέν" ή "σημάδι λίρας"), τότε αγνοούνται όλοι οι χαρακτήρες μέχρι την πρώτη γραμμή ένα "σχόλιο", και αλλιώς δεν ασκείται από το κέλυφος.

Επεξεργάστηκε στις 5/2/2019 για να διορθώσει μερικά λάθη και να αποσαφηνίσει κάποια γλώσσα. Δυσλεξία-R-Me


Απάντηση 2:

Η διαφορά είναι μικρή, αλλά σημαντική.

#! / usr / bin / bash

Αυτό λέει στο σύστημα, "Εκτέλεση του εκτελέσιμου bash που βρίσκεται στο / usr / bin."

#! / usr / bin / env bash

Αυτό λέει στο σύστημα, "Μάθετε από το περιβάλλον σας όπου εγκαθίσταται το εκτελέσιμο bash και εκτελέστε το από εκεί".

Στο PC μου, το bash είναι εγκατεστημένο στο / bin / bash. Θεωρείται από τους διαχειριστές συντήρησης ότι είναι αρκετά κρίσιμο για το σύστημα να είναι εκεί. Αν προσπάθησα να εκτελέσω μια δέσμη ενεργειών με το / usr / bin / bash shebang, θα αποτύχει. Από την άλλη πλευρά, / usr / bin / env τρέχει απλά το bash που το σύστημά μου γνωρίζει.