NET Core 跨平台执行命令、脚本
一.前言
我们可能会遇到需要在程序中执行一些系统命令,来获取一些信息;或者调用shell脚本。.NET Core 目前已经可以跨平台执行,那么它如何跨平台执行命令呢,请看下面的讲解。
二.ProcessStartInfo、Process 类介绍
我们主要用到的两个类就是 ProcessStartInfo
和 Process
,他们的用法和.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执行会收到下面的信息:
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}");
}
}
执行结果如下:
从执行结果可以看出,我们通过编写的程序来执行dotnet --info
命令获取的结果几乎一样,只有第一行的提示,我们通过cmd执行命令输出的是中文,我们通过程序调用执行输出的是英文,这个问题,有兴趣的朋友可以研究一下。
2.在Linux上执行
使用的系统环境为CentOS 7.2,.NET Core sdk版本为2.0.3。
直接执行命令结果如下:
我将代码上传到git server,然后在linux上clone然后执行结果如下:
可以看到我们获取执行输出是没有问题的,但是获取进程开始执行出错了,无法从进程检索该信息,现在我们移除统计执行时间的代码:
这下我们执行就没有问题了。从这里我们可以得出结论:由于平台的差异,获取一些信息可能会出现异常,所以我们实际一定要在多个平台上测试。
3.在OSX上运行
我在OSX上的.NET Core SDK版本为2.0.0 很久没更新了。
直接执行命令:
从git Clone代码,执行结果如下:
可以看出我们在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 文件夹下。
因为我们需要根据不同的操作类型,选择不同的脚本来进行执行,所以我们需要在代码里面判断一下操作系统类型。我们可以通过 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下运行
在windows下运行是完全正常的。
4.在OSX运行
直接运行会报一个权限异常,如下:
使用命令加入执行权限:
chmod +x OSX.sh
然后再次执行:
可以看到成功执行了脚本。
5.在Linux上运行
直接运行也是会有权限问题的:
同样使用命令加入执行权限:
chmod +x linux.sh
然后再次执行:
可以看到成功执行了我们的脚本。
4.容易犯的错误
看见上面的例子,我都成功执行了,其实我踩了几个坑,花了我不少时间来解决。
1.sh脚本一定要指定命令解析器
也就是这句话,放在sh脚本开头
#!/bin/bash
2.不管是windows linux osx 脚本编码必须为 ANSI
不然程序执行的时候,读取字符会出错,造成执行异常。
五.写在最后
希望本文能给大家带来帮助,如有问题欢迎和我讨论。
本文所用代码地址:https://github.com/stulzq/BlogDemos/tree/master/DotnetCmd
NET Core 跨平台执行命令、脚本的更多相关文章
- expect实现远程主机自动执行命令脚本
2014年第一个脚本,哈哈!!! expect实现远程主机自动执行命令脚本: #!/usr/bin/expect -- if { [llength $argv] < 4 } { puts &qu ...
- webshell下执行命令脚本汇集
cmd1.asp <object runat=server id=shell scope=page classid="clsid:72C24DD5-D70A-438B-8A42-984 ...
- shell中使用expect命令进行远程执行命令脚本
expect是用来实现自动交互功能的工具之一,使用expect-send来实现交互过程. 注意: 1.脚本的执行方法与bash shell不一样,比如:expect example.sh 2.向一个脚 ...
- ssh 免交互登录 ,远程执行命令脚本。
##免交互SSH登录auto_login_ssh () { expect -c "set timeout -1; spawn -noecho ssh -o ...
- linux关机时候执行命令脚本或程序
Write a service file and place it in /etc/systemd/system/beforeshuttingdown.service code: [Unit] Des ...
- 使用paramiko远程登录并执行命令脚本
#!/usr/bin/env python #coding=utf-8 import paramiko, getpass,sys,traceback class ssh_utils(): def lo ...
- 批量复制及执行命令shell脚本
平时在处理一个或几个机器运行环境时,一个机器一个机器处理也能接受,但是如果是一批机器,几十或几百台,要是一台一台去安装环境,光是输入同一的命令,估计你自己都想吐,所有聪明的人会想一些偷懒的办法,确实可 ...
- linux上执行jmeter脚本
1.linux上安装jmeter 将windows上的zip包直接放到linux上 进入bin目录,chmod 777 jmeter 修改环境变量: 1 2 3 4 # vim /etc/profil ...
- linux执行sh脚本文件命令
linux执行sh脚本文件命令 很多时候需要多个命令来完成一项工作,而这个工作又常常是重复的,这个时候我们自然会想到将这些命令写成sh脚本,下次执行下这个脚本一切就都搞定了,下面就是发布代码的一个脚本 ...
随机推荐
- MakeFile 文件的作用
makefile文件保存了编译器和连接器的参数选项,还表述了所有源文件之间的关系(源代码文件需要的特定的包含文件,可执行文件要求包含的目标文件模块及库等).创建程序(make程序)首先读取makefi ...
- navicat for mysql远程连接ubuntu服务器的mysql数据库
经常玩服务器上的mysql数据库,但是基于linux操作Mysql多有不便,于是就想着使用GUI工具来远程操作mysql数据库.已经不是三次使用navicat-for-mysql了,但是每次连接远程服 ...
- phantomjs 爬去动态页面
最近有一个小需求,需要根据用户输入的某宝的店铺 url,检查地址是否存在,并抓取店铺名称.某宝店铺 url 的 title 通常是 xx-xx-xx 的形式,中间的 xx 就是对应的店铺名称. 这个需 ...
- 负载均衡之让nginx跑起来
一个简单的原因,我不得不考虑负载 小源做了个网站,很简单,传统的java开放框架,和一个tomcat搞定,让人没想到的是网站既然火起来了,很快一个tomcat就搞不定了,怎么办? 网站访问量很大,既然 ...
- MySQL 表名区分大小写设置
1.关闭MySQL服务: 控制面板主页-管理工具-服务-MySQL服务 2.在服务器运行目录找到my.ini 或者my.cnf文件: 在[mysqld]下面增加一行添加 :lower_ ...
- JAVA_Lock
今天是毕业入职的第一个周末,一直对多线程并发方面的知识比较感兴趣,因为目前我手里的项目并没有涉及到并发方面的知识,所以怕以后遗忘,也便于以后复习和使用,所以总结了一下Lock里面的一些类的方法.具体的 ...
- CDH安装系统环境准备——虚拟机网络配置
虚拟机网络配置教程如下: 1.修改网络配置文件[root@master ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0配置IP地址.网关.掩码.DNS ...
- jQuery插件之-----弹性运动
<!doctype html><html><head><meta charset="utf-8"><title>弹性运动 ...
- SQL遇到的问题
1.问题描述:拼接sql字符串涉及到表变量时报错. 解决办法:把表变量的定义一同放在字符串中. 2.问题描述:EF添加实体后,调用存储过程调用不到 解决办法:必须先db.SaveChanges()后 ...
- Day4_闭包含数
闭包函数: 闭包函数是在作用域的前提下 闭包含数:定义在函数内部的函数,包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包含数. eg: x=1 def f1(): x ...