Есть ли способ для сценария оболочки узнать, какая программа его выполнила?

13

Есть ли в * nix мире способ для сценария оболочки иметь информацию о том, какая программа его выполнила?


Пример:

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

в этом воображаемом сценарии script_xyzбудет иметь информацию о пути ( /path/to/script1)

или

PID процесса

субъекта, который его выполнил.

Примечание: мне любопытно узнать о различных решениях и подходах, я не ожидаю, что это действительно возможно

Милош Чаконович
источник
3
Ваш субъект спрашивает, какая программа выполнила скрипт. Но ваш актуальный вопрос, кажется, спрашивает переводчика сценария. О каком из этих двух вопросов действительно идет речь?
Касперд
@kasperd Вы правы. Вопрос был о программе, но это на самом деле переводчик. Вот почему я чувствовал, что это невозможно в первую очередь.
Милош Чаконович

Ответы:

23

Часто возникает путаница между процессом разветвления и выполнением.

Когда вы делаете по приглашению bashоболочки.

$ sh -c 'exec env ps'

Процесс P1, выдающий это $приглашение, в настоящий момент выполняет bashкод. Этот bashкод разветвляет новый процесс P2, который выполняется, /bin/shкоторый затем выполняется /usr/bin/env, который затем выполняется /bin/ps.

Таким образом , P2 , в свою очередь выполняется код bash, sh, envи ps.

ps(или любая другая команда, такая как скрипт, который мы использовали бы здесь) не может знать, что она была выполнена envкомандой.

Все это можно сделать , это выяснить , что его родительский процесс идентификатор, который в этом случае будет либо P1 или 1если P1 умер в промежутке или на Linux другого процесса , который был назначен в качестве subreaper вместо 1.

Затем он может запросить у системы, какая команда выполняется в данный момент (например, readlink /proc/<pid>/exeв Linux) или какие аргументы были переданы последней команде, которую он выполнил (например, в ps -o args= -p <pid>).

Если вы хотите, чтобы ваш сценарий знал, что его вызвало, надежным способом было бы, чтобы вызывающий сообщал об этом. Это может быть сделано, например, через переменную окружения. Например, script1может быть написано как:

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

И 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будет ( обычно ) содержать путь к script1. В некоторых случаях это может быть относительный путь, и путь будет относительно текущего рабочего каталога на момент script1запуска. Так что, если script1изменит текущий рабочий каталог перед вызовом script2, script2получит неверную информацию относительно того, что вызвало его. Поэтому может быть предпочтительнее убедиться, что он $INVOKERсодержит абсолютный путь (предпочтительно с сохранением базового имени), например, записав в 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

В оболочках POSIX $PPIDбудет содержаться pid родительского процесса, который выполнил оболочку во время инициализации этой оболочки. После этого, как видно выше, родительский процесс может измениться, если процесс id $PPIDумирает.

zshв zsh/systemмодуле, может запросить текущий родительский pid текущей (под) оболочки с помощью $sysparams[ppid]. В оболочках POSIX вы можете получить текущий ppid процесса, который выполнил интерпретатор (при условии, что он все еще выполняется) ps -o ppid= -p "$$". С помощью bashвы можете получить ppid текущей (под) оболочки с помощью ps -o ppid= -p "$BASHPID".

Стефан Шазелас
источник
8

Да, программа может знать, кто ее родитель.

Для иллюстрации давайте создадим два сценария bash. Первый сообщает свой PID и запускает второй скрипт:

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

Второй скрипт сообщает свой идентификатор процесса, PID своего родителя и командную строку, используемую для запуска родителя:

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

Теперь давайте запустим это:

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

Как вы можете видеть, второй скрипт действительно знает PID своего родителя. Используя ps, этот PID показывает командную строку, используемую для вызова родителя.

Более подробное обсуждение PPID см. В ответе Стефана Шазеласа .

John1024
источник
Благодарю. Запустив ваши примеры сценариев, я получаю s1, s2и PPIDзначения, но потом, в несколько строк после ERROR: Unsupported SysV option.и в несколько строк с дополнительным объяснением и - пустое значение дляParent command
Милош Чаконович
Джон использует некоторые функции ps, которые недоступны (или предоставляются по-другому) на вашей платформе, проверьте страницу справочника ps (1).
Jasen
@Miloshio Я тестировал выше, используя psиз procps-ngпакета, версия 3.3.12. Как предположил Jasen, вы, вероятно, используете другую версию, для которой может потребоваться другой синтаксис для печати командной строки родителя. Попробуй ps -f | grep $PPID.
John1024