posh

command module
v1.3.59 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 15 Imported by: 0

README

posh

A portable Unix-like command shell for Windows, written in Go — similar in spirit to GNU bash.

posh runs interactively with full line-editing and persistent history, executes shell scripts, and provides the core shell primitives you expect: pipelines, redirections, variable expansion, command substitution, arithmetic expansion, glob expansion, background jobs, and a set of built-in commands.

The binary name is derived from the executable stem — rename it and all prompts update automatically.


Installation

Download binary (easiest)

Grab the latest pre-built binary from the Releases page — no Go required.

File Platform
posh.exe Windows (amd64)
posh-linux-amd64 Linux (amd64)

Place it somewhere on your PATH and you're done. On Linux, mark it executable first:

chmod +x posh-linux-amd64
mv posh-linux-amd64 ~/.local/bin/posh
go install

Requires Go 1.21+.

go install github.com/fermat-tech/posh@latest

The binary lands in %USERPROFILE%\go\bin (Windows) or ~/go/bin (Linux/macOS), which should already be on your PATH.

Build from source
git clone https://github.com/fermat-tech/posh.git
cd posh
go build -o posh.exe .   # Windows
go build -o posh .       # Linux / macOS

Usage

posh                   Interactive REPL
posh script.posh       Execute a script file
posh -c "command"      Run a single command and exit

Features

Pipelines
ls | wc -l
cat file.txt | grep foo | sort
Redirections
echo hello > out.txt
cat >> log.txt
sort < input.txt
cmd 2> errors.txt
cmd &> all.txt        # stdout + stderr
Variables
NAME=world
echo Hello $NAME
echo "Hello ${NAME}"
echo $?               # last exit code
echo $$               # current PID
Parameter expansion
text="Hello World"
echo "${text:6:5}"        # World   (substring: offset 6, length 5)
echo "${text:6}"          # World   (to end)
echo "${text: -5}"        # World   (negative offset, counts from end)
echo "${text:0:-6}"       # Hello   (negative length, trims from end)
echo "${#text}"           # 11      (length in characters)
echo "${UNSET:-default}"  # default (value if unset/empty)
echo "${NAME:+set}"       # set     (alternate value if set)
echo "${V:=init}"         # init    (assign default if unset)
Arrays
# indexed
arr=(one two three)
arr[1]=TWO            # set one element
arr+=(four)           # append
echo "${arr[1]}"      # one element (negative index counts from the end)
echo "${arr[@]}"      # all elements (each its own word when quoted)
echo "${#arr[@]}"     # number of elements
echo "${#arr[1]}"     # length of one element
echo "${!arr[@]}"     # the indices

# associative
declare -A roles=([admin]=rw [guest]=r)
roles[user]=r
echo "${roles[admin]}"   # value by key
echo "${!roles[@]}"      # keys (sorted); "${roles[@]}" gives the values
echo "${#roles[@]}"      # number of entries
Command substitution
echo "Today is $(date)"
FILES=$(ls *.go)
Arithmetic expansion
echo $((2 + 3 * 4))
SIZE=$((1024 * 1024))
Glob expansion
echo *.go
ls src/**/*.txt
Tilde expansion
cd ~
cat ~/notes.txt
Logical operators and sequences
make && echo "Build OK"
test -f file || echo "Not found"
cd /tmp; ls; cd -
Background jobs
long-running-command &
jobs
Subshells
(cd /tmp && ls)

Built-in commands

Command Description
cd [dir] Change directory (default: $HOME)
pwd Print working directory
echo [-n] [args] Print arguments
printf fmt [args] Formatted output
export [VAR[=val]] Export variable to child processes
unset VAR... Remove variable
env Print exported environment
set [VAR=val] Set or list shell variables
declare [-A|-a] name Declare variables/arrays (typeset alias)
source FILE / . FILE Execute file in current shell context
alias [name[=val]] Define or list aliases
unalias name... Remove aliases
history Show command history
type name... Show whether name is a builtin, alias, or external
jobs List background jobs
true / false / : Exit 0 / exit 1 / no-op
help Show built-in help
exit [n] Exit with status n

Startup file

posh sources ~/.poshrc on interactive startup.

# ~/.poshrc
alias ll='ls -la'
export EDITOR=notepad
PS1='\u@\h \w \$ '

Prompt (PS1)

Escape Meaning
\u Current username
\h Short hostname
\H Full hostname
\w Working directory (~ abbreviated)
\W Basename of working directory
\$ $ for normal users, # for root
\n Newline
\\ Literal backslash

Line editing (interactive mode)

posh has a built-in line editor with two modes: emacs (default) and vi (set -o vi). Common keys in both:

