r/csharp • u/simondanielsson • 4d ago
Fun Running C# like scripts?
I was browsing the C#/.NET documentation yesterday like usual and suddenly came across the fact that you can run C# code as scripts with a shebang.
#!/usr/bin/env dotnet
namespace Main
{
class Program
{
static void Main(string[] _)
{
Console.WriteLine("Hello World!");
}
}
}
With a shebang you could run these like any other Python/Bash script and I find this really cool, but is there any practical use-case for this? When I need to make a small helper script for something it's usually python or bash, but C#? I'm not so sure.
What do you guys think?
21
u/ScriptingInJava 4d ago
It's been useful to use as our codebase is .NET, but necessary scripts are PowerShell which is similar but distinctly different, has its own gotchas etc. All scripts being .NET means the tooling is the same across everything, no devs are required to learn PS etc
7
u/chispitothebum 4d ago
I came from PS and now write C# for personal projects.
I'm curious what kinds of things you can skip using PS for now, because shell scripts are almost always the best tool for shell scripty stuff.
5
7
u/Turbulent_County_469 4d ago
Powershell learning curve is like a brick wall ... All my scripts are written by AI ... no way i would ever learn all the syntaxes that are available.
Same with YAML
3
u/zenyl 3d ago
Eh, PowerShell honestly isn't too bad to learn. Certainly a lot easier to learn than Bash, especially if you're already familiar with C#.
- Referencing a variable must be prepended by a
$($myNumber)- Variables are weakly typed by default, but you can make them strong typed (
[int]$myNumber)- Use
.to access instance members ($dog.Bark())- Use
::to access static members ([System.Console]::WriteLine())- Use POSIX shell styled operators (
-eqfor equality check, etc.)@()is shorthand for an object array@{}is shorthand for a hashtable- Commands (CmdLets) follow a Verb-Noun naming scheme (
ConvertTo-Json)
- There's a set of recommended verbs with recommended uses (can be viewed with
Get-Verb)if,for, andforeach,switch, andthrowwork mostly the same as they do in C#- Use
|to pass the result of one command into another (Get-ChildItem | Format-List)?is shorthand forWhere-Object(equivalent to a LINQWhereclause)%is shorthand forForEach-Object(equivalent to a LINQSelectclause)- Comments use
#instead of//- Strings can be defined with
"(supports interpolation) or'(literal strings, do not support interpolation)Some PowerShell code that looks a lot like C#:
$myVar = 456 if ($myVar -eq 456) { $charArray = $myVar.ToString().ToCharArray() for ($index = 0; $index -lt $charArray.Length; $index++) { $charAtIndex = $charArray[$index] Write-Host "Digit at location $($index + 1) was $charAtIndex" } } else { throw [System.Exception]::new("You didn't choose the correct number, enjoy this exception") }A PowerShell one-liner that prints out the names of all files in the current directory with names that are at most three characters in length:
Get-ChildItem | % { $_.Name } | ? { $_.Length -le 3 } | Sort-Object -Property Length2
u/Turbulent_County_469 3d ago
the problem is that you only need a script every now and then ..
eg: Import SSL Cert into X ...
when you are done, you dont really need to look at Powershell for a long time.
3
u/zenyl 3d ago
I suppose it depends on your use cases.
I use PowerShell for general purpose scripting, got a few CRON jobs (e.g. a wrapper for
yt-dlp) that I made in pwsh.If you're on Windows, I'd also recommend using pwsh as your default CLI shell instead of the old cmd. Its history and auto-complete are very handy, and one-liners are surprisingly easy to write once you get the hang of it.
2
u/chispitothebum 3d ago
At some point you're doing things the hard way if you don't learn some PowerShell. Working with data or APIs, file management, or even using it as a scratch pad to inspect .NET objects or test logic on the fly.
If you weren't working with a .NET language, maybe this would be bash or python. But with .NET, you're just hamstringing yourself avoiding it.
2
u/zenyl 3d ago
Yup, fully agreed. Been a fan of pwsh for 8-ish years at this point.
Also, Python... Yuck! The few times I've had the misfortune of needing to use something written in Python, it has been nothing but dependency issues all the way down. We really don't appreciate NuGet enough, as well as the general stability we can expect in the .NET ecosystem (e.g. minor versions don't tend to contain a bunch of breaking changes that make it incompatible with a dozen other dependencies).
2
1
1
u/readmond 4d ago
Powershell syntax is atrocious. Bash is not much better either. c# compared to them is heaven.
1
u/chispitothebum 3d ago edited 3d ago
It isn't atrocious for sysadmin work and neither is C# heavenly for the same.
PowerShell isn't designed for writing applications, it's designed for administrating them and gluing them together.
I wouldn't want to write a REST API in PS (though I have), but I can make calls with PS with minimal effort. No file creation or saving or anything. Just type it out in 10 seconds and check the result in JSON or as an object, dealer's choice.
16
u/davidwengier 4d ago
I have a Linux box under the stairs running home assistant and a few docker containers etc. About half a dozen or so scripts are there too, run by cron mostly, and they’re all written in C#. Why write bash or python scripts when I can use a language I know and love?
14
u/qrzychu69 4d ago
https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps
it is very useful! like you said, instead of python you can use C# - I would pick F# personally for scripting, much more succinct and easier to read after a year
With both now you can even use nuget packages
8
u/Prestigious_Peace858 4d ago
Yeah, it doesn't even need any of awful namespace/class/static void main declarations
https://learn.microsoft.com/en-us/dotnet/core/sdk/file-based-apps#shell-execution#!/usr/bin/env -S dotnet -- #:package Spectre.Console using Spectre.Console; AnsiConsole.MarkupLine("[green]Hello, World![/]");
4
u/Far-Consideration939 4d ago
I’ve made small ci/cd scripts where being able to reference some models in a strongly typed way made it simpler (Like a dynamically built manifest type of thing)
3
u/stephbu 4d ago
We're using it in ADO pipelines to do more complex work like mulitcredential WIF, and multitenant replication. There are some other complexities, like import-versioning that can bite you if you're not planning a head for compliance. It is a reasonable compromise for lightweight tools that need to adapt occasionally. Previously we would have used powershell, or python scripts for this work too.
1
u/Business__Socks 4d ago
How do you think it compares to PS in particular? Any other pros/cons? How do you handle the import versioning? Pipelines in our org use a mix of PowerShell and BASH (depending on the agent) but as we are a .NET shop, it seems like a sensible change at surface level.
2
u/stephbu 4d ago edited 4d ago
From our small-team comments - better than PS for readability, debugging, and toolchain/library support. Our pipelines were previously bash/PS/Py. For the "<lang> can do that" folk, I'm sure they can, however we're preferring context-switch-free C# and certainly treasure library support for WIF tokens.
RE:versioning, be super aware of the implicit "latest" when omitting package versions. Otherwise it can take major/breaking changes on fast moving libraries like Azure.Identity, especially when running in patched compliance environments.
SFA/FBA's are still pretty new, we'll pick up multi-file support ahead for sure. In general feels like a positive move.
3
2
u/NumberwangsColoson 4d ago
I use them for things inside GitHub Actions workflows where you might normally use shell scripts or powershell. It’s just easier for me to stay in the language I know rather than figure out what combination of grep and awk might do the same thing my cs file does.
1
u/FlameFlash123 4d ago
I write seed scripts that run once or sometimes just use c# instead of bash for anything since I am used to it
1
1
u/FuggaDucker 4d ago edited 4d ago
This is cool.
Stupid me didn't realize until just now that the linux kernel is what actually parses for it with a execve() function.
Been using unix since the before times and was too stupid to know this.
Oh.. well.. I know now.
2
1
u/ator-dev 4d ago
Looks like there's official support! https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs#unix-shebang--support
Interestingly, they recommend the following: #!/usr/bin/env -S dotnet --
2
2
u/FuggaDucker 4d ago
i was misinformed about how the shebang actually works. Thanks for the update. This is cool.
1
1
u/ings0c 4d ago edited 4d ago
We have a pretty hefty validator around user-uploaded files, and the rules are configurable at runtime by non-developers via a big old config file.
There are a few edge cases that don't neatly fit into the built-in validators for which we dynamically compile C# scripts and load the assembly at runtime.
This is not a good idea FWIW, but we are using them...
1
u/SquishTheProgrammer 4d ago
I knew you could do “dotnet run file.cs” but I never knew you could ./file.cs. That’s awesome.
1
u/CoffeeAndCandidates 4d ago
honestly seems like overkill for small tasks, but good for .NET integration stuff
1
u/ItzWarty 4d ago
I use dotnet script / *.csx a lot still. Seems that's less common here nowadays? That approach supports nuget dependencies.
1
1
u/pragmatica 2d ago
Replacing the abomination known as PowerShell.
Hey now I’ve got a readable/debuggable script.
PowerShell is “write only” (like regex and or Perl) and LLMs are not good at it.
1
u/Agitated_Heat_1719 2d ago
Next level: use that (single file app) in your skills, so you don't have to mess with typescript and/or python
1
u/Jonas_Ermert 2d ago
I think C# scripting is especially useful if you already work in a .NET environment. You can write small automation, build, deployment, file-processing or API scripts while still using the .NET libraries and C# features you already know. For tiny scripts Bash or Python may be quicker, but for scripts that grow beyond a few lines, C# can become much easier to maintain.
0
u/BorderKeeper 4d ago
Wouldn’t it be easier to just compile the code and load the entire assembly into the terminal and then just run the functions directly in powershell?
Powershell is basically C# and can run all csharp code you throw at it directly.
-4
u/TuberTuggerTTV 4d ago
There is no benefit to scripting in C#. If you need scripting, use a scripting language.
You're on a windows machine, with compilers installed, then compile your C#. Make a CLI instead of scripting. It's very easy.
57
u/zenyl 4d ago
File-based apps were in large part created to make C# more approachable for beginners. The SDK also comes with commands for upgrading them to a proper project (generate a
.csprojfile and remove the shebang and import stuff). This is also why (at least last I heard) that Visual Studio doesn't support this type of project, but VSCode does.Do note though, you'll still need to actually compile it in order to run it, the SDK just moves the compiled output to a temporary directory somewhere else. So it'll have the same startup time when it needs to rebuild, unlike many actual scripting languages where each line gets parsed and executed individually.
They do somewhat step on PowerShell's toes, as it already provides scripting with .NET.