vlsh is an interactive Unix shell written in V. It is designed to be simple, fast, and hackable — with a clean codebase that is easy to read, modify, and extend.
- Pipes, redirection, and AND-chains —
cmd1 | cmd2,> file,>> file,cmd1 && cmd2 - Glob expansion —
*.v,./*.deb,~/docs/**expanded before execution - Tilde and environment-variable expansion —
~/path,$VAR,VAR=val cmd - Command history — up/down arrow browsing and
Ctrl+Rincremental search; shared across all sessions (last 5000 entries in~/.vlsh_history) - Tab completion — files and directories;
cdcompletes only directories; plugins can register custom completions (e.g. SSH hostname completion) - Aliases — defined in
~/.vlshrcor managed live withaliases add/remove - Plugin system — drop a
.vfile into~/.vlsh/plugins/; vlsh compiles and loads it automatically. Plugins can add commands, decorate the prompt, run pre/post hooks around every command, provide custom tab completions, and contribute live text to the mux status bar (mux_statuscapability). Browse, install, and delete plugins from the official remote repository at https://github.com/vlshcc/plugins usingplugins remote,plugins install, andplugins delete. - Terminal multiplexer — built-in
muxcommand splits the terminal into resizable panes, each running its own shell. Supports mouse selection, copy/paste, a status bar, per-pane scrollback history (up to 1000 lines, scrollable via mouse wheel orCtrl+V+PageUp/PageDown), and all common VT100 sequences so editors likevimandnanowork correctly inside panes. - Native
.vshscript support — execute V shell scripts directly without invokingv runmanually - Session environment variables —
venv add/rm/listfor temporary per-session variable management - Theming — prompt and UI colours configurable via
style setand~/.vlshrc
The latest release is v1.0.11. Pre-built packages for 64-bit Linux are available on the releases page.
Debian / Ubuntu — install via .deb:
curl -LO https://github.com/DavidSatimeWallin/vlsh/releases/download/v1.0.11/vlsh_1.0.11_amd64.deb
sudo dpkg -i vlsh_1.0.11_amd64.debThe package installs the binary to /usr/bin/vlsh and automatically adds it
to /etc/shells via the postinst script.
Other Linux — standalone binary:
curl -LO https://github.com/DavidSatimeWallin/vlsh/releases/download/v1.0.11/vlsh_1.0.11_amd64_linux
chmod +x vlsh_1.0.11_amd64_linux
sudo mv vlsh_1.0.11_amd64_linux /usr/local/bin/vlsh- V — install with
v upor from https://github.com/vlang/v
git clone https://github.com/dvwallin/vlsh.git
cd vlsh
v .
./vlshOr run directly without compiling first:
v run .After building, copy the binary to a directory on your system PATH:
sudo cp vlsh /usr/local/bin/vlshVerify it is accessible:
which vlsh # should print /usr/local/bin/vlsh
vlsh --version 2>/dev/null || vlsh -c 'version'-
Add vlsh to the list of approved shells:
echo /usr/local/bin/vlsh | sudo tee -a /etc/shells
-
Change your login shell:
chsh -s /usr/local/bin/vlsh
-
Log out and back in (or open a new terminal). Your session should now start in vlsh.
To revert at any time:
chsh -s /bin/bash # or /bin/zsh, etc.I'm using vlsh as my daily shell. That does not mean you should be. It is likely that you will run into bugs when using vlsh.
vlsh will look for the configuration file $HOME/.vlshrc.
Here's an example -file:
"paths
path=/usr/local/bin
path=/usr/bin;/bin
"aliases
alias gs=git status
alias gps=git push
alias gpl=git pull
alias gd=git diff
alias gc=git commit -sa
alias gl=git log
alias vim=nvim
"style (define in RGB colors)
style_git_bg=44,59,71
style_git_fg=251,255,234
style_debug_bg=255,255,255
style_debug_fb=251,255,234
vlsh.v – main entry point, prompt rendering, read-eval loop
cfg/cfg.v – config file (~/.vlshrc) parsing, aliases, paths, style
cmds/cmds.v – built-in commands: help, cd
cmds/ls.v – built-in colorised ls
cmds/cp.v – built-in overwrite-copy (ocp)
exec/exec.v – external-command execution, pipe chains, I/O redirection
mux/mux.v – terminal multiplexer entry point and event loop
mux/pane.v – per-pane VT100 parser and cell grid
mux/layout.v – binary-tree layout engine (splits/resize/navigation)
mux/render.v – grid-to-terminal renderer
mux/input.v – key-sequence parser for mux prefix bindings
mux/pty.v – PTY helpers (thin wrappers around C functions)
mux/pty_helpers.h – C helpers: raw mode, winsize, select, forkpty, exec
utils/utils.v – shared helpers: parse_args, fail/warn/ok/debug
plugins/ – plugin loader and hook dispatcher
A plain-text file read on every command. Lines beginning with " are comments.
| Directive | Example | Meaning |
|---|---|---|
path=<dir> |
path=/usr/bin;/bin |
Add dirs to executable search path (;-separated) |
alias <name>=<cmd> |
alias gs=git status |
Define a command alias |
style_git_bg=r,g,b |
style_git_bg=44,59,71 |
Git-branch prompt background colour |
style_git_fg=r,g,b |
style_git_fg=251,255,234 |
Git-branch prompt foreground colour |
style_debug_bg=r,g,b |
Debug-output background (when VLSHDEBUG=true) |
|
style_debug_fg=r,g,b |
Debug-output foreground |
| Command | Description |
|---|---|
aliases list |
List all defined aliases |
aliases add <name>=<cmd> |
Add or update an alias |
aliases remove <name> |
Remove an alias |
cd [dir] |
Change directory (~ expands to $HOME; home if omitted) |
echo [args…] |
Print arguments; expands $VAR and $0; supports > / >> |
exit |
Exit the shell |
help [cmd] |
Show command list, or detailed help for a specific command |
ls [dir] |
Colorised directory listing (falls through to system ls when flags are passed) |
mux |
Enter terminal multiplexer mode |
ocp <src> <dst> |
Copy file, overwriting destination |
path list |
Show PATH entries |
path add <dir> |
Append a directory to PATH |
path remove <dir> |
Remove a directory from PATH |
plugins list |
List locally installed plugins |
plugins enable <name> |
Enable a disabled plugin by name |
plugins enable all |
Enable every plugin at once |
plugins disable <name> |
Disable a plugin by name |
plugins disable all |
Disable every plugin at once |
plugins reload |
Hot-reload all plugins |
plugins remote |
List plugins available in the remote repository |
plugins remote search <query> |
Filter remote plugins by name |
plugins install <name> |
Download and install a plugin from the remote repository |
plugins delete <name> |
Delete an installed plugin |
style list |
Show current style/colour settings |
style set <key> <r> <g> <b> |
Set a prompt colour (RGB 0–255) |
venv list |
List shell environment variables set via venv |
venv add <NAME> <value> |
Set an environment variable for the current session |
venv rm <NAME> |
Unset an environment variable |
version |
Print the vlsh version |
Pipes – chain commands with |:
ls | grep .v | wc -l
Output redirection – write or append stdout to a file:
echo "hello" > file.txt
echo "world" >> file.txt
AND-chains – run the next command only if the previous one succeeds:
touch /tmp/x && echo "file created"
Tilde expansion – ~ and ~/path are expanded to $HOME in both commands and arguments:
vi ~/.config/nvim/init.vim
Environment-variable prefix – set variables only for the duration of one command:
FIELD_LIST='used,avail' df -h --no-sync .
Variable expansion in echo – $VAR expands to the environment variable value; $0 expands to vlsh.
Command history – up/down arrows browse history; Ctrl+R searches history.
All instances share a global history file at ~/.vlsh_history (last 5000 entries).
Tab completion – completes file and directory names. When the command is cd, only directories are suggested. Plugins can register a completion capability to provide custom completions for their commands (e.g. SSH hostnames for ssh).
Plugins – drop .v source files into ~/.vlsh/plugins/. Each plugin can expose commands, pre/post-run hooks, prompt decorations, and custom tab completions.
Aliases – defined in ~/.vlshrc or managed with the aliases built-in; resolved before PATH lookup.
Debug mode – set VLSHDEBUG=true in the environment to print internal debug output.
.vsh scripts – vlsh natively runs V shell scripts (.vsh files) via v run. You can execute them directly without specifying v run manually:
./myscript.vsh
~/bin/myscript.vsh
myscript.vsh # looked up in current directory
A .vsh file is a regular V source file with all os module functions available globally (no os. prefix needed). Use the following shebang to make the file directly executable outside vlsh too:
#!/usr/bin/env -S vExample script (hello.vsh):
#!/usr/bin/env -S v
println('Hello from a .vsh script!')
files := ls('.') or { [] }
for f in files {
println(f)
}vlsh looks for the v binary in the configured path= directories first, then falls back to the system PATH.
Plugins extend vlsh with new commands and prompt decorations without modifying the shell binary.
- Place a
.vsource file in~/.vlsh/plugins/. - vlsh compiles it automatically on startup (requires
vin PATH) and caches the binary alongside the source. - The binary is called by the shell with a single argument that tells it what to do (see the protocol below).
- Use the built-in
pluginscommand to manage them at runtime.
Your plugin's main() must handle these arguments:
| Argument | Description |
|---|---|
capabilities |
Print what the plugin provides, one item per line (see table below) |
run <command> [args…] |
Execute a registered command |
prompt |
Print a single line shown above the - prompt |
pre_hook <cmdline> |
Called before every command runs |
post_hook <cmdline> <exit_code> |
Called after every command finishes |
complete <input> |
Print one tab-completion candidate per line for the current input |
mux_status |
Print a single line shown centred in the mux status bar (polled ~1 s) |
Capability tokens (printed in response to capabilities):
| Token | Effect |
|---|---|
command <name> |
Registers <name> as a shell command dispatched via run |
prompt |
Shell calls prompt before each prompt and displays the output above - |
pre_hook |
Shell calls pre_hook <cmdline> before every command |
post_hook |
Shell calls post_hook <cmdline> <exit_code> after every command |
completion |
Shell calls complete <input> on Tab; plugin prints full replacement strings |
mux_status |
Shell calls mux_status roughly once per second in mux mode; plugin prints a single line that appears centred in the status bar |
plugins list – list all locally installed plugins
plugins enable <name> – enable a previously disabled plugin
plugins enable all – enable every plugin at once
plugins disable <name> – disable a plugin without deleting it
plugins disable all – disable every plugin at once
plugins reload – recompile and reload all plugins
plugins remote – list plugins in the remote repository
plugins remote search <query> – filter remote plugins by name
plugins install <name> – download and install a remote plugin
plugins delete <name> – delete a locally installed plugin
Plugins are sourced from the official repository at
https://github.com/vlshcc/plugins. No external tooling is required — the
install subcommand downloads plugin source files directly. After installing
a new plugin, run plugins reload to compile and activate it.
A minimal template that shows all four capabilities. Copy it to get started:
cp examples/hello_plugin.v ~/.vlsh/plugins/myplugin.vIt registers a hello [name] command, contributes a [ example plugin ] prompt line, has empty pre_hook / post_hook stubs ready to be filled in, and demonstrates the mux_status capability with a static label.
// Respond to 'capabilities'
println('command hello')
println('prompt')
println('pre_hook')
println('post_hook')
println('mux_status')
// Respond to 'run hello [name]'
println('Hello, ${name}!')
// Respond to 'prompt'
println('[ example plugin ]')
// Respond to 'mux_status'
println('[ example plugin ]')Shows the current git branch and short commit hash in the prompt, styled with your style_git_bg / style_git_fg colours.
cp examples/git.v ~/.vlsh/plugins/git.v
plugins reloadWhen inside a git repository the line above the - prompt becomes:
main a1b2c3d
(coloured block using the 24-bit ANSI colour values from ~/.vlshrc)
The plugin reads colours from ~/.vlshrc; defaults are 44,59,71 (dark blue-grey background) and 251,255,234 (near-white foreground). Override them with:
style set style_git_bg 44 59 71
style set style_git_fg 251 255 234
ssh_hosts provides tab completion for SSH hostnames. When you type ssh <prefix> and press Tab, it returns matching hosts gathered from ~/.ssh/config and ~/.ssh/known_hosts. It also supports user@<prefix> notation.
cp examples/ssh_hosts.v ~/.vlsh/plugins/ssh_hosts.v
plugins reloadUsage:
ssh web<Tab> → ssh webserver, ssh web01 …
ssh root@db<Tab> → ssh root@db1, ssh root@db2 …
Sources read (automatically, no configuration needed):
~/.ssh/config—Hostentries (wildcard patterns like*are skipped)~/.ssh/known_hosts— all non-hashed entries (hashed|1|…lines are skipped)
vman is a man-page style viewer for the official V module documentation at
modules.vlang.io. It fetches the HTML for any
standard-library module, strips the markup, and displays the result in less
(with ANSI colour support) so you can scroll, search, and quit just like a
regular man page.
cp examples/v_man.v ~/.vlsh/plugins/v_man.v
plugins reloadUsage:
vman os
vman strings
vman net.http
vman math
What it does:
- Fetches
https://modules.vlang.io/<module>.html. - Converts the HTML to ANSI-formatted text:
- headings become bold (h1 also underlined)
- inline
codeandpreblocks are cyan - lists, paragraphs, and horizontal rules are preserved
<script>,<style>, and<noscript>blocks are stripped
- Opens the result in
less -R(ormoreiflessis unavailable). Falls back to printing directly if neither pager is found.
share uploads a file to dpaste.com and prints the resulting URL. It is available as a plugin in the official repository.
plugins install share
plugins reloadUsage:
share <file>
Start with mux. A new vlsh session fills the terminal. All key sequences require the Ctrl+V prefix.
| Key | Action |
|---|---|
Ctrl+V + | |
Split active pane vertically (left / right) |
Ctrl+V + - |
Split active pane horizontally (top / bottom) |
Ctrl+V + ←/→/↑/↓ |
Navigate to the adjacent pane |
Ctrl+V + Ctrl+←/→ |
Resize pane horizontally |
Ctrl+V + Ctrl+↑/↓ |
Resize pane vertically |
Ctrl+V + o |
Cycle focus to the next pane |
Ctrl+V + PageUp |
Scroll active pane back into scrollback history |
Ctrl+V + PageDown |
Scroll active pane forward toward live output |
Ctrl+V + q |
Exit mux (only when all panes have been closed) |
Ctrl+V + Ctrl+V |
Send a literal Ctrl+V to the active pane |
| Mouse click | Click a pane to make it active |
| Mouse wheel | Scroll active pane up/down through scrollback history |
Each pane retains up to 1000 lines of scrollback history. While scrolled back, an orange indicator in the top-right corner of the pane shows how many lines above live output you are. Panes close automatically when their shell process exits. The terminal is fully restored on exit and a confirmation message is printed.
The status bar at the top shows vlsh mux on the left and the live pane count on the right. Plugins that declare the mux_status capability contribute text that is displayed centred between those labels and refreshed roughly once per second.
cfg – get() !Cfg, paths() ![]string, aliases() !map[string]string, style() !map[string][]int, add_path, remove_path, add_alias, remove_alias, set_style
exec – Task.prepare_task() !int runs a command string (with pipes, redirection, tilde expansion) and returns the exit code
utils – parse_args(string) []string tokenises a command line respecting single/double quotes; fail/warn/ok/debug for formatted output
mux – enter(status_providers []string) is the public entry point; internally uses Mux, Pane, LayoutNode, InputHandler
plugins – load() []Plugin, dispatch(…) bool, completions(loaded, input) []string, run_pre_hooks, run_post_hooks, prompt_segments(loaded) string, mux_status_binaries(loaded) []string, enable(name), disable(name), enable_all(), disable_all(), remote_available() ![]string, install(name) !, delete_plugin(name) !
vlsh is provided as-is, without warranty of any kind. The creator and contributors are not liable for any damage, data loss, system instability, security issues, or any other consequence — direct or indirect — that may arise from using vlsh or any of its plugins. You use this software entirely at your own risk. This applies equally to the shell itself, the bundled example plugins, and any third-party plugins you install.
Originally created by onyxcode
MIT License
Copyright (c) [2021-2026] [David Satime Wallin david@snogerup.com]
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.