Διεργασίες

exec#

Εγκατάλειψη αυτού του προγράμματος και εκτέλεση άλλου στην ίδια διεργασία.

Η exec αντικαθιστά την τρέχουσα εικόνα διεργασίας με ένα άλλο πρόγραμμα. Δεν επιστρέφει σε επιτυχία - η εκτέλεση συνεχίζεται στο νέο πρόγραμμα, με το ίδιο id διεργασίας, ανοιχτά filehandles υποκείμενα στη σημαία close-on-exec, και όποιο περιβάλλον ίσχυε. Επιστρέφει ψευδές (και θέτει την $!) μόνο όταν ο στόχος δεν μπόρεσε να εκτοξευθεί και εκτελέστηκε άμεσα παρά μέσω του κελύφους. Καταφύγετε στην system όταν θέλετε το παιδί να εκτελεστεί και ο έλεγχος να επιστρέψει.

Σύνοψη#

exec LIST
exec PROGRAM LIST
exec { PROGRAM } LIST

Τι επιστρέφεται#

Τίποτα, σε επιτυχία - η διεργασία έχει αντικατασταθεί και αυτός ο διερμηνέας Perl έχει χαθεί. Σε αποτυχία η exec επιστρέφει ψευδές και αφήνει την αιτία στην $!. Η αποτυχία είναι ανιχνεύσιμη μόνο για τη διαδρομή άμεσου exec· όταν το όρισμα παραδίδεται στο κέλυφος, ένα πρόγραμμα που λείπει αναφέρεται από το ίδιο το κέλυφος και η exec εξακολουθεί να θεωρείται ότι «πέτυχε» στην εκτόξευση του /bin/sh.

exec '/usr/bin/vi', $file
    or die "couldn't exec vi: $!";

Επειδή η exec κανονικά δεν επιστρέφει, η Perl εκπέμπει προειδοποίηση υπό use warnings αν εμφανίζεται σε περιβάλλον κενού ακολουθούμενη από άλλη δήλωση που δεν είναι die, warn ή exit. Είτε χειριστείτε την αποτυχία ενσωματωμένα με or die, είτε τυλίξτε την κλήση σε μπλοκ για να σιωπήσετε την προειδοποίηση:

exec('foo') or die "couldn't exec foo: $!";
{ exec('foo') }; die "couldn't exec foo: $!";

Κέλυφος ή όχι κέλυφος - ο κανόνας της μονής συμβολοσειράς#

Η exec επιλέγει μεταξύ δύο μηχανισμών εκτόξευσης με βάση τη μορφή της LIST:

  • Δύο ή περισσότερα ορίσματα → η exec καλεί την execvp(3) άμεσα με τη λίστα. Δεν εμπλέκεται κέλυφος, καμία ερμηνεία μετα-χαρακτήρων, κανένας διαχωρισμός σε λέξεις.

  • Ακριβώς ένα όρισμα → η exec σαρώνει αυτή τη μοναδική συμβολοσειρά για μετα-χαρακτήρες κελύφους. Αν βρεθούν, ολόκληρη η συμβολοσειρά περνά στο /bin/sh -c για ανάλυση. Αν δεν βρεθούν, η συμβολοσειρά διαχωρίζεται σε λευκούς χαρακτήρες και παραδίδεται απευθείας στην execvp.

Ο κανόνας έχει σημασία τόσο για ορθότητα όσο και για ασφάλεια. Μια συμβολοσειρά ενός ορίσματος που περιέχει κενό αλλά καθόλου μετα-χαρακτήρες διαχωρίζεται σε λέξεις και εκτελείται άμεσα - κάτι που συχνά είναι αυτό που θέλετε. Μια συμβολοσειρά ενός ορίσματος που περιέχει |, >, $, ;, χαρακτήρες glob ή εισαγωγικά περνά μέσω του κελύφους, και το κέλυφος θα επεκτείνει ευχαρίστως μπαλαντέρ, θα ερμηνεύσει μεταβλητές και θα διαχωρίσει με βάση το $IFS.

