On Thursday 18 December 2008 12:07, Stephan Mühlstrasser wrote:
> Hi,
>
> I've not yet seen any examples on how to deal with external processes
> in Clojure (I hope I didn't overlook something in clojure-contrib).
>
> The following is my attempt to start a sub-process and to pass
> through stdout and stderr. The shell command prints out 1000 lines
> "hello" and a final "command finished". The problem is that nothing
> is printed by the Clojure program. If I increase the number of lines
> for example to 2000 (change "head -1000" to "head -2000"), I see a
> lot of output, but it is cut off somewhere in the middle and the
> final "command finished" does never appear.
I just did something virtually identical to this:
user=> (sh date)
Thu Dec 18 12:19:42 PST 2008
(That's a macro for the convenience of not having to either use string literals
or quote the arguments. The real work is done in a function.)
> ...
>
> (let [pb (new ProcessBuilder ["sh" "-c" "yes hello | head -1000; echo
> command finished"])
> proc (.start pb)
> stdout (reader (.getInputStream proc))
> stderr (reader (.getErrorStream proc))
> stdout-agent (agent stdout)
> stderr-agent (agent stderr)]
> (send stdout-agent copy (writer *out*))
> (send stderr-agent copy (writer *err*))
> (await stdout-agent stderr-agent)
> (.waitFor proc)
> (shutdown-agents)
> (println "done"))
>
> Is this use of agents incorrect?
I would say it's an appropriate use, but you need to do it a little
differently: First of all, use (send-off ...) or you'll have to wait for
the agent to complete. Then use (await ...) on the agents.
> Why can the program terminate before all the output from the sub-
> process has been passed through?
As long as the sub-process produces no more output than the operating
system's pipe buffering limit, it can complete without blocking.
> Is there a better way to synchronize with sub-processes in Clojure,
> or is it necessary to synchronize completely at the Java level?
I don't understand this question.
Here's what my implementation looks like. It does not stand alone as
shown, but you can probably figure out what the missing pieces do:
(def *shell* "bash")
(def *shopt* "-c")
(defn- cat-proc-stream
"Copy all the bytes from stream to the writer"
[stream writer]
(binding [*out* writer]
(cat-stream stream)))
(defn shf
"Invoke a platform / external command"
[& args]
(let [out *out*
err *err*
cmd+args (flatten args)
builder (if (and (= (count cmd+args) 1) (string? (first cmd+args)))
(ProcessBuilder. (into-array (conj [] *shell* *shopt* (first
cmd+args))))
(ProcessBuilder. (into-array (map str cmd+args))))
process (.start builder)
stdout-copier (agent nil)
stderr-copier (agent nil)]
(send-off stdout-copier #(cat-proc-stream %2 err) (.getErrorStream process))
(send-off stderr-copier #(cat-proc-stream %2 out) (.getInputStream process))
(await stdout-copier stderr-copier))
)
(defmacro sh
"Invoke a platform / external command without evaluating arguments"
[& args]
`(shf '~args))
> Thanks
> Stephan
Randall Schulz
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---