Czy istnieje sposób, aby skrypt powłoki wiedział, który program go wykonał?

13

Czy w świecie * nix istnieje sposób, aby skrypt powłoki zawierał informacje o tym, który program go wykonał?


Przykład:

/path/to/script1 /path/to/script_xyz

w tym urojonym scenariuszu script_xyzmiałby informacje o ścieżce ( /path/to/script1)

lub

proces PID

podmiotu, który go wykonał.

Uwaga: jestem ciekawy różnych rozwiązań i podejść, nie spodziewam się, że to faktycznie będzie możliwe

Miloš Đakonović
źródło
3
Twój temat pyta, który program wykonał skrypt. Ale wydaje się, że twoje rzeczywiste pytanie dotyczy interpretera skryptu. Które z tych pytań naprawdę dotyczy twojego pytania?
kasperd
@kasperd Masz rację. Pytanie dotyczyło programu, ale tak naprawdę to interpreter. Właśnie dlatego miałem wrażenie, że nie jest to możliwe.
Miloš Đakonović

Odpowiedzi:

23

Często występuje pomieszanie między rozwidlaniem procesów a wykonywaniem.

Gdy zrobisz to z polecenia bashpowłoki.

$ sh -c 'exec env ps'

Proces P1 wydający ten $monit aktualnie uruchamia bashkod. Ten bashkod powoduje uruchomienie nowego procesu P2 , /bin/shktóry następnie wykonuje się /usr/bin/env, a następnie wykonuje /bin/ps.

Więc P2 jest z kolei wykonany kod bash, sh, envi ps.

ps(lub jakiekolwiek inne polecenie, takie jak skrypt, którego użylibyśmy tutaj zamiast tego) nie ma sposobu, aby wiedzieć, że zostało ono wykonane przez envpolecenie.

Wszystko, co może zrobić, to dowiedzieć się, jaki jest jego identyfikator procesu nadrzędnego, którym w tym przypadku byłby P1 lub 1gdyby P1 zmarł w tym przedziale czasu, lub w systemie Linux inny proces, który został wyznaczony jako podrzędny zamiast 1.

Następnie może zapytać system o to, jakie polecenie aktualnie wykonuje ten proces (jak readlink /proc/<pid>/exew systemie Linux) lub jakie argumenty zostały przekazane do ostatniego polecenia, które wykonał (jak w przypadku ps -o args= -p <pid>).

Jeśli chcesz, aby skrypt wiedział, co go wywołało, niezawodnym sposobem byłoby, aby wywołujący go powiedział. Można to zrobić na przykład za pomocą zmiennej środowiskowej. Na przykład script1można zapisać jako:

#! /bin/sh -
INVOKER=$0 script2 &

I script2:

#! /bin/sh -
printf '%s\n' "I was invoked by $INVOKER"
# and in this case, we'll probably find the parent process is 1
# (if not now, at least one second later) as script1 exited just after
# invoking script2:
ps -fp "$$"
sleep 1
ps -fp "$$"
exit

$INVOKERbędzie ( ogólnie ) zawierać ścieżkę do script1. W niektórych przypadkach może to być ścieżka względna, a ścieżka będzie względna do bieżącego katalogu roboczego w momencie script1uruchomienia. Więc jeśli script1zmieni bieżący katalog roboczy przed wywołaniem script2, script2otrzyma błędne informacje o tym, co go wywołało. Dlatego może być wskazane, aby upewnić się, że $INVOKERzawiera ścieżkę bezwzględną (najlepiej zachowując basename), na przykład pisząc script1jako:

#! /bin/sh -
mypath=$(
  mydir=$(dirname -- "$0") &&
  cd -P -- "$mydir" &&
  pwd -P) && mypath=$mypath/$(basename -- "$0") || mypath=$0

... some code possibly changing the current working directory
INVOKER=$mypath script2

W powłokach POSIX $PPIDbędzie zawierał pid rodzica procesu, który wykonał powłokę w momencie inicjalizacji tej powłoki. Następnie, jak pokazano powyżej, proces nadrzędny może ulec zmianie, jeśli proces id $PPIDumrze.

zshw zsh/systemmodule może zapytać o bieżący nadrzędny pid bieżącej (pod-) powłoki za pomocą $sysparams[ppid]. W powłokach POSIX można uzyskać bieżący ppid procesu, który wykonał interpreter (zakładając, że nadal działa) ps -o ppid= -p "$$". Za pomocą bashmożna uzyskać ppid bieżącej (pod-) powłoki za pomocą ps -o ppid= -p "$BASHPID".

Stéphane Chazelas
źródło
8

Tak, program może wiedzieć, kto jest jego rodzicem.

Aby to zilustrować, stwórzmy dwa skrypty bash. Pierwszy zgłasza swój PID i rozpoczyna drugi skrypt:

$ cat s1.sh
#!/bin/bash
echo s1=$$
bash s2.sh

Drugi skrypt zgłasza swój identyfikator procesu, PID swojego rodzica oraz wiersz poleceń użyty do uruchomienia rodzica:

$ cat s2.sh
#!/bin/bash
echo s2=$$ PPID=$PPID
echo "Parent command: $(ps -o cmd= -q $PPID)"

Teraz uruchommy to:

$ bash s1.sh
s1=17955
s2=17956 PPID=17955
Parent command: bash s1.sh

Jak widać, drugi skrypt w rzeczywistości zna PID swojego rodzica. Użycie pstego PID ujawnia wiersz poleceń użyty do wywołania elementu nadrzędnego.

Bardziej szczegółowe omówienie PPID znajduje się w odpowiedzi Stéphane Chazelas .

John1024
źródło
Dzięki. Uruchamiam przykładowe skrypty, które otrzymuję s1, s2i PPIDwartości, ale potem, w wielu liniach po ERROR: Unsupported SysV option.i kilku liniach z dodatkowym wyjaśnieniem i - pustą wartością dlaParent command
Miloš Đakonović
John używa funkcji ps, która nie jest dostępna (lub jest dostarczana inaczej) na twojej platformie, sprawdź stronę podręcznika ps (1).
Jasen
@Miloshio testowałem powyższe korzystania psz procps-ngpakietu w wersji 3.3.12. Jak sugerował Jasen, prawdopodobnie używasz innej wersji, która może wymagać innej składni do wydrukowania linii poleceń rodzica. Spróbować ps -f | grep $PPID.
John1024,