Key Action
Up / Down Navigate history
Ctrl+R / Ctrl+S Incremental reverse / forward history search
Ctrl+C Interrupt (return to prompt)
Ctrl+D Exit on empty line
Tab Filename completion

Emacs mode adds Ctrl+A/Ctrl+E (line start/end), Ctrl+B/Ctrl+F (back/forward), Ctrl+P/Ctrl+N (history), Ctrl+U/Ctrl+K (kill to start/end), Ctrl+W (kill word), and Ctrl+Y (yank).

Vi mode adds modal editing (Esc for command mode) with the usual motions, and history search from command mode: /pattern (older), ?pattern (newer), and n / N to repeat in the same / opposite direction.

History is saved to ~/.posh_history (max 1000 entries).


Command lookup

On Windows, posh respects PATHEXT when looking up commands — it will find .EXE, .CMD, .BAT, .PS1, and any other extension listed in PATHEXT.


Color

Color in the prompt follows NO_COLOR (see no-color.org). Set NO_COLOR=1 to disable.


License

MIT

Documentation

Overview

Command posh is a portable Unix-like command shell for Windows, written in Go and similar in spirit to GNU bash.

Posh runs interactively with full line editing and persistent history, executes shell scripts, and provides the core shell primitives: pipelines, redirections, variable and command substitution, arithmetic and glob expansion, control flow, shell functions, background jobs, and a set of built-in commands. Globbing, tilde, and brace expansion are performed by the shell itself, so they work identically regardless of the underlying Windows command processor.

The binary name is derived from the executable stem: rename the executable and all prompts and diagnostics update to match.

Usage

posh                Start an interactive REPL.
posh script.posh    Execute a script file.
posh -c "command"   Run a single command string and exit.

On interactive startup, posh sources ~/.poshrc if it exists.

Pipelines and lists

Commands are joined into pipelines with "|", and pipelines into lists with the sequencing and short-circuit operators:

ls | grep .go | wc -l
make && echo ok
test -f file || echo missing
cd /tmp; ls; cd -

A trailing "&" runs a pipeline as a background job (see Jobs).

Redirections

Posh supports the full set of file-descriptor redirections:

cmd > out.txt        Truncate stdout to a file.
cmd >> out.txt       Append stdout to a file.
cmd < in.txt         Read stdin from a file.
cmd 2> err.txt       Redirect stderr.
cmd 2>> err.txt      Append stderr.
cmd &> all.txt       Redirect stdout and stderr.
cmd 2>&1             Duplicate stderr onto stdout.
cmd N> file          Redirect arbitrary descriptor N.
cmd N>&-             Close descriptor N.

Here-documents and here-strings

A here-document feeds the following lines to a command's stdin until a line matching the delimiter is reached:

cat << EOF
expanded: $HOME
EOF

When the delimiter is quoted (<< 'EOF' or << "EOF") the body is literal and no expansion is performed. The <<- form strips leading tab characters from the body and the closing delimiter, so heredocs can be indented inside compound commands. A here-string supplies a single expanded word as stdin, followed by a newline:

cat <<< "$greeting"

Expansion

Word expansion happens in the usual order: tilde, then parameter, command, and arithmetic substitution, then word splitting and pathname (glob) expansion.

echo Hello $NAME              Parameter expansion.
echo "Hello ${NAME}"          Braced parameter expansion.
echo "${NAME:-default}"       Default / :+ alternate / := assign-if-unset.
echo "${#NAME}"               Length in characters.
echo "${NAME:6:5}"            Substring (offset:length; negative counts back).
echo $?                       Exit status of the last command.
echo $$                       Shell process id.
echo "Today is $(date)"       Command substitution.
echo $((2 + 3 * 4))           Arithmetic expansion.
echo *.go                     Pathname (glob) expansion.
cd ~                          Tilde expansion.
cp file.{txt,bak}             Brace expansion.
printf '%s\n' $'a\tb'         ANSI-C quoting.

Single quotes preserve every character literally; double quotes allow parameter, command, and arithmetic substitution while suppressing word splitting and globbing; a backslash escapes the following character.

Arrays

Posh supports indexed and associative arrays:

arr=(one two three)          Indexed array literal.
arr[1]=TWO                   Assign a single element (arithmetic subscript).
arr+=(four)                  Append elements.
echo "${arr[1]}"             Element by index (negative counts from the end).
echo "${arr[@]}"             All elements (each a separate word when quoted).
echo "${arr[*]}"             All elements joined into one word.
echo "${#arr[@]}"            Number of elements.
echo "${#arr[1]}"            Character length of one element.
echo "${!arr[@]}"            The indices.

