Elisp: Start External Process

By Xah Lee. Date: . Last updated: .

Start External Process, Wait for it to Finish

call-process

(call-process PROGRAM &optional INFILE DESTINATION DISPLAY &rest ARGS)

Call PROGRAM in separate process, wait for it to finish. (unless DESTINATION is 0). PROGRAM is searched in exec-path

  • INFILE nil or a filepath. The content is passed to stdin. Use call-process-region if you want to pass a region from buffer.
  • DESTINATION is a buffer for output (including standard error.).
    • Or t for current buffer.
    • Or nil for discard output.
    • Or 0 for don't wait for the program to terminate.
    • Or (:file FILE) means write to file. (overwrite existing)
    • Or (REAL-BUFFER STDERR-FILE). (See elisp manual.)
  • DISPLAY non-nil means redisplay buffer as output is inserted.
  • ARGS are strings passed as command arguments to PROGRAM. (each is a string). If you have a list, use apply. 〔see Elisp: Apply Function (List to Args)
;; example of call-process on linux
;; passing it a file path
(cond
 ((eq system-type 'windows-nt)
  nil)
 ((eq system-type 'darwin)
  nil)
 ((eq system-type 'gnu/linux)
  (call-process
   shell-file-name nil 0 nil
   shell-command-switch
   (format "%s %s"
           "xdg-open"
           (file-name-directory xpath)))))

Example: Unknown Number of Args

;; example of call-process when you need to feed it a lot file names, known at runtime

;; the file names is a list
;; besides filenames, you may have many args. make it a list
;; then use append to join them
;; then use apply to feed it to call-process as args

(let ((xoutBuf (get-buffer-create "*my output*")))
  (apply
   'call-process
   (append
    (list "exiftool" nil xoutBuf t "-all=" "-overwrite_original")
    FileList nil)))

more examples:

call-process-region

(call-process-region START END PROGRAM &optional DELETE BUFFER DISPLAY &rest ARGS)

Call a external program, send input to stdin. Wait for it to finish.

  • START and END are region positions. If START is string, use that. ignore END.
  • PROGRAM is the program name to call. If not full file path, search in exec-path
  • DELETE if true, delete the input region.
  • BUFFER is the buffer to display output. If BUFFER is 0, returns immediately with value nil.
  • DISPLAY if true, update buffer as input comes in.
  • ARGS the args passed to the external command.
;; 2024-12-22
;; call powershell to list dir

;; if you use shell-command, emacs calls cmd.exe
;; but you want to call powershell
;; it's very difficult if the argument is a complicated string involving quote or backslashes or in filename arguments
;; best solution, is use call-process-region, and passing the powershell args as stdin
;; powershell can take stdin as args. by
;; powershell -Command -

 (let ((xoutBuf (get-buffer-create "*my PowerShell output*")))
  (call-process-region
   (format "dir %s" (expand-file-name "~/Downloads/"))
   nil "powershell" nil xoutBuf t "-Command" "-")
  (display-buffer xoutBuf))
;; 2024-11-23
;; example of calling powershell .net to get image width and height

;; if you use shell-command, emacs calls cmd.exe
;; but you want to call powershell
;; it's very difficult if the argument is a complicated string involving quote or backslashes or in filename arguments
;; best solution, is use call-process-region, and passing the powershell args as stdin

(let ((my-img-path (expand-file-name "~/Downloads/2024-10-18_image.jpg"))
      (xoutBuf (get-buffer-create "*my PowerShell output*"))
      xcmd xoutput )
  (setq xcmd
        (concat
         "Add-Type -AssemblyName System.Drawing; $ximg=[System.Drawing.Image]::FromFile('"
         my-img-path
         "');  Write-Output $ximg.Width , $ximg.Height "
         ))

  (call-process-region xcmd nil "PowerShell" nil xoutBuf t "-Command" "-")
  (setq xoutput (with-current-buffer xoutBuf (buffer-string)))
  ;; (pop-to-buffer xoutBuf)
  (kill-buffer xoutBuf)
  (split-string xoutput))

;; sample output
;; ("4032" "3024")

Start External Process, No Wait

start-process

(start-process NAME BUFFER PROGRAM &rest PROGRAM-ARGS)

Start a program in a subprocess. (do not wait for it to finish) Return the process object for it.

  • NAME is name for process. Can be any.
  • BUFFER is the buffer (or buffer name) for process output (both standard output and standard error streams). Or nil.
  • PROGRAM is the program file name to start. It is searched for in exec-path. If nil, just associate a pty with the buffer.
  • PROGRAM-ARGS are arguments (each is a string). If you have a list, use apply. 〔see Elisp: Apply Function (List to Args)
;; 2024-11-25
;; example of opening a file on windows using default app

(let ((xpath (expand-file-name "~/Downloads/2024-10-18_image.jpg"))
      (xoutBuf (get-buffer-create "*my output*"))
      (xcmdlist (list "PowerShell" "-Command" "Invoke-Item" "-LiteralPath")))
  (apply 'start-process (append (list "xah open file" xoutBuf) xcmdlist (list xpath) nil)))
;; example of start-process when you need to feed it a lot file names, known at runtime
(let ((xoutBuf (get-buffer-create "*my output*")))
  (let ((process-connection-type nil))
    (apply
     'start-process
     (append
      (list "exiftool" xoutBuf "exiftool" "-all=" "-overwrite_original")
      FileList nil))))
;; example of start-process

;; open a list of files in windows default app
(cond
 ((eq system-type 'windows-nt)
  (let ((xoutBuf (get-buffer-create "*my open in external app*"))
        (xcmdlist (list "PowerShell" "-Command" "Invoke-Item" "-LiteralPath")))
    (mapc
     (lambda (x)
       (message "%s" x)
       (apply
        'start-process
        (append
         (list "my open in external app" xoutBuf) xcmdlist
         (list (format "'%s'" (if (string-match "'" x) (replace-match "`'" t t x) x))) nil)))
     xfileList)))
 (t nil))
start-process-shell-command

(start-process-shell-command NAME BUFFER COMMAND)

similar to start-process, but starting a shell to start the command.

make-process

(make-process &rest ARGS)

The core function for creating process. similar to start-process. See elisp manual.

call-process vs shell-command

Advantage of call-process

Disadvantage is that you need to handle the process and output, and a bit harder to code.

Advantage of shell-command

Examples: Command Wrapper Calling External Program

Reference

Emacs Lisp, Call Shell Command