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?
Nice! I changed the dotimes part with (println (slurp *in*))
so you can pipe a file to this script
@clojurians-slack100 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 @russell.matney, @borkdude! 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?
Either way, I'm very glad that I can now use fzf from bb! \o/
@clojurians-slack100 :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.
@clojurians-slack100 it does, at least on my machine. just press a couple of up arrows and then hit enter
$ cat src/babashka/main.clj | bb examples/fzf.clj " (:require\n"
^ I commented out :err :inherit there
Interesting! On my Ubuntu 18.04 it definitely doesn't respond
Not even ^c
I have to kill
bb
Maybe that's an old version of fzf?
$ fzf --version
0.24.3 (brew)
fzf --version
0.24.1 (d4c9db0)
Does the same with 0.24.3 o_O
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")
let me try without -m
still works for me
ctrl-c also works
which bb version is this? 0.2.3?
Yip
maybe a subtle difference between linux and macos then
Yeah. That's what's so interesting π
could be an implementation difference inside fzf
maybe in linux it blocks on writing to stderr first
what do you get for :err :string
?
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...?
For :err :string
I get no output but input works!
right
then I think my guess was right
that or maybe the buffer for stderr is bigger in macos
Thanks for helping flesh this out, though. And many thanks for bb π:babashka:
It's quite a cool tool, this fzf
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
thanks @borkdude - i took at shot at this last night, but couldn't wrap my head around the input/output - thanks for the clean example!
@clojurians-slack100 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*))
TIL: rofi π
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 '[<http://clojure.java.io|clojure.java.io> :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)))
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"
this stops at the first hidden directory, pretty useless, just a demo ;)
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"]
Thread/sleep
does not do anything on babashka?
@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
gah youβre right I forgot it was ms not seconds π
Thank you! I'm quite fond of them. They're key to one-liners being so easy to write.
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
So it would be nice if we could have something that worked in both cases
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
I now finished watching your presentation. Really good. Posted it to Reddit too :)
awesome, thank you!
glad you liked it
Thanks for posting! I'll add it to the bb readme
oh cool, thank you!
Excellent explanation of the i/o flags :) Going to watch the rest tomorrow.