🙌

どこでも動かせるC# Scriptを、File-based Appsとdotnet publishで実現できるようになった!(プレビュー版)

に公開

概要

バッチファイルと同じように「Windows環境ならどこでも、何もインストールせずに実行できる」という1ファイルスクリプトを、C#で書けるようになりました!

ということに気付いて、まだプレビュー版ですがこれはぜひ紹介したいと思って記事を書きました。

正確には次の2択で、1が新しく可能になったという話です。

  1. 1ファイルスクリプトを1コマンドでビルドして、出来上がったexeを使う(使う環境には、.NET等のインストールは不要)
  2. .NET SDKをインストールし、1ファイルスクリプトをそのまま実行する(インストール必要)

そのためバッチファイルと完全に同等の手軽さではないですが・・・運用環境で使う小物スクリプトを書いたり保守する上での手軽さはかなり近くなったと思います。

※注意:この機能は .NET 10 Preview 6 で実装されたもので、まだプレビュー版です

最初に結論まとめ

  1. 拡張子”.cs”のC#ファイルを1つ作成する(ここではmain.csとします)
  2. .NET SDKをインストールしている環境で、 dotnet publish main.csを実行
  3. main.exe のできあがり。このファイルは.NET SDKもランタイムも不要で、そのままWindows上で実行できます!

説明

背景

Windowsならどこでも実行できるお手軽スクリプトといえばやはりバッチファイル、続いてPowerShellスクリプトが使われていると思います。しかしこれらの言語は独自性が強いので、それ専業ならともかくたまに使うのはけっこう辛いです。そうしたスクリプトもC#で書きたいと思っていました。

今までも dotnet-script などがあったため、1ファイルのスクリプトをC#で書いて実行することは出来ました。しかし、この方法だと .NET SDK のインストールが必須となります。自社管理のサーバーで使うスクリプトならともかく、ユーザー(クライアント)環境で使うには条件が厳しい・・・。

この問題を解決してくれる方法が登場しました。C#の1ファイルスクリプトを、 Native AOT でビルドすることで、 .NET ランタイムが無くても動くネイティブのexeを作るという方法です。

まず前提のFile-based Appsについて

まずそもそも、C#の1ファイルスクリプトを実行する機能は .NET 9 までにはリリースされておらず、先ほども触れた dotnet-script などがその用途を補っていました。

.NET 10 Preview 4 あたりで、「File-based Apps」という機能が .NET 公式に追加されました。次のようにコマンド一発で C# のファイルを実行できるものです。

dotnet run main.cs

これはこれで嬉しいですが、 .NET SDK のインストールが必要なことには変わりありませんでした。

File-based Apps の Publish と Native AOT 対応で新しい使い方が可能に

.NET 10 の Preview 6 で、 File-based apps enhancements というリリースノートが追加されました。

色々追加されていますが、今回のポイントは「Publish support and native AOT」のところです。そのまま事実しか書いていないのでうっかり見逃しそうになりますが、これはつまり、「 .NET ランタイム無しで実行可能なexeを、1ファイルスクリプトからビルドできる」ということです。

実行時に、 .NET SDK どころか、 .NET ランタイムすら不要になるということです。これは一気に使えるシーンが増えますね!

しかもビルドもとても簡単です。

dotnet publish main.cs

これだけです。プロジェクトやソリューションを作る必要すらありません。

これなら、スクリプトファイル(~.cs)はそのまますぐに読めて編集できる状態で置いておき、スクリプト変更時に必ずこのコマンドを打てばいいだけなので、バッチファイルに近い感覚で運用できるのではないでしょうか。

試してみた(上手く行った)

とりあえず、ごく簡易なファイル操作スクリプトで試してみました。このGistに入れてあります。この内容は本題と関係ないので、気になる人だけ開いてください。

dotnet publish を打ってみると・・・

dotnet publish main.cs
復元が完了しました (3.8 秒)
プレビュー版の .NET を使用しています。https://aka.ms/dotnet-support-policy をご覧ください
  main 成功しました (8.9 秒) → %Temp%\dotnet\runfile\main-a638fb674794aeab9f6f904090cc07bbd147e13a60051f5fc41a785f90936aa5\publish\release\

13.4 秒後に 成功しました をビルド

すぐにできあがりです。ビルドしたファイルは、固定で %Temp%\dotnet\runfile\ フォルダの下へ作られるようです。

このフォルダを開いてみると、exeとpdbファイルが1つずつあります。とてもシンプルです。exeの依存関係を見てみると、次の通りです。Windowsに入っているDLLと、UCRT(普通のWin10以降なら確実に入っている)だけが依存対象なので、どこでも動きそうですね。

dumpbin /dependents "%Temp%\dotnet\runfile\main-a638fb674794aeab9f6f904090cc07bbd147e13a60051f5fc41a785f90936aa5\publish\release\main.exe"
Microsoft (R) COFF/PE Dumper Version 14.44.35213.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file %Temp%\dotnet\runfile\main-a638fb674794aeab9f6f904090cc07bbd147e13a60051f5fc41a785f90936aa5\publish\release\main.exe

File Type: EXECUTABLE IMAGE

  Image has the following dependencies:

    ADVAPI32.dll
    bcrypt.dll
    KERNEL32.dll
    ole32.dll
    api-ms-win-crt-heap-l1-1-0.dll
    api-ms-win-crt-math-l1-1-0.dll
    api-ms-win-crt-string-l1-1-0.dll
    api-ms-win-crt-convert-l1-1-0.dll
    api-ms-win-crt-stdio-l1-1-0.dll
    api-ms-win-crt-runtime-l1-1-0.dll
    api-ms-win-crt-locale-l1-1-0.dll

このexeファイルを、クリーンなWindows 11環境に持っていて実行すると・・・次のようにコンソール出力がちゃんと表示されて、動きました!(C#で書いたコンソール出力です)


main.exe "C:\Users\User\Desktop\dir"
Target folder: C:\Users\User\Desktop\dir
Moving files from subfolders to the top directory...
Processing: dir2
  Moved: dir2\file1.txt -> file1.txt
  Deleted: Empty folder dir2

Result:
  Number of files moved: 1
  Number of folders deleted: 1
Process completed.

まとめ

C#の1ファイルスクリプトを簡単なコマンドでexeにビルドでき、それを .NET ランタイム等の無い環境で実行できました。これなら、バッチファイルの用途をかなり置き換えることも可能だと思います。

C#なら大得意だがバッチファイルは面倒という人がいたら、ぜひ使っていきましょう!

Discussion