declare -A m                 Declare an associative (string-keyed) array.
m[admin]=rw                  Assign by key.
declare -A m=([a]=1 [b]=2)   Inline initialization.
echo "${m[admin]}"           Value for a key.
echo "${!m[@]}"              The keys (sorted); "${m[@]}" gives the values.

A bare reference ($arr or ${arr}) yields element 0. `unset arr[i]` removes one element; `unset arr` removes the whole variable.

Control flow

Posh implements the standard compound commands:

if COND; then LIST; elif COND; then LIST; else LIST; fi
for NAME in WORDS; do LIST; done
for NAME in WORDS; { LIST; }   bash brace-body form ({ } replace do/done)
while COND; do LIST; done
until COND; do LIST; done
case WORD in PATTERN) LIST;; esac
(( expression ))            Arithmetic command (status reflects the result).
{ LIST; }                   Group commands in the current shell.
( LIST )                    Run a list in a subshell.

Functions

Functions are defined with either the POSIX or the keyword form and are invoked like any other command (no parentheses at the call site). They are registered when their definition is evaluated, so a function must be defined before it is called.

greet() { echo "Hello, $1"; }
function greet { echo "Hello, $1"; }

greet world

Inside a function, $1, $2, ... are the positional parameters and "return" sets the exit status.

Jobs

A pipeline ending in "&" runs in the background; "jobs" lists active jobs, and "fg", "bg", "wait", and "kill" manage them.

long-running-command &
jobs
fg %1

Built-in commands

cd        Change the working directory (default: $HOME).
pwd       Print the working directory.
echo      Print arguments.
printf    Formatted output.
export    Export a variable to child processes.
unset     Remove a variable.
env       Print the exported environment.
set       Set shell variables and options (set -o).
declare   Declare variables/arrays (declare -A / -a); alias typeset.
source/.  Execute a file in the current shell context.
alias     Define or list aliases.
unalias   Remove aliases.
history   Show command history.
type      Report how a name would be resolved.
help      Show built-in help.
clear     Clear the screen.
test/[    Evaluate a conditional expression.
read      Read a line into variables.
shift     Shift positional parameters.
trap      Run a command on a signal.
eval      Evaluate arguments as a command.
mkdir     Create directories.
jobs/fg/bg/wait/kill/ps   Job and process control.
break/continue/return     Loop and function control flow.
true/false/:              Status-only no-ops.
exit      Exit the shell.

The file utilities ls, wc, which, grep, egrep, find, head, tail, and less are provided as built-ins that delegate to the corresponding win* sibling tools when they are present on PATH, falling back to any same-named tool otherwise.

Command lookup

A bare command name is resolved as alias, then function, then built-in, then an executable on PATH. On Windows, PATH lookup honors PATHEXT, so .EXE, .CMD, .BAT, .PS1, and any other configured extension are found automatically. A name that contains a path separator is run directly.

Line editing

The interactive editor offers an emacs mode (the default) and a vi mode selected with "set -o vi"; both are provided by the same built-in editor. History is saved to ~/.posh_history. Common keys include Up/Down to navigate history, Tab to complete the word under the cursor, Ctrl+C to interrupt, and Ctrl+D to exit on an empty line.

Emacs mode adds Ctrl+A/E (line start/end), Ctrl+B/F (back/forward char), Ctrl+P/N (previous/next history), Ctrl+U/K (kill to start/end), Ctrl+W (kill word), Ctrl+Y (yank), and Ctrl+R / Ctrl+S for incremental reverse / forward history search.

In vi mode, ESC enters command mode for vi-style motions and history navigation; "/pattern" and "?pattern" search the history (older / newer) with n / N to repeat, and Ctrl+R / Ctrl+S also work. Tab still completes in either mode.

Prompt

The prompt is taken from $PS1 (default "\u@\h \w \$ ") with the usual escapes: \u (user), \h and \H (short and full hostname), \w and \W (working directory and its basename), \$ ("#" for root, otherwise "$"), \n (newline), and \\ (a literal backslash).

Environment

On Windows, HOME is synthesized from USERPROFILE when it is not already set, so "$HOME" and "~" work without configuration. Color in the prompt follows the NO_COLOR convention; set NO_COLOR to any value to disable it.

Directories

Path Synopsis
internal
eval
Package eval executes a posh AST.
Package eval executes a posh AST.
lexer
Package lexer tokenizes posh shell input.
Package lexer tokenizes posh shell input.
parser
Package parser defines the posh AST and recursive-descent parser.
Package parser defines the posh AST and recursive-descent parser.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL