Shell Documentation

Shells

Introduction

A shell is a command-line interpreter that lets users interact with the operating system by typing commands. Shells also serve as scripting environments for automating tasks. MidnightBSD includes several shells in the base system and additional shells are available through mports.

Every user account has a login shell assigned to it. When you open a terminal or log in via SSH, the login shell starts and reads its startup files before presenting a prompt. Interactive shells and non-interactive (script) shells may read different startup files, depending on the shell.

System Shells

The following shells are included in the MidnightBSD base system and are available without installing any additional packages.

sh — Bourne Shell

/bin/sh is the standard POSIX-compatible Bourne shell. It is the most portable shell for scripting because scripts written for sh will run on virtually any Unix-like system. MidnightBSD's sh is based on the Almquist shell (ash) and supports the full POSIX shell specification.

sh is the recommended shell for system scripts, startup scripts, and any script that must run in a minimal environment. It reads ~/.profile at login.

It is based on ash.

tcsh — TENEX C Shell

/bin/tcsh is an enhanced version of the C shell (csh) and is the default login shell for the root account in MidnightBSD. It provides interactive features such as command-line editing, history, and tab completion.

tcsh reads ~/.tcshrc or ~/.cshrc for interactive configuration and ~/.login at login. Environment variables are set with the setenv command rather than the export syntax used by Bourne-compatible shells:

setenv EDITOR vim

Shell variables local to tcsh are set with set:

set prompt = "%n@%m %~ %# "
mksh — MirBSD Korn Shell

/bin/mksh is the MirBSD Korn Shell, a lightweight and standards-compliant implementation of the Korn shell. It is compatible with POSIX sh and supports many ksh extensions such as arrays, arithmetic expressions, and associative arrays. It is a good choice for scripting when you need features beyond POSIX sh but want to avoid a dependency on bash.

mksh reads ~/.mkshrc for interactive configuration.

Additional Shells via mports

More shells are available through mports. After installation each shell's path must be listed in /etc/shells before it can be set as a login shell. Shells installed from mports are typically placed under /usr/local/bin/.

bash — GNU Bourne-Again Shell

bash is the most widely used shell on Linux systems and is familiar to many users. It supports interactive features such as readline editing, history expansion, job control, arrays, and extensive scripting extensions beyond the POSIX standard.

Install bash from mports:

mport install bash

bash reads ~/.bash_profile or ~/.profile at login and ~/.bashrc for interactive non-login shells.

zsh — Z Shell

zsh is a powerful interactive shell with extensive customization options, advanced tab completion, spelling correction, theme support, and compatibility with both sh and ksh syntax. It is the basis for popular frameworks such as Oh My Zsh.

Install zsh from mports:

mport install zsh

zsh reads ~/.zshenv always, ~/.zprofile at login, and ~/.zshrc for interactive shells.

fish — Friendly Interactive Shell

fish is designed for ease of use out of the box. It provides syntax highlighting as you type, autosuggestions based on history, and web-based configuration. fish does not aim to be POSIX compatible and uses its own scripting syntax, so sh scripts cannot be sourced directly.

Install fish from mports:

mport install fish

fish reads ~/.config/fish/config.fish for interactive configuration.

Changing Your Shell

The /etc/shells file lists the shells approved for use as login shells on the system. Only paths listed in this file may be selected with chsh. A typical /etc/shells looks like:

# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/sh
/bin/csh
/bin/tcsh
/bin/mksh
/usr/local/bin/bash
/usr/local/bin/zsh
/usr/local/bin/fish

When you install a shell from mports it is usually added to /etc/shells automatically. If it is not, add the full path manually as root using your preferred editor.

Use chsh to change your login shell. Running it without arguments opens an editor where you can change your shell entry:

chsh

You can also pass the new shell path directly with the -s flag:

chsh -s /usr/local/bin/bash

To change another user's shell, root may specify the username:

chsh -s /usr/local/bin/zsh username

The change takes effect at the next login. To verify the current login shell for a user:

getent passwd username

Or check your own:

echo $SHELL

Environment Variables

Environment variables pass configuration and state information to processes. They are inherited by child processes. In Bourne-compatible shells (sh, bash, zsh, mksh) variables are exported with the export command. In tcsh, use setenv.

Set and export a variable in sh/bash/zsh/mksh:

export VARNAME="value"

Set a variable in tcsh:

setenv VARNAME "value"

List all current environment variables:

env

Print a single variable:

echo $VARNAME
EDITOR

EDITOR specifies the non-visual fallback text editor used by programs such as crontab -e, svn commit, and various other utilities when they need to open an editor. It is typically set to a simple terminal editor.

export EDITOR=vi
VISUAL

