Как получить PID subshell в Korn Shell (эквивалент $ BASHPID)

8

В bash у вас есть эта удобная переменная: $ BASHPID, которая всегда возвращает PID запущенной в данный момент подоболочки. Как я могу получить PID подоболочки в ksh? Например, см. Код ниже:

#!/usr/bin/ksh93

echo "PID at start: $$"

function run_in_background {
  echo "PID in run_in_background $$"
  run_something &
  echo "PID of backgrounded run_something: $!"
}

function run_something {
  echo "*** PID in run_something: $$"
  sleep 10;
}    

run_in_background
echo "PID of run in background $!"

Это выводит следующее:

PID at start: 5328
PID in run_in_background 5328
*** PID in run_something: 5328
PID of backgrounded run_something: 5329
PID of run in background 5329

То, что я хочу, это строка, начинающаяся с ****вывода PID подоболочки, в случае примера это будет 5329.

Паткос Чаба
источник

Ответы:

10

Я не думаю, что это доступно в ksh. Есть решение POSIX, которое включает запуск внешнего процесса:

sh -c 'echo $PPID'

В Linux readlink /proc/selfтакже будет работать, но я не вижу каких-либо преимуществ (это может быть незначительно быстрее; это может быть полезно для варианта BusyBox, который имеет, readlinkно нет $PPID, но я не думаю, что он есть).

Обратите внимание, что для того, чтобы получить значение в оболочке, вы должны быть осторожны, чтобы не запускать эту команду в недолговечной подподкрылке. Например, p=$(sh -c 'echo $PPID')может отображаться вывод подоболочки, которая вызывается shв подстановке команд (или нет, некоторые оболочки оптимизируют этот случай). Вместо этого беги

p=$(exec sh -c 'echo $PPID')
Жиль "ТАК - перестань быть злым"
источник
Я уже видел это предложение, но оно не работает. Это дает мне третий PID ... однако я проверю это снова в понедельник, когда я вернусь на работу.
Паткос Чаба
@PatkosCsaba В ksh это, вероятно, сработает, потому что ksh оптимизирует вилки, но в некоторых других оболочках, таких как bash, вы должны быть осторожны, чтобы случайно не получить pid суб-суб-оболочки. Смотрите мой обновленный ответ.
Жиль "ТАК ... перестать быть злым"
Это работает: $(exec sh -c 'echo $PPID')однако первоначальная простая команда sh -c 'echo $PPID'дает третий PID. Так что спасибо за решение. Принятый.
Паткос Чаба
1
@FranklinYu Это потому, что ни один из них не создает подоболочки. Bash оптимизирует, (sh -c 'echo $PPID')чтобы избежать создания подоболочки. Контраст с (sh -c 'echo $PPID'; true). Эта оптимизация срабатывает только в том случае, если вы пытаетесь получить доступ $BASHPIDв качестве самой последней вещи перед выходом подоболочки, то есть только в тех случаях, когда вы ничего не можете сделать со значением. Таким образом, на практике вы можете заменить $BASHPIDна $(sh -c 'echo $PPID').
Жиль "ТАК - прекрати быть злым"
1
@FranklinYu Я не думаю, что есть что-то встроенное. Конечно, вы можете использовать портативный метод sh -c 'echo $PPID'.
Жиль "ТАК - перестать быть злым"
3

Вы можете достичь того, чего хотите, но вам нужно поместить run_something в отдельный скрипт. Я не совсем уверен, почему, но $$ не переоценивается, когда он используется в функции в том же скрипте, который его вызывает. Я предполагаю, что значение $$ назначается один раз после синтаксического анализа скрипта и перед его выполнением.

run_in_background.sh

#
echo "PID at start: $$"

    function run_in_background {
      echo "PID in run_in_background $$"
      ./run_something.sh &
      echo "PID of backgrounded run_something: $!"
    }

    run_in_background
    echo "PID of run in background $!"

run_something.sh

#
echo "*** PID in run_something: $$"
sleep 10;

вывод

PID at start: 24395
PID in run_in_background 24395
PID of backgrounded run_something: 24396
PID of run in background 24396
*** PID in run_something: 24396
Джастин Роу
источник
1
# KSH_VERSION hasn't always been a nameref, nor has it always existed.
# Replace with a better test if needed. e.g.:
# https://www.mirbsd.org/cvs.cgi/contrib/code/Snippets/getshver?rev=HEAD
if [[ ${!KSH_VERSION} == .sh.version ]]; then
    # if AT&T ksh
    if builtin pids 2>/dev/null; then # >= ksh93 v- alpha
        function BASHPID.get { .sh.value=$(pids -f '%(pid)d'); }
    elif [[ -r /proc/self/stat ]]; then # Linux / some BSDs / maybe others
        function BASHPID.get { read -r .sh.value _ </proc/self/stat; }
    else # Crappy fallback
        function BASHPID.get { .sh.value=$(exec sh -c 'echo $PPID'); }
    fi
