cnblogs Post date: 2009-11-20 17:28
前几天我想在一个程序中运行类似这样一个命令行“javac test.java”,环境变量我都设定得好好的,但使用Process.Start时依旧会有找不到文件的异常。后来发现Process.Start函数不支持自动搜索PATH环境变量。

很是郁闷。闲着没事,写了个能搜索PATH的运行命令行函数Execute(),详见下面代码。

        /// <summary>
        /// 运行一个命令
        /// </summary>
        /// <param name="cmdLine">要运行的命令行</param>
        /// <param name="workDirectory">命令的工作目录</param>
        /// <returns>该命令对应的进程</returns>
        public static Process Execute(string cmdLine, string workDirectory)
        {
            string fileName;
            string args;
            SplitCmdLineIntoFileAndArg(cmdLine, out fileName, out args);
            Process pro = null;

            pro = Process.Start(new ProcessStartInfo()
            {
                FileName = fileName,
                Arguments = args,
                WorkingDirectory = workDirectory,
            });

            return pro;
        }

其中最重要的莫过于如何寻找可执行文件所在路径了。我们知道windows和Linux中都有个环境变量PATH,其中包含了可执行文件的搜索路径,于是寻找可执行文件路径的问题就很容易解决了。这时我又想到:在windows中命令行中的可执行文件的扩展名经常可以被省略。如何才能给文件填上扩展名呢?这要牵扯到另外一个环境变量了——PATHEXT,它包含可执行文件的扩展名,其顺序就是命令行可执行文件的搜索顺序。如此一来整个问题就解决了。其代码如下:

        /// <summary>
        /// 把命令行分割为文件名和参数
        /// </summary>
        /// <param name="cmdLine">输入的命令行,不能为null</param>
        /// <param name="fileName">输出的文件名</param>
        /// <param name="args">参数</param>
        /// <exception name="FileNotFoundException">找不到相应的可执行文件,命令行无法解析</exception>
        static void SplitCmdLineIntoFileAndArg(string cmdLine, out string fileName, out string args)
        {
            if (cmdLine == null)
                throw new ArgumentNullException("cmdLine", "cmdLine必须是有效的命令行");
            fileName = string.Empty;
            args = string.Empty;
            bool hasFound = false;
            //环境变量path,可执行文件的路径
            string path = Environment.GetEnvironmentVariable("path")+";.";
            var splitedPath = path.Split(';');
            //环境变量pathext,可执行文件的扩展名
            string pathext = Environment.GetEnvironmentVariable("pathext");
            string[] splitedPathext = pathext.Split(';');
            //有些系统中可能没有这个变量,那么就忽略扩展名
            if (splitedPathext == null || splitedPathext.Length == 0)
            {
                splitedPathext = new string[] { string.Empty };
            }

            for (int i = 0; i <= cmdLine.Length; ++i)
            {
                //命令行由空白字符分割,而可执行文件名就是第一个参数。有可能其路径中包含空白字符,故搜索时还要向后搜索
                if (i == cmdLine.Length || char.IsWhiteSpace(cmdLine, i))
                {
                    hasFound = false;
                    string partFileName=cmdLine.Substring(0,i);

                    foreach (string pathx in splitedPath)
                    {
                        fileName = pathx + "\\\\" + partFileName;
                        if (File.Exists(fileName))
                        {
                            hasFound = true;
                            break;
                        }
                        else
                        {
                            foreach (string pathextx in splitedPathext)
                            {
                                fileName = pathx + "\\\\" + partFileName + pathextx;
                                if (File.Exists(fileName))
                                {
                                    hasFound = true;
                                    break;
                                }
                            }                           
                        }
                        if (hasFound)
                            break;
                    }
                    if (hasFound)
                    {
                        i++;
                        if (i >= cmdLine.Length)
                            args = string.Empty;
                        else
                            args = cmdLine.Substring(i);
                        break;
                    }
                }
            }
            if (!hasFound)
            {
                throw new FileNotFoundException("无法找到文件\\""+cmdLine+'"', cmdLine);
            }
        }

测试一下:

     static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();
            try
            {
                sw.Start();
                Process pro = Execute("winver", ".");
                sw.Stop();
                Console.WriteLine(pro.StartInfo.FileName + " " + pro.StartInfo.Arguments);
                Console.WriteLine("elapsed:{0}", sw.ElapsedMilliseconds);
                pro.WaitForExit(10000);

            }
            catch(Exception e)
            {
                Console.Error.WriteLine(e.ToString());
            }

        }

运行结果:

**C:\\windows\\system32\\winver.EXE**
**elapsed:77**
****