一.前言
我們可能會遇到需要在程序中執行一些系統命令,來獲取一些信息;或者調用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