This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-11-17
Channels
- # architecture (115)
- # asami (64)
- # aws (2)
- # babashka (65)
- # beginners (91)
- # calva (21)
- # clara (3)
- # cljdoc (5)
- # cljsrn (1)
- # clojure (331)
- # clojure-australia (15)
- # clojure-europe (16)
- # clojure-italy (7)
- # clojure-nl (8)
- # clojure-uk (10)
- # clojurescript (10)
- # conjure (4)
- # core-typed (6)
- # cryogen (12)
- # data-science (1)
- # datomic (9)
- # depstar (2)
- # emacs (1)
- # events (1)
- # figwheel-main (1)
- # fulcro (2)
- # graalvm (9)
- # helix (4)
- # java (45)
- # kaocha (4)
- # malli (14)
- # meander (4)
- # membrane (34)
- # mid-cities-meetup (6)
- # pedestal (3)
- # re-frame (7)
- # reitit (4)
- # shadow-cljs (4)
- # spacemacs (5)
- # sql (29)
- # tools-deps (22)
What's the babashka.process way to do echo -e "foo\nbar\nbaz" | fzf
, such that fzf
uses the piped input, and can still read input from the keyboard? If :in
is :inherit
-ed, input can't come from somewhere else; if :in
is not :inherit
-ed, fzf
can't read from the keyboard
It dawned on me that I don't really know how the above command's stdin is managed anyway. I hypothesized that stdin is read from the pipe (the echo
command), the pipe is then closed, and then (re-?)connected to the tty. That appears to be wrong, because /proc/<pid>/fd/0
was clearly pointing at a pipe, after the command was accepting input from the keyboard. So now I'm thinking that stdin is read from the pipe only, and keyboard input is then read directly from /dev/tty
. But if that's the case, why can't fzf
read from /dev/tty
when stdin isn't inherited from bb?
I just found https://junegunn.kr/2016/02/using-fzf-in-your-program, and the clojure example worked for me when run with bb
- could probably be ported to a more terse bb/process function
(require '[ :as io])
(import 'java.lang.ProcessBuilder$Redirect)
(defmacro with-filter
[command & forms]
`(let [sh# (or (System/getenv "SHELL") "sh")
pb# (doto (ProcessBuilder. [sh# "-c" ~command])
(.redirectError
(ProcessBuilder$Redirect/to (io/file "/dev/tty"))))
p# (.start pb#)
in# (io/reader (.getInputStream p#))
out# (io/writer (.getOutputStream p#))]
(binding [*out* out#]
(try ~@forms (.close out#) (catch Exception e#)))
(take-while identity (repeatedly #(.readLine in#)))))
(with-filter "fzf -m"
(dotimes [n 50]
(println n)
(Thread/sleep 5)))
Nice! I changed the dotimes part with (println (slurp *in*))
so you can pipe a file to this script
@UJY23QLS1 Using babashka.process:
(require '[babashka.process :as p])
(defn fzf [s]
(let [proc (p/process ["fzf" "-m"]
{:in s :err :inherit
:out :string})]
(:out @proc)))
(fzf (slurp *in*))
Thanks @UGNFWPPGF, @U04V15CAJ! It looks like it's the :err :inherit
that makes the difference. It kinda makes sense that it should be inherited, but I'm still not all that clear on why it has this effect. Do you have more insight?
@UJY23QLS1 :err :inherit
only influences the output, so the rendering in the screen that fzf does
From Java you can only send one inputstream. But I think fzf will consume it until EOF and then maybe it will switch to reading the keyboard. This is my guess.
It seems like there's more than that going on, though: if you don't include :err :inherit
, fzf understandably won't output anything, but it will also not respond to any input.
@UJY23QLS1 it does, at least on my machine. just press a couple of up arrows and then hit enter
I'm testing with this:
(require '[babashka.process :as p])
(defn fzf [s]
(let [proc (p/check (p/process ["fzf" #_"-m"]
{:in s #_#_:err :inherit
:out :string}))]
(:out proc)))
; (fzf (slurp *in*))
(fzf "foo\nbar\nbaz")
so:
(defn fzf [s]
(let [proc (p/process ["fzf" #_"-m"]
{:in s :err :string ;; inherit
:out :string})]
(:out @proc)))
I know the library fzf uses that provides is_tty()
has different low level (termios) implementations for the different OSs. Maybe it stems from there...?
It's a game changer, yes. I resisted at first, but it quickly convinced me. As a vim user, it has also become indispensable to navigation https://github.com/junegunn/fzf.vim#commands
@UJY23QLS1 if you use rofi, the integration there is nice too - something like
(require '[babashka.process :refer [process]])
(defn rofi [s]
(let [proc (process
["rofi" "-i" "-dmenu" "-mesg" "Select" "-sync" "-p" "*"]
{:in s :err :inherit
:out :string})]
(:out @proc)))
(rofi (slurp *in*))
file walker with babashka.fs:
user=> (fs/walk-file-tree {:pre-visit-dir (fn [dir attrs] (prn '-> (str (fs/relativize dir))) (if (fs/hidden? dir) :terminate :continue))})
-> ""
-> "test"
-> "test/babashka"
-> ".cpcache"
a few docstrings: https://babashka.org/fs/babashka.fs.html
of course, the multi-arg counter part to io/file
:
user=> (fs/path "foo/bar" "baz")
#object[sun.nio.fs.UnixPath 0x21baa903 "foo/bar/baz"]
@nha why do you think that? it should work:
$ time bb -e '(Thread/sleep 5000)'
bb -e '(Thread/sleep 5000)' 0.01s user 0.01s system 0% cpu 5.028 total
Some people love them, others find them confusing. I'm contemplating an alternative here: https://github.com/borkdude/babashka/issues/613 Just an idea so far.
One downsize of using flags is that these things don't work well with full blown scripts
I made a separate doc page for the io flags now, so people know how to port it to script usage: https://github.com/borkdude/babashka/blob/master/doc/io-flags.md