1

I'm creating a script that uses an external program that interacts with a server. I want to test first that the program is installed before trying to use it.

I looked up the preferred way to tell if a program was installed and found this post: How can I check if a program exists from a Bash script?

TLDR: It recommends "command -v <prog-name>" over "which <prog-name>" since it is POSIX compatible. The command should return 0 if the program was found, >0 otherwise.

So I used readProcessWithExitCode from System.Process as follows

readProcessWithExitCode "command" ["-v", "<some-program>"] ""

I get the following error when testing in GHCI

Exception: command: readCreateProcessWithExitCode: posix_spawnp: does not exist (No such file or directory)

I tried to use 'which' on 'command'. It tells me it does not exist although I can use it and it works as described in the man pages in my terminal.

What's going on here and how do I see if something is installed using Haskell?

Some system information:

  • GHC: 9.0.2
  • resolver: lts-19.11
  • "I use Arch btw"
VilePoison
  • 75
  • 4
  • 1
    `command` is a _shell_ builtin, not a program. And it will also ok other built-in commands (including `command -v command`), so not really suitable. I'd say `which` _is_ the better choice for what you want. And it also allows you to look at the actual path and decide whether that's suitable. – leftaroundabout Jun 18 '22 at 19:31
  • Alright. @leftaroundabout Should what you have be an answer? – VilePoison Jun 18 '22 at 19:36
  • 1
    No, because I'm not confident to recommend `which` – there _are_ certainly some things that can go wrong with it, and likely there's a better solution with something from the `unix` package. – leftaroundabout Jun 18 '22 at 19:53
  • 1
    Document that your script requires `` to work correctly, and let the caller ensure that it is installed correctly. – chepner Jun 18 '22 at 20:25

1 Answers1

5

I recommend that you simply run the program you want to run, and catch the exception you get if it isn't available. Like this:

catch
    (callProcess "lol-this-does-not-exist" []) -- or whatever
    (\e -> if isDoesNotExistError e then putStrLn "yikes" else throw e)
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • Probably should use something like `--version` as the argument. – leftaroundabout Jun 18 '22 at 20:20
  • 3
    @leftaroundabout I disagree completely. Simply run the command you actually want to run. If what you really wanted was to run `mpv my-fair-lady.mp3`, then run `mpv my-fair-lady.mp3`, not `mpv --version`. – Daniel Wagner Jun 18 '22 at 20:28
  • @DainelWagner This makes sense, is it better to use callProcess or readProcessWithExitCode? I figured the latter is better since it provides the error code. – VilePoison Jun 19 '22 at 13:06
  • 1
    @VilePoison I'm not tied to `callProcess`; I used it only because it was the simplest one that I could use to demonstrate my point. (Hence the `or whatever` comment -- use the function that's best for your needs.) – Daniel Wagner Jun 19 '22 at 17:51
  • @DanielWagner Oh I see. I'm not an expert with process calling in general. I was just curious if `callProcess` has unique executions for each exit code. Thanks for the help – VilePoison Jun 19 '22 at 17:59