Using fzf in your program
2016. 02. 10.

fzf is written in Golang and distributed as an executable binary instead of as a library you can link to your Go program. Some might see this as a limitation, it is, but you can still use fzf for interactive filtering in different programming languages, just like you use it in bash or zsh.

In this post, I will demonstrate how it can be done in a few programming languages that I use regularly.


We write with_filter function that takes fzf command as the first argument and a block which produces the input to the command, and returns the selected entries as an array.

def with_filter(command)
  io = IO.popen(command, 'r+')
    stdout, $stdout = $stdout, io
    yield rescue nil
    $stdout = stdout

with_filter('fzf -m') do
  1000.times do |n|
    puts n
    sleep 0.005

The function is generic in the sense you can use it with any other external interactive/non-interactive filters, e.g. fzf-tmux or grep 123.


We do the same with Clojure. The code here is a bit more involved, but it will give you a hint on how it can be done in other JVM languages.

(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])
                  (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 1000]
    (println n)
    (Thread/sleep 5)))

You might have noticed the reference to /dev/tty which makes the code not portable across different platforms. However, fzf only works on Unix environment, so this is a non-issue, at least for now.


This Go example isn't as elegant as the previous ones as there's no "automagic" stdout redirection for the goroutine that feeds the input, so we have to use fmt.Fprintln instead of fmt.Println.

package main

import (

func withFilter(command string, input func(in io.WriteCloser)) []string {
    shell := os.Getenv("SHELL")
    if len(shell) == 0 {
        shell = "sh"
    cmd := exec.Command(shell, "-c", command)
    cmd.Stderr = os.Stderr
    in, _ := cmd.StdinPipe()
    go func() {
    result, _ := cmd.Output()
    return strings.Split(string(result), "\n")

func main() {
    filtered := withFilter("fzf -m", func(in io.WriteCloser) {
        for i := 0; i < 1000; i++ {
            fmt.Fprintln(in, i)
            time.Sleep(5 * time.Millisecond)

That's all for now. I'd be happy to hear about more examples in different languages that are not listed here.

» capture | close