s c h e m a t i c s : c o o k b o o k

/ Cookbook.ProcessCaptureOutput

This Web


WebHome 
WebChanges 
TOC (with recipes)
NewRecipe 
WebTopicList 
WebStatistics 

Other Webs


Chicken
Cookbook
Erlang
Know
Main
Plugins
Sandbox
Scm
TWiki  

Schematics


Schematics Home
Sourceforge Page
SchemeWiki.org
Original Cookbook
RSS

Scheme Links


Schemers.org
Scheme FAQ
R5RS
SRFIs
Scheme Cross Reference
PLT Scheme SISC
Scheme48 SCM
MIT Scheme scsh
JScheme Kawa
Chicken Guile
Bigloo Tiny
Gambit LispMe
GaucheChez

Lambda the Ultimate
TWiki.org

Capturing the Output of a Process

Problem

You want to run an external process and capture its output.

Solution

The function system/output below uses the process function in MzLib?'s process.ss to capture the output of the program.

(require (lib "process.ss"))

;; system/output : string -> (U string #f)
;;
;; Synchronously run the given command through the shell and
;; capture standard output.
;;
;; Returns the standard output or #f if the command failed
;;
;; If the command blocks for any reason (e.g. waiting for
;; input) this function will as well.
(define (system/output command-string)
  (let ([p (open-output-string)])
    (parameterize ([current-output-port p])
      (if (system command-string)
          (get-output-string p)
          #f))))

system/output is used as follows:

> (display (system/output "ls"))
fsm-1.eps
fsm-1.fig
fsm-1.fig.bak
fsm-2.fig
fsm-2.fig.bak
...

Here is an alternative implementation based directly on the subprocess primitives:

(require (lib "port.ss"))

;; lookup-exe: path -> path
;; Resolves relative exe lookups using PATH.
(define (lookup-exe a-path)
  (cond
    [(complete-path? a-path)
     a-path]
    [(find-executable-path a-path #f)
     => (lambda (path) path)]
    [else
     (error 'lookup-exe "Can't find executable ~s" a-path)]))


;; system/output: path [string]* -> string
;; Executes command with the given arguments.  If stderr has anything
;; written to it, its output will be interleaved in the string in some
;; unpredictable way.
;; If th executable can't be found, returns an error.
(define (system/output command . args)
  (let*-values ([(cmd-path) (lookup-exe command)]
                [(subp stdout stdin stderr)
                 (apply subprocess #f #f #f cmd-path args)]
                [(outp) (open-output-string)]
                [(threads)
                 (list (thread (lambda () (copy-port stdout outp)))
                       (thread (lambda () (copy-port stderr outp))))])
    (close-output-port stdin)
    (subprocess-wait subp)
    (for-each sync threads)
    (get-output-string outp)))

Here is a variation that takes stdin pipe:

(require scheme/system)
(define (system/input->output command-string input-port)
  (let ([out (open-output-string)]
        [err (open-output-string)])
    (let ([code (parameterize ([current-output-port out]
                               [current-error-port err]
                               [current-input-port input-port])
                  (system/exit-code command-string))])
      (if (= 0 code)
          (get-output-string out)
          (raise (format "Subprocess failed with exit code ~a" code))))))

Discussion

system/output runs its command via the shell, so programs are resolved using the PATH, and shell functions such as wildcards can be used in commands.

Note there was a subtle bug in the previous version of system/output, which built on the process function (which in turn builds on the built-in subprocess function). The ports returned by process have a limited buffer for output, and once that buffer is full execution will halt. This leads to strange behaviour -- processes that produce small output will succeed, but when the output gets larger they will suddenly hang. It is much simpler to build on system which takes care of asynchronously copying the output to the current-output-port.


Comments about this recipe

Contributors

-- NoelWelsh - 25 Feb 2005

-- JamesStewart - 29 Mar 2005

-- DannyYoo - 20 Feb 2008: added second implementation based on subprocess.

CookbookForm
TopicType: Recipe
ParentTopic: ProcessRecipes
TopicOrder: 999

 
 
Copyright © 2004 by the contributing authors. All material on the Schematics Cookbook web site is the property of the contributing authors.
The copyright for certain compilations of material taken from this website is held by the SchematicsEditorsGroup - see ContributorAgreement & LGPL.
Other than such compilations, this material can be redistributed and/or modified under the terms of the GNU Lesser General Public License (LGPL), version 2.1, as published by the Free Software Foundation.
Ideas, requests, problems regarding Schematics Cookbook? Send feedback.
/ You are Main.guest