Skip to main content

File-Based Apps

https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/

https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs

It only took 10 years but Microsoft did what I had always wanted them to do and come up with a way to make single file C# "scripts" a reality without relying on a third-party system like csharpscript. I would have loved this at my first job where we just needed a lot of strongly-typed scripts that were first class citizens for COM Interop, but we got by with Python.

I spun together a small idea with Copilot to read prompt input and spit out the token count of that input:

#!/usr/bin/dotnet run
// Token Counter - counts whitespace-separated tokens in input text.
//
// Usage:
//   dotnet run TokenCounter.cs -- "some text to count"      ← CLI argument
//   dotnet run TokenCounter.cs -- --input path.txt          ← read from file
//   echo "some text" | dotnet run TokenCounter.cs           ← piped / scripted input
//   cat file.txt    | dotnet run TokenCounter.cs
#:package Microsoft.ML.Tokenizers@2.0.0
#:package Microsoft.ML.Tokenizers.Data.O200kBase@2.0.0

using Microsoft.ML.Tokenizers;

string? text = null;

if (args.Length > 0)
{
    if (args[0] == "--input" || args[0] == "-i")
    {
        if (args.Length < 2)
        {
            Console.Error.WriteLine("Error: --input requires a path argument.");
            return 1;
        }

        string path = args[1];
        if (!File.Exists(path))
        {
            Console.Error.WriteLine($"Error: file not found: {path}");
            return 1;
        }

        text = await File.ReadAllTextAsync(path);
    }
    else if (args[0] == "--help" || args[0] == "-h")
    {
        Console.WriteLine("""
            Token Counter — counts whitespace-separated tokens in input text.

            Usage:
              dotnet run TokenCounter.cs -- <text>
              dotnet run TokenCounter.cs -- --input <path>
              <command> | dotnet run TokenCounter.cs

            Options:
              -i, --input <path>  Read input from a file
              -h, --help          Show this help message
            """);
        return 0;
    }
    else
    {
        // Treat all args as the text to count (join with spaces)
        text = string.Join(' ', args);
    }
}
else if (!Console.IsInputRedirected)
{
    Console.Error.WriteLine("Error: no input provided. Pass text as an argument, use --file, or pipe input.");
    Console.Error.WriteLine("Run with --help for usage.");
    return 1;
}
else
{
    // Read from stdin (piped / scripted)
    text = await Console.In.ReadToEndAsync();
}

var tokenizer = TiktokenTokenizer.CreateForEncoding("o200k_base");

int count = tokenizer.CountTokens(text);
Console.WriteLine($"Tokens: {count}");
return 0;

Minus the comment-block. documentation, that's 60-ish lines of code. As with all agentically-developed things, I don't think this is a reasonably "complete" script; I'd want to do more with the prompt, leverage System.CommandLine or CommandLineParser, etc., but this is a pretty good example of what you can do with one file.

This fits in really well with a .NET shop's agentic flow where you can have a hook that does token analysis without having to learn Python.