¿Hay alguna manera para que el script de shell sepa qué programa lo ha ejecutado?

13

En * nix world, ¿hay alguna forma de que el script de shell tenga información sobre qué programa lo ha ejecutado?


Ejemplo:

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

en este escenario imaginario, script_xyztendría información de ruta ( /path/to/script1)

o

PID de proceso

de entidad que lo ha ejecutado.

Nota: Tengo curiosidad por las diferentes soluciones y enfoques, no espero que esto sea realmente posible

Miloš Đakonović
fuente
3
Su sujeto pregunta qué programa ejecutó el script. Pero su pregunta real parece ser pedir el intérprete del guión. ¿De cuál de los dos es realmente su pregunta?
kasperd
@kasperd Tienes razón. La pregunta era sobre el programa, pero en realidad es intérprete. Es por eso que tuve la sensación de que esto no es posible en primer lugar.
Miloš Đakonović

Respuestas:

23

A menudo hay confusión entre la bifurcación del proceso y la ejecución.

Cuando lo haces en el indicador de un bashshell.

$ sh -c 'exec env ps'

El proceso P1 que emite ese $indicador está ejecutando actualmente el bashcódigo. Ese bashcódigo bifurca un nuevo proceso P2 que se ejecuta /bin/shy luego se ejecuta /usr/bin/env, que luego se ejecuta /bin/ps.

Así P2 a su vez ha ejecutado el código de bash, sh, envy ps.

ps(o cualquier otro comando como un script que usaríamos aquí) no tiene forma de saber que ha sido ejecutado por el envcomando.

Todo lo que puede hacer es averiguar cuál es su ID de proceso principal, que en este caso sería P1 o 1si P1 ha muerto en el intervalo o en Linux otro proceso que se ha designado como un subárea en lugar de 1.

Luego puede consultar al sistema qué comando está ejecutando ese proceso actualmente (como con readlink /proc/<pid>/exeLinux) o qué argumentos pasaron al último comando que ejecutó (como con ps -o args= -p <pid>).

Si desea que su script sepa qué lo invocó, una forma confiable sería que el invocador lo dijera. Eso podría hacerse, por ejemplo, a través de una variable de entorno. Por ejemplo, script1podría escribirse como:

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

Y 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

$INVOKER( generalmente ) contendrá una ruta a script1. Sin embargo, en algunos casos, puede ser una ruta relativa, y la ruta será relativa al directorio de trabajo actual en el momento en que script1comenzó. Entonces, si script1cambia el directorio de trabajo actual antes de llamar script2, script2obtendrá información incorrecta sobre lo que lo llamó. Por lo tanto, puede ser preferible asegurarse$INVOKER contenga una ruta absoluta (preferiblemente manteniendo el nombre base) como escribiendo script1:

#! /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

En los depósitos POSIX, $PPID contendrá el pid del padre del proceso que ejecutó el shell en el momento de la inicialización de ese shell. Después de eso, como se vio anteriormente, el proceso padre puede cambiar si el proceso de identificación $PPIDmuere.

zshen el zsh/systemmódulo, puede consultar el actual pid matriz de la cáscara de corriente (sub-) con $sysparams[ppid]. En shells POSIX, puede obtener el ppid actual del proceso que ejecutó el intérprete (suponiendo que todavía se esté ejecutando) ps -o ppid= -p "$$". Con bash, puede obtener el ppid del actual (sub) shell con ps -o ppid= -p "$BASHPID".

Stéphane Chazelas
fuente
8

Sí, un programa puede saber quién es su padre.

Para ilustrar, creemos dos scripts de bash. El primero informa su PID e inicia el segundo script:

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

El segundo script informa su ID de proceso, el PID de su padre y la línea de comando utilizada para ejecutar el padre:

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

Ahora, vamos a ejecutarlo:

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

Como puede ver, el segundo script conoce, de hecho, el PID de su padre. Usando ps, ese PID revela la línea de comando utilizada para invocar al padre.

Para una discusión sobre PPID con más profundidad, vea la respuesta de Stéphane Chazelas .

John1024
fuente
Gracias. Ejecución de scripts de ejemplo consigo s1, s2yPPID los valores, pero luego, en múltiples líneas después ERROR: Unsupported SysV option.y varias líneas con una explicación adicional y - valor vacío paraParent command
Miloš Đakonović
John está utilizando alguna función ps que no está disponible (o se proporciona de manera diferente) en su plataforma, consulte su página de manual ps (1).
Jasen
@Miloshio Probé lo anterior usando psel procps-ngpaquete, versión 3.3.12. Como sugirió Jasen, es probable que esté utilizando una versión diferente que puede requerir una sintaxis diferente para imprimir la línea de comando de los padres. Tratar ps -f | grep $PPID.
John1024