2010-04-26 15 views
9
#!/usr/bin/env perl 
use warnings; use strict; 
use 5.012; 
use IPC::System::Simple qw(system); 

system('xterm', '-geometry', '80x25-5-5', '-bg', 'green', '&'); 

say "Hello"; 
say "World"; 

Próbowałem to uruchomić xterm polecenie w tle, ale to nie działa:Jak mogę uruchomić polecenia systemowe Perla w tle?

Nie ścieżka absolutna znaleziono skorupy: &

Jaki byłby prawo sposób, aby to zadziałało?

+0

testowany tutaj wydaje się jakoś pracę dla mnie. co próbujesz osiągnąć? – xenoterracide

+0

czy ma otworzyć terminal, a następnie wydrukować hello world w terminalu? – xenoterracide

+0

lub próbujesz wykonać asynchroniczne wywołanie, aby otworzyć terminal? – xenoterracide

Odpowiedz

19

system funkcja Perl posiada dwa tryby:

  1. biorąc pojedynczy łańcuch i przekazania go do powłoki poleceń umożliwiające znaki specjalne mają być przetwarzane
  2. biorąc listę ciągi, wykonywanie pierwszego i przekazywanie pozostałych łańcuchów jako argumentów

W pierwszej postaci należy uważać, aby unikać znaków, które mogą mieć specjalne znaczenie dla powłoki. Druga forma jest ogólnie bezpieczniejsza, ponieważ argumenty są przekazywane bezpośrednio do programu, który jest wykonywany bez udziału powłoki.

W twoim przypadku wydajesz się mieszać te dwie formy. Znak & ma tylko znaczenie "uruchom ten program w tle", jeśli zostanie przekazany do powłoki. W twoim programie znak ampersand jest przekazywany jako piąty argument do polecenia xterm.

Jakob Kruse powiedział, że prostą odpowiedzią jest użycie pojedynczego ciągu znaków o numerze system. Jeśli którykolwiek z argumentów pochodzi z niezaufanego źródła, musisz użyć cytowania lub ucieczki, aby je zabezpieczyć.

Jeśli wolisz skorzystać z formularza wielu argumentów to musisz zadzwonić fork() a następnie prawdopodobnie używać exec() zamiast system().

+4

** tldr; ** 'system (" ./ script.sh $ arg1 &"); 'zamiast' system ("./ script.sh", "$ arg1", "&");' – Rombus

13

Należy zauważyć, że forma lista system jest specjalnie tam nie znaków traktują jak & jak Shell meta-znaków.

Od odpowiedzi perlfaq8 „s do How do I start a process in the background?


(nadesłane przez brian d Foy)

Nie ma jednego sposobu na uruchomienie kodu w tle, dzięki czemu nie trzeba czekać aby to się skończyło, zanim twój program przejdzie do innych zadań. Zarządzanie procesami zależy od konkretnego systemu operacyjnego, a wiele technik jest w perlipc.

Kilka modułów CPAN może być w stanie pomóc, w tym IPC::Open2 lub IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background i Win32::Process. Istnieje wiele innych modułów, których możesz użyć, więc sprawdź te przestrzenie nazw również w innych opcjach.

Jeśli jesteś na systemie Unix-like, może być w stanie uciec z wywołania systemowego, gdzie położyć & na końcu polecenia:

system("cmd &") 

Można również spróbować użyć widelca , jak opisano w perlfunc (chociaż jest to to samo, co wiele modułów zrobi dla ciebie).

stdin, stdout i stderr są wspólne

Zarówno główny proces w tło, a jeden (proces "dziecko") mają te same stdin, stdout i stderr uchwytów plików. Jeśli obaj spróbują uzyskać do nich dostęp od razu, mogą się zdarzyć dziwne rzeczy. Możesz je zamknąć lub ponownie otworzyć dla dziecka. Możesz obejść ten problem otwierając potok (patrz open in perlfunc), ale w niektórych systemach oznacza to, że proces potomny nie może przeżyć nadrzędnego. Sygnały Sygnał SIGCHLD i ewentualnie SIGPIPE również będą potrzebne SIGCHLD jest wysyłany, gdy proces w tle zakończy się. SIGPIPE jest wysyłany, gdy piszesz do uchwytu pliku, którego proces potomny został zamknięty (niepobijany SIGPIPE może spowodować, że twój program cicho umrze). To nie jest problem z systemem ("cmd &").

Zombies

Musisz być przygotowany, aby "zbierać" procesu dziecko, kiedy to kończy.

$SIG{CHLD} = sub { wait }; 

$SIG{CHLD} = 'IGNORE'; 

Można również użyć podwójnego widelca. Natychmiast poczekasz() na pierwsze dziecko, a demon init będzie czekał() na wnuczka po jego wyjściu.

unless ($pid = fork) { 
    unless (fork) { 
     exec "what you really wanna do"; 
     die "exec failed!"; 
    } 
    exit 0; 
} 
waitpid($pid, 0); 

Zobacz sygnałów perlipc dla innych przykładów kodu, aby to zrobić. Zombie nie stanowią problemu w systemie ("prog &").

0

To nie jest czysto wyjaśnienie dla Perla. Ten sam problem występuje pod C i innymi językami.

najpierw zrozumieć, co robi polecenie System :

  1. Forks
  2. pod wezwaniem procesowego dziecko exec
  3. Proces rodzic czeka rozwidloną procesie potomnym, aby zakończyć

Nie ma znaczenia, czy przekazujesz wiele argumentów czy jeden argument. Różnica polega na tym, że w przypadku wielu argumentów polecenie jest wykonywane bezpośrednio.Z jednym argumentem, komenda jest owinięty przez powłokę, a na końcu wykonywane jako:

/bin/sh -c your_command_with_redirections_and_ambersand 

Po przejechaniu polecenia jako some_command PAR1 par2 &, to między interpretera Perl i polecenia jest sh lub bash proces używany jako opakowanie i czeka na zakończenie some_command. Twój skrypt czeka na interpreter powłoki i nie jest wymagany dodatkowy waitpid, ponieważ funkcja Perla system robi to za Ciebie.

Gdy chcesz wdrożyć ten mechanizm bezpośrednio w skrypcie, należy:

  1. Użyj funkcji widelec. Zobacz przykład: http://users.telenet.be/bartl/classicperl/fork/all.html
  2. Pod warunkiem dziecka (if), użyj funkcji exec. Twój użytkownik jest podobny do system, patrz instrukcja. Zauważ, exec powoduje, że dziecko przetwarza program/treść/dane przez wykonaną komendę.
  3. W stanie nadrzędnym (jeśli fork kończy się z niezerowym), należy użyć waitpid, używając pid zwrócony przez funkcję fork.

Dlatego można uruchomić proces w tle. Mam nadzieję, że to jest proste.

Najprostszy przykład:

if (my $pid = fork) { #exits 0 = false for child process, at this point is brain split 
    # parent ($pid is process id of child) 
    # Do something what you want, asynchronously with executed command 
    waitpid($pid); # Wait until child ends 
    # If you don't want to, don't wait. Your process ends, and then the child process will be relinked 
    # from your script to INIT process, and finally INIT will assume the child finishing. 
    # Alternatively, you can handle the SIGCHLD signal in your script 
} 
else { 
    # Child 
    exec('some_command arg1 arg2'); #or exec('some_command','arg1','arg2'); 
    #exit is not needed, because exec completely overwrites the process content 
} 
+0

dziękuję Peter za bardzo dobre poprawki:) – Znik

Powiązane problemy