zoukankan      html  css  js  c++  java
  • .NET Core 跨平台执行命令、脚本

    一.前言

    我们可能会遇到需要在程序中执行一些系统命令,来获取一些信息;或者调用shell脚本。.NET Core 目前已经可以跨平台执行,那么它如何跨平台执行命令呢,请看下面的讲解。

    二.ProcessStartInfo、Process 类介绍

    我们主要用到的两个类就是 ProcessStartInfoProcess ,他们的用法和.NET Framework下是一样的。

    1. ProcessStartInfo 类

    ProcessStartInfo主要设置一些我们需要创建的进程的参数。比如需要启动的应用程序的文件名,参数等等。

    (1)构造方法

    它有三个构造方法:

    public ProcessStartInfo();
    public ProcessStartInfo(string fileName);
    public ProcessStartInfo(string fileName, string arguments);
    

    fileName:用于启动进程的应用程序。

    arguments:在进程启动时传递给应用程序的命令行参数。

    (2)主要属性

    CreateNoWindow:指示是否在新窗口中启动进程。

    RedirectStandardError:指示应用程序的错误输出是否写入到流中。

    RedirectStandardInput:指示是否从应用程序读取应用程序的输入流。

    RedirectStandardOutput:指示应用程序的文本输出是否写入流。

    StandardErrorEncoding:错误输出内容编码。

    StandardOutputEncoding:文本输出内容编码。

    UseShellExecute:指示是否使用操作系统shell启动进程。如果启动进程时使用shell,则为true; 如果应该直接从可执行文件创建进程,则为false。 默认值是true。

    该类并没有定义自己的方法,因为它主要设置一些创建进程需要的参数信息。

    2. Process 类

    该类的主要作用是提供对本地和远程进程的访问,并使你能够启动和停止本地系统进程。

    (1).主要属性

    ExitCode:获取退出代码。0表示正常, 非0表示非正常退出。

    ExitTime:获取关联进程退出的时间。

    StartTime:获取关联进程启动的时间。

    HasExited:获取一个值,指示相关进程是否已终止。

    MachineName:获取运行关联进程的计算机的名称。

    SessionId:获取关联进程的终端服务会话标识符。

    StandardError:获取读取应用程序错误输出的流。

    StandardInput:获取应用程序输入内容的流。

    StandardOutput:获取用于读取应用程序文本输出的流。

    Threads:获取关联进程中正在运行的线程集合。

    (2).主要方法

    Start :启动进程

    BeginErrorReadLine:异步开始读取应用错误输出。

    BeginOutputReadLine:异步开始读取应用标准输出。

    CancelErrorRead:取消读取错误输出。

    CancelOutputRead:取消读取标准输出。

    Close:释放与此组件关联的所有资源。

    CloseMainWindow:通过向其主窗口发送关闭消息来关闭具有用户界面的进程。

    Kill:立即停止关联的进程。

    Refresh:放弃已经在进程中缓存的关联进程的任何信息。

    WaitForExit:等待关联进程退出,可以设置超时时间,如不设置则一直等待。

    (3)事件

    一共有三个事件:

    ErrorDataReceived:接收到关联进程输出错误数据。

    OutputDataReceived:接收到关联进程输出标准数据。

    Exited:关联进程退出

    三.在Windows OSX Linux 下执行命令

    这里我选择.NET Core带的 dotnet --info输出.NET Core SDK&Runtime相关的信息。

    我们通过cmd执行会收到下面的信息:

    1526997869810

    1.编写代码执行命令

    编写的代码如下:

    static void Main()
    {
        //创建一个ProcessStartInfo对象 使用系统shell 指定命令和参数 设置标准输出
        var psi = new ProcessStartInfo("dotnet", "--info") {RedirectStandardOutput = true};
        //启动
        var proc=Process.Start(psi);
        if (proc == null)
        {
            Console.WriteLine("Can not exec.");
        }
        else
        {
            Console.WriteLine("-------------Start read standard output--------------");
            //开始读取
            using (var sr = proc.StandardOutput)
            {
                while (!sr.EndOfStream)
                {
                    Console.WriteLine(sr.ReadLine());
                }
    
                if (!proc.HasExited)
                {
                    proc.Kill();
                }
            }
            Console.WriteLine("---------------Read end------------------");
            Console.WriteLine($"Total execute time :{(proc.ExitTime-proc.StartTime).TotalMilliseconds} ms");
            Console.WriteLine($"Exited Code : {proc.ExitCode}");
        }
    }
    

    执行结果如下:

    1526998256861

    从执行结果可以看出,我们通过编写的程序来执行dotnet --info命令获取的结果几乎一样,只有第一行的提示,我们通过cmd执行命令输出的是中文,我们通过程序调用执行输出的是英文,这个问题,有兴趣的朋友可以研究一下。

    2.在Linux上执行

    使用的系统环境为CentOS 7.2,.NET Core sdk版本为2.0.3。

    直接执行命令结果如下:

    1527002061546

    我将代码上传到git server,然后在linux上clone然后执行结果如下:

    1527002713367

    可以看到我们获取执行输出是没有问题的,但是获取进程开始执行出错了,无法从进程检索该信息,现在我们移除统计执行时间的代码:

    1527002815611

    这下我们执行就没有问题了。从这里我们可以得出结论:由于平台的差异,获取一些信息可能会出现异常,所以我们实际一定要在多个平台上测试。

    3.在OSX上运行

    我在OSX上的.NET Core SDK版本为2.0.0 很久没更新了。

    直接执行命令:

    1527003121467

    从git Clone代码,执行结果如下:

    1527003229266

    可以看出我们在OSX上执行是没有问题的。

    四.在Windows OSX Linux 下执行脚本

    1.编写测试脚本

    编写脚本的主要逻辑为输出程序当前目录结构,然后输出一句话 “dotnet in 操作系统类型”

    Windows: win.bat

    @echo off
    dir
    echo "dotnet in Windows"
    

    Linux: linux.sh

    #!/bin/bash
    ls
    echo "dotnet in Linux"
    

    OSX: OSX.sh

    #!/bin/bash
    ls
    echo "dotnet in OSX"
    

    2.编写测试代码

    我将所有的脚本都放在 项目根目录/shell 文件夹下。

    1527007043163

    因为我们需要根据不同的操作类型,选择不同的脚本来进行执行,所以我们需要在代码里面判断一下操作系统类型。我们可以通过 RuntimeInformation.IsOSPlatform来判断。

    static void Main()
    {
        string fileName="shell/";
    
        //根据系统使用不同的shell文件
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            fileName += "win.bat";
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            fileName += "linux.sh";
        }
        else
        {
            fileName += "OSX.sh";
        }
        //创建一个ProcessStartInfo对象 使用系统shell 指定命令和参数 设置标准输出
        var psi = new ProcessStartInfo(fileName) { RedirectStandardOutput = true };
        //启动
        var proc = Process.Start(psi);
        if (proc == null)
        {
            Console.WriteLine("Can not exec.");
        }
        else
        {
            Console.WriteLine("-------------Start read standard output--------------");
            //开始读取
            using (var sr = proc.StandardOutput)
            {
                while (!sr.EndOfStream)
                {
                    Console.WriteLine(sr.ReadLine());
                }
    
                if (!proc.HasExited)
                {
                    proc.Kill();
                }
            }
            Console.WriteLine("---------------Read end------------------");
            Console.WriteLine($"Exited Code : {proc.ExitCode}");
        }
    }
    

    3.在Windows下运行

    1527005342188

    在windows下运行是完全正常的。

    4.在OSX运行

    直接运行会报一个权限异常,如下:

    1527005149158

    使用命令加入执行权限:

    chmod +x OSX.sh
    

    然后再次执行:

    1527006740920

    可以看到成功执行了脚本。

    5.在Linux上运行

    直接运行也是会有权限问题的:

    1527005510138

    同样使用命令加入执行权限:

    chmod +x linux.sh
    

    然后再次执行:

    1527006220329

    可以看到成功执行了我们的脚本。

    4.容易犯的错误

    看见上面的例子,我都成功执行了,其实我踩了几个坑,花了我不少时间来解决。

    1.sh脚本一定要指定命令解析器

    也就是这句话,放在sh脚本开头

    #!/bin/bash
    

    2.不管是windows linux osx 脚本编码必须为 ANSI

    不然程序执行的时候,读取字符会出错,造成执行异常。

    五.写在最后

    希望本文能给大家带来帮助,如有问题欢迎和我讨论。

    本文所用代码地址:https://github.com/stulzq/BlogDemos/tree/master/DotnetCmd

  • 相关阅读:
    Chrome Native Messaging 与本地程序之间的通信
    由于扩展配置问题而无法提供您请求的页面。如果该页面是脚本,请添加处理程序。如果应下载文件,请添加 MIME 映射。
    linux
    maven POM.xml 标签详解 转
    java-线程观察整个生命周期
    java-Web项目开发中Spring整合Junit单元测试
    java-Map集合的四种遍历方式
    Java-8内置的核心函数式接口接口
    正则表达式
    Java-Atomic系列12大类实例讲解和原理分解
  • 原文地址:https://www.cnblogs.com/stulzq/p/9074965.html
Copyright © 2011-2022 走看看