Jindřich Mynarz
Jindřich Mynarz

Reputation: 1613

Detect non-empty STDIN in Clojure

How do you detect non-empty standard input (*in*) without reading from it in a non-blocking way in Clojure?

At first, I thought calling using the java.io.Reader#ready() method would do, but (.ready *in*) returns false even when standard input is provided.

Upvotes: 4

Views: 480

Answers (2)

Scott
Scott

Reputation: 1688

Is this what you are looking for? InputStream .available

(defn -main [& args]
  (if (> (.available System/in) 0)
    (println "STDIN: " (slurp *in*))
    (println "No Input")))

$ echo "hello" | lein run
STDIN:  hello

$ lein run
No Input

Update: It does seem that .available is a race condition checking STDIN. n alternative is to have a fixed timeout for STDIN to become available otherwise assume no data is coming from STDIN

Here is an example of using core.async to attempt to read the first byte from STDIN and append it to the rest of the STDIN or timeout.

(ns stdin.core
  (:require
   [clojure.core.async :as async :refer [go >! timeout chan alt!!]])
  (:gen-class))

(defn -main [& args]
  (let [c (chan)]
    (go (>! c (.read *in*)))
    (if-let [ch (alt!! (timeout 500) nil
                       c ([ch] (if-not (< ch 0) ch)))]
      (do
        (.unread *in* ch)
        (println (slurp *in*)))

      (println "No STDIN")))) 

Upvotes: 4

Alan Thompson
Alan Thompson

Reputation: 29958

Have you looked at PushbackReader? You can use it like:

  1. Read a byte (blocking). Returns char read or -1 if stream is closed.
  2. When returns, you know a byte is ready.
  3. If the byte is something you're not ready for, put it back
  4. If stream is closed (-1 return val), exit.
  5. Repeat.

https://docs.oracle.com/javase/8/docs/api/index.html?java/io/PushbackReader.html

If you need it to be non-blocking stick it into a future, a core.async channel, or similar.

Upvotes: 0

Related Questions