Type: Shell Scripting Language
Extensions: (Preferred) .easy, (Candidate) .es
Version: 0.1.0
A simple shell that works cross-platform. It can't get easier.
EasyShell brings simplicity to process automation - with a handful of syntax, it enables automating common build tasks.
Easy Shell is a minimal shell scripting language implemented in C#. It combines:
- Shell-like “one command per line” scripting
- Strongly-typed global variables
- Control-flow keywords (
IF/ELSE/WHILE/FUNC/CALL) - Direct invocation of fully-qualified .NET members (methods, fields, properties)
- Sub-command expressions using parentheses for inline evaluation
-
It's minimal and for lazy people - no advanced bash like functions and mostly for scripting use. Also less risk of "forgetting some keyword/syntax" (like often with PowerShell).
-
Serve as a successor to
MiniParcelfor shell purpose soMiniParcelcan focus more on a "human-readable graph language". -
Default value interchange format is string, but variables enforce a declared type.
-
Reflection invocation attempts to match overloads by argument count + best conversion fit.
-
External executable calls return stdout (or stderr when stdout is empty).
- Strongly typed variables via
INT,BOOL,STRING,DOUBLE,HANDLE - Case-insensitive variable names
- Global scope for all variables (no function parameters needed)
- Expressions as parenthesized sub-commands:
(== $X 10)or(System.String.Format "x={0}" $X) - Control flow
IF ... ELSEIF ... ELSE ... ENDWHILE ... ENDFUNC name ... ENDandCALL name
- Reflection-based .NET invocation
- Static:
System.IO.File.WriteAllText "path" "content" - Instance:
CALL $handle MethodName [args...]
- Static:
- External process execution for non-keyword, non-qualified commands
- Comments with
#
# Get time as handle
HANDLEVAR NOW System.DateTime.Now
# Convert time to string via instance call
STRINGVAR DATE (CALL $NOW ToString)
# Prepare content via static call
STRINGVAR VALUE (System.String.Format "Current time: {0}" $DATE)
# Write file
STRINGVAR PATH "C:/Value"
System.IO.File.WriteAllText $PATH $VALUE
es> $Date = (format "{0:yyyyMMdd}" (GetDate))
es> print $date
20251214
es> $Date = (format "{0:yyyyMMdd}" GetDate)
es> print $date
GetDate
easy # REPL
easy path/to/script.easyeasy --help
easy --versionAnything after # is ignored.
# This is a comment
STRING Name "Charles" # trailing comment
Declare variables with a type command:
INTVAR Count 10
BOOLVAR Enabled TRUE
DOUBLEVAR Pi 3.14159
STRINGVAR Title "Hello"
HANDLEVAR Now System.DateTime.Now
Rules:
- Variable names are case-insensitive
- Variables are strongly typed
HANDLEstores an object instance (from .NET calls or other results)- Empty strings are supported, e.g.
STRING VALUE ""
Reference: $Name
Assignment:
$Count = 11
$Title = "Updated"
Values can be literals or expressions:
$Title = (System.String.Format "Count={0}" $Count)
- Strings:
"hello world" - Booleans:
TRUE,FALSE(also accepts common equivalents) - Integers:
123 - Doubles:
3.14
A line is generally a command invocation:
- Keywords (language built-ins)
- External executables (e.g.,
git,ping,curldepending on environment) - Fully-qualified .NET members (reflection invoked)
Any argument may be an expression: a parenthesized command that is evaluated first and yields a value.
STRING X "10"
BOOL IsTen (== $X 10)
==,!=,>,<,>=,<=
Examples:
BOOL A (== 5 5)
BOOL B (> 10 2)
BOOL C (<= 3 3)
INT X 10
IF (>= $X 10)
STRING Msg "X is at least 10"
ELSEIF (== $X 9)
STRING Msg "X is 9"
ELSE
STRING Msg "X is something else"
END
INT I 0
WHILE (< $I 3)
System.Console.WriteLine (System.String.Format "I={0}" $I)
$I = (+ $I 1) # if you add a + command later; otherwise assign directly
END
Note: If arithmetic commands are not implemented yet, you can update values using .NET calls you provide, or extend the engine with
+,-, etc.
Functions are named blocks with global variable access. They do not take arguments; “return values” are done by setting variables.
FUNC WriteGreeting
System.Console.WriteLine "Hello from a function"
END
CALL WriteGreeting
System.Console.WriteLine "Hello"
STRING S (System.String.Format "X={0}" 42)
Static property/field access (no arguments):
HANDLEVAR Now System.DateTime.Now
Use CALL <handle> <method> [args...]:
HANDLEVAR Now System.DateTime.Now
STRINGVAR Stamp (CALL $Now ToString)
System.Console.WriteLine $Stamp
- .NET SDK (recommended: .NET 8+)
- Powershell 7 (for first build)
- Alternatively, EasyShell binary
- Alternatively, build directly with
dotnet
Use pwsh from the BuildScripts folder:
pwsh ./BuildEasyShell.ps1Or using easy itself:
easy ./BuildEasyShell.easy
Or using dotnet:
dotnet run -- ./BuildScripts/BuildEasyShell.easy
Expected folder structure:
<build-root>/
├─ External/
│ └─ EasyShell/
│ ├─ BuildScripts/
│ │ ├─ BuildEasyShell.easy
│ │ └─ BuildEasyShell.ps1
│ ├─ EasyShell.csproj
│ └─ ...
├─ Publish/
│ ├─ Utilities/
│ │ └─ EasyShell/
│ │ └─ Current/
│ └─ Packages/The build script lives directly under External/EasyShell/BuildScripts.
It uses:
$BuildRoot = (Get-Item -LiteralPath $PSScriptRoot).Parent.Parent.Parent.FullNameSo BuildScripts must be three levels below <repo-root>:
<build-root>/External/EasyShell/BuildScriptsThe script publishes External/EasyShell into:
<build-root>/Publish/Utilities/EasyShell/CurrentThen creates a package in:
<build-root>/Publish/Packages- v0.1.0: Initial setup.
MIT
- Fork / branch
- Add tests for new language features
- Keep scripts in
Examples/small and focused - Open a PR with a short description of behavior changes