Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn A Full Path Into A Path With Environment Variables

Tags:

c#

I want to turn a full path into an environment variable path using c#

Is this even possible?

i.e.

C:\Users\Username\Documents\Text.txt -> %USERPROFILE%\Documents\Text.txt
C:\Windows\System32\cmd.exe -> %WINDIR%\System32\cmd.exe
C:\Program Files\Program\Program.exe -> %PROGRAMFILES%\Program\Program.exe
like image 377
nlstack01 Avatar asked Aug 09 '19 19:08

nlstack01


People also ask

How do you append PATH to PATH variable?

To add a path to the PATH environment variableOn the Start menu, right-click Computer. On the context menu, click Properties. In the System dialog box, click Advanced system settings. On the Advanced tab of the System Properties dialog box, click Environment Variables.

Is PATH an environment variable?

PATH is an environment variable on Unix-like operating systems, DOS, OS/2, and Microsoft Windows, specifying a set of directories where executable programs are located. In general, each executing process or user session has its own PATH setting.


1 Answers

It is possible by going over all environment variables and checking which variable's value is contained in the string, then replacing that part of the string with the corresponding variable name surrounded by %.

First naive attempt:

string Tokenify(string path)
{
    foreach (DictionaryEntry e in Environment.GetEnvironmentVariables())
    {
        int index = path.IndexOf(e.Value.ToString());
        if (index > -1)
        {
            //we need to make sure we're not already inside a tokenized part.
            int numDelimiters = path.Take(index).Count(c => c == '%');
            if (numDelimiters % 2 == 0)
            {
                path = path.Replace(e.Value.ToString(), $"%{e.Key.ToString()}%");
            }
        }
    }
    return path;
}

The code currently makes a faulty assumption that the environment variable's value appears only once in the path. This needs to be corrected, but let's put that aside for now.

Also note that not all environment variables represent directories. For example, if I run this method on the string "6", I get "%PROCESSOR_LEVEL%". This can be remedied by checking for Directory.Exists() on the environment variable value before using it. This will probably also invalidate the need for checking whether we are already in a tokenized part of the string.

You may also want to sort the environment variables by length so to always use the most specific one. Otherwise you can end up with:

%HOMEDRIVE%%HOMEPATH%\AppData\Local\Folder

instead of:

%LOCALAPPDATA%\Folder

Updated code that prefers the longest variable:

string Tokenify(string path)
{
    //first find all the environment variables that represent paths.
    var validEnvVars = new List<KeyValuePair<string, string>>();
    foreach (DictionaryEntry e in Environment.GetEnvironmentVariables())
    {       
        string envPath = e.Value.ToString();
        if (System.IO.Directory.Exists(envPath))
        {
            //this would be the place to add any other filters.
            validEnvVars.Add(new KeyValuePair<string, string>(e.Key.ToString(), envPath));
        }
    }

    //sort them by length so we always get the most specific one.
    //if you are dealing with a large number of strings then orderedVars can be generated just once and cached.
    var orderedVars = validEnvVars.OrderByDescending(kv => kv.Value.Length);

    foreach (var kv in orderedVars)
    {
        //using regex just for case insensitivity. Otherwise just use string.Replace.
        path = Regex.Replace(path, Regex.Escape(kv.Value), $"%{kv.Key}%", RegexOptions.IgnoreCase);
    }
    return path;
}

You may still want to add checks to avoid double-tokenizing parts of the string, but that is much less likely to be an issue in this version.

Also you might want to filter out some variables like drive roots, e.g. (%HOMEDRIVE%) or by any other criteria.

like image 94
Rotem Avatar answered Nov 14 '22 03:11

Rotem