VISUAL specifies the preferred full-screen visual editor. Many programs check VISUAL first and fall back to EDITOR if it is not set. Set it to a full-featured editor such as vim, nano, or emacs.

export VISUAL=vim
HOME

HOME is the path to the current user's home directory. It is set automatically at login from the password database entry. The tilde (~) shorthand in shells expands to the value of HOME.

echo $HOME
PATH

PATH is a colon-separated list of directories the shell searches when looking for commands. Adding a directory to PATH makes executables in that directory available without specifying the full path.

export PATH="$PATH:/usr/local/bin:/usr/local/sbin"
MANPATH

MANPATH is a colon-separated list of directories searched by man when looking for manual pages. If unset, man uses a built-in default. Set it when you have manual pages installed in non-standard locations.

export MANPATH="/usr/share/man:/usr/local/share/man"
PAGER

PAGER specifies the program used to display output one screen at a time. Commands like man use PAGER to display their output. The default is usually more; less is a popular alternative that supports backward scrolling.

export PAGER=less
TERM

TERM identifies the terminal type, which tells programs how to control the display (cursor movement, colors, clearing the screen, etc.). It is normally set automatically when you log in or open a terminal emulator. Common values are xterm, xterm-256color, and vt100. Setting it incorrectly can cause display corruption in full-screen programs like vi and top.

export TERM=xterm-256color
COLORTERM

COLORTERM tells color-aware applications whether the terminal supports 24-bit (truecolor) output. Terminal emulators that support truecolor set this to truecolor or 24bit. Many modern applications such as vim, neovim, and tmux check this variable to decide whether to use full RGB colors.

export COLORTERM=truecolor
LSCOLORS / LS_COLORS

LSCOLORS controls the colors used by ls on BSD systems when the -G flag is used. Each pair of characters encodes the foreground and background color for a file type. Linux-style tools may use LS_COLORS instead, which uses a different format understood by the GNU dircolors utility.

export LSCOLORS=ExFxCxDxBxegedabagacad
LANG and LC_* Variables

LANG sets the default locale, which affects character encoding, date formatting, number formatting, and message language for locale-aware programs. More specific locale categories can be overridden individually with variables such as LC_ALL, LC_CTYPE, and LC_MESSAGES.

export LANG=en_US.UTF-8
TZ

TZ overrides the system timezone for a single user or process. The value should be a timezone name from /usr/share/zoneinfo.

export TZ=America/New_York
TMPDIR

TMPDIR specifies the directory used for temporary files created by programs and shell scripts. If unset, /tmp is used. Changing it is useful when /tmp is small and a larger scratch space is available elsewhere.

export TMPDIR=/var/tmp

Pipes and Redirection

Pipes and redirection are fundamental shell features that control where command input comes from and where output goes. They work the same way across all shells covered in this document.

Standard Streams

Every process has three standard streams:

Output Redirection

Redirect stdout to a file, creating or overwriting it:

command > output.txt

Append stdout to a file instead of overwriting:

command >> output.txt

Redirect stderr to a file:

command 2> errors.txt

Redirect both stdout and stderr to the same file:

command > output.txt 2>&1

Discard output by redirecting to /dev/null:

command > /dev/null 2>&1
Input Redirection

Redirect a file to stdin so the command reads from it instead of the keyboard:

command < input.txt

A here-document feeds multiple lines of text directly to a command's stdin. The delimiter (EOF in this example) marks the end of the input:

command <<EOF
line one
line two
EOF

A here-string passes a single string as stdin (supported in bash, zsh, mksh, and ksh):

command <<< "some text"
Pipes

A pipe (|) connects the stdout of one command to the stdin of another, letting you chain commands together to process data in stages:

command1 | command2

Chain multiple commands:

cat /var/log/messages | grep error | sort | uniq -c | sort -rn

To pipe both stdout and stderr through a pipe, redirect stderr to stdout first:

command 2>&1 | less
Pipelines and Exit Status

In most shells, the exit status of a pipeline is the exit status of the last command. In bash and zsh, you can enable the pipefail option so the pipeline fails if any command in it fails:

set -o pipefail
Command Substitution

Command substitution captures the output of a command and uses it as a value in another command. The preferred modern syntax uses $():

today=$(date +%Y-%m-%d) echo "Files: $(ls | wc -l)"

The older backtick syntax is equivalent but harder to nest:

today=`date +%Y-%m-%d`
Process Substitution

Process substitution (supported in bash and zsh) treats the output of a command as if it were a file. This is useful when a command requires a filename argument but you want to feed it the output of another command:

diff <(sort file1.txt) <(sort file2.txt)
Tee

The tee command reads from stdin and writes to both stdout and one or more files at the same time. This lets you view output in the terminal while also saving it:

make 2>&1 | tee build.log