Search path for executable

Is there a more or less portable way of getting the path of an executable if available? When is_unix(), I guess something like

function find_in_path(program)
    try chomp(readstring(`which $program`))
    catch
        nothing
    end
end

works, but I don’t know how to deal with is_windows().

On Windows which is where

It seems that where.exe is the equivalent.

I am not a Windows user, but from googling it looks like where on Windows is closer to locate on Unix (searches the whole filesystem, not only executables).

If PATH environment variable is correctly set on Windows, this should work on any system:

function foo(exec)
    for dir in split(ENV["PATH"], @static is_windows() ? ";" : ":")
        if isdir(dir) && exec in readdir(dir)
            return joinpath(dir, exec)
        end
    end
    return ""
end

This is more or less what which does (you should also check that the file is actually executable)

1 Like

And be aware that which isn’t guaranteed to be present in minimal linux environments like some docker containers

1 Like

Thanks for the solution.

How can I do that? I found uperm etc, but I could not figure out the user’s uid.

In order to check if the file is executable by the owner, you can do this:

function foo(exec)
    for dir in split(ENV["PATH"], @static is_windows() ? ";" : ":")
        if isdir(dir) && exec in readdir(dir)
            file = joinpath(dir, exec)
            if isfile(file) && (uperm(file) & 0x01 > 0)
                return file
            end
        end
    end
    return ""
end

I also added the test that the file is a regular file, also directory are executable.

But maybe you want to determine if it’s executable by the current user? Note that which uses -x which doesn’t check if current user can run the program. For example, with both bash and dash, on my system:

$ [ -x /sbin/ifconfig ] && echo "Yes, we can" || echo "No, we can't"
Yes, we can

even if my user can’t run that program.

Alternatively this would also work

try success(`exe`)
catch
    error("exe not found")
end

I wouldn’t start a process just to see if my user can run it. For example, the yes command runs forever until it’s killed.

depending on the program, a -h, --help, -v, or --version flag would usually be safe to try. that doesn’t help you locate it, but can tell you if it’s on the path somewhere and the first one found is runnable.

fair point but I think if you know in advance the exe you want to check it’s a simple and effective way especially if you do as tkelman suggestions and use it with a flag such at -h
BTW
with your script the @static is not necessary
Also oddly enough this check:
uperm(file) & 0x01 > 0
fails for exe that I can actually execute on windows
e.g. something as simple as
“cmd.exe” fails with that check since it has permission 06 even thought it is executable

I like your script the following should be more robust

function foo(exec)
   if is_windows() && rsplit(exec, '.')[end] != "exe"
           exec *= ".exe"
    end
    for dir in split(ENV["PATH"], is_windows() ? ";" : ":")
        if isdir(dir) && exec in readdir(dir)
            file = joinpath(dir, exec)
            if isfile(file)
                if (uperm(file) & 0x01 > 0) || (if is_windows(); uperm(file) == 0x06 end)
                    return file
                end
            end
        end
    end
    return ""
end

such a function seems like a good candidate for inclusion in base

1 Like

Yeah, much better now :slight_smile:

Perhaps you could make a PR, or at least open an issue.

For the record, now Julia has Sys.which:

julia> Sys.which("julia")
"/usr/bin/julia"
2 Likes