UniTaskで簡単なProcessをサクッと実行して結果を文字列で得る


# 概要

- macOS BigSur 11.4
- Unity 2021.1.28f1
- UniTask 2.2.4

Unityでもコマンド実行した結果を使いたいな〜ってときに使えるやつです

# 使用クラス

- [ProcessStartInfo](https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.processstartinfo?view=netstandard-2.0)
- [Process](https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.process?view=netstandard-2.0)

# 具体例: Gitのコミットハッシュを取得してTextに表示する

特に解説することはなかったので早速コードをドン

```CommitHashLabel.cs
public class CommitHashLabel : MonoBehaviour
{
    [SerializeField] private Text text;

    private void Start() => InitCommitHashAsync().Forget();

    private async UniTaskVoid InitCommitHashAsync()
    {
        try
        {
            var hash = await GetCommitHashAsync();
            text.text = hash;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }

    private async UniTask<string> GetCommitHashAsync()
    {
        var processStartInfo = new ProcessStartInfo
        {
            FileName = "git",
            Arguments = "rev-parse --short HEAD",
            CreateNoWindow = false,
            StandardOutputEncoding = Encoding.UTF8,
            RedirectStandardOutput = true,
            UseShellExecute = false
        };

        var process = Process.Start(processStartInfo);
        if (process == null)
        {
            throw new ArgumentException("Process cannot start.");
        }

        await UniTask.SwitchToThreadPool();
        process.WaitForExit();
        await UniTask.SwitchToMainThread();

        return (await process.StandardOutput.ReadToEndAsync().AsUniTask()).Trim();
    }
}
```

これで実行時に勝手にコミットハッシュを表示してくれます
`Start`に書いていますが`Awake`でやっても大丈夫だと思います

:::note warn
Editor上でしか動作しないので使用の際には注意！
:::

# 補足

## ProcessStartInfo

### `ProcessStartInfo.WorkingDirectory` 実行場所の指定

指定がないと`Environment.CurrentDirectory`が指定されます
Unityのプロジェクトをgitで管理しているのであれば指定がなくても問題なく使用できると思います

## Process

### `Process.WaitForExit`

これをメインスレッドで実行してしまうと処理が止まってしまうのでスレッドプールに移動してから呼ぶことで非同期っぽくしています
Unityで.NET6が使えるようになったら [WaitForExitAsync](https://docs.microsoft.com/ja-jp/dotnet/api/system.diagnostics.process.waitforexitasync?view=net-6.0)とかいうゴキゲンなメソッドも使えるようになるみたいです


# 汎用的に使えるようにする

ProcessStartInfoの作成とProcessの実行を行うクラスを作成します

``` UniTaskProcessExecutor.cs
public class UniTaskProcessExecutor
{
    private readonly ProcessStartInfo processStartInfo;

    public UniTaskProcessExecutor(string filename, string arguments)
    {
        processStartInfo = new ProcessStartInfo
        {
            FileName = filename,
            Arguments = arguments,
            CreateNoWindow = false,
            StandardOutputEncoding = Encoding.UTF8,
            RedirectStandardOutput = true,
            UseShellExecute = false
        };
    }

    public async UniTask<string> ExecuteAsync()
    {
        var process = Process.Start(processStartInfo);
        if (process == null)
        {
            throw new ArgumentException("Process cannot start.");
        }

        await UniTask.SwitchToThreadPool();
        process.WaitForExit();
        await UniTask.SwitchToMainThread();

        return (await process.StandardOutput.ReadToEndAsync().AsUniTask()).Trim();
    }
}

```

``` CommitHashLabel.cs
public class CommitHashLabel : MonoBehaviour
{
    [SerializeField] private Text text;

    private void Start() => InitCommitHashAsync().Forget();

    private async UniTaskVoid InitCommitHashAsync()
    {
        try
        {
            var process = new UniTaskProcessExecutor("git", "rev-parse --short HEAD");
            var hash = await process.ExecuteAsync();
            text.text = hash;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

```

---

クラス名にUniTaskをつけてしまうと検索性が著しく落ちるので別の名前をつけたほうがいいです