Fork me on GitHub
#babashka
<
2023-12-14
>
leifericf09:12:29

I need to check whether some Linux command is available in the user's shell. Here is what I came up with:

(defn sh->out
  [opts & args]
  (-> (apply process/sh opts args)
      :out))

(defn command-available? [command]
  (when-not (= (or nil "")
               (sh->out "command" "-v" command))
    true))

(command-available? "wget") ;; => true

(command-available? "flippetyfloppetyfloop") ;; => nil (punning)
Is this a reasonable approach, or is there a better way to do that?

elken09:12:22

which <command>

๐Ÿ™Œ 1
leifericf09:12:16

Ah! Of course. Thanks!

leifericf09:12:44

which something returns something not found command -v something returns nil (or "" in Babashka) I suppose it's a bit easier to check for nil or "" than to parse the command output to look for not found? :thinking_face:

elken09:12:59

Which also returns a non-zero exit status iirc

leifericf09:12:25

Ah! Yes, it does, indeed.

leifericf09:12:30

I was tricked by my shell.

borkdude09:12:08

(babashka.fs/which "wget")

๐Ÿ˜ฎ 2
๐Ÿ”ฅ 3
elken09:12:59

Ah I thought there was something in fs ๐Ÿ˜„

leifericf09:12:03

Since we're on the topic. My Babashka script depends on https://learn.microsoft.com/en-us/cli/azure/install-azure-cli (the az command). Does Babashka provide any cross-platform mechanisms to help install missing shell commands?

borkdude09:12:02

no, bb isn't a cross-platform package manager

๐Ÿ‘ 1
leifericf09:12:21

Thanks! Then, I suppose I'll abort and tell the user to install it themselves.

borkdude09:12:41

that might be the best, I think I prefer that 99% of the time over some script installing tools for me

๐Ÿ‘ 2
leifericf09:12:56

Now that's clean!

(defn command-available? [command]
  (when (file/which command) true))
I'm porting a 2K line shell script to Babashka, and that one function replaces a 28-line shell function.

๐Ÿ˜€ 3
leifericf10:12:55

Sharing snippet to parse and compare version numbers in case it's useful to others:

(defn sh-out->json [opts & args]
  (-> (apply process/sh opts args)
      :out
      (json/parse-string true)))

(defn parse-version-num [version-str]
  (->> (string/split version-str #"\.")
       (mapv #(Integer/parseInt %))))

(defn compare-version-nums [num-1 num-2]
  (compare (parse-version-num num-1)
           (parse-version-num num-2)))

(defn az-required-version? [required-num]
  (let [installed-num (-> (sh-out->json "az" "version" "--output" "json")
                          :azure-cli)]
    (when (-> (compare-version-nums installed-num required-num)
              (>= 0))
      true)))
Improvement suggestions are welcome! I'm still learning Clojure.

borkdude10:12:41

Thereโ€™s a nice lib for this in case you need to support more edge cases, itโ€™s called version-clj

๐Ÿ˜ฎ 1
๐Ÿ‘€ 1
borkdude10:12:47

It works in bb

borkdude10:12:20

Integer/parseInt can be replaced with parse-long

๐Ÿ’ก 1
borkdude10:12:37

although that doesn't throw in case the input is not a number

leifericf11:12:02

It appears I've finally fallen into the Clojure trap of just writing my own instead of searching for libs ๐Ÿ˜‚

leifericf11:12:11

Does a "TUI library" exist for Clojure that works well with Babashka? I don't necessarily need a full-fledged TUI with menus, colors, loading bars, etc. Just something that loops, prompting the user for a selection or freeform input, then does something with that input, prints user feedback, and repeats, sort of like a REPL.

leifericf11:12:11

Frickin' fantastic! Thanks!