exec '/bin/echo', 'Your arguments are:', @ARGV;  # direct execvp
exec "sort $outfile | uniq";                     # pipeline → shell
exec "ls /tmp";                                  # no metachars → split + execvp

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

Μορφή έμμεσου αντικειμένου - ορισμός του argv[0], παράκαμψη του κελύφους#

Η τοποθέτηση ενός μπλοκ ή βαθμωτού πριν την LIST (χωρίς κόμμα) λέει στην exec δύο πράγματα ταυτόχρονα: εκτέλεσε αυτό το πρόγραμμα, και πέρασε εκείνα τα ορίσματα ως argv. Το πρώτο στοιχείο της LIST γίνεται το argv[0] - το όνομα που βλέπει το παιδί για τον εαυτό του - το οποίο δεν χρειάζεται να ταιριάζει με τη διαδρομή του προγράμματος.

exec { '/bin/echo' } 'echo', 'hi';   # argv[0] is "echo"
my $shell = '/bin/csh';
exec $shell '-sh';                   # login-shell convention: argv[0] = "-sh"
exec { '/bin/csh' } '-sh';           # same, more direct

Η μορφή έμμεσου αντικειμένου αντιμετωπίζει πάντα την LIST ως λίστα πολλαπλών τιμών, ακόμη και αν έχει ένα μόνο στοιχείο. Αυτό απενεργοποιεί εντελώς τη σάρωση μετα-χαρακτήρων:

my @args = ('echo surprise');
exec @args;              # one element → shell scan → /bin/sh -c 'echo surprise'
exec { $args[0] } @args; # always direct execvp, "echo surprise" is argv[0]

Στη δεύτερη μορφή δεν υπάρχει πρόγραμμα στον δίσκο κυριολεκτικά ονομαζόμενο echo surprise, οπότε η execvp αποτυγχάνει, η exec επιστρέφει ψευδές, και η $! κρατά την αιτία. Αυτή είναι η ασφαλής συμπεριφορά: ο καλών μπορεί να αποφασίσει τι θα κάνει, αντί να εκτελεί το κέλυφος κάτι απροσδόκητο.

Καθολική κατάσταση που επηρεάζει#

  • $! - τίθεται σε αποτυχία στο errno από την υποκείμενη κλήση execvp(3). Έχει νόημα μόνο όταν η exec πράγματι επιστρέφει.

  • $? - μένει αμετάβλητη από την ίδια την exec, αλλά μια αποτυχημένη προσπάθεια μέσω κελύφους μπορεί να αφήσει την κατάσταση εξόδου του κελύφους ορατή σε μια περικλείουσα system.

  • Τα ανοιχτά filehandles της διεργασίας, το περιβάλλον (%ENV), ο τρέχων κατάλογος εργασίας, η umask, οι διαθέσεις σημάτων και τα όρια πόρων μεταφέρονται όλα στο πρόγραμμα αντικατάστασης - η exec αντικαθιστά το πρόγραμμα, όχι τη διεργασία.

Παραδείγματα#

Εκτέλεση προγράμματος με ρητή λίστα ορισμάτων, άμεση execvp:

exec '/usr/bin/git', 'status', '--short'
    or die "exec git: $!";

Σκόπιμη παράδοση γραμμής εντολής στο κέλυφος, με χρήση μετα-χαρακτήρων:

exec "find . -name '*.tmp' -print0 | xargs -0 rm"
    or die "exec pipeline: $!";

Ορισμός διακριτού argv[0] ώστε η γραμμή ps του παιδιού να αναγνωρίζει τον ρόλο του:

exec { '/usr/local/bin/worker' } 'worker[queue=email]', @flags
    or die "exec worker: $!";

Fork-and-exec - το κλασικό μοτίβο για εκτόξευση παιδιού χωρίς αντικατάσταση του εαυτού σας:

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    exec '/usr/bin/gzip', '-9', $file
        or die "exec gzip: $!";
}
waitpid $pid, 0;

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

my @cmd = build_command();           # may contain one element or many
exec { $cmd[0] } @cmd
    or die "exec $cmd[0]: $!";

Οριακές περιπτώσεις#

  • Δεν επιστρέφει ποτέ σε επιτυχία. Οποιοσδήποτε κώδικας μετά από σκέτη exec είναι προσβάσιμος μόνο σε αποτυχία. Υπό use warnings, μια επόμενη δήλωση εκτός των die, warn ή exit πυροδοτεί προειδοποίηση - υπαινιγμός της Perl ότι πιθανώς εννοούσατε system.

  • Παγίδα μονού ορίσματος. Η exec $cmd όπου το $cmd προήλθε από είσοδο χρήστη αποτελεί διάνυσμα έγχυσης κελύφους αν το $cmd περιέχει μετα-χαρακτήρες. Χρησιμοποιήστε τη μορφή έμμεσου αντικειμένου ή περάστε μια προ-διαχωρισμένη λίστα.

  • Ενταμίευση εξόδου. Η Perl προσπαθεί να εκκενώσει handles ανοιγμένα για έξοδο πριν παραδώσει τον έλεγχο στο νέο πρόγραμμα, αλλά αυτό δεν εξασφαλίζεται σε κάθε πλατφόρμα. Αν έχετε εκκρεμή έξοδο σε μη εκκενωμένο handle, θέστε την $| στο επιλεγμένο handle - ή καλέστε τη μέθοδο autoflush σε κάθε IO::Handle - πριν την exec.

  • Μπλοκ END και DESTROY. Η exec δεν εκτελεί μπλοκ END, και δεν καλεί DESTROY στα αντικείμενά σας. Η εικόνα διεργασίας αντικαθίσταται· καθαρισμός σε επίπεδο Perl δεν συμβαίνει ποτέ. Οτιδήποτε χρειάζεται καθαρό τερματισμό (προσωρινά αρχεία, αφαίρεση lockfile, εκκένωση καταγραφών) πρέπει να συμβεί πριν την exec.

  • Close-on-exec. Τα filehandles με τη σημαία FD_CLOEXEC ορισμένη κλείνουν από τον πυρήνα κατά τη διάρκεια της execvp· τα υπόλοιπα μένουν ανοιχτά. Ελέγξτε το με fcntl ή με το κατώφλι filehandle συστήματος $^F.

  • Η αποτυχία μέσω κελύφους είναι αόρατη. Όταν η exec δρομολογείται μέσω /bin/sh -c, ένα στοχοπρόγραμμα που λείπει αναφέρεται από το κέλυφος και η ίδια η exec έχει ήδη αντικατασταθεί από το /bin/sh. Ο κώδικας Perl σας δεν θα δει επιστροφή αποτυχίας.

  • Λίστα ενός ορίσματος χωρίς μετα-χαρακτήρες εξακολουθεί να διαχωρίζεται σε λευκούς χαρακτήρες και να περνά στην execvp - οπότε η exec "ls /tmp" είναι άμεσο exec του /bin/ls με ένα όρισμα, όχι κλήση κελύφους.

  • Κενή LIST είναι σφάλμα κατά την εκτέλεση· δεν υπάρχει πρόγραμμα προς εκτέλεση.

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • system - εκτέλεση προγράμματος και αναμονή του· σχεδόν σίγουρα αυτό που θέλατε αν η exec βρίσκεται στην κορυφή του αρχείου

  • fork - συνδυασμός με την exec στο παιδί για εκτόξευση προγράμματος χωρίς αντικατάσταση του γονέα

  • wait - συλλογή ενός παιδιού που παράγεται από fork + exec

  • die - τερματισμός του τρέχοντος προγράμματος με σφάλμα· ο ιδιωματικός σύντροφος του exec ... or die

  • $! - λόγος σφάλματος που τίθεται όταν αποτυγχάνει το άμεσο exec

  • $| - σημαία αυτόματης εκκένωσης· θέστε πριν την exec ώστε καμία ενταμιευμένη έξοδος να μη χαθεί όταν αντικαθίσταται η εικόνα διεργασίας