如何在*每個*進程開始時將PID列印到終端機?

如何在*每個*進程開始時將PID列印到終端機?

如果我開始一個非同步(「後台」)進程,一些訊息,包括新進程的 PID,在進程運行之前被列印到終端;例如

$ sleep 3 &
[1] 8217
$ 
[1]  + done       sleep 3
$ 

有沒有辦法在開始時列印這樣的資訊(特別是PID)每一個進程,而不僅僅是那些非同步啟動的進程?


背景

想要這樣做的原因是,由於我日常工作設定的特殊性,經常會發生這樣的情況:同步長時間運行的進程無法回應Ctrl-C。 (總是,使這些進程「長時間運行」的原因是它們產生的輸出比我預期的要多得多。)停止此類進程的最可靠方法是kill -9從不同的視窗存取它,並且最好有它的為此,PID 隨時可用。

更新:在我原來的帖子中,我忽略了這Ctrl-Z不是一個選擇。 (我正在開發在 Emacs 下運行的 shell,因此Ctrl-Z只需暫停 Emacs。)

答案1

作為史蒂芬·基特解釋說,讓 zsh 列印 PID 會相當困難。但您可以透過其他方式取得資訊。

您可以按Ctrl+Z暫停進程,然後 zsh 顯示其 PID。如果你想殺死它,請嘗試先按Ctrl+ C,直接殺死它。如果Ctrl+C失敗,請嘗試Ctrl+\進行「更難」的終止(Ctrl+C發送 SIGINT,通常告訴程式停止當前操作並將控制權交還給用戶;Ctrl+\發送 SIGQUIT,通常告訴程式嚴重崩潰)。您甚至可以在 Emacs 中執行此操作:在 Shell 模式下,按C-c C-z傳遞C-z到終端、C-c C-c傳遞C-cC-c C-\傳遞C-\。在 Term 模式下,C-zC-\是直接傳遞的,但需要C-c C-c傳遞單一C-c.

如果進程更改了終端設定或阻止了訊號,則透過終端定位它並進行查殺的便捷方法是透過終端。找出終端是什麼;您可以使用tty終端機內的命令來執行此操作。您可以將這部分作為提示符或終端標題的一部分(我將其放在終端標題中)。 Emacs 不顯示終端標題,但它允許您透過計算以下表達式來存取資訊:

(process-tty-name (get-buffer-process (current-buffer)))

若要在大多數模式(包括 Shell 模式)下計算表達式,請鍵入M-:然後輸入表達式。在術語模式下,鍵入C-c M-x eval-expression RET然後輸入表達式。如果您經常使用此功能,請將以下命令綁定到相關模式下的按鍵:

(defun buffer-process-tty-name ()
  (interactive)
  (let ((tty (process-tty-name (get-buffer-process (current-buffer)))))
    (if (interactivep) (message "%s" tty))
    tty))

一旦您知道了終端名稱,您就可以使用例如ps -t pts/42pgrep -t pts/42來列出連接到該終端的進程。

答案2

似乎沒有一種方法可以記錄這些信息,並更詳細地研究它,很難正確設計(我最初認為這也很難實現,但是伊爾卡丘修復了這個問題)。

我認為實施問題是將資訊記錄在正確的位置。當 shell 啟動一個子進程時,它會分叉,然後執行該子進程(除非它可以用子進程取代自身,在這種情況下它不會分叉)。當它分叉時,該過程會重複;其中一個副本從fork()呼叫中取得回傳碼 0,這表示它是子進程,而另一個副本則取得子進程的進程標識符,這表示它是父進程。子進程繼承父進程的檔案描述符,因此它可以在進行設定之前記錄自己的 pid exec()

設計問題是選擇記錄什麼。如果我們記錄 shell 啟動的所有進程,我們最終會得到大量無關的日誌,例如對於建立提示所涉及的過程!即使對於「真實」命令,我們也需要決定為管道和其他複合命令記錄哪些內容...然後為了保持一致性,我們需要為內置命令或 shell 替換的情況提供一些記錄內容本身與孩子(儘管我認為互動式shell 不會發生這種情況,除非您手動exec)。

如果您想進一步探索這一點,請查看addproc()printjobs()in jobs.c,以及zexecve()in exec.c

相關內容