4

I have an emacs shell buffer which may or may not be running a certain interactive program such as ftp or python. I wish to write a lisp function which will interact with this program, but I would first like to make sure that said program is indeed running. My question is therefore:

Is there a Lisp function designed to inquire which program, if any, is running inside a given shell buffer?

Such programs often set their own prompt, so I thought of using

(buffer-substring (car comint-last-prompt) (cdr comint-last-prompt)) 

to determine the current prompt, but prompts often get garbled, so I found this not to be the ideal solution. Another attempt involved using

(shell-command-to-string "pgrep -fa MYPROGRAM")

but this would risk a false positive in case MYPROGRAM is running somewhere else in the system, other than under my shell buffer.

sds
  • 5,928
  • 20
  • 39
Ruy
  • 787
  • 4
  • 11
  • @sds, thanks for migrating my question to the apropriate forum! Next time I'll try to research which forum to use more carefully! – Ruy Aug 06 '19 at 22:41

3 Answers3

6

Here's a simple function that gets the pid of the process running in the current buffer and then calls the pstree command to get the process tree of that process:

(defun iproc ()
  (interactive)
  (let ((pid (process-id (get-buffer-process (current-buffer)))))
    (shell-command (format "pstree -p %d" pid))))

If I have a *shell* buffer running bash, run bash again to create a subshell and then run python3 in the subshell, I do M-x iproc and get the following output:

bash(4231)---bash(12567)---python3(12603)

Of course, the pids are going to be different. Also the function needs some error checking: if the current buffer does not have a process running, the function fails. Here's a slightly better version:

(defun iproc ()
  (interactive)
  (let ((process (get-buffer-process (current-buffer))))
    (if process
        (shell-command (format "pstree -p %d" (process-id process)))
      (message "No process"))))
NickD
  • 27,023
  • 3
  • 23
  • 42
1

You should start with Process Information:

(process-command (get-buffer-process "*Python*"))
==> ("python3" "-i")
(process-status (get-buffer-process "*Python*"))
==> run

You should not be running ftp and python under bash under Emacs, but rather use the specific modes for them (e.g., M-x run-python &c).

If you insist on doing it your way, you should use process sentinels.

sds
  • 5,928
  • 20
  • 39
  • 2
    If I understand the question, that won't give us the right information. The `process-command` tells us the top-level process for a comint buffer (i.e., bash in usual circumstances), but it won't tell us if that bash process is currently running a python or ftp interactive session. – Tyler Aug 06 '19 at 17:37
  • I should have known `M-x ftp` existed! (I knew `M-x python`) – Tyler Aug 06 '19 at 18:41
  • @Tyler: 10 years ago someone complained to me that doing `grep` in `*shell*` and copy/pasting the file names into `C-x C-f` prompt was tedious. I pointed them to `M-x grep`. ;-) – sds Aug 06 '19 at 18:49
  • The reference to "ftp" and "python" in my question is simply supposed to illustrate what I mean by an interactive program. The fundamental point of the question is whether or not it is possible to use elisp to determine if a certain subprocess of "shell" is running. – Ruy Aug 06 '19 at 22:35
  • @Ruy: okay, and the answer is to use sentinels. – sds Aug 07 '19 at 02:34
0

Updated, integrated solution from @Nickd

(defun ar-process-at-point (&optional arg)
  "Report state of current process buffer.

With optional \\[universal-argument]: keep previous reports"
  (interactive "P")
  (get-buffer-create "Report Process")
  (let ((proc (format "Process: %s\n" (process-name (get-buffer-process (current-buffer)))))
    (proc-command (format "Process: %s\n" (process-command (get-buffer-process (current-buffer)))))
        (pid (progn (shell-command (format "pstree -p %d" (process-id (get-buffer-process (current-buffer)))) "Report Process")
            (with-current-buffer "Report Process" (buffer-substring-no-properties (point-min) (point-max)))))
    (fe (format "field-end (point): %s\n" (field-end (point))))
    (fb (format "field-beginning (point): %s\n" (field-beginning (point))))
    (fap (format "(field-at-pos (point)): %s\n" (field-at-pos (point))))
    (po (format "(point): %s\n" (point)))
    (lbp (format "(line-beginning-position): %s\n" (line-beginning-position)))
    (iftm (format "inhibit-field-text-motion lbp: %s\n" (let ((inhibit-field-text-motion t)) (line-beginning-position))))
    (pf (format "process-filter: %s\n" (ignore-errors process-filter)))
    (pm (format "(process-mark process): %s\n" (ignore-errors (process-mark (get-buffer-process (current-buffer)))))))
    (save-excursion
      (delete-other-windows)
      (split-window)
      (set-buffer (get-buffer-create "Report Process"))
      (unless (eq 4 (prefix-numeric-value arg)) (erase-buffer))
      (goto-char (point-max))
      (insert proc)
      (insert proc-command)
      (insert pm)
      (insert pid)
      (insert fe)
      (insert fb)
      (insert fap)
      (insert po)
      (insert lbp)
      (insert iftm)
      (insert pf)
      (newline 1)
      (unless (eq 4 (prefix-numeric-value arg)) (goto-char (point-min)))
      (display-buffer "Report Process")
      (other-buffer))))
Andreas Röhler
  • 1,894
  • 10
  • 10
  • 1
    Unfortunately I could not make it work, perhaps because I am not too knowledgeable about elisp. But here is what I did: in an emacs shell buffer I am running an interactive program. In that buffer I issued "ESC-x my-current-process-command RET". The minibuffer then showed "(/bin/bash -i)" but it did not mention the interactive program running under my shell, which is precisely what I am looking for. Did I do anything wrong? – Ruy Aug 07 '19 at 12:21
  • @Ruy Updated. Originally written for use-cases, where Emacs connects to Python directly. – Andreas Röhler Aug 08 '19 at 11:37