4

If I start an asynchronous ("backgrounded") process, some info, including the new process's PID, gets printed to the terminal before the process runs; for example

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

Is there a way to have such info (especially the PID) printed at the start of every process, not just those that get started asynchronously?


Background

The reason for wanting this is that, due to the peculiarities of my everyday working set up, often enough it happens that a synchronous long-running process fails to respond to Ctrl-C. (Invariably, what makes these processes "long-running" is that they produce a lot more output than I had anticipated.) The surest way to stop such a process is to kill -9 it from a different window, and it would be nice to have its PID readily on hand for this.

UPDATE: In my original post I neglected to mention that Ctrl-Z is not an option. (I'm working on a shell running under Emacs, so Ctrl-Z just suspends Emacs.)

  • 1
    Interesting question. With bash and ksh93, one can have a DEBUG trap execute before the shell runs the command on the command line, but since the command hasn't yet been executed, there's obviously no PID to output while executing the trap. – Kusalananda Apr 10 '17 at 13:10
  • @Kusalananda: likewise, zsh has preexec, but I don't know how to get the PID of the process that's about to start... – kjo Apr 10 '17 at 14:48
  • 1
    Looking at the source code, there doesn’t appear to be anything usable to do this (between the fork, which gives you the new pid, and the exec — or rather, after the fork on the parent side). You could change addproc to log every new process added to the job table, which would work in the majority of cases. – Stephen Kitt Apr 10 '17 at 15:29
  • Not really an answer to the question, but it might solve your problem anyway. You can stop and "background" a running process by pressing Ctrl + Z. That leaves you with a stopped (but still existing) process and access to the shell. Now you can run kill %1 to kill this process (or rather the first backgrounded process, to be exact). So you do not even need to know the PID. – Adaephon Apr 10 '17 at 15:32
  • @StephenKitt: I updated my post to explain why I can't use Ctrl-Z. – kjo Apr 10 '17 at 16:27
3

As Stephen Kitt explains, making zsh print the PID would be rather difficult. But you can get at the information in other ways.

You can press Ctrl+Z to suspend the process, then zsh displays its PID. And if you want to kill it, try pressing Ctrl+C first, to kill it directly. If Ctrl+C fails, try Ctrl+\ for a “harder” kill (Ctrl+C sends SIGINT, which conventionally tells a program to stop its current action and hand back control to the user; Ctrl+\ sends SIGQUIT, which conventionally tells a program to crash hard). You can do this even from within Emacs: in Shell mode, press C-c C-z to pass C-z to the terminal, C-c C-c to pass C-c, C-c C-\ to pass C-\. In Term mode, C-z and C-\ are passed directly, but you need C-c C-c to pass a single C-c.

If the process changes the terminal settings or blocks the signals, a convenient way to locate it for killing is by terminal. Find out what the terminal is; you can do this with the tty command inside the terminal. You might make this part of your prompt or part of the terminal title (I put it in the terminal title). Emacs doesn't display the terminal title, but it gives you access to the information by evaluating the following expression:

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

To evaluate an expression in most modes including Shell mode, type M-: then enter the expression. In Term mode, type C-c M-x eval-expression RET then enter the expression. If you use this often, bind the following command to a key in the relevant modes:

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

Once you know the terminal name, you can use e.g. ps -t pts/42 or pgrep -t pts/42 to list the processes that are attached to that terminal.

3

There doesn’t seem to be a way of logging this information, and looking into it in more detail, it would be hard to design correctly (I initially thought it would be hard to implement too, but ilkkachu fixed that).

I thought the implementation problem was logging the information in the right place. When a shell starts a child, it forks, and then execs the child (unless it can replace itself with the child, in which case it doesn’t fork). When it forks, the process is duplicated; one of the copies gets a return code of 0 from the fork() call, which indicates it’s the child, and the other gets the child’s process identifier, which indicates it’s the parent. The child inherits the parent’s file descriptors, so it can log its own pid before setting things up for exec().

The design problem is choosing what to log. If we log all processes started by the shell, we end up with lots of extraneous logs, e.g. for processes involved in constructing the prompt! Even for “real” commands, we need to decide what to log for pipelines and other compound commands... And then for consistency we need to come up with something to log for built-in commands, or for circumstances in which the shell replaces itself with the child (although I don’t think that can happen for an interactive shell, unless you manually exec).

If you want to explore this further, check out addproc() and printjobs() in jobs.c, and zexecve() in exec.c.

  • So log the the PID from the child? AFAIU the child process has to handle all file descriptor setup for the to-be-started application anyway, so it shouldn't be impossible to have an fd at hand for logging? – ilkkachu Apr 10 '17 at 20:02
  • Oh dear, yes, thanks @ilkkachu, that’s perfectly feasible. – Stephen Kitt Apr 10 '17 at 21:10
  • The right place to print out the PID would be at the point of creating a process group, and then print the PGID. That way only proper jobs would be printed out, and only one per pipeline. – Gilles Apr 10 '17 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.