From: Thomas W. <tw...@ti...> - 2002-08-23 11:45:55
|
Hi, I am thinking about the following problem for a couple of hours now, but it seems I cannot get it work. I want to call a Unix command from within CLISP and return its output as a list of strings. The following definition does just what I want: (defun system-call (cmd &rest args) (let ((in (ext:run-program cmd :arguments args :input nil :output :stream))) (let ((res (loop as l = (read-line in nil) while l do collect l))) (close in) res))) However, if the Unix command produces an error message to standard error I cannot catch (or discard it): [16]> (system-call "/usr/bin/ls" "-l" "/nothing") /nothing: No such file or directory NIL Any ideas? Thanks, Thomas |
From: John K. H. <hi...@al...> - 2002-08-23 12:23:13
|
> However, if the Unix command produces an error message to standard > error I cannot catch (or discard it): One option that is kludgy but will at definitely work is to make the following 2-line program (call it "combine_output"): ---- Cut here --- #!/bin/sh $* 2>&1 ---- Cut here --- Then use SYSTEM-CALL as you have defun'ed it, but using "combine_output" as the program, and your real program as the first argument: (system-call "/path/to/comine_output" "/usr/bin/ls" "-l" "/nothing" "/more-nothing") This worked for me (I got a list of two strings) on Linux. If you want to _discard_ the errors (hmmmmmmm :) make a script called "discard_errors" that does: ---- Cut here --- #!/bin/sh $* 2>/dev/null ---- Cut here --- Then call as above. BTW, what that "2" refers to is the Unix "standard error" stream. Unix assigns the numbers 0,1,2 to three default streams, "input," "output" and "errors" (respectively) which it creates with every program. In the shell, you can re-direct where these go by referring to the streams by number as above. The directive "2>1" means "make stream 2 (errors) go wherever stream 1 (normal output) goes." In the case of merging the outputs, they will get interleaved in no predictable order - this may create problems for you parsing them. Because of buffering, Unix won't even guarantee that they appear in the merged output in the order they were written to by the program (!). If you need access to the output and the error streams separately, you may have to get uglier and save the errors to a temp. file and read that. If you're discarding the error stream (hmmmm :) there's no problem. There might be a better way but I thought I'd at least give you an "out" to get through the day. Good day! --- John Hinsdale, Alma Mater Software, Inc., Tarrytown, NY 10591-3710 USA hi...@al... | http://www.alma.com/staff/hin | +1 914 631 4690 |
From: Randall R S. <rrs...@cr...> - 2002-08-23 13:58:46
|
John, Thomas, A slight improvement on John's solution is to use the "exec" shell built-in. This command causes the shell to overlay or replace itself (using the Unix / POSIX "exec" system call) with the specified command. Otherwise, the shell will "fork" and create a sub-process to run that command. When there's only one command, this is a wasted overhead. Thus the "comine_output" script would look like this: -==--==--==--==- #!/bin/sh exec $* 2>&1 -==--==--==--==- It's also a very good idea to quote the arguments individually. To do that, use this alternate variable expansion syntax: -==--==--==--==- #!/bin/sh exec "$@" 2>&1 -==--==--==--==- If you don't do this, arguments with spaces or shell metacharacters (there are many) will causes error or misinterpretations of your invocation. The "exec" command should be available in all shells. Only bourne-shell-like shells have "$@". That list includes (at least) the Bourne Shell, ASH, BASH and KSH. Good luck. Randall Schulz Mountain View, CA USA At 05:23 2002-08-23, John K. Hinsdale wrote: > > However, if the Unix command produces an error message to standard > > error I cannot catch (or discard it): > >One option that is kludgy but will at definitely work is to make the >following 2-line program (call it "combine_output"): > >---- Cut here --- >#!/bin/sh >$* 2>&1 >---- Cut here --- > >Then use SYSTEM-CALL as you have defun'ed it, but using >"combine_output" as the program, and your real program as the first >argument: > >(system-call "/path/to/comine_output" "/usr/bin/ls" "-l" "/nothing" >"/more-nothing") > >This worked for me (I got a list of two strings) on Linux. > >If you want to _discard_ the errors (hmmmmmmm :) make a script called >"discard_errors" that does: >---- Cut here --- >#!/bin/sh >$* 2>/dev/null >---- Cut here --- > >Then call as above. > >BTW, what that "2" refers to is the Unix "standard error" stream. >Unix assigns the numbers 0,1,2 to three default streams, "input," >"output" and "errors" (respectively) which it creates with every >program. In the shell, you can re-direct where these go by referring >to the streams by number as above. The directive "2>1" means "make >stream 2 (errors) go wherever stream 1 (normal output) goes." > >In the case of merging the outputs, they will get interleaved in no >predictable order - this may create problems for you parsing them. >Because of buffering, Unix won't even guarantee that they appear in >the merged output in the order they were written to by the program >(!). If you need access to the output and the error streams >separately, you may have to get uglier and save the errors to a >temp. file and read that. If you're discarding the error stream >(hmmmm :) there's no problem. > >There might be a better way but I thought I'd at least give you an >"out" to get through the day. > >Good day! > >--- >John Hinsdale, |
From: Marco B. <em...@be...> - 2002-08-23 14:29:07
|
Thomas Wager <tw...@ti...> writes: > (defun system-call (cmd &rest args) > (let ((in (ext:run-program cmd :arguments args > :input nil > :output :stream))) ((in (ext:run-shell-command (format nil "~S ~{ \'~S\'~} 2>&1" cmd args) :input nil :output :stream))) > (let ((res > (loop as l = (read-line in nil) > while l > do > collect l))) > (close in) > res))) this avoids having to create a seperate program, but will you will have problems if elements of args contains #\' chars. 1) wouldn't it be nice to have an :ERROR-STREAM keyword arg ala :INPUT and :OUTPUT? (Sam, if you don't have objections i'd be happy to take a whack at this.) 2) Why can't one specify a stream as the arg to :OUTPUT or :INPUT? -- -Marco Ring the bells that still can ring. Forget your perfect offering. There is a crack in everything. That's how the light gets in. -Leonard Cohen |
From: Sam S. <sd...@gn...> - 2002-08-23 15:02:27
|
> * In message <m2u...@be...> > * On the subject of "Re: [clisp-list] How to suppress / redirect standard error?" > * Sent on 23 Aug 2002 16:29:41 +0200 > * Honorable Marco Baringer <em...@be...> writes: > > 1) wouldn't it be nice to have an :ERROR-STREAM keyword arg ala :INPUT > and :OUTPUT? (Sam, if you don't have objections i'd be happy to > take a whack at this.) go ahead! > 2) Why can't one specify a stream as the arg to :OUTPUT or :INPUT? a missing feature? thanks! -- Sam Steingold (http://www.podval.org/~sds) running RedHat7.3 GNU/Linux <http://www.camera.org> <http://www.iris.org.il> <http://www.memri.org/> <http://www.mideasttruth.com/> <http://www.palestine-central.com/links.html> Lisp: its not just for geniuses anymore. |