C#で外部コマンドとしてPowerShellのGet-ChildItemを実行し標準出力を取得する。

コンピュータ

WPFでエクスプローラーのようなファイルマネージャを作成していまして、ファイルの一覧表示などのUI部分は形が見えてきたので、ファイルを操作するコマンドを組み込もうかと思っています。
C#でコピー命令などを一つ一つ作成するつもりでいますが、外部コマンドに任せてしまったほうが楽が出来るのではないかと思いつきC#から外部コマンドを実行する方法を調べてみました。

// 環境変数のPATHのディレクトリからファイル名を検索しフルパスを返す。
static string GetExecFullPathFromEnvPath(string filename)
{
    string result = "";

    // 環境変数からPathを取得
    var pathText = Environment.GetEnvironmentVariable("Path");

    // Path文字列を文字配列配列に変換
    var paths = pathText?.Split(";") ?? [];

    foreach(var path in paths)
    {
        if (!Directory.Exists(path)) continue;

        string fullPath = Path.Join(path, filename);
        if (File.Exists(fullPath))
            return fullPath;        
    }

    return result;
}

string fileName = "pwsh.exe";
string fullPath = GetExecFullPathFromEnvPath(fileName);

if (fullPath != "")
{
    //string cmd = "-version";
    string cmd = "-C \"Get-ChildItem | Write-Host\"";
    System.Diagnostics.ProcessStartInfo psinfo = new()
    {
        FileName = fullPath,
        Arguments = cmd,
        CreateNoWindow = true,
        UseShellExecute = false,
        RedirectStandardOutput = true,
    };
    var ps = System.Diagnostics.Process.Start(psinfo);
    if (ps is null) throw new Exception("エラー");
    string stdout = ps.StandardOutput.ReadToEnd();
    if (0 == ps.ExitCode)
    {
        Console.WriteLine("標準出力");
        Console.WriteLine(stdout);
    }
}

結果:

PS H:\csharp\dotnet8\console\EnvPath01> dotnet run
標準出力
H:\csharp\dotnet8\console\EnvPath01\bin
H:\csharp\dotnet8\console\EnvPath01\obj
H:\csharp\dotnet8\console\EnvPath01\EnvPath01.csproj
H:\csharp\dotnet8\console\EnvPath01\Program.cs

C#から外部コマンドを実行する場合System.Diagnostics.Process.Start()で実行出来るようです。
その際、実行する外部コマンドのフルパスが必要とのことですので、環境変数のPATHから該当するコマンド(実行ファイル)が存在するか確認してから実行するプログラムにしてみました。

サンプルプログラムで呼び出している外部コマンドは”PWSH.exe”で引数に”Get-ChildItem | Write-Host”を渡しています。
要はPowarShell7のシェルでls(Get-ChildItemのエイリアス)を実行していることになります。

当初Write-Hostへのパイプラインは無しで試したのですが、vscodeで実行したところ以下の様になりました。

一見問題なさそうですが文字に色がついて、問題があることに気が付きました。
PowerShellの出力は基本的にオブジェクトですが、標準出力でC#で取得する場合文字列となるため、PowerShell側で必要な情報を文字列に変換しないとC#側の扱いが面倒です。
とりあえず簡単に済ませる為Write-Hostへパイプラインをつなげてみました。

外部コマンドとしてPowerShell(pwsh.exe)を呼び出してみましたが、PowerShellであれば同じ.NETですので別な方法も出来そうな気もしますが、今回はこれで満足できる結果が得られたので終了とします。

コメント