How to check if a program exists in a system using Julia?

I wanted to query if a program is installed in a Linux system. From bash I would do something as type -p program or similar, as explained here.

If I try something such as read(`type -p program`) within julia I get an error. command -v program or hash program also do not work. If I try which program I get an error if the program does not exist. Any ideas?

In the meantime I am querying if the file/program exists in a known location. Thanks!

1 Like

If speed is not a concern here, then you can always use:

julia> a = run(`type -p ls`)

which will throw an error if the process does not return with exit code 0.

1 Like

I don’t think your suggestion works, I get an error. Secondly, read or run are very similar, only that read catches stdout and run returns nothing instead.

The problem I think is that run or read do not call the shell at all, and although many standard bash calls have been implemented (such as tail, less, cat, ls or others), hash, type or command haven’t. I do not know if they should, though.

An alternative could be

try run(`which nano`)
catch
end

Thanks.

Well, my suggestion is working fine here. I am using macOS, what error are you seeing?

I am using Ubuntu 18.04, julia v1.0.3.

julia> a = run(`type -p ls`)
ERROR: IOError: could not spawn `type -p ls`: no such file or directory (ENOENT)
Stacktrace:
 [1] _jl_spawn(::String, ::Array{String,1}, ::Cmd, ::Tuple{RawFD,RawFD,RawFD}) at ./process.jl:367
 [2] (::getfield(Base, Symbol("##493#494")){Cmd})(::Tuple{RawFD,RawFD,RawFD}) at ./process.jl:509
 [3] setup_stdio(::getfield(Base, Symbol("##493#494")){Cmd}, ::Tuple{RawFD,RawFD,RawFD}) at ./process.jl:490
 [4] #_spawn#492(::Nothing, ::Function, ::Cmd, ::Tuple{RawFD,RawFD,RawFD}) at ./process.jl:508
 [5] _spawn at ./process.jl:504 [inlined]
 [6] #run#503(::Bool, ::Function, ::Cmd) at ./process.jl:662
 [7] run(::Cmd) at ./process.jl:661
 [8] top-level scope at none:0

Did you try another command? Like jus ls?

Yes, I did. ls, which, tail, less, cat, etc. all work. Only type, hash or command fail. Even nano works and goes into editor mode.

type is a command built into bash, so it is not provided as a separate program on many versions of Linux. You can do

read(`bash -c "type -p ls"`)
4 Likes

Thanks Ralph, I like the suggestion. Although this is the output I get:
UInt8[0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x6c, 0x73, 0x0a]
Can it be helped?

Does chomp(read(bash -c “type -p ls”,String)) do what you want?

Well, chomp(join(map(Char, read(bash -c “type -p ls”)))) does.
What a mouthful, I don’t know if it can be shorter. Only chomp didn’t work, because I get an array.

You want Sys.which("program"), which searches the PATH directly, avoids spawning a shell, and is cross-platform.

12 Likes

Thanks, this is perfect.

You can’t, since type is a shell builtin. Try something like

run(`bash -c 'type -p ls'`)

This is a bit old so maybe by now there is a better way (I couldn’t find it :slight_smile: ). What worked for me is invoking the command and looking for IOErrors. If the command is found and invoked but the command itself is incorrectly invoked, different kinds of errors will result.

function run(command::Cmd)
  try Base.run(command)

  catch ex
    if isa(ex, IOError)
      @error "Can not find $(command.exec[1]). Please make sure that $(command.exec[1]) is installed and accessible."
    end

    rethrow(ex)
  end
end

I am wondering if you overlooked @stevengj’s solution above.

2 Likes

I did! Thanks for pointing it out!