在上篇的《使用C#給Linux寫Shell腳本》結尾中,我們留下了一個關於C#如何調用BashShell的問題。在文章發布之后,我留意到有讀者留言推薦使用“Pash”(一款類PowerShell的東西),在我下載並安裝了該項目之后,嘗試之下發現這仍然不是我們想要的。似乎C#還真的沒有提供這種(輸出重定向)功能,這也迫使我們采取了其他方式來實現。在本篇中,我們將提升“恫嚇”等級並順帶把這個難題一並解決,各位看官請系好安全帶。
本篇中,我們將介紹:
(1)、C#直接調用BashShell所遭遇的問題
(2)、使用C的popen方式調用BashShell
(3)、通過調用C來間接的調用BashShell
一、C#直接調用BashShell所產生的問題
使用C#調其他應用,毫無疑問最直接的方法就是“System.Diagnostics”中的Process.Start了。但當我們使用Process.Start時,卻發現連最簡單的命令都無法調用,更無從談起調用並接受返回了。
上圖為其中一種錯誤(當然還會有更多的問題出現,這里就不列舉了)。
二、使用C的popen方式調用
正由於Process.Start無法直接調用BashShell的命令,我們需要繞道而行。
我們先看下C語言,C語言調用Shell的方式有多種,我們選擇了popen函數的方式進行調用,先看一下以下的這個demo:
#include<stdio.h> int main(){ FILE *fp; char buffer[255]; fp=popen("ls /home/le","r"); fread(buffer,255,255,fp); pclose(fp); printf("%s",buffer); }
通過poepn管道並完成輸出重定向。
三、通過調用C來間接調用Shell
既然C已經可以實現對BashShell的調用已經管道重定向,那我們則可以再通過C#調用C的方式,進而間接的完成對BashShell的調用。
我們先對自己的C函數進行改造,改造后的代碼如下(對具體操作有疑問的讀者可參見《如何讓C為C#提供函數》):
#include<stdio.h> #include<string.h> void* ConvertToCStr(char* input,char* res,int *length){ int i; for(i=0;i<*length;i++){ res[i]=*(input+2*i); } res[i]='\0'; } void* BashHelper(char* cmdStr,int* cmdLength,char* output,int* length){ FILE* fp; char buffer[*length]; char cmd[*cmdLength+1]; ConvertToCStr(cmdStr,cmd,cmdLength); fp=popen(cmd,"r"); fread(buffer,*length,*length,fp); pclose(fp); strcat(output,buffer); }
同樣的我們也把C# Shell進行改造(沒有Intellisense果然難寫,我先在控制台寫好再拷貝過來)
#!/bin/env csharp using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; class Clib { public static string InvokeBash(string cmdStr) { char[] output = new char[255]; unsafe { fixed (char* c = cmdStr) fixed (char* op = output) { int cmdLenth = cmdStr.Length; int outputLength = output.Length; Clib.BashHelper(c, &cmdLenth, op, &outputLength); return Marshal.PtrToStringAnsi((IntPtr)op); } } } [DllImport("/你存放so的地址/shell.so", CallingConvention = CallingConvention.StdCall)] static unsafe extern void BashHelper(char* cmdStr, int* cmdLength, char* output, int* length); } var cmdStr = "/bin/ls /"; var output = Clib.InvokeBash(cmdStr); Console.Write(output);
完成之后,我們再次在Shell中調用。
成功執行BashShell命令並把返回輸出重定向到C#中。
可能有讀者會有這么一個疑問:“這跟直接寫BashShell沒啥差別啊?!”此言差矣,C#有C#的優勢,Bash有Bash的優勢,將兩者結合起來后,可以形成互補,利用Bash可以快速的操作Linux,而一些Bash無法提供的功能,譬如寫入數據庫、調用某些服務的API、做其他BashShell無法做的事情等。
好的,本篇就寫這么多了,非C內行,文中可能有不科學之處,僅提供思路,勿拍磚哈。謝謝。
原文地址:http://jhonge.net/Home/Single2/1938