Quick Start
Run a shell script
using Computerwelt.Emulation.Bash;
var bash = Bash.CreateBuilder()
.WithWorkingDirectory("/home/agent")
.WithUsername("agent")
.WithHostname("sandbox")
.WithLimits(ExecutionLimits.Strict)
.Build();
var result = await bash.ExecAsync("""
mkdir -p /home/agent/data
printf 'banana\napple\ncherry\n' > /home/agent/data/fruit.txt
sort /home/agent/data/fruit.txt | head -2
""");
Console.WriteLine(result.Stdout); // apple\nbanana
Console.WriteLine(result.ExitCode); // 0
The session keeps state between calls the way a terminal does: a cd, an exported variable or a
file written by one ExecAsync is there for the next one.
Run a Python program
using Computerwelt.Emulation.Python;
var runner = new PythonRunner();
var result = runner.Run("""
import json
data = [{"id": i, "square": i * i} for i in range(5)]
print(json.dumps(data))
""");
Console.WriteLine(result.Succeeded); // True
Console.WriteLine(result.Stdout);
RunResult carries Stdout, Stderr, ExitCode, the globals the program ended with, and, when it
raised, the exception and its traceback as Python would have printed them.
Both, over one filesystem
using Computerwelt;
using Computerwelt.Emulation.Bash;
var bash = Bash.CreateBuilder()
.WithPython()
.Build();
var result = await bash.ExecAsync("""
cat > /report.py <<'PY'
import json, os
rows = [line.split(',') for line in open('/data.csv').read().splitlines()[1:]]
print(json.dumps({"rows": len(rows)}))
PY
printf 'id,name\n1,a\n2,b\n' > /data.csv
python /report.py
""");
Console.WriteLine(result.Stdout); // {"rows": 2}
The Python program's open, os and os.path are backed by the shell's filesystem, so a file the
script wrote is a file the program reads.
Handle failure
var result = await bash.ExecAsync("cat /does/not/exist");
if (result.ExitCode != 0)
{
Console.Error.WriteLine(result.Stderr); // cat: /does/not/exist: No such file or directory
}
Ordinary non-zero exits arrive as an ExecResult, not an exception. Exit code 127 means the command
was not found, 126 means it was not executable, and 2 means a parse or usage error. Those codes leak
into scripts, so they are kept exact.
A limit that is reached, by contrast, raises: a traversal that quietly stopped part-way would report a subset as though it were the whole.
Read next
- The virtual filesystem — giving the sandbox something to read.
- Extending the shell — adding commands of your own.
- Limits — the budgets, and how to pick them.