[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Q] HOWTO redirect a i/o-port in Unix??



Your solution would not work (in general) if you are redirecting more than one
port at once because it may cause a dead-lock situation.
For example, let us consider a program "myprog" which writes a lot on its stdout
and little (or nothing) into stderr. I am going to redirect both stdout and
stderr into two files 'myprog.stdout' and 'myprog.stderr' correspondently.
I use your method of redirecting which essentially means that I read chars from
one port and write them to another one. Thus, I have two read/write loops -- one
for stdout redirection another one for stderr.

The dead-lock symptoms are:
1). MzScheme will block in the myprog's stderr read/write loop when there is
    nothing to read from the stderr.
2). "myprog" will block at the same time if it fills up the output buffer
    of its stdout pipe. This pipe is not being consumed at the MzScheme end
    because MzScheme is blocked by item 1).
Classic catch-22 situation and double-block on both processes.
Surely, one can make use of non-blocking read/write operations and put
multithreading to work to avoid these difficulties.
However, a much simplier solution would be to provide a primitive function like
(redirect-port! FROM TO) which essentially does dup2 and let Operating System
handle all the gory details of I/O operations. 

thanks,
--Leo

P.S. I am *REALLY* impressed by the rapid response time on this mailing list.
I posted my original message at 8:35pm PST and got several answers back in a
matter of few minutes not to mention that it was 10:35pm in Texas!!!!


>>>>> "MF" == Matthew Flatt <mflatt@cs.utah.edu> writes:
    MF> Quoting Leo Razoumov:
    >> More specifically, I am looking for a way to redirect stdout (and stderr) of a
    >> subprocess created with (process "myprog") into a file.

    MF> Quoting Matthias Felleisen:
    >> 
    >> Here is a (stupid) trick that I used in my Web page building scripts: 
    >> 
    >> (with-output-to-file "date.txt" 
    >> (lambda () 
    >> (display (read-line (car (process "date"))))))
    >> 
    >> This copies text from the process port to the file "date.txt". 

    MF> That works often. But sometimes the data coming from the process isn't
    MF> line-based, and often you want the data to get pushed from one port to
    MF> the other "in the background".

    MF> Here's a fairly general and efficient function:

    MF>  (define (copy-stream input-port output-port)
    MF>   (thread (lambda ()
    MF>             (let ([s (make-string 4096)])
    MF>               (let loop ()
    MF>                 (let ([l (read-string! s input-port)])
    MF>                   (unless (eof-object? l)
    MF>                     (display (if (< l 4096) (substring s 0 l) s)
    MF>                              output-port)
    MF>                     (loop))))
              
    MF>               ;; For most contexts, it's good to
    MF>               ;; close the ports at this point:
    MF>               (close-input-port input-port)
    MF>               (close-output-port output-port)))))

    MF> Here's an example of its use:

    MF>  ;; Pipe "ls" output to "wc":
    MF>  (define ls-p (process "ls"))
    MF>  (define wc-p (process "wc"))

    MF>  (copy-stream (car ls-p)   ; car is stdout of ls
    MF>               (cadr wc-p)) ; cadr is stdin of wc-p
    MF>  (define t (copy-stream (car wc-p)
    MF>                         (current-output-port)))
    MF>  ;; wait until "wc" is done:
    MF>  (thread-wait t)


    MF> Matthew