11

Given a command-line style path to a command such as bin/server.exe or ping, how can I get the full path to this executable (as cmd or Process.Start would resolve it)?

I tried Path.GetFullPath, but it always expands relative to the working directory. It expands bin/server.exe correctly, however given ping it returns c:\users\matt\ping (non-existent). I want c:\Windows\system32\ping.exe.

Edit: I would like the same behaviour as cmd. Some considerations:

  1. When there is a local executable with the same name as one in the path, cmd prefers the local one
  2. cmd can expand the command server to server.bat or server.exe (adding the file extension)

I also tried Windows' command-line tool called where . It does almost I want:

Displays the location of files that match the search pattern. By default, the search is done along the current directory and in the paths specified by the PATH environment variable.

>where ping
C:\Windows\System32\PING.EXE
>where bin\server
INFO: Could not find files for the given pattern(s).

(This question is hard to search around because of the two different meanings of the word 'path')

Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 5
    "two different meanings of the word 'path'" - actually, you probably want the other meaning: search through the PATH environment variable to find the first folder that contains ping.exe. It looks like you want the API method [SearchPath](http://msdn.microsoft.com/en-us/library/windows/desktop/aa365527.aspx) - I'm not sure if this is available in .NET too, and [this question](http://stackoverflow.com/questions/1684392/is-there-a-managed-api-for-kernel32-searchpath) suggests it isn't. – Rup Jul 26 '12 at 11:14
  • Adding the file extension: that's easy too - you need to also look at the environment variable PATHEXT, semi-colon separated, and try combining those with the program name and PATH part. – Rup Jul 26 '12 at 15:06
  • related- http://stackoverflow.com/questions/3855956/check-if-an-executable-exists-in-the-windows-path – barlop May 01 '16 at 09:21
  • Related: https://stackoverflow.com/questions/11242368/powershell-test-if-executable-in-path – Martin Ba Aug 16 '17 at 07:53

5 Answers5

15

Considering PATHEXT too, stealing from Serj-Tm's answer (sorry! +1 to him):

public static string WhereSearch(string filename)
{
    var paths = new[]{ Environment.CurrentDirectory }
            .Concat(Environment.GetEnvironmentVariable("PATH").Split(';'));
    var extensions = new[]{ String.Empty }
            .Concat(Environment.GetEnvironmentVariable("PATHEXT").Split(';')
                       .Where(e => e.StartsWith(".")));
    var combinations = paths.SelectMany(x => extensions,
            (path, extension) => Path.Combine(path, filename + extension));
    return combinations.FirstOrDefault(File.Exists);
}

Sorry the indentation's a bit all-over-the-place - I was trying to make it not scroll. I don't know if the StartsWith check is really necessary - I'm not sure how CMD copes with pathext entries without a leading dot.

Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
Rup
  • 33,765
  • 9
  • 83
  • 112
  • Ah, I never knew about $PATHEXT – Colonel Panic Sep 29 '12 at 14:43
  • 2
    For the completeness, you should include the [following check](http://blogs.msdn.com/b/oldnewthing/archive/2011/07/25/10189298.aspx): static string GetExecutablePathFromAppPaths(string exename) { const string appPaths = @"Software\Microsoft\Windows\CurrentVersion\App Paths"; var executableEntry = Path.Combine(appPaths, exename); using (var key = Registry.CurrentUser.OpenSubKey(executableEntry) ?? Registry.LocalMachine.OpenSubKey(executableEntry)) { return (key != null) ? (string)key.GetValue(null) : null; } } – Vlad May 24 '13 at 14:19
  • @ColonelPanic what are you talking about. this is regarding Windows. Windows uses %VAR% not $VAR this is not *nix – barlop Apr 26 '16 at 21:00
  • where is DarkGray's answer? – barlop Apr 26 '16 at 21:04
  • 1
    @barlop Looks like he's renamed himself in the past few years. It's the one I commented on, now by Serj-Tm. – Rup Apr 26 '16 at 21:08
  • cmd doesn't recognize extensions in PATHEXT that don't start with a dot, but one would have to be completely blind to put an extension in there without a dot. and a PATHEXT with an extension without a dot would be misconfigured, I think it's OK to assume a non corrupt PATHEXT and thus that every entry there starts with a dot.. If you want to take into account there not being a dot, it'd probably be better to exit with an error saying their PATHEXT variable is corrupt and they should fix it! – barlop Apr 26 '16 at 22:12
  • where you have this line `var extensions = new[]{ String.Empty }.Concat(Environment.GetEnvironmentVariable("PATHEXT").Split(';') .Where(e => e.StartsWith(".")));` Is there any reason for that new[]{String.Empty}.Concat, i.e. wouldn't it be more compact to say `var extensions=Environment.GetEnvironmentVariable("PATHEXT").Split(';').Where(e => e.StartsWith("."));` ? – barlop Apr 27 '16 at 07:55
  • @barlop I think it's to catch the case where you include the extension in the search query, i.e. `WhereSearch("PING.EXE")` instead of just `WhereSearch("PING")`. But it was a few years ago! – Rup Apr 27 '16 at 09:59
  • @Rup is that a response to my "cmd doesn't recognize........." comment, or my "where you have this......." comment? – barlop Apr 27 '16 at 10:05
  • @barlop The second one: 'is there any reason...'. Honestly I didn't over-think this: I just came up with something that solved the problem in the question and worked in a few simple tests. I don't think I've looked this since then. You're right, there are probably edge-cases it misses or could handle better. Feel free to post a better version if you want, e.g. if you want to better deal with malformed PATHEXT. – Rup Apr 27 '16 at 10:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110370/discussion-between-barlop-and-rup). – barlop Apr 27 '16 at 10:52
9
public static string GetFullPath(string filename)    
{
 return new[]{Environment.CurrentDirectory}
  .Concat(Environment.GetEnvironmentVariable("PATH").Split(';'))
  .Select(dir => Path.Combine(dir, filename))
  .FirstOrDefault(path => File.Exists(path));
}
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61
  • 1
    Neat - you've got `fileName` in one place and `program` in the other though. (You can also write the last line just `.FirstOrDefault(File.Exists)` since `File.Exists` already accepts and returns the correct types, but I think it's clearer as-is.) – Rup Jul 26 '12 at 11:41
4

If you're only interested in searching the current directory and the paths specified in the PATH environment variable, you can use this snippet:

public static string GetFullPath(string fileName)
{
    if (File.Exists(fileName))
        return Path.GetFullPath(fileName);

    var values = Environment.GetEnvironmentVariable("PATH");
    foreach (var path in values.Split(';'))
    {
        var fullPath = Path.Combine(path, fileName);
        if (File.Exists(fullPath))
            return fullPath;
    }

    return null;
}
Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
3

You have to search the entire disk.

Windows can respond to things like, iexplore, ping, cmd, etc, because they are in the registry under this key:

HKEY_LOCAL_MACHINE
   SOFTWARE
       Microsoft
           Windows
               CurrentVersion
                   App Paths

The only other way is to search the entire disk for the application.

EDIT: My understanding was, that you want to search for any random executable name, not the ones that are already known to Windows..

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
2
internal class Program
{
    static void Main(string[] args)
    {
        string fullPath = GetExactPathFromEnvironmentVar("ping.exe");
        if (!string.IsNullOrWhiteSpace(fullPath))
            Console.WriteLine(fullPath);
        else
            Console.WriteLine("Not found");
    }

    static string GetExactPathFromEnvironmentVar(string program)
    {
        var pathVar = System.Environment.GetEnvironmentVariable("PATH");
        string[] folders = pathVar.Split(';');

        foreach (var folder in folders)
        {
            string path = Path.Combine(folder, program);
            if (File.Exists(path))
            {
                return path;
            }
        }

        return null;
    }
}

HTH

Tamir
  • 3,833
  • 3
  • 32
  • 41