🐧 Virtux
A realistic Linux terminal, simulated entirely in Python - runs anywhere Python runs.
Virtux gives you a full Linux shell experience - filesystem, permissions, piping, 40+ coreutils-style commands - without a VM, container, Docker daemon, or WSL install. It's pure Python, starts in milliseconds, and behaves close enough to a real shell that it's genuinely useful for teaching, testing, and sandboxed scripting.
pip install virtux
user@virtux:~$ mkdir -p projects/my-app && cd projects/my-app
user@virtux:~/projects/my-app$ echo "Hello World!" > README.md
user@virtux:~/projects/my-app$ cat README.md | grep Hello
Hello World!
Table of Contents
- Why Virtux?
- Features
- Installation
- Quick Start
- Usage Modes
- Command Reference
- Python API
- Plugin System
- Configuration
- How It Compares
- FAQ
- Contributing
- License
Why Virtux?
Most "learn Linux" tools fall into two camps: a real VM/container (heavy, slow to spin up, needs admin rights) or a toy JS terminal in a browser (not scriptable, no real Python integration). Virtux sits in between:
| Need | Virtux | Real VM / Docker | Browser sandbox |
|---|---|---|---|
| Works on Windows without WSL | ✅ | ❌ | ✅ |
| Starts in under a second | ✅ | ❌ | ✅ |
| Real piping, redirection, exit codes | ✅ | ✅ | ⚠️ limited |
| Embeddable in a Python app/test suite | ✅ | ⚠️ awkward | ❌ |
| Zero system dependencies | ✅ | ❌ | ✅ |
| Persistent state between runs | ✅ | ✅ | ⚠️ varies |
If you need to teach shell basics, test CLI tools without touching the real filesystem, or embed a sandboxed shell inside another Python app, Virtux is built for exactly that.
Features
- 🖥️ Realistic shell - colorized prompt, tab completion, reverse-search history (
Ctrl+R), multi-line editing - 📁 Virtual filesystem - a full hierarchical tree (
/home,/etc,/tmp,/var, …) backed by an in-memory or on-disk store - 🔗 Real piping & redirection -
|,>,>>,<,2>,&>,&&,||,;, subshells with() - 🔒 Users & permissions -
chmod,chown,chgrp,umask,sudo,su, full rwx + octal permission model with enforcement - 📝 40+ built-in commands - see the full reference below
- 💾 Persistent sessions - your virtual filesystem and history survive across runs (stored in
~/.virtux/) - 🔌 Plugin system - add your own commands via Python entry points, no fork required
- 🛡️ Fully sandboxed - nothing Virtux does ever touches your real filesystem, network, or processes
- 🧪 Scriptable & testable - drive it from
pytest, assert on output/exit codes, reset state between tests - 🌍 Cross-platform - identical behavior on Windows, macOS, and Linux since it isn't backed by the host shell
Installation
pip install virtux
Requires Python 3.9+. No compiled extensions, no system packages, no admin/root access needed.
To verify:
virtux --version
Quick Start
Interactive Shell
virtux
╦ ╦╦╦═╗╔╦╗╦ ╦═╗ ╦
╚╗╔╝║╠╦╝ ║ ║ ║╔╩╦╝
╚╝ ╩╩╚═ ╩ ╚═╝╩ ╚═
Linux Terminal Emulator v0.1.0
user@virtux:~$ mkdir -p projects/my-app
user@virtux:~$ echo "Hello World!" > projects/my-app/README.md
user@virtux:~$ cat projects/my-app/README.md
Hello World!
user@virtux:~$ ls -la projects/
drwxr-xr-x 2 user user 4096 Jun 23 12:00 my-app
user@virtux:~$ find / -name "*.md"
/home/user/projects/my-app/README.md
user@virtux:~$ chmod 600 projects/my-app/README.md && ls -l projects/my-app/
-rw------- 1 user user 13 Jun 23 12:00 README.md
One-Off Command
Run a single command and exit - handy for quick checks or shell scripts:
virtux -c "ls -la /etc"
Script Execution
Run a .sh file just like bash script.sh would:
virtux script.sh
# script.sh
#!/usr/bin/env virtux
mkdir -p /tmp/build
for f in *.txt; do
cp "$f" /tmp/build/
done
echo "Build staged: $(ls /tmp/build | wc -l) files"
Usage Modes
| Mode | Command | Best for |
|---|---|---|
| Interactive | virtux |
Learning, exploring, manual testing |
| One-shot | virtux -c "cmd" |
Scripts, CI checks, quick lookups |
| Script file | virtux script.sh |
Reusable automation, classroom exercises |
| Embedded API | from virtux import VirtuxShell |
Apps, test suites, custom tooling |
Command Reference
📁 Filesystem (15 commands)
ls cd pwd mkdir rmdir rm cp mv touch ln find stat du df tree
📝 Text processing (13 commands)
cat head tail grep wc sort uniq cut sed awk echo tee diff
⚙️ System (23 commands)
whoami uname hostname date cal uptime env export unset alias unalias history clear exit man help which type id printenv true false sleep
🔒 Shell & permissions (9 commands)
chmod chown chgrp umask sudo su source bash sh
🌐 Network (6 commands, simulated)
ping ifconfig ip curl wget ssh
Network commands return realistic, deterministic output but never make real network calls - safe to script against without side effects.
📦 Archives & compression (5 commands)
tar gzip gunzip zip unzip
📊 Process management (4 commands, simulated)
ps top kill jobs
Run man <command> or help inside the shell for full usage and flags for any of the above.
Python API
Embed Virtux directly in your own application or test suite:
from virtux import VirtuxShell
shell = VirtuxShell()
output = shell.execute("echo 'Hello from Python!'")
print(output) # Hello from Python!
Capturing exit codes and errors
result = shell.run("ls /nonexistent")
print(result.stdout) # ""
print(result.stderr) # "ls: /nonexistent: No such file or directory"
print(result.exit_code) # 2
Resetting state between tests
import pytest
from virtux import VirtuxShell
@pytest.fixture
def shell():
s = VirtuxShell(persist=False) # fresh, in-memory filesystem
yield s
s.reset()
def test_mkdir_creates_directory(shell):
shell.execute("mkdir /tmp/demo")
assert shell.execute("test -d /tmp/demo && echo yes") == "yes\n"
Pre-seeding a virtual filesystem
shell = VirtuxShell()
shell.fs.write_file("/etc/myapp.conf", "debug=true\n")
shell.execute("cat /etc/myapp.conf") # debug=true
Plugin System
Add custom commands without forking the project, using standard Python entry points:
# my_virtux_plugin/plugin.py
def register(registry):
@registry.register("mycommand", help_text="My custom command")
def cmd_mycommand(ctx):
ctx.writeln("Hello from my plugin!")
return 0
# pyproject.toml
[project.entry-points."virtux.plugins"]
my_plugin = "my_virtux_plugin.plugin:register"
Once installed (pip install . or pip install my-virtux-plugin), mycommand is available automatically in every Virtux session - no extra registration step needed.
ctx gives plugin authors access to:
| Member | Purpose |
|---|---|
ctx.writeln(text) / ctx.write(text) |
Write to stdout |
ctx.error(text) |
Write to stderr |
ctx.args |
Parsed argument list |
ctx.fs |
Virtual filesystem handle |
ctx.env |
Current environment variables |
ctx.cwd |
Current working directory |
Configuration
| CLI flag | Effect |
|---|---|
virtux |
Launch interactive shell |
virtux -c "command" |
Execute a single command and exit |
virtux script.sh |
Run a script file |
virtux --reset |
Wipe the persisted virtual filesystem and start clean |
virtux --no-persist |
Run with an in-memory filesystem that discards on exit |
virtux --version |
Print the installed version |
By default, session state (filesystem + history) is stored at ~/.virtux/. Override with the VIRTUX_HOME environment variable:
export VIRTUX_HOME=/tmp/my-virtux-sandbox
virtux
How It Compares
| Virtux | WSL | Docker container | xterm.js + node-pty | |
|---|---|---|---|---|
| Install footprint | pip install |
OS feature, GBs | Docker daemon required | Node + native pty bindings |
| Cold start | Milliseconds | Seconds–minutes | Seconds | Seconds |
| Cross-platform parity | Identical everywhere | Windows-only | Needs Docker Desktop | Needs a real OS shell underneath |
| Touches real filesystem | Never | Yes | Yes (namespaced) | Yes |
| Embeds in a Python test suite | Native | Awkward | Possible via SDK, heavy | Not applicable |
Virtux isn't a replacement for a real Linux box when you need actual binaries, real networking, or kernel-level behavior - it's a lightweight, safe stand-in for teaching, prototyping, and testing shell-driven workflows.
FAQ
Does Virtux run real binaries? No. Every command is a Python implementation that mimics real Linux behavior closely enough for scripting, teaching, and testing - but nothing is shelled out to your OS.
Can it break my actual files?
No. The virtual filesystem is fully isolated, whether it's in-memory (--no-persist) or persisted under ~/.virtux/.
Does sudo/su do anything real?
They simulate the permission model (UID/GID switching, permission checks) within the virtual filesystem only - there's no real privilege escalation possible.
Can I use it in CI?
Yes - virtux --no-persist -c "..." is ideal for CI: deterministic, no host side effects, no setup steps.
Contributing
Issues and PRs are welcome. Before opening a PR:
git clone https://github.com/RishiBuilds/virtux.git
cd virtux
pip install -e ".[dev]"
pytest
Please include tests for new commands or behavior changes - see tests/ for examples of the expected style.
License
MIT License - see LICENSE for details.
Metadata
Release files for virtux 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| virtux-0.1.0.tar.gz | 62.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| virtux-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 123.6 kB
Release files / virtux-0.1.0.tar.gz
| Download URL | virtux-0.1.0.tar.gz |
|---|---|
| Size | 62.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
071c606df4fefb5f11f4504407f3c7af936ca8aafdb6520146fe413bd3e024c7
|
|
BLAKE2b-256 checksum How to use checksums |
30b4b395341fe3bac69ecd568fae5fa5f2a61d9f07324d2f51dfa75ba7b91379
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / virtux-0.1.0-py3-none-any.whl
| Download URL | virtux-0.1.0-py3-none-any.whl |
|---|---|
| Size | 60.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6a4b4bf9b0b84e50731122b5e1bd599d246999d82d8a4a771747a396e864dfca
|
|
BLAKE2b-256 checksum How to use checksums |
99150168a9030c686615d02fcfdf00ef98a07f69a8f7bbb310d8e8638f4dcfe4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|