Making a system call that returns the stdout outpu

2019-01-05 02:39发布

Perl and PHP do this with backticks. For example,

$output = `ls`;

Returns a directory listing. A similar function, system("foo"), returns the operating system return code for the given command foo. I'm talking about a variant that returns whatever foo prints to stdout.

How do other languages do this? Is there a canonical name for this function? (I'm going with "backtick"; though maybe I could coin "syslurp".)

27条回答
我欲成王,谁敢阻挡
2楼-- · 2019-01-05 03:07

In C on Posix conformant systems:

#include <stdio.h> 

FILE* stream = popen("/path/to/program", "rw");
fprintf(stream, "foo\n"); /* Use like you would a file stream. */
fclose(stream);
查看更多
地球回转人心会变
3楼-- · 2019-01-05 03:09

Well, since this is system dependent, there are many languages that do not have a built-in wrapper for the various system calls needed.

For example, Common Lisp itself was not designed to run on any specific system. SBCL (the Steel Banks Common Lisp implementation), though, does provide an extension for Unix-like systems, as do most other CL implementations. This is much more "mighty" than just getting the output, of course (you have control over the running process, can specify all kinds of stream directions, etc., confer to the SBCL manual, chapter 6.3), but it is easy to write a little macro for this specific purpose:

(defmacro with-input-from-command ((stream-name command args) &body body)
  "Binds the output stream of command to stream-name, then executes the body
   in an implicit progn."
  `(with-open-stream
       (,stream-name
         (sb-ext:process-output (sb-ext:run-program ,command
                                                    ,args
                                                    :search t
                                                    :output :stream)))
     ,@body))

Now, you can use it like this:

(with-input-from-command (ls "ls" '("-l"))
  ;;do fancy stuff with the ls stream
  )

Perhaps you want to slurp it all into one string. The macro is trivial (though perhaps more concise code is possible):

(defmacro syslurp (command args)
  "Returns the output from command as a string. command is to be supplied
   as string, args as a list of strings."
  (let ((istream (gensym))
        (ostream (gensym))
        (line (gensym)))
    `(with-input-from-command (,istream ,command ,args)
       (with-output-to-string (,ostream)
         (loop (let ((,line (read-line ,istream nil)))
                 (when (null ,line) (return))
                 (write-line ,line ,ostream)))))))

Now you can get a string with this call:

(syslurp "ls" '("-l"))
查看更多
叼着烟拽天下
4楼-- · 2019-01-05 03:10

Here's another Lisp way:

(defun execute (program parameters &optional (buffer-size 1000))
  (let ((proc (sb-ext:run-program program parameters :search t :output :stream))
        (output (make-array buffer-size :adjustable t :fill-pointer t 
                            :element-type 'character)))
    (with-open-stream (stream (sb-ext:process-output proc))
      (setf (fill-pointer output) (read-sequence output stream)))
    output))

Then, to get your string:

(execute "cat" '("/etc/hosts"))

If you want to run a command that creates prints a great deal of info to STDOUT, you can run it like this:

(execute "big-writer" '("some" "parameters") 1000000)

The last parameter preallocates a large amount of space for the output from big-writer. I'm guessing this function could be faster than reading the output stream one line at a time.

查看更多
登录 后发表回答