Cách nhận PID của subshell trong Korn Shell (tương đương với $ BASHPID)


8

Trong bash, bạn có biến tiện dụng này: $ BASHPID sẽ luôn trả về bộ lọc của lớp con hiện đang chạy. Làm thế nào tôi có thể có được một PID của subshell trong ksh? Ví dụ, xem mã dưới đây:

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

Điều này xuất ra như sau:

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

Những gì tôi muốn là dòng bắt đầu với ****đầu ra PID của subshell, trong trường hợp ví dụ sẽ là 5329.

Câu trả lời:


10

Tôi không nghĩ rằng nó có sẵn trong ksh. Có một giải pháp POSIX bao gồm chạy một quy trình bên ngoài:

sh -c 'echo $PPID'

Trên Linux, readlink /proc/selfcũng sẽ hoạt động, nhưng tôi không thấy bất kỳ lợi thế nào (nó có thể nhanh hơn một chút; nó có thể hữu ích trên một biến thể BusyBox có readlinknhưng không $PPID, nhưng tôi không nghĩ là có một).

Lưu ý rằng để có được giá trị trong shell, bạn cần cẩn thận không chạy lệnh đó trong shell con phụ ngắn. Ví dụ, p=$(sh -c 'echo $PPID')có thể hiển thị đầu ra của lớp con gọi shtrong lệnh thay thế (hoặc có thể không, một số shell tối ưu hóa trường hợp đó). Thay vào đó, hãy chạy

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

Tôi đã thấy đề nghị này rồi nhưng nó không hoạt động. Nó mang lại cho tôi một bộ ba thứ ba ... tuy nhiên tôi sẽ kiểm tra lại vào thứ hai khi tôi làm việc trở lại.
Patkos Csaba

@PatkosCsaba Trong ksh có thể nó sẽ hoạt động vì ksh tối ưu hóa các dĩa, nhưng trong một số shell khác như bash, bạn cần cẩn thận để không vô tình lấy pid của sub-shell. Xem câu trả lời cập nhật của tôi.
Gilles 'SO- ngừng trở nên xấu xa'

Điều này đang hoạt động: $(exec sh -c 'echo $PPID')Tuy nhiên, lệnh đơn giản ban đầu sh -c 'echo $PPID'cung cấp cho một PID thứ ba. Vì vậy, cảm ơn cho giải pháp. Đã được chấp nhận.
Patkos Csaba

1
@FranklinYu Đó là bởi vì không phải là tạo ra một subshell. Bash tối ưu hóa (sh -c 'echo $PPID')để tránh tạo ra một subshell. Tương phản với (sh -c 'echo $PPID'; true). Tối ưu hóa này chỉ có tác dụng nếu bạn cố gắng truy cập $BASHPIDnhư là điều cuối cùng trước khi thoát khỏi lớp con, tức là chỉ trong trường hợp bạn không thể làm gì với giá trị. Vì vậy, trong thực tế, bạn có thể thay thế $BASHPIDbằng $(sh -c 'echo $PPID').
Gilles 'SO- ngừng trở nên xấu xa'

1
@FranklinYu Tôi không nghĩ có bất cứ thứ gì được tích hợp. Tất nhiên bạn có thể sử dụng phương thức di động sh -c 'echo $PPID'.
Gilles 'SO- ngừng trở nên xấu xa'

3

Bạn có thể đạt được những gì bạn muốn, nhưng bạn cần đặt run_s Something vào một tập lệnh riêng. Tôi không chắc chắn tại sao, nhưng $$ không được đánh giá lại khi nó được sử dụng trong một hàm trong cùng một kịch bản đang gọi nó. Tôi đoán rằng giá trị của $$ được gán một lần sau khi tập lệnh được phân tích cú pháp và trước khi nó được thực thi.

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_s Something.sh

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

đầu ra

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

Hai nitpicks: if [[ ${!KSH_VERSION} = .sh.version ]]; then(chỉ một =) và elif [[ -z ${BASHPID+_} ]]; then(tránh sử dụng ẩn -ntrong dấu ngoặc vuông, pdksh cũ không biết điều đó).
mirabilos

0

Theo câu trả lời của @Gilles mà tôi gặp phải trong khi giải quyết một vấn đề khác mà tôi gặp phải, tôi đã cùng nhau đưa ra một chương trình kiểm tra nhanh, củng cố lý thuyết rằng câu trả lời đúng là:

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

Tôi đã tìm thấy đôi khi execkhông cần thiết nhưng tôi đã xác nhận rằng sử dụng nó là cách duy nhất để có được chính xác tất cả thời gian trong tất cả các vỏ tôi đã thử. Đây là bài kiểm tra của tôi:

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

và đầu ra của nó

------
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

Thay vỏ ưa thích của bạn trong công việc: sh, bash, mksh, ksh, vv ...

Tôi không hiểu tại sao trường hợp 2 và 3 cho kết quả khác nhau, cũng như tại sao kết quả cho trường hợp 3 khác nhau giữa các vỏ. Tôi đã cố gắng bash, kshmkshtrên Arch Linux FWIW.


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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.