Làm thế nào tôi có thể có được pid của một subshell?


12

Làm thế nào tôi có thể có được pid của một subshell?

Ví dụ:

$ echo $$
16808

Điều này không hoạt động, bởi vì vỏ ban đầu mở rộng $$:

$ ( echo $$ )
16808

Tại sao trích dẫn đơn không hoạt động? Sau khi shell ban đầu loại bỏ trích dẫn duy nhất, liệu subshell không tự mở rộng $$?

$ ( echo '$$' )
$$

Tại sao cũng evalkhông hoạt động? Được evalđiều hành bởi subshell? Tại sao nó lại cho tôi bộ vỏ ban đầu?

$ ( eval echo '$$' )
16808

Cảm ơn.


Tôi đề nghị mở lại, bởi vì các câu hỏi về cơ bản là khác nhau theo quan điểm của tôi ("làm thế nào để tránh $$mở rộng" so với "pid khác nhau trong subshell").
peterh - Phục hồi Monica

Câu trả lời:


12

Ngoài bash's $BASHPID, bạn có thể làm điều đó portably với:

pid=$(exec sh -c 'echo "$PPID"')

Thí dụ:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

Bạn có thể biến nó thành một hàm:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

Lưu ý rằng một số shell (ví dụ: zshhoặc ksh93) KHÔNG khởi động một quy trình con cho mỗi lớp con được tạo bằng (...); trong trường hợp đó, $pidcó thể cuối cùng giống như $$, điều này hoàn toàn đúng, bởi vì đó là PID của quá trình getpidđược gọi từ.


1
Không. Nhưng làm ơn đừng cho rằng một lớp con nhất thiết phải chạy trong một quy trình con - chẳng hạn, đó không phải là trường hợp ksh93.
mosvy

1
Nó sẽ hoạt động tốt trong ksh93 - nó sẽ luôn trả về giá trị của quá trình mà nó được gọi từ đó. Đó là (...)từ ví dụ có thể không sinh ra một quy trình riêng biệt, như trong ví dụ bash.
mosvy

1
Ngoài ra, một số shell thích zshhoặc yashtối ưu hóa một fork()lệnh cuối cùng trong một lớp con. Họ thậm chí có thể tối ưu hóa ngã ba cho subshell nếu đó là lệnh cuối cùng trong tập lệnh để bạn getpidthậm chí có thể báo cáo cha mẹ của $$. Bạn có thể định nghĩa getpidlà: getpid(){ sh -c 'echo "$PPID"'; return; }để vô hiệu hóa tránh vấn đề.
Stéphane Chazelas

1
@HaroldFischer 1. không có exechoặc không có sự tối ưu hóa đó, sh -c ...quá trình sẽ là một đứa cháu, thay vì một đứa con của quá trình $(...)sử dụng thay thế lệnh và $PPIDsẽ là mấu chốt của $(...)lớp con. Đó chính xác là những gì xảy ra trong ví dụ set -E+ trap ERRbash ở trên.
mosvy

1
@HaroldFischer 2. test "$1"kiểm tra xem có phải $1là một chuỗi rỗng hay không - một cách nhanh chóng và bẩn thỉu để kiểm tra xem hàm đó có được đưa ra một varnameđối số để gán pid cho hay không; sử dụng một chức năng không phải là ý tưởng sáng nhất ở vị trí số 1.
mosvy

18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

Từ hướng dẫn:

BASHPID

Mở rộng đến ID tiến trình của quy trình bash hiện tại. Điều này khác với $$trong một số trường hợp nhất định, chẳng hạn như các lớp con không yêu cầu bash được khởi tạo lại.

$

Mở rộng đến ID tiến trình của trình bao. Trong một ()lớp con, nó mở rộng đến ID tiến trình của lớp vỏ hiện tại, không phải lớp con.

Liên quan:


Cảm ơn. (1) "Khởi tạo lại" nghĩa là gì? (2) Bạn cũng có thể xem xét tại sao những cách tôi đã thử không hoạt động?
Tim

@Tim Tôi tin rằng điều này được trả lời bởi Gilles ở đây . Bash chỉ đơn giản là không cập nhật $$trong subshells.
Kusalananda

Ý bạn là tôi nên luôn luôn sử dụng $ BASHPID thay cho $$ trong mọi trường hợp trong bash? Khi nào tôi sẽ sử dụng cái nào?
Tim

@Tim Nó phụ thuộc vào việc bạn, trong một subshell, muốn lấy ID tiến trình của tập lệnh hay của subshell. Cả hai khả năng đều được cung cấp và cái nào là chính xác phụ thuộc vào ứng dụng. Không có câu trả lời cụ thể hơn có thể được đưa ra cho điều đó.
Kusalananda

1
@Tim Không thể tìm thấy PID của vỏ cha của lớp con một cách đáng tin cậy trừ khi bạn sắp xếp để lưu $BASHPIDvào một biến và sử dụng nó trong lớp con. Có $PPID, nhưng đó là PID gốc của shell theo nghĩa tương tự đó $$là PID của shell (nó không được đặt lại trong một lớp con). Không có $BASHPPIDbiến.
Kusalananda
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.