Chcę rozpocząć proces (np. MyCommand) i uzyskać jego pid (aby pozwolić go później zabić).
Próbowałem ps i filtruj według nazwy, ale nie mogę rozróżnić procesu według nazw
myCommand
ps ux | awk '/<myCommand>/ {print $2}'
Ponieważ nazwy procesów nie są unikalne.
Mogę uruchomić proces przez:
myCommand &
Odkryłem, że mogę uzyskać ten PID poprzez:
echo $!
Czy jest jakieś prostsze rozwiązanie?
Z przyjemnością wykonam moją komendę i otrzymam jej PID w wyniku polecenia z jednej linii.
myprogram
byłoby niesamowite&
jako poprzedniej linii, w przeciwnym razie echo zasadniczo zwróci puste.command & echo $!
zawiesza wykonanie na tym etapie :(Zawiń polecenie małym skryptem
źródło
Możesz użyć
sh -c
i,exec
aby uzyskać PID polecenia nawet przed jego uruchomieniem.Aby rozpocząć
myCommand
, aby wydrukować jego PID, zanim zacznie działać, możesz użyć:Jak to działa:
To uruchamia nową powłokę, drukuje PID tej powłoki, a następnie używa
exec
wbudowanego do zastąpienia powłoki poleceniem, upewniając się, że ma ten sam PID. Kiedy twoja powłoka uruchamia polecenie zexec
wbudowanym narzędziem, to faktycznie staje się tym poleceniem , a nie bardziej powszechnym zachowaniem polegającym na tworzeniu nowej kopii, która ma swój własny PID, a następnie staje się poleceniem.Uważam, że jest to o wiele prostsze niż alternatywy obejmujące asynchroniczne wykonywanie (z
&
), kontrolę zadań lub wyszukiwanie za pomocąps
. Te podejścia są w porządku, ale chyba że masz konkretny powód, aby z nich korzystać - na przykład być może polecenie już działa, w takim przypadku szukanie PID lub sterowanie zadaniami miałoby sens - sugeruję rozważenie tego w pierwszej kolejności. (I na pewno nie rozważyłbym napisania złożonego skryptu lub innego programu, aby to osiągnąć).Ta odpowiedź zawiera przykład tej techniki.
Części tego polecenia można czasami pominąć, ale nie zwykle.
Nawet jeśli używana powłoka jest w stylu Bourne'a, a zatem obsługuje
exec
wbudowaną semantykę, ogólnie nie powinieneś próbować unikaćsh -c
(lub równoważnego) tworzenia nowego, osobnego procesu powłoki w tym celu, ponieważ:myCommand
, nie będzie ona czekała na uruchomienie kolejnych poleceń.sh -c 'echo $$; exec myCommand; foo
nie będzie mógł uruchomić sięfoo
po zamianie namyCommand
. O ile nie piszesz skryptu, który uruchamia to jako ostatnie polecenie, nie możesz po prostu użyćecho $$; exec myCommand
w powłoce, w której uruchamiasz inne polecenia.(echo $$; exec myCommand)
może być ładniejszy niż składniowosh -c 'echo $$; exec myCommand'
, ale po uruchomieniu$$
wewnątrz(
)
, daje PID powłoki macierzystej, a nie samej podpowłoce. Ale to PID podpowłoki będzie PID nowego polecenia. Niektóre powłoki zapewniają własne nieprzenośne mechanizmy wyszukiwania PID podpowłoki, których można użyć do tego celu. W szczególności, w Bash 4 ,(echo $BASHPID; exec myCommand)
działa.Na koniec zauważ, że niektóre powłoki wykonają optymalizację , uruchamiając polecenie tak, jakby do
exec
(tj. Najpierw rezygnują z rozwidlenia), gdy wiadomo, że powłoka nie będzie musiała nic robić później. Niektóre powłoki próbują to zrobić za każdym razem, gdy jest to ostatnie polecenie do uruchomienia, podczas gdy inne zrobią to tylko wtedy, gdy nie będzie żadnych innych poleceń przed lub po poleceniu, a inne w ogóle tego nie zrobią. Efekt jest taki, że jeśli zapomnisz napisaćexec
i po prostu użyć,sh -c 'echo $$; myCommand'
to czasami da ci odpowiedni PID w niektórych systemach z niektórymi powłokami. Odradzam poleganie na takim zachowaniu , a zamiast tego zawsze uwzględnianie,exec
kiedy tego potrzebujesz.źródło
myCommand
muszę ustawić wiele zmiennych środowiskowych w moim skrypcie bash. Czy zostaną one przeniesione do środowiska, w którym uruchomionoexec
polecenie?exec
polecenie. Jednak to podejście nie działa, gdymyCommand
uruchamiane są inne procesy, z którymi trzeba pracować; kiedy wydajemykill -INT <pid>
gdziepid
uzyskano w ten sposób, sygnał nie dociera do podprocesów rozpoczętych przezmyCommand
, natomiast jeśli uruchomięmyCommand
w bieżącej sesji i Ctrl + C, sygnały propagują się poprawnie.sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
Nie znam prostszego rozwiązania, ale nie używam $! wystarczająco dobry? Zawsze możesz przypisać wartość do innej zmiennej, jeśli będziesz jej potrzebować później, jak powiedzieli inni.
Na marginesie, zamiast pipowania z ps możesz użyć
pgrep
lubpidof
.źródło
użyj exec ze skryptu bash po zarejestrowaniu pid do pliku:
przykład:
załóżmy, że masz skrypt o nazwie „forever.sh”, który chcesz uruchomić z argumentami p1, p2, p3
kod źródłowy forever.sh:
utwórz plik reaper.sh:
uruchom forever.sh przez reaper.sh:
forever.sh robi tylko rejestrowanie linii do syslog co 5 sekund
masz teraz pid w /var/run/forever.sh.pid
i forever.sh uruchamia aok. syslog grep:
możesz to zobaczyć w tabeli procesów:
źródło
exec "$@"
zamiastexec $*
. Technicznie rzecz biorąc, musisz zachować nie białe znaki, ale występowanie znaków w parametrze powłoki IFS (domyślnie jest to spacja, tabulator i nowa linia).W powłoce bash alternatywą
$!
może byćjobs -p
wbudowana. W niektórych przypadkach!
in$!
interpretowane jest przez powłokę przed (lub zamiast) rozszerzeniem zmiennej, co prowadzi do nieoczekiwanych wyników.To na przykład nie zadziała:
podczas gdy to:
źródło
Możesz użyć czegoś takiego jak:
Lub
Te dwa polecenia mogą być połączeniami za pomocą
;
lub&&
. W drugim przypadku pid zostanie ustawiony tylko wtedy, gdy pierwsze polecenie się powiedzie. Możesz pobrać identyfikator procesu z$pid
.źródło
$!
po&&
lub;
nigdy nie da identyfikatora PID procesu rozpoczętego dla lewej strony separatora poleceń.$!
jest ustawiany tylko dla procesów uruchamianych asynchronicznie (np. zwykle z,&
ale niektóre powłoki mają także inne metody)To jest trochę hack-odpowiedź i prawdopodobnie nie zadziała dla większości ludzi. Jest to również ogromne ryzyko związane z bezpieczeństwem, więc nie rób tego, chyba że jesteś pewien, że będziesz bezpieczny, a dane wejściowe zostaną zdezynfekowane i ... cóż, masz pomysł.
Skompiluj tutaj mały program C do pliku binarnego o nazwie
start
(lub cokolwiek chcesz), a następnie uruchom program jako./start your-program-here arg0 arg1 arg2 ...
Krótko mówiąc, to wydrukuje PID
stdout
, a następnie załaduje program do procesu. Powinien nadal mieć ten sam PID.źródło
stdout
nie wydaje się, aby został odnotowany w tym przykładzie:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
sleep 10 & PID_IS=$!; echo $PID_IS