elif [[ ! ${BASHPID+_} ]]; then
   echo 'BASHPID requires Bash, ksh93, or mksh >= R41' >&2
   exit 1
fi
ormaaj
источник
Два придира: if [[ ${!KSH_VERSION} = .sh.version ]]; then(только один =) и elif [[ -z ${BASHPID+_} ]]; then(избегайте использования неявных -nв двойных квадратных скобках, старый pdksh не знал этого).
Мирабилось
0

После ответа @Gilles, с которым я столкнулся при решении другой проблемы, с которой я столкнулся, я собрал программу быстрого тестирования, которая подкрепляет теорию о том, что правильный ответ:

MYPID=$(exec sh -c 'echo $PPID')

Я обнаружил, что бывают случаи, когда execэто не требуется, но я подтвердил, что это единственный способ получить правильный pid все время во всех оболочках, которые я пробовал. Вот мой тест:

#!/bin/sh
pids() {
  echo ------
  pstree -pg $PPID
  echo 'PPID = ' $PPID
  echo '$$ = ' $$
  echo 'BASHPID =' $BASHPID
  echo -n 'sh -c echo $PPID = '; sh -c 'echo $PPID'
  echo -n '$(sh -c '\''echo $PPID'\'') = '; echo $(sh -c 'echo $PPID') 
  echo -n '$(exec sh -c '\''echo $PPID'\'') = '; echo $(exec sh -c 'echo $PPID') 
  echo -n 'p=$(sh -c '\''echo $PPID'\'') = '; p=$(sh -c 'echo $PPID') ; echo $p
  echo -n 'p=$(exec sh -c '\''echo $PPID'\'') = '; p=$(exec sh -c 'echo $PPID') ; echo $p
}
pids
pids | cat
echo -e "$(pids)"

и его вывод

------
bash(5975,5975)---pidtest(13474,13474)---pstree(13475,13474)
PPID =  5975
$$ =  13474
BASHPID = 13474
sh -c echo $PPID = 13474
$(sh -c 'echo $PPID') = 13474
$(exec sh -c 'echo $PPID') = 13474
p=$(sh -c 'echo $PPID') = 13474
p=$(exec sh -c 'echo $PPID') = 13474
------
bash(5975,5975)---pidtest(13474,13474)-+-cat(13482,13474)
                                       `-pidtest(13481,13474)---pstree(13483,13474)
PPID =  5975
$$ =  13474
BASHPID = 13481
sh -c echo $PPID = 13481
$(sh -c 'echo $PPID') = 13481
$(exec sh -c 'echo $PPID') = 13481
p=$(sh -c 'echo $PPID') = 13481
p=$(exec sh -c 'echo $PPID') = 13481
------
bash(5975,5975)---pidtest(13474,13474)---pidtest(13489,13474)---pstree(13490,13474)
PPID =  5975
$$ =  13474
BASHPID = 13489
sh -c echo $PPID = 13489
$(sh -c 'echo $PPID') = 13492
$(exec sh -c 'echo $PPID') = 13489
p=$(sh -c 'echo $PPID') = 13495
p=$(exec sh -c 'echo $PPID') = 13489

Подставьте ваш любимый снаряд в притоне: sh, bash, mksh, kshи т.д. ...

Я не понимаю, почему случаи 2 и 3 дают разные результаты, и почему результаты для случая 3 отличаются между оболочками. Я попробовал bash, kshи mkshна Arch Linux FWIW.

starfry
источник
0
#!/bin/ksh
 function os_python_pid {
/usr/bin/python  -c 'import os,sys ; sys.stdout.write(str(os.getppid()))'
}
function os_perl_pid {
/usr/bin/perl -e 'print getppid'
}

echo "I am $$"
    echo "I am $(os_python_pid) :: $(os_perl_pid)"
function proce {
  sleep 3
  echo "$1 :: $(os_python_pid) :: $(os_perl_pid)"
}

for x in aa bb cc; do
  proce $x &
  echo "Started: $!"
done
Алты
источник