164 lines
5.7 KiB
Markdown
164 lines
5.7 KiB
Markdown
# bash-for-windows
|
|
|
|
A bash-compatible shell for Windows, written in Go. Runs natively as a single `.exe` — no WSL, Cygwin, or MSYS2 required.
|
|
|
|
## Install
|
|
|
|
**Build first**, then run the installer:
|
|
|
|
```bash
|
|
./build.sh --release # produces release/bash.exe + installer scripts
|
|
```
|
|
|
|
Copy the `release/` folder to your Windows machine, then double-click **`install.bat`**.
|
|
|
|
The installer:
|
|
- Copies `bash.exe` to `%LOCALAPPDATA%\Programs\BashForWindows\`
|
|
- Adds it to your user **PATH**
|
|
- Registers a **"Bash for Windows"** profile in Windows Terminal so it shows up in the `+` dropdown
|
|
|
|
```powershell
|
|
# Optional flags
|
|
.\install.ps1 -InstallDir "C:\Tools\bash" # custom install path
|
|
.\install.ps1 -NoTerminal # skip Windows Terminal profile
|
|
.\install.ps1 -Uninstall # clean removal
|
|
```
|
|
|
|
## Features
|
|
|
|
### Shell language
|
|
- **Control flow** — `if/elif/else/fi`, `for/do/done`, `while/until`
|
|
- **Functions** — `name() { ... }` and `function name { ... }`, with `local`/`return`
|
|
- **Arithmetic** — `$(( expr ))`, `$((i + 1))`, `$((n % 2))`
|
|
- **Command substitution** — `$(cmd)`, including pipelines: `x=$(echo foo | tr a-z A-Z)`
|
|
- **Variable expansion** — `$VAR`, `${VAR}`, `${VAR:-default}`, `${VAR:=val}`, `${#VAR}`, `${VAR%suffix}`, `${VAR#prefix}`
|
|
- **Glob expansion** — `*.txt`, `src/**`
|
|
- **Tilde expansion** — `~/Documents`
|
|
- **Quotes** — single `'literal'`, double `"with $vars"`, backslash escapes
|
|
- **Inline comments** — `echo hello # this is ignored`
|
|
|
|
### I/O
|
|
- **Pipelines** — `cmd1 | cmd2 | cmd3`
|
|
- **Redirection** — `>`, `>>`, `<`, `2>`, `2>&1`, `&>`
|
|
- **Background jobs** — `cmd &`
|
|
- **Command chaining** — `&&`, `||`, `;`
|
|
|
|
### Interactive
|
|
- **Command history** with arrow keys (saved to `~/.bash_history`)
|
|
- **Tab completion** for commands and file paths
|
|
- **Multi-line input** — `if`/`for`/`while`/functions continue on the next line
|
|
- **Prompt** shows current directory and last exit code when non-zero
|
|
|
|
### Built-in commands
|
|
| Category | Commands |
|
|
|----------|----------|
|
|
| Shell | `cd`, `pwd`, `echo`, `exit`, `export`, `set`, `unset`, `source`/`.`, `alias`, `unalias`, `type`, `command`, `which`, `env` |
|
|
| Control | `true`, `false`, `test`/`[`, `break`, `continue`, `return`, `shift`, `read`, `printf` |
|
|
| Variables | `declare`, `local` |
|
|
| Files | `ls`, `cat`, `cp`, `mv`, `rm`, `mkdir`, `touch`, `find`, `basename`, `dirname` |
|
|
| Text | `grep`, `sed`, `sort`, `uniq`, `wc`, `head`, `tail`, `cut`, `tr`, `tee`, `xargs` |
|
|
| System | `date`, `sleep`, `clear`, `jobs` |
|
|
|
|
## Usage
|
|
|
|
```
|
|
bash # interactive shell
|
|
bash -c 'echo hello' # run a command string
|
|
bash script.sh # run a script file
|
|
bash script.sh arg1 # pass arguments ($1, $2, ...)
|
|
```
|
|
|
|
### Running bash scripts
|
|
|
|
Any file with a `#!/usr/bin/env bash` or `#!/bin/bash` shebang is automatically detected and executed through bash-for-windows — no need to invoke `bash` explicitly.
|
|
|
|
**Run by passing the path directly:**
|
|
```powershell
|
|
bash myscript.sh
|
|
bash myscript # extension is optional
|
|
bash C:\scripts\deploy.sh production
|
|
```
|
|
|
|
**Or put the script on PATH and call it by name:**
|
|
|
|
If the script is in a directory that is on your `PATH` (e.g. the bash-for-windows install directory), you can call it directly from the interactive shell or from PowerShell:
|
|
|
|
```
|
|
waifufetch
|
|
waifu
|
|
deploy
|
|
```
|
|
|
|
Bash-for-windows detects the shebang, runs the script through its own interpreter, and passes any arguments as `$1`, `$2`, etc.
|
|
|
|
**CRLF line endings are handled automatically.** Scripts checked out on Windows often have `\r\n` line endings. Bash-for-windows strips the carriage returns before executing, so `#!/usr/bin/env bash\r` in the shebang line never causes the `env: 'bash\r': No such file or directory` error you get with WSL.
|
|
|
|
**Adding a script to PATH:**
|
|
|
|
The easiest place to drop scripts is the same directory bash-for-windows is installed in:
|
|
|
|
```powershell
|
|
$d = "$env:LOCALAPPDATA\Programs\BashForWindows"
|
|
Copy-Item .\myscript $d\myscript
|
|
```
|
|
|
|
That directory is already on `PATH` after running `install.ps1`, so the script is immediately callable from any shell.
|
|
|
|
### Examples
|
|
|
|
```bash
|
|
# Variables and arithmetic
|
|
name="World"
|
|
echo "Hello $name"
|
|
echo $((40 + 2))
|
|
|
|
# Loops and functions
|
|
is_even() {
|
|
if [ $(($1 % 2)) -eq 0 ]; then return 0; else return 1; fi
|
|
}
|
|
for n in 1 2 3 4 5 6; do
|
|
if is_even $n; then echo "$n is even"; fi
|
|
done
|
|
|
|
# Pipelines and redirection
|
|
printf "127.0.0.1 localhost\n127.0.0.2 example\n" | grep localhost | wc -l
|
|
find . -name "*.go" | xargs grep "TODO" > todo_files.txt
|
|
echo "result=$(date)" >> log.txt
|
|
```
|
|
|
|
## Build from source
|
|
|
|
Requires Go 1.21+.
|
|
|
|
```bash
|
|
./build.sh # builds Linux debug + Windows .exe into build/
|
|
./build.sh --release # also creates release/ folder ready to distribute
|
|
```
|
|
|
|
```bash
|
|
# Manual cross-compile
|
|
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o bash.exe .
|
|
```
|
|
|
|
## Project layout
|
|
|
|
```
|
|
cmd/bash/ entry point, readline REPL
|
|
internal/shell/
|
|
shell.go Execute, parseBlocks, splitStatements, IsIncomplete
|
|
expand.go expandWord, tokenize, arithmetic, glob
|
|
exec.go pipelines, redirections, external commands
|
|
control.go if/for/while/until, function define/call
|
|
builtins.go all built-in commands and coreutils
|
|
install.ps1 Windows installer (PATH + Windows Terminal profile)
|
|
install.bat double-click wrapper for install.ps1
|
|
build.sh build script
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
# NOTE:
|
|
|
|
Just because this is a port of Bash does not mean every Linux/Unix command will work out of the box. While the core Bash syntax, logic, and a set of built-in commands have been implemented, any external command requires either a native Windows executable or a dedicated port to function. |