

命令列工具的 stdout, stderr 輸出與 .NET 整合應用
source link: https://blog.darkthread.net/blog/stdout-stderr/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

昨天提到的 Linux 掃描工具 - scanimage,剛好有個經典輸出分流行為,scanimage 將圖檔傳到標準輸出(Standard Output),故可加上 > tab.tiff 轉存成檔案,加上 -v -p 參數,過程會顯示偵錯資訊及執行進度,則是顯示在主控端(Console):
這恰好展示了 DOS、Linux 及大部分命令列環境的 Standard Output 與 Standard Error 輸出分流概念,UNIX/C 世界的術語是 stdout、stderr,對映 .NET 與 Process 溝通的 StandardOutput、StandardError 屬性。
依據 GNU C Library 定義,除了輸出錯誤訊息,診斷相關資訊也是從 stderr 輸出:參考
Variable: FILE * stdout
The standard output stream, which is used for normal output from the program.
Variable: FILE * stderr
The standard error stream, which is used for error messages and diagnostics issued by the program.
因此,scanimage 接收掃描結果會從 stdout 輸出被導向檔案,診斷資訊則走 stderr 輸出到主控端。指令最後加個 2> msg.txt 則會把 stderr 導向 msg.txt,畫面將看不到診斷訊息,而是被轉存到 msg.txt:
如果你批次指令寫得夠多,可能看過 "2>&1" 像咒語一樣的寫法,它的意思是將 stderr 也合併到 stdout,但加的位置很關鍵。例如:
# 2>&1 先寫,result.txt 只會有 robots.txt 的內容
curl -v https://dotnet.microsoft.com/robots.txt 2>&1 >result.txt
# 2>&1 放在最後,result.txt 才會有包含診斷訊息及 robots.txt
curl -v https://dotnet.microsoft.com/robots.txt >result.txt 2>&1
如果有點難理解,可想成 2(stderr), 1(stdout) 是 Reference 變數。
2>&1 >result.txt,1 一開始指向主控端,先 2>&1 把 2 也導向向主控端輸出,之後將 1 導向 result.txt 時 2 不受影響。
>result.txt 2>&1,先將 1 導向 result.txt,2>&1 將 2 也導向 1 輸出的 result.txt,故二者結果會合併。
在寫 .NET Console 程式時,怎麼決定輸出到 stdout 還是 stderr 呢?很簡單,使用 Console.Error.Write()/WriteLine() 就好,以下 .NET 6 Console 程式會分別輸出到 stderr 及 stdout,正常執行時二者都是顯示在螢幕上,但加上 >stdout.txt 2>stderr.txt 就能觀察到差異。
那,要怎麼做出像 scanimage 一樣進度數字在原地跳動的效果呢?
江湖一點訣,說破不值五毛錢,不要 WriteLine(),改為 Write() 並在最前方加一個 "\r" 讓游標移到第一欄即可。
如果從 .NET 程式執行外部程式時,要怎麼接收 stdout 與 stderr?
就以 scanimage 為對象,我試寫了一個範例:參考
using System.Diagnostics;
var si = new ProcessStartInfo()
{
FileName = "scanimage",
Arguments = "-v -p --format tiff -d \"brother4:net1;dev0\" -x 100 -y 100 --source FlatBed --resolution 150",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using (var p = new Process())
{
var progress = false;
p.ErrorDataReceived += new DataReceivedEventHandler(
(sender, e) => {
var s = e.Data;
if (!string.IsNullOrEmpty(s))
{
//when redirected, no \r included in progress update, add it
if (s.Contains("%"))
{
Console.Write("\r" + s);
progress = true;
}
else
{
if (progress)
{
Console.WriteLine();
progress = false;
}
Console.WriteLine(s);
}
}
});
p.StartInfo = si;
p.Start();
p.BeginErrorReadLine();
using (var ms = new MemoryStream())
{
byte[] buff = new byte[8192];
int len = 0;
do
{
len = p.StandardOutput.BaseStream.Read(buff, 0, buff.Length);
if (len > 0) ms.Write(buff, 0, len);
} while (len > 0);
File.WriteAllBytes("image.tiff", ms.ToArray());
}
p.WaitForExit();
}
為了即時顯示進度,StandardError 用了 BeginErrorReadLine(),而其中有個小眉角,當偵測到 stderr 被導向時,scanimage 輸出進度百分比時不會前置 \r 以配合 Log 檔案輸出,我用了點技巧補上 \r 使其呈現原有的效果。執行結果如下:
希望以上的分享對常用或常寫命令列程式的朋友有些幫助。
題外話,原本覺得 .NET 6 把 Program.cs 簡化到 namespace、class、void Main(string[] args) 丟光光是公然偷懶,道德淪喪的行為,害 C# 都不 C# 了,但寫過幾支測試用小程式之後我決定改口 - Top-Level Statements 真香! 哈。
Recommend
-
30
Introduction Among the popular operating systems, they have all standardized on using standard input, standard output, and standard error with file desciptors 0, 1, and 2 respectively. This allows you to pipe the inputs and outputs...
-
20
Introduction When you use print() in python the output goes to standard output or sys.stdout. You can directly call sys.stdout.write() instead of using print(), but you can also co...
-
28
Introduction Operating systems recognize a couple special file descriptor IDs: STDIN - 0 - Input usally coming in from keyboard. STDOUT - 1 - Output from the...
-
17
Bash打印中的stdout与stderr 2017-07-26 10:58:36 +08 字数:2134 标签: Bash 简介stdout与stderr
-
10
将命令的输出重定向到文件或将其通过管道传递到另一个命令时,您可能会注意到错误消息已打印在屏幕上。在Bash和其他Linux Shell中,执行程序时,它使用三个标准I/O流。 每个流由一个数字文件描述符表示:0-stdin...
-
9
使用 PowerShell 串接 EXE 輸出串流結果-黑暗執行緒 突發奇想,如果手上有個現成 Command Line 的監控性質程式(.EXE),會在執行過程即時輸出事件或統計資訊,有沒有可能把它接進 PowerShell Pipeline 即時分析並做出回應呢? 舉個例子,像是 ping -...
-
10
nohup: redirecting stderr to stdout解决办法
-
8
【笨問題】命令列執行結果同時輸出到螢幕跟檔案 2022-05...
-
4
地端雲端 AD 整合 (Azure AD Connect Sync)-黑暗執行緒 時至今日,企業必須使用雲端服務,或將應用程式部署到雲端供人員在外使用的情境日益普遍。服務存取需被控管,必然有帳號密碼及權限管理需求,整合企業既有 AD 帳號群組會比另建一套帳號...
-
9
Conversation Contributor In all other places (e.g. bootstrap.py, opt-dis...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK