简体   繁体   中英

How to run an interactive CLI program from within Clojure?

I'd like to run an interactive CLI program from within Clojure (eg, vim) and be able to interact with it.

In bash and other programming languages, I can do that with

vim > `tty`

I tried to do the same in Clojure:

(require '[clojure.java.shell :as shell])
(shell/sh "vim > `tty`")

but it just opens vim without giving me tty.


Background: I'm developing a Clojure CLI tool which parses emails and lets a user edit the parsed data before saving them on the disk. It works the following way:

  1. Read a file with email content and parse it. Each email is stored as a separate file.
  2. Show a user the parsed data and let the user edit the data in vim. Internally I create a temporary file with the parsed data, but I don't mind doing it another way if that would solve my issue.
  3. After a user finished editing the parsed data (they might decide to keep it as it is) append the data to a file on a disk. So all parsed data are saved to the same file.
  4. Go to 1st step if there are any files with emails left.

This code relies on Clojure Java interop to make use of Java's ProcessBuilder class.

(defn -main
[]
;use doseq instead of for because for is lazily evaluated
(doseq [i [1 2 3]]
;extract current directory from system variable
(let [file-name (str "test" i ".txt")
      working-directory (trim-newline (:out (sh "printenv" "PWD")))]
  (spit file-name "")

  ;this is where fun begins. We use ProcessBuilder to forward commands to terminal
  ;we pass a list of commands and their arguments to its constructor
  (let [process-builder (java.lang.ProcessBuilder. (list "vim" (str working-directory "/" file-name)))
  ;inherit is a configuration constant
        inherit (java.lang.ProcessBuilder$Redirect/INHERIT)]
  ;we configure input, output and error redirection
  (.redirectOutput process-builder inherit)
  (.redirectError process-builder inherit)
  (.redirectInput process-builder inherit)

  ;waitFor used to block execution until vim is closed
  (.waitFor (.start process-builder))
  )
  ;additional processing here
)
)
;not necessary but script tends to hang for around 30 seconds at end of its execution
;so this command is used to terminate it instantly
(System/exit 0)
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM