MKSH(1)

๐Ÿ“ฆ
version 59c ยท 5 versions
, in ubuntu resolute, ubuntu stonking

๐Ÿ’… MKSH(1) โ€” General Commands Manual ๐Ÿ’…

okay bestie, this is the manual for mksh, the MirBSD Korn shell โ€” also answers to sh. no cap, she's a whole command interpreter. ๐Ÿ’–


๐ŸŒธ NAME ๐ŸŒธ

mksh, sh โ€” MirBSD Korn shell

โœจ SYNOPSIS โœจ

mksh [-+abCefhiklmnprUuvXx] [-+o option] [-T [!]tty|-] [file [arg1 ...]]
mksh [-+abCefhiklmnprUuvXx] [-+o option] [-T [!]tty|-] -c cmd [arg0 ...]
mksh [-+abCefhiklmnprUuvXx] [-+o option] [-T [!]tty|-] -s [arg1 ...]
builtin-name [argument ...]

๐Ÿ’– DESCRIPTION ๐Ÿ’–

mksh is a command interpreter, and she's here for both interactive use and shell script use. Her command language is a superset of both sh(C) and POSIX shell language, and she's largely compatible with the original Korn shell. Sometimes this manual page will give you scripting advice; while it sometimes takes portable shell scripting or various standards into account, all information is first and foremost presented with mksh in mind and should be taken as such. periodt.

๐Ÿฆ‹ I use Android, OS/2, etc. so what...? ๐Ÿฆ‹

Please refer to: http://www.mirbsd.org/mksh-faq.htm#sowhatismksh

๐ŸŽ€ Invocation ๐ŸŽ€

Most builtins can be called directly โ€” for example if a link or symlink(7) points to mksh, or if argv[0] is set correspondingly. But bestie, this does not make sense for, or work properly with, all built-in utilities though. Debug builds can also be invoked as --unparse to display the parse tree of each command before running it. iconic.

The options are as follows:

  • -c โ€” mksh will execute the command(s) contained in cmd, setting $0 to arg0 (if present), $1 to the next argument, etc. If compiled with -DMKSH_MIDNIGHTBSD01ASH_COMPAT and in -o sh mode, a -- argument directly following cmd is ignored for compatibility with the legacy FreeBSD sh; this is deprecated and may go away in the future.
  • -i โ€” Interactive shell. A shell that reads commands from standard input is โ€œinteractiveโ€ if this option is used or if both standard input and standard error are attached to a tty(4). An interactive shell has job control enabled, ignores the SIGINT, SIGQUIT and SIGTERM signals, and prints prompts before reading input (see the PS1 and PS2 parameters). It also processes the ENV parameter or the mkshrc file (see below). For non-interactive shells, the trackall option is on by default (see the set command below).
  • -l โ€” Login shell. If the name the shell is called as (i.e. argv[0]) or its basename begins with a dash (hyphen-minus) - or if this option is given, the shell is a โ€œlogin shellโ€; see โ€œStartup filesโ€ below.
  • -p โ€” Privileged shell. A shell is โ€œprivilegedโ€ if the real user ID from getuid(2) or group ID from getgid(2) does not match the effective user ID or group ID. Clearing the privileged option causes the shell to set its effective user ID and group ID to its initial real user ID and group ID, respectively. For further implications, see set -p and โ€œStartup filesโ€. If the shell is privileged and this flag is not set explicitly on invocation, nor during processing the startup files, the โ€œprivilegedโ€ flag is cleared automatically afterwards.
  • -r โ€” Restricted shell. A shell is โ€œrestrictedโ€ if the basename the shell is called with, after dash removal, begins with r or if this option is used. The following restrictions come into effect after the shell processes any profile and ENV files:
    • Command names cannot be specified with pathnames, either absolute or relative; the -p flag of the command built-in utility is not usable. The ENV, PATH and SHELL parameters cannot be changed.
    • The current location is fixed: the cd builtin is disabled.
    • Redirections that create files, i.e. >, >|, >> and <>, cannot be used, and the HISTFILE parameter cannot be changed.
  • -s โ€” mksh will read and execute commands from standard input; all non-option arguments are assigned to the positional parameters.
  • -T - โ€” Detach from the controlling terminal, return immediately (daemonise).
  • -T [!]name โ€” Spawn mksh on the tty(4) device given. The paths name, /dev/ttyCname and /dev/ttyname are attempted in order. If name is prefixed with an exclamation mark (!), wait for the spawned shell to return, report its exit status or terminating signal visually. Exit 0 if spawned.

In addition to the above, the flags [-+abCefhkmnUuvXx] and [-+o option], respectively for single-letter and long options, as described for the set built-in utility, can be used on the command line.

If neither the -c nor the -s options are specified, mksh will read and execute commands as if the -s flag was passed iff the file argument is absent or -; otherwise, it sets $0 to file and reads commands from it. Further arguments arg1 ... are assigned to positional parameters.

The exit status of the shell is 127 if the file specified on the command line could not be opened, or non-zero if a fatal error occurred during execution of the script. Otherwise, the errorlevel is that of the last command executed, 0 if no command was executed.

๐ŸŒท Startup files ๐ŸŒท

For the actual location of these files, see โ€œFILESโ€. A login shell processes the system profile first. A privileged shell then processes the suid profile. A non-privileged login shell processes the user profile next. A non-privileged interactive shell checks the value of the ENV parameter after subjecting it to parameter, command, arithmetic and tilde (~) substitution; if unset or empty, the user mkshrc profile is processed; otherwise, if a file whose name is the substitution result exists, it is processed; non-existence is silently ignored. A privileged shell then drops privileges if neither was the -p option given on the command line nor set during execution of the startup files.

๐Ÿงš Command syntax ๐Ÿงš

The shell begins parsing its input by removing any backslash-newline combinations, then breaking it into words. Words (which are sequences of characters) are delimited by unquoted whitespace characters (space, tab and newline) or meta-characters (<, >, |, ;, (, ) and &). Aside from delimiting words, spaces and tabs are ignored, while newlines usually delimit commands. The meta-characters are used in building the following tokens: <, <&, <<, <<<, >, >&, >>, &>, etc. are used to specify redirections (see โ€œInput/output redirectionโ€ below); | is used to create pipelines; |& is used to create co-processes (see โ€œCo-processesโ€ below); ; is used to separate commands; & is used to create asynchronous pipelines; && and || are used to specify conditional execution; ;;, ;& and ;| are used in case statements; (( ... )) is used in arithmetic expressions; and lastly, ( ... ) is used to create subshells.

Whitespace and meta-characters can be quoted individually using a backslash (\), or in groups using double (") or single (') quotes. Note that the following characters are also treated specially by the shell and must be quoted if they are to represent themselves: \, ", ', #, $, `, ~, {, }, *, ? and [. The first three of these are the above mentioned quoting characters (see โ€œQuotingโ€ below); #, if used at the beginning of a word, introduces a comment โ€” everything after the # up to the nearest newline is ignored; $ is used to introduce parameter, command and arithmetic substitutions (see โ€œSubstitutionโ€ below); ` introduces an old-style command substitution (see โ€œSubstitutionโ€ below); ~ begins a directory expansion (see โ€œTilde expansionโ€ below); { and } delimit csh(1)-style alternations (see โ€œBrace expansionโ€ below); and finally, *, ? and [ are used in file name generation (see โ€œFile name patternsโ€ below).

As words and tokens are parsed, the shell builds commands, of which there are two basic types: simple-commands, typically programmes that are executed, and compound-commands, such as for and if statements, grouping constructs and function definitions.

A simple-command consists of some combination of parameter assignments (see โ€œParametersโ€ below), input/output redirections (see โ€œInput/output redirectionsโ€ below) and command words; the only restriction is that parameter assignments come before any command words. The command words, if any, define the command that is to be executed and its arguments. The command may be a shell built-in command, a function or an external command (i.e. a separate executable file that is located using the PATH parameter; see โ€œCommand executionโ€ below). Note that all command constructs have an exit status: for external commands, this is related to the status returned by wait(2) (if the command could not be found, the exit status is 127; if it could not be executed, the exit status is 126); the exit status of other command constructs (built-in commands, functions, compound-commands, pipelines, lists, etc.) are all well-defined and are described where the construct is described. The exit status of a command consisting only of parameter assignments is that of the last command substitution performed during the parameter assignment or 0 if there were no command substitutions.

Commands can be chained together using the | token to form pipelines, in which the standard output of each command but the last is piped (see pipe(2)) to the standard input of the following command. The exit status of a pipeline is that of its last command, unless the pipefail option is set (see there). All commands of a pipeline are executed in separate subshells; this is allowed by POSIX but differs from both variants of AT&T UNIX ksh, where all but the last command were executed in subshells; see the read builtin's description for implications and workarounds. A pipeline may be prefixed by the ! reserved word which causes the exit status of the pipeline to be logically complemented: if the original status was 0, the complemented status will be 1; if the original status was not 0, the complemented status will be 0.

Lists of commands can be created by separating pipelines by any of the following tokens: &&, ||, &, |& and ;. The first two are for conditional execution: cmd1 && cmd2 executes cmd2 only if the exit status of cmd1 is zero; || is the opposite โ€” cmd2 is executed only if the exit status of cmd1 is non-zero. && and || have equal precedence which is higher than that of &, |& and ;, which also have equal precedence. Note that the && and || operators are "left-associative". For example, both of these commands will print only "bar":

$ false && echo foo || echo bar
$ true || echo foo && echo bar

The <code>&</code> token = "go do your thing bestie, I'm not waiting up for you" energy. It makes the command before it run asynchronously โ€” like, the shell kicks it off but does NOT sit there waiting for it to finish. (Don't worry, she's still keeping receipts; she does track the status of async commands, peek "Job control" below ๐Ÿ’…). Now here's the โœจimportantโœจ bit: when an async command starts while job control is disabled (which is, like, most scripts), that command gets launched with the signals SIGINT and SIGQUIT ignored, and its input redirected from /dev/null โ€” except any redirections you literally spelled out in the asynchronous command itself win. Precedence, queen ๐Ÿ‘‘.

The |& operator is a whole different vibe: it starts a co-process, which is just a special kind of asynchronous process (slide over to "Co-processes" below, iykyk).

ngl this part trips people up: a command must follow && and ||, but it does not have to follow &, |& or ;. And the exit status of a list? It's whatever the last command that executed returned โ€” except for asynchronous lists, where the exit status is just 0. periodt.


Compound commands get built out of these reserved words. Heads up: they're only recognised when they're unquoted and sitting as the first word of a command (so no sneaking parameter assignments or redirections in front of them, bestie):

case     else     function     then      ! (
do       esac     if           time      [[      ((
done     fi       in           until     {
elif     for      select       while     }

Now, in all the compound command descriptions below: any command list (we call it list) that's followed by a reserved word has to end with a semicolon, a newline, or a (syntactically correct) reserved word. Like, all of these are valid, no cap:

$ { echo foo; echo bar; }
$ { echo foo; echo bar<newline>}
$ { { echo foo; echo bar; } }

And this one? Not valid. Absolutely not. ๐Ÿšซ

$ { echo foo; echo bar }

๐Ÿ’– Compound commands ๐Ÿ’–

  • case word in [[(] pattern [| pattern] ...) list <terminator>] ... esac โ€” the case statement tries to match word against a pattern you hand it, and the list attached to the first pattern that matches successfully gets executed. The patterns here work just like file name patterns, except the rules about . and / are dropped entirely. Tiny detail that bites people: any unquoted space before and after a pattern is stripped, so if you want a space inside a pattern you must quote it, fr. And both the word and the patterns go through parameter, command and arithmetic substitution, plus tilde substitution.

For historical reasons (auntie ksh era ๐Ÿ’€) you can use open and close braces instead of in and esac, like: case $foo { (ba[rz]|blah) date ;; }

The list *<terminator>*s are:

  • ;; โ€” terminate after the list.
  • ;& โ€” fall through into the next list.
  • ;| โ€” evaluate the remaining pattern-list tuples.

The exit status of a case statement is the exit status of the list that got executed; if no list runs, the exit status is zero.

  • for name [in word ...] ; do list; done โ€” for every word in the word list you gave, the parameter name gets set to that word and list runs. The exit status of a for statement is the last exit status of list; if list never executes at all, the exit status is zero. Skip the in and the positional parameters ($1, $2, etc.) get used instead โ€” and in that case use a newline instead of the semicolon ; for portability, bestie. Historical flex: open and close braces work in place of do and done, like for i; { echo $i; } โ€” but that's not portable, so don't @ me.
  • function name { list; } โ€” defines the function name (see "Functions" below ๐ŸŒธ). Important: all redirections specified after a function definition are performed whenever the function is executed, not when the definition itself runs. Big difference, remember it.
  • name() command โ€” basically the same as function (see above and "Functions" below). Most amounts of space and tab after name get ignored, slay.
  • function name() { list; } โ€” a bashism for name() { list; } (the function keyword is just ignored here, iconic).
  • if list; then list; [elif list; then list;] ... [else list;] fi โ€” if the first list exits with status zero, the second list runs; otherwise the list after the elif, if there is one, runs with the same consequences. If all the lists after the if and the elifs fail (i.e. exit non-zero), then the list after else runs. The exit status of an if statement is that of whatever non-conditional (not the first) list got executed; if no non-conditional list is executed, the exit status is zero.
  • select name [in word ...]; do list; done โ€” select hands you a whole automatic menu situation ๐Ÿ“. It prints an enumerated list of the specified words on standard error, followed by a prompt (PS3: normally #? ). Then it reads a number from standard input corresponding to one of the enumerated words, sets name to the selected word (or unsets it if the selection isn't valid), sets REPLY to whatever was read (leading and trailing space stripped), and runs list. If you enter a blank line (i.e. zero or more IFS octets), the menu just reprints and list does not run.

When list finishes, the enumerated list prints again if REPLY is empty, then the prompt prints, and so on forever. This keeps going until an end-of-file is read, an interrupt is received, or a break statement is executed inside the loop. Exit status of a select statement is zero if a break statement was used to exit the loop, non-zero otherwise. Omit in word ... and the positional parameters get used. And for historical reasons, open and close braces may replace do and done, like: select i; { echo $i; }

  • time [-p] [pipeline] โ€” the "Command execution" section describes the time reserved word. When it's not a reserved word, a builtin runs the passed command.
  • until list; do list; done โ€” works exactly like while (below), except the body list only executes while the exit status of the first list is non-zero.
  • while list; do list; done โ€” a pre-checked loop, bestie. Its body list runs as often as the exit status of the first list is zero. The exit status of a while statement is the last exit status of the list in the loop body; if the body never executes, the exit status is zero.
  • [[ expression ]] โ€” similar to the test and [ ... ] commands (described later), with these โœจexceptionsโœจ:
    • Field splitting and globbing are not performed on arguments.
    • The -a (AND) and -o (OR) operators are replaced with && and ||, respectively.
    • Operators (e.g. -f, =, !) must be unquoted.
    • Parameter, command and arithmetic substitutions are performed as expressions are evaluated, and lazy expression evaluation is used for the && and || operators. Meaning: in the statement below, $(<foo) is evaluated if and only if the file foo exists and is readable:
$ [[ -r foo && $(<foo) = b*r ]]
  • The second operand of = and != is a pattern (e.g. the comparison [[ foobar = f*r ]] succeeds). This even works indirectly, while quoting forces literal interpretation:
$ bar=foobar; baz='f*r'         # or: baz='f+(o)b?r'
$ [[ $bar = $baz ]]; echo $? # 0
$ [[ $bar = "$baz" ]]; echo $? # 1
  • { list; } โ€” compound construct; list is executed, but not in a subshell. Note that { and } are reserved words, not meta-characters. ๐Ÿ’…
  • (list) โ€” execute list in a subshell, forking. There is no implicit way to pass environment changes from a subshell back up to its parent. Sorry babe.
  • (( expression )) โ€” the arithmetic expression expression gets evaluated; equivalent to let "expression" in a compound construct. See the let command and "Arithmetic expressions" below.

๐ŸŽ€ Quoting ๐ŸŽ€

Quoting is how you stop the shell from treating characters or words specially. Three methods, take notes:

  1. \ quotes the character that follows it โ€” unless it's at the end of a line, in which case both the \ and the newline get stripped.
  2. A single quote (') quotes everything up to the next single quote (this can span lines, no biggie).
  3. A double quote (") quotes all characters except $, \ and `, up to the next unescaped double quote. Inside double quotes, $ and ` keep their usual meaning (i.e. parameter, arithmetic or command substitution) โ€” except no field splitting is carried out on the results of double-quoted substitutions, and the old-style form of command substitution has backslash-quoting for double quotes enabled.

And the backslash rules inside a double-quoted string? If a \ is followed by ", $, \ or `, only the \ is removed, i.e. the combination is replaced by that second character. If it's followed by a newline, both the \ and the newline are stripped. Otherwise, both the \ and the character following it stay unchanged.

Oh and this is a fun one: if a single-quoted string is preceded by an unquoted $, C style backslash expansion (see below) gets applied โ€” even single quote characters inside can be escaped and then they don't terminate the string. The expanded result is treated as any other single-quoted string. And if a double-quoted string is preceded by an unquoted $? The $ is simply ignored. that's it. ๐Ÿฆ‹

๐ŸŒˆ Backslash expansion ๐ŸŒˆ

Wherever backslashes get expanded, certain C and AT&T UNIX ksh or GNU bash style escapes get translated. These include \a, \b, \f, \n, \r, \t, \U########, \u#### and \v. For \U######## and \u####, the # means a hexadecimal digit (up to 4 or 8 of them); these translate a Universal Coded Character Set codepoint to UTF-8 (go read "CAVEATS" on UCS limitations, bestie ๐Ÿ‘€). Also, \E and \e both expand to the escape character.

In the print builtin mode, octal sequences must have the optional up-to-three octal digits # prefixed with the digit zero (\0###); hexadecimal sequences \x## are limited to up to two hexadecimal digits #; both octal and hexadecimal sequences convert to raw octets. And \% โ€” where % is none of the above โ€” translates to \% (backslashes are retained, periodt).

C style mode has opinions about your escapes, bestie, and honestly? Same. Let's break it down:

  • raw octetโ€“yielding octal sequences \### must not have the one up to three octal digits prefixed with the digit zero
  • hexadecimal sequences \x## greedily eat up as many hexadecimal digits # as they can and terminate with the first non-xdigit
  • below \x100 these produce raw octets; above, they're equivalent to \U#
  • the sequence \c%, where % is any octet, translates to Ctrl-% โ€” so \c? becomes DEL, and literally everything else is bitwise ANDed with 0x9F
  • \%, where % is none of the above, translates to just %: backslashes are trimmed even before newlines ๐Ÿ’…

โœจ Aliases โœจ

So there are two flavours of aliases: normal command aliases and tracked aliases. Two icons, different jobs ๐Ÿซถ

Command aliases are normally used as a short hand for a long or often used command. The shell expands command aliases (i.e. substitutes the alias name for its value) when it reads the first word of a command. An expanded alias is re-processed to check for more aliases. If a command alias ends in a space or tab, the following word is also checked for alias expansion. And the alias expansion process stops when:

  • a word that is not an alias is found,
  • a quoted word is found, or
  • an alias word that is currently being expanded is found.

Aliases are specifically an interactive feature: while they do happen to work in scripts and on the command line in some cases, aliases are expanded during lexing, so their use must be in a separate command tree from their definition; otherwise, the alias will not be found. Noticeably, command lists (separated by semicolon, in command substitutions also by newline) may be one same parse tree. ๐Ÿ™ˆ

The following command aliases are defined automatically by the shell:

autoload='\\builtin typeset -fu'
functions='\\builtin typeset -f'
hash='\\builtin alias -t'
history='\\builtin fc -l'
integer='\\builtin typeset -i'
local='\\builtin typeset'
login='\\builtin exec login'
nameref='\\builtin typeset -n'
nohup='nohup '
r='\\builtin fc -e -'
type='\\builtin whence -v'

Tracked aliases let the shell remember where it found a particular command. The first time the shell does a path search for a command that is marked as a tracked alias, it saves the full path of the command. The next time the command is executed, the shell checks the saved path to see that it's still valid, and if so, avoids repeating the path search. Tracked aliases can be listed and created using alias -t. Note that changing the PATH parameter clears the saved paths for all tracked aliases ๐Ÿ˜ญ. If the trackall option is set (i.e. set -o trackall or set -h), the shell tracks all commands. This option is set automatically for non-interactive shells. For interactive shells, only the following commands are automatically tracked: cat(1), cc(1), chmod(1), cp(1), date(1), ed(1), emacs(1), grep(1), ls(1), make(1), mv(1), pr(1), rm(1), sed(1), sh(1), vi(1) and who(1).


โœจ Substitution โœจ

The first step the shell takes in executing a simple-command is to perform substitutions on the words of the command. There are three kinds of substitution: parameter, command and arithmetic. No cap, just three:

  • Parameter substitutions, described in detail in the next section, take the form $name or ${name...}
  • Arithmetic substitutions take the form $((expression))
  • Command substitutions take the form $(command) or (deprecated) `command` or (executed in the current environment) ${ command;}, and they evaluate to the output of command with any trailing newlines stripped. The latter form requires a space, tab or newline after the opening brace and that the closing brace be recognised as a keyword (i.e. is preceded by a newline or semicolon).

Those ${ command;} guys are also called funsubs (function substitutions) and they behave similar to functions in that shell options are shared and local and return work โ€” though, in contrast to valsubs (see below), exit does not terminate the parent shell, for compatibility with AT&T UNIX ksh93. ๐Ÿ’€

Another variant of substitution are the valsubs (value substitutions) ${|command;} which are also executed in the current environment, like funsubs, but share their I/O with the parent. Instead, they evaluate to whatever the, initially empty, expression-local variable REPLY is set to within the commands; exit affects the parent like in a function call. Iconic behaviour honestly.

If a substitution appears outside of double quotes, the results of the substitution are generally subject to word or field splitting according to the current value of the IFS parameter. The IFS parameter specifies a list of octets which are used to break a string up into several words; any octets from the set space, tab and newline that appear in the IFS octets are called โ€œIFS whitespaceโ€. Sequences of one or more IFS whitespace octets, in combination with zero or one non-IFS whitespace octets, delimit a field. As a special case, leading and trailing IFS whitespace is stripped (i.e. no leading or trailing empty field is created by it); leading or trailing non-IFS whitespace does create an empty field. ๐Ÿซ 

Example: If IFS is set to โ€œ<space>:โ€ and VAR is set to โ€œ<space>A<space>:<space><space>B::Dโ€, the substitution for $VAR results in four fields: โ€œAโ€, โ€œBโ€, โ€œโ€ (an empty field) and โ€œDโ€. Note that if the IFS parameter is set to the empty string, no field splitting is done; if it is unset, the default value of space, tab and newline is used.

Also, note that the field splitting applies only to the immediate result of the substitution. Using the previous example, the substitution for $VAR:E results in the fields: โ€œAโ€, โ€œBโ€, โ€œโ€ and โ€œD:Eโ€, not โ€œAโ€, โ€œBโ€, โ€œโ€, โ€œDโ€ and โ€œEโ€. This behaviour is POSIX compliant but incompatible with some other shell implementations which do field splitting on the word which contained the substitution or use IFS as a general whitespace delimiter.

The results of substitution are, unless otherwise specified, also subject to brace expansion and file name expansion (see the relevant sections below).

A command substitution of the regular (comsub), deprecated, funsub or valsub form is replaced by the output generated by the specified command, which is run in a subshell โ€” except for the funsub and valsub types which run in the current execution environment. For $(command), ${ command;} and ${|command;} forms, normal quoting rules are used when command is parsed; however, for the deprecated `command` form, a \ followed by any of $, ` or \ is stripped (as is " when the substitution is part of a double-quoted string); a backslash followed by any other character is unchanged. As a special case in command substitutions, a command of the form <file is interpreted to mean substitute the contents of file, so that $(<foo) has the same effect, if foo is readable, as $(cat foo) but is much more performant. ๐Ÿ”ฅ

Heads up bestie ๐Ÿ˜ญ some shells do not use a recursive parser for command substitutions, leading to failure for certain constructs; to be portable, use as workaround x=$(cat) <<\EOF (or the newline-keeping x=<<\EOF extension) instead to merely slurp the string. IEEE Std 1003.1 (โ€œPOSIX.1โ€) recommends using case statements of the form x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac) instead, which would work but not serve as example for this portability issue.
x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
# above fails to parse on old shells; below is the workaround
x=$(eval $(cat)) <<\EOF
case $foo in bar) echo $bar ;; *) echo $baz ;; esac
EOF

Arithmetic substitutions are replaced by the value of the specified expression. For example, the command print $((2+3*4)) displays 14. See โ€œArithmetic expressionsโ€ for a description of an expression. Math ate, fr fr. ๐Ÿงฎโœจ


โœจ Parameters โœจ

Parameters are shell variables; they can be assigned values, and their values can be accessed using a parameter substitution. A parameter name is either one of the special single punctuation character or positional parameters described below, or a letter followed by zero or more letters, digits or underscores. The latter form can be accessed as array by appending an index of the form [expr] (in which expr is an arithmetic expression). Array indices range from 0 to 4294967295 (2^32-1), inclusive, in mksh. Slay.

Parameter substitutions take the form $name, ${name} or ${name[expr]} where name is a parameter name. Substitutions of an array in scalar context, i.e. without an expr in the latter form mentioned above, expand the element with the key โ€œ0โ€. Substitution of all array elements with ${name[*]} and ${name[@]} works equivalent to $* and $@ for positional parameters. If substitution is performed on a parameter (or an array parameter element) that is not set, an empty string is substituted โ€” unless the nounset option (set -u) is set, in which case an error occurs. โš ๏ธ

Parameters can be assigned values in a number of ways. Bestie, there's a whole menu:

  1. First, the shell implicitly sets some parameters like โ€œ#โ€, โ€œPWDโ€ and โ€œ$โ€; this is the only way the special single character parameters are set.
  2. Second, parameters are imported from the shell's environment at startup.
  3. Third, parameters can be assigned values on the command line: for example, FOO=bar sets the parameter โ€œFOOโ€ to โ€œbarโ€; multiple parameter assignments can be given on a single command line and they can be followed by a simple-command, in which case the assignments are in effect only for the duration of the command (such assignments are also exported; see below for the implications of this). Note that both the parameter name and the = must be unquoted for the shell to recognise a parameter assignment. The construct FOO+=baz is also recognised; the old and new values are string-concatenated with no separator.
  4. Fourth, it's with the export, readonly and typeset commands; see their descriptions in the โ€œCommand executionโ€ section.
  5. Fifth, for and select loops set parameters as well as the getopts, read and set -A commands.
  6. Lastly, parameters can be assigned values using assignment operators inside arithmetic expressions (see โ€œArithmetic expressionsโ€ below) or using the ${name=value} form of the parameter substitution (see below).

Parameters with the export attribute (set using the export or typeset -x commands, or by parameter assignments followed by simple commands) are put in the environment (see environ(7)) of commands run by the shell as name=value pairs. When the shell starts up, it extracts parameters and their values from its environment, setting the export attribute for those.

Modifiers can be applied to the ${name} form of parameter substitution:

  • ${name:-word} โ€” if name is set and not empty, it is substituted; otherwise, word is substituted. ๐ŸŽ€
  • ${name:+word} โ€” bestie, if name is set and not empty, word gets substituted in. Otherwise? Nothing. Nada. Empty vibes only โœจ
  • ${name:=word} โ€” if name is set and not empty, it gets substituted. If not, word gets assigned to it, and then the resulting value of name is what you get. It's giving "assign and deliver" ๐Ÿ’…
  • ${name:?word} โ€” if name is set and not empty, it's substituted. If not, word is printed on standard error (preceded by name:) and an error occurs โ€” which normally means your shell script, function, or a script sourced using the . built-in terminates. If word is omitted, the string parameter null or not set is used instead.
ok so heads up for ALL of the above, bestie: word is actually considered quoted, and special parsing rules apply. Also the parsing rules differ depending on whether the expression is double-quoted: word then uses double-quoting rules, except for the double quote itself (") and the closing brace, which โ€” if backslash escaped โ€” gets quote removal applied. iykyk ๐Ÿซถ

In the modifiers above, the : can be omitted, and in that case the conditions only depend on name being set (as opposed to set and not empty). If word is needed, parameter, command, arithmetic and tilde substitution are performed on it; if word isn't needed, it is not evaluated. no wasted energy periodt ๐Ÿ’…

The following forms of parameter substitution can also be used:

  • ${#name} โ€” the number of positional parameters if name is *, @ or not specified at all; otherwise it's the length (in characters ๐ŸŽ€) of the string value of parameter name.
  • ${#name[*]}
  • ${#name[@]} โ€” the number of elements in the array name.
  • ${%name} โ€” the width (in screen columns) of the string value of parameter name, or -1 if ${name} contains a control character. ๐Ÿ’€
  • ${!name} โ€” the name of the variable referred to by name. This will be name except when name is a name reference (bound variable), created by the nameref command (which is an alias for typeset -n). Also, name cannot be one of most special parameters (see below ya girl's list ๐Ÿซถ).
  • ${!name[*]}
  • ${!name[@]} โ€” the names of indices (keys) in the array name.
  • ${name#pattern}
  • ${name##pattern} โ€” if pattern matches the beginning of the value of parameter name, the matched text is deleted from the result of substitution. A single # gives you the shortest match, and two of them (##) give you the longest match. slay โœ‚๏ธ
  • ${name%pattern}
  • ${name%%pattern} โ€” like ${...#...} but it deletes from the end of the value. back it up bestie ๐Ÿฆ‹
  • ${name/pattern/string}
  • ${name/#pattern/string}
  • ${name/%pattern/string}
  • ${name//pattern/string} โ€” the longest match of pattern in the value of parameter name is replaced with string (deleted if string is empty; the trailing slash (/) may be omitted in that case). A leading slash followed by # or % causes the pattern to be anchored at the beginning or end of the value, respectively. Empty unanchored patterns cause no replacement. A single leading slash or a pattern that matches the empty string causes the replacement to happen only once; two leading slashes cause all occurrences of matches in the value to be replaced. Heads up though โ€” this may be slow on long strings ๐ŸŒ
  • ${name@/pattern/string} โ€” the same as ${name//pattern/string}, EXCEPT that both pattern and string are expanded anew for each iteration. Use with KSH_MATCH. โœจ
  • ${name:pos:len} โ€” the first len characters of name, starting at position pos, are substituted. Both pos and :len are optional. If pos is negative, counting starts at the end of the string; if it's omitted, it defaults to 0. If len is omitted or greater than the length of the remaining string, all of it is substituted. And both pos and len are evaluated as arithmetic expressions ๐Ÿงฎ
  • ${name@#} โ€” the hash (using the BAFH1-0 algorithm) of the expansion of name. This is also used internally for the shell's hashtables. no cap, it's the same machinery underneath ๐Ÿ‘‘
  • ${name@Q} โ€” a quoted expression safe for re-entry, whose value is the value of the name parameter, is substituted.
  • ${name@^} โ€” the value of name in extended caret notation, with both caret (^) and backslash (\) backslash-escaped to avoid ambiguity.
also quick note bestie: pattern may need extended globbing pattern (@(...)), single ('...') or double ("...") quote escaping โ€” unless -o sh is set. ๐Ÿ™ˆ

โœจ the special parameters โœจ (they're literally built different)

The following special parameters are implicitly set by the shell and cannot be set directly using assignments. they're the main characters of the parameter world, you don't assign to them, they assign to you ๐Ÿ’…

  • ! โ€” process ID of the last background process started. If no background processes have been started, the parameter is not set. ๐Ÿซฅ
  • # โ€” the number of positional parameters ($1, $2, etc.).
  • $ โ€” the PID of the shell, or, if it's a subshell, the PID of the original shell.
โš ๏ธ WARNING โš ๏ธ do NOT use this mechanism for generating temporary file names, bestie, i'm literally begging ๐Ÿ˜ญ see mktemp(1) instead. fr fr.
  • - โ€” the concatenation of the current single letter options (see the set command below for a list of options).
  • ? โ€” the exit status of the last non-asynchronous command executed. If the last command was killed by a signal, $? is set to 128 plus the signal number โ€” but at most 255. ๐Ÿ’€
  • 0 โ€” the name of the shell, determined like this: the first argument to mksh if it was invoked with the -c option and arguments were given; otherwise the file argument, if it was supplied; or else the name the shell was invoked with (i.e. argv[0]). $0 is also set to the name of the current script, or to the name of the current function if it was defined with the function keyword (i.e. a Korn shell style function). she knows who she is ๐Ÿ‘‘
  • 1 .. 9 โ€” the first nine positional parameters that were supplied to the shell, function, or script sourced using the . ("dot") builtin. Further positional parameters may be accessed using ${number}. ๐Ÿงš
  • * โ€” all positional parameters (except 0), i.e. $1, $2, $3, ... . If used outside of double quotes, parameters are separate words (which are subjected to word splitting); if used within double quotes, parameters are separated by the first character of the IFS parameter (or the empty string if IFS is unset). ๐ŸŒธ
  • @ โ€” same as $*, unless it's used inside double quotes, in which case a separate word is generated for each positional parameter. If there are no positional parameters, no word is generated. "$@" can be used to access arguments, verbatim, without losing empty arguments or splitting arguments with spaces (IFS, actually). literally the icon of safe argument passing ๐Ÿซถ

๐ŸŒˆ the parameters the shell sets and/or uses ๐ŸŒˆ

These are set and/or used by the shell:

  • _ โ€” (underscore) when an external command is executed by the shell, this parameter is set in the environment of the new process to the path of the executed command. And in interactive use, this parameter is also set in the parent shell to the last word of the previous command. ๐Ÿ‘€
  • BASHPID โ€” the PID of the shell or subshell.
  • CDPATH โ€” like PATH, but used to resolve the argument to the cd built-in command. Note that if CDPATH is set and does not contain . or an empty string element, the current directory is not searched. Also, the cd built-in command will display the resulting directory when a match is found in any search path other than the empty path. ๐Ÿ—บ๏ธ
  • COLUMNS โ€” set to the number of columns on the terminal or window. If never unset and not imported, always set dynamically; unless the value as reported by stty(1) is non-zero and sane enough (minimum is 12x3), defaults to 80; similar for LINES. This parameter is used by the interactive line editing modes and by the select, set -o and kill -l commands to format information columns. Importing from the environment or unsetting this parameter removes the binding to the actual terminal size in favour of the provided value. ๐Ÿ“
  • ENV โ€” if this parameter is found to be set after any profile files are executed, the expanded value is used as a shell startup file. It typically contains function and alias definitions. ๐Ÿ’–
  • EPOCHREALTIME โ€” time since the epoch, as returned by gettimeofday(2), formatted as decimal tv_sec followed by a dot (.) and tv_usec padded to exactly six decimal digits. โฐ
  • EXECSHELL โ€” if set, this parameter is assumed to contain the shell that is to be used to execute commands that execve(2) fails to execute and which do not start with a #!shell sequence.
  • FCEDIT โ€” the editor used by the fc command (see below). โœ๏ธ
  • FPATH โ€” like PATH, but used when an undefined function is executed (or when a command cannot be found using PATH) to locate the function definition file. See "Functions" below. ๐Ÿฆ‹
  • HISTFILE โ€” the name of the file used to store command history. When assigned to or unset, the file is opened, history is truncated then loaded from the file; subsequent new commands (possibly consisting of several lines) are appended once they successfully compiled. Also, several invocations of the shell will share history if their HISTFILE parameters all point to the same file. ๐Ÿ‘ฏ
Note: if HISTFILE is unset or empty, no history file is used. This is different from AT&T UNIX ksh. ๐Ÿšจ
  • HISTSIZE โ€” the number of commands normally stored for history. The default is 2047. The maximum is 65535. ๐Ÿ“š
  • HOME โ€” the default directory for the cd command and the value substituted for an unqualified ~ (see "Tilde expansion" below). ๐Ÿ 
  • IFS โ€” internal field separator, used during substitution and by the read command, to split values into distinct arguments; normally set to space, tab and newline. See "Substitution" above for details. โœ‚๏ธ
Note: this parameter is not imported from the environment when the shell is started. ๐Ÿ™…
  • KSHEGID โ€” the effective group id of the shell at startup.
  • KSHGID โ€” the real group id of the shell at startup.
  • KSHUID โ€” the real user id of the shell at startup.
  • KSH_MATCH โ€” the last string that matched, bestie ๐Ÿ’…. In a future version this is gonna be an indexed array with indexes 1 and up capturing matching groups (iykyk). It gets set by string comparisons (= and !=) in double-bracket test expressions when a match is found (so, when != returns false), by case when a match is encountered, and by all of these substitution ops: ${x#pat}, ${x##pat}, ${x%pat}, ${x%%pat}, ${x/pat/rpl}, ${x/#pat/rpl}, ${x/%pat/rpl}, ${x//pat/rpl} and ${x@/pat/rpl}. Want an example? Go peek at the end of the Emacs editing mode docs โœจ.
  • KSH_VERSION โ€” the shell's name (its self-identification) and version. Read-only, no touching ๐Ÿ”’. Also peep the version commands in the โ€œEmacs editing modeโ€ and โ€œVi editing modeโ€ sections down below.
  • LINENO โ€” the line number of the function or shell script that's running right now ๐Ÿ“.
  • LINES โ€” set to the number of lines in your terminal or window. Defaults to 24, and it's always set โ€” unless it got imported or unset. See COLUMNS.
  • OLDPWD โ€” the previous working directory. It's unset if cd hasn't successfully changed directories since the shell started, or if the shell literally doesn't know where it is ๐Ÿซ .
  • OPTARG โ€” when you're using getopts, this holds the argument for a parsed option, if that option needs one.
  • OPTIND โ€” the index of the next argument to be processed when using getopts. Assign 1 to it and getopts will start over from the beginning of the arguments next time it's invoked ๐Ÿ”.
  • PATH โ€” a colon-separated list of directories (semicolon on OS/2) that get searched when the shell looks for commands and for files sourced with the โ€œ.โ€ command (see below). An empty string coming from a leading or trailing (semi)colon, or from two adjacent ones, counts as a โ€œ.โ€ โ€” aka the current directory ๐Ÿ‘€.
  • PATHSEP โ€” a colon (semicolon on OS/2), purely for the user's convenience ๐Ÿ’–.
  • PGRP โ€” the process ID of the shell's process group leader.
  • PIPESTATUS โ€” an array holding the errorlevel (exit status) codes, one by one, of the last pipeline that ran in the foreground ๐Ÿ“Š.
  • PPID โ€” the process ID of the shell's parent.
  • PS1 โ€” the primary prompt for interactive shells. Parameter, command and arithmetic substitutions all happen in it, and ! gets replaced with the current command number (see the fc command below). Want a literal ! in your prompt? Just put โ€œ!!โ€ in PS1 ๐Ÿ™ˆ.

The default prompt is โ€œ$ โ€ for non-root users and โ€œ# โ€ for root. And lowkey important: if mksh is invoked by root and PS1 does not contain a # character, the default value will be used even if PS1 already exists in the environment ๐Ÿ‘‘

The mksh distribution ships with a sample dot.mkshrc containing a sophisticated example, but you might vibe with this one instead (heads up: ${HOSTNAME:=$(hostname)} and the root-vs-user distinguishing clause are, in this example, executed at PS1 assignment time, while $USER and $PWD are escaped and therefore get evaluated every single time a prompt is displayed ๐Ÿ‘€):

PS1='${USER:=$(id -un)}'"@${HOSTNAME:=$(hostname)}:\$PWD $(
        if (( USER_ID )); then print \$; else print \#; fi) "

Also note that since the command-line editors try to figure out how long the prompt is (so they know how far it is to the edge of the screen ๐Ÿ“), escape codes in your prompt tend to mess everything up. You can tell the shell not to count certain sequences (like escape codes) by prefixing your prompt with a character (like Ctrl+A) followed by a carriage return, and then delimiting the escape codes with that character. Any occurrences of that character in the prompt just aren't printed. And look, don't blame me for this hack โ€” it's derived from the original ksh88(1), which did print the delimiter character, so you were out of luck if you didn't have any non-printing characters ๐Ÿ’€.

Since backslashes and other special characters may be interpreted by the shell, to set PS1 you either escape the backslash itself or use double quotes. The double-quote route is more practical, fr. Here's a more complex example that avoids directly entering special characters (for example with ^V in the emacs editing mode), and embeds the current working directory โ€” in reverse video (colour would work too) โ€” into the prompt string โœจ:

x=$(print \\001) # otherwise unused char
PS1="$x$(print \\r)$x$(tput so)$x\$PWD$x$(tput se)$x> "

Due to a strong suggestion from David G. Korn, mksh now also supports this form ๐ŸŽ€:

PS1=$'\1\r\1\e[7m\1$PWD\1\e[0m\1> '
  • PS2 โ€” the secondary prompt string, by default โ€œ> โ€, used when more input is needed to complete a command.
  • PS3 โ€” the prompt used by the select statement when reading a menu selection. Default is โ€œ#? โ€.
  • PS4 โ€” used to prefix commands that get printed during execution tracing (see the set -x command below). Parameter, command and arithmetic substitutions are performed before it's printed. Default is โ€œ+ โ€. You might wanna set it to โ€œ[$EPOCHREALTIME] โ€ instead, to get timestamps โฐ.
  • PWD โ€” the current working directory. May be unset or empty if the shell doesn't know where it is ๐Ÿซฅ.
  • RANDOM โ€” every single time RANDOM is referenced, it first gets assigned a number between 0 and 32767 from a Linear Congruential PRNG ๐ŸŽฒ.
  • REPLY โ€” the default parameter for the read command when you don't give any names. It's also used in select loops to store the value read from standard input.
  • SECONDS โ€” the number of seconds since the shell started. Or, if the parameter has been assigned an integer value, the number of seconds since that assignment plus the value that got assigned โฑ๏ธ.
  • TMOUT โ€” if you set it to a positive integer in an interactive shell, it specifies the maximum number of seconds the shell will wait for input after printing the primary prompt (PS1). Exceed that time and the shell exits ๐Ÿšช๐Ÿ’จ.
  • TMPDIR โ€” the directory where temporary shell files get created. If this parameter isn't set, or doesn't contain the absolute path of a writable directory, temp files get created in /tmp.
  • USER_ID โ€” the effective user id of the shell at startup.

๐ŸŒธ Tilde expansion ๐ŸŒธ

Tilde expansion runs in parallel with parameter substitution, and it applies to words starting with an unquoted ~. In parameter assignments (like the ones preceding a simple-command, or the ones appearing in the arguments of a declaration utility), tilde expansion happens after any assignment โ€” i.e. after the equals sign โ€” or after an unquoted colon (:); login names are delimited by colons too. The Korn shell, except in POSIX mode, always expands tildes after unquoted equals signs, not just in assignment context (see below), and enables tab completion for tildes after all unquoted colons during command line editing โŒจ๏ธโœจ.

The characters following the tilde, up to the first / if there is one, are assumed to be a login name. If that login name is empty, + or -, the simplified value of the HOME, PWD or OLDPWD parameter is substituted, respectively. Otherwise the password file gets searched for the login name, and the tilde expression is substituted with that user's home directory. If the login name isn't found in the password file, or if any quoting or parameter substitution happens inside the login name, no substitution is performed ๐Ÿ™….

The home directories of previously expanded login names get cached and reused. The alias -d command can list, change and add to this cache (e.g. alias -d fac=/usr/local/facilities; cd ~fac/bin). Obsessed with a good cache ๐Ÿ’พ.

๐Ÿฆ‹ Brace expansion (alternation) ๐Ÿฆ‹

Brace expressions look like this:

prefix{str1,...,strN}suffix

The expressions expand to N words, each one being the concatenation of prefix, stri and suffix (e.g. โ€œa{c,b{X,Y},d}eโ€ expands to four words: โ€œaceโ€, โ€œabXeโ€, โ€œabYeโ€ and โ€œadeโ€). As that example shows, brace expressions can be nested, and the resulting words are not sorted ๐ŸŒช๏ธ. Brace expressions must contain an unquoted comma (,) for expansion to occur (so {} and {foo} are not expanded, sorry bestie). Brace expansion happens after parameter substitution and before file name generation.

๐Ÿ’… File name patterns ๐Ÿ’…

A file name pattern is a word containing one or more unquoted ?, *, +, @ or ! characters, or [...] sequences. Once brace expansion has been done, the shell replaces file name patterns with the sorted names of all the files matching the pattern (and if no files match, the word is left unchanged, no cap). The pattern elements mean this:

  • ? โ€” matches any single character.
  • * โ€” matches any sequence of octets.
  • [...] โ€” matches any of the octets inside the brackets. Ranges of octets can be specified by separating two octets with a - (e.g. โ€œ[a0-9]โ€ matches the letter a or any digit). Character classes can be specified by wrapping the class name between [: and :] (e.g. โ€œ[[:alpha:][:digit:].]โ€ matches any ASCII letter or digit and the full stop) ๐ŸŒˆ.

To represent itself, a - must either be quoted or be the first or last octet in the octet list. Same energy for ]: if it's meant to represent itself instead of the end of the list, it must be quoted or be the first octet in the list. Also, a ! appearing at the start of the list has special meaning (see below), so to represent itself it must be quoted or appear later in the list. And a ^ at the beginning of the list must be quoted or appear later. Got it? Got it ๐Ÿ’–.

  • [!...] โ€” just like [...], except it matches any octet not inside the brackets.
  • *(pattern|...|pattern) โ€” matches any string of octets that matches zero or more occurrences of the specified patterns. Example: the pattern *(foo|bar) matches the strings โ€œโ€, โ€œfooโ€, โ€œbarโ€, โ€œfoobarfooโ€, etc. ๐ŸŽฏ
  • +(pattern|...|pattern) โ€” matches any string of octets that matches one or more occurrences of the patterns you listed. Example: +(foo|bar) matches foo, bar, foobar, etc. it's giving "one or more, bestie" โœจ
  • ?(pattern|...|pattern) โ€” matches the empty string, or a string that matches one of the patterns. Example: ?(foo|bar) only matches "", foo and bar. yes the empty string is invited too ๐Ÿ’…
  • @(pattern|...|pattern) โ€” matches a string that matches exactly one of the patterns. Example: @(foo|bar) only matches foo and bar. pick one, queen ๐Ÿ‘‘
  • !(pattern|...|pattern) โ€” matches any string that does not match one of the patterns. Examples: !(foo|bar) matches every string except foo and bar; !(*) matches no strings at all; !()* matches all strings (think about it ๐Ÿง ). the villain arc of globbing i fear ๐Ÿ’€

The following character classes are supported (note all POSIX references assume the C locale; EBCDIC systems use the bytes from the codepage that map to the named ASCII characters so e.g. [[:upper:]] is correct while [A-Z] will contain probably-unwanted characters on EBCDIC systems):

  • < โ€” (BSD) the null string at the beginning of a word
  • > โ€” (BSD) the null string at the end of a word
  • alnum โ€” (POSIX) alphanumerical (alpha or digit)
  • alpha โ€” (POSIX) alphabetical (upper or lower)
  • ascii โ€” (GNU bash) any 7-bit ASCII character except NUL
  • blank โ€” (POSIX) space or horizontal tab
  • cntrl โ€” (POSIX) ASCII C0 control characters (\x00โ€“\x1F) or \x7F
  • digit โ€” (POSIX) ASCII decimal digits (0โ€“9)
  • graph โ€” (POSIX) alnum or punct (!โ€“~)
  • lower โ€” (POSIX) ASCII lowercase letters (aโ€“z)
  • print โ€” (POSIX) space or graph (\x20โ€“~)
  • punct โ€” (POSIX) punctuation (graph except alnum):
!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~
  • sh_alias โ€” (mksh) valid in alias names: alnum or !%+,-.:@[]_
  • sh_edq โ€” (mksh) quoted by tab completion:
"#$&'()*:;<=>?[\\`{|}~
  • sh_ifs โ€” (mksh) IFS whitespace, IFS non-whitespace, NUL (via $IFS)
  • sh_ifsws โ€” (mksh) IFS WS candidates: space, horizontal tab, linefeed
  • sh_nl โ€” (mksh) linefeed or (OS/2 TEXTMODE only) carriage return
  • sh_quote โ€” (mksh) characters requiring quoting, minus space:
\x09\x0A"#$&'()*;<=>?[\\]`{|}~
  • space โ€” (POSIX) horizontal tab, line feed, vertical tab, form feed, carriage return, space (\x09โ€“\x0D\x20)
  • upper โ€” (POSIX) ASCII uppercase letters (Aโ€“Z)
  • word โ€” (GNU bash) alphanumerical (alnum) or underscore (_)
  • xdigit โ€” (POSIX) hexadecimal digits (0โ€“9Aโ€“Faโ€“f) a.k.a. nybbles

Note that complicated globbing, especially with alternatives, is slow; using separate comparisons may (or may not) be faster. no cap, sometimes the simple way just eats ๐Ÿ”ฅ

Note that mksh (and pdksh) never matches . and .., but AT&T UNIX ksh, Bourne sh and GNU bash do. different strokes bestie ๐Ÿ™ˆ

Note that none of the above pattern elements match either a period (.) at the start of a file name or a slash (/), even if they are explicitly used in a [...] sequence; also, the names . and .. are never matched, even by the pattern .*. them's just not invited, periodt ๐Ÿ’…

If the markdirs option is set, any directories that result from file name generation are marked with a trailing /.


๐ŸŒธ Input/output redirection ๐ŸŒธ

When a command is executed, its standard input, standard output and standard error (file descriptors 0, 1 and 2, respectively) are normally inherited from the shell. Three exceptions to this are commands in pipelines, for which standard input and/or standard output are those set up by the pipeline, asynchronous commands created when job control is disabled, for which standard input is initially set to /dev/null, and commands for which any of the following redirections have been specified:

  • >file โ€” standard output is redirected to file. If file does not exist, it is created; if it does exist, is a regular file, and the noclobber option is set, an error occurs; otherwise, the file is truncated. Note that this means the command cmd <foo >foo will open foo for reading and then truncate it when it opens it for writing, before cmd gets a chance to actually read foo. sabotage, honestly ๐Ÿ˜ญ
  • >|file โ€” same as >, except the file is truncated, even if the noclobber option is set. it does not care, it will clobber ๐Ÿ’…
  • >>file โ€” same as >, except if file exists it is appended to instead of being truncated. Also, the file is opened in append mode, so writes always go to the end of the file (see open(2)). cozy little addition at the end โœจ
  • <file โ€” standard input is redirected from file, which is opened for reading.
  • <>file โ€” same as <, except the file is opened for reading and writing.
  • <<marker โ€” after reading the command line containing this kind of redirection (called a "here document"), the shell copies lines from the command source into a temporary file until a line matching marker is read. When the command is executed, standard input is redirected from the temporary file. If marker contains no quoted characters, the contents of the temporary file are processed as if enclosed in double quotes each time the command is executed, so parameter, command and arithmetic substitutions are performed, along with backslash (\) escapes for $, `, \ and \newline, but not for ". If multiple here documents are used on the same command line, they are saved in order. ๐ŸŽ€

If no marker is given, the here document ends at the next << and substitution will be performed. If marker is only a set of either single '' or double "" quotes with nothing in between, the here document ends at the next empty line and substitution will not be performed. sneaky little rules, iykyk

  • <<-marker โ€” same as <<, except leading tabs are stripped from lines in the here document.
  • <<<word โ€” same as <<, except that word is the here document. This is called a here string. cute one-liner energy fr fr ๐ŸŒˆ
  • <&fd โ€” standard input is duplicated from file descriptor fd. fd can be a single digit, indicating the number of an existing file descriptor; the letter p, indicating the file descriptor associated with the output of the current co-process; or the character -, indicating standard input is to be closed.
  • >&fd โ€” same as <&, except the operation is done on standard output.
  • &>file โ€” same as >file 2>&1. This is a deprecated (legacy) GNU bash extension supported by mksh which also supports the preceding explicit fd digit, for example, 3&>file is the same as 3>file 2>&3 in mksh but a syntax error in GNU bash. legacy behavior bestie, it's giving elder energy
  • &>|file, &>>file, &>&fd โ€” same as >|file, >>file or >&fd, followed by 2>&1, as above. These are mksh extensions. โœจ

In any of the above redirections, the file descriptor that is redirected (i.e. standard input or standard output) can be explicitly given by preceding the redirection with a single digit. Parameter, command and arithmetic substitutions, tilde substitutions, and, if the shell is interactive, file name generation are all performed on the file, marker and fd arguments of redirections. Note, however, that the results of any file name generation are only used if a single file is matched; if multiple files match, the word with the expanded file name generation characters is used. Note that in restricted shells, redirections which can create files cannot be used. ๐Ÿšซ

For simple-commands, redirections may appear anywhere in the command; for compound-commands (if statements, etc.), any redirections must appear at the end. Redirections are processed after pipelines are created and in the order they are given, so the following will print an error with a line number prepended to it:

$ cat /foo/bar 2>&1 >/dev/null | pr -n -t

File descriptors created by I/O redirections are private to the shell. ๐Ÿคซ


๐Ÿงฎ Arithmetic expressions ๐Ÿงฎ

Integer arithmetic expressions can be used with the let command, inside $((...)) expressions, inside array references (e.g. name[expr]), as numeric arguments to the test command, and as the value of an assignment to an integer parameter.

โš ๏ธ Warning: This also affects implicit conversion to integer, for example as done by the let command. Never use unchecked user input, e.g. from the environment (although the shell tracks import status and refuses to automatically coerce those), in arithmetic context! this is the part where you don't get sloppy ๐Ÿ˜ญ

Expressions are calculated using signed arithmetic and the mksh_ari_t type (a 32-bit signed integer), unless they begin with a sole # character, in which case they use mksh_uari_t (a 32-bit unsigned integer).

Expressions may contain alpha-numeric parameter identifiers, array references and integer constants and may be combined with the following C operators (listed and grouped in increasing order of precedence):

Unary operators:

+ - ! ~ ++ --

Binary operators:

,
= += -= *= /= %= <<= >>= ^<= ^>= &= ^= |=
||
&&
|
^
&
== !=
< <= > >=
<< >> ^< ^>
+ -
* / %

Ternary operators:

?: (precedence is immediately higher than assignment)

Grouping operators:

( )

Integer constants and expressions are calculated using an exactly 32-bit wide, signed (two's complement) or unsigned, type with silent wraparound on integer overflow. Integer constants may be specified with arbitrary bases using the notation base#number, where base is a decimal integer specifying the base (up to 36), and number is a number in the specified base. Additionally, base-16 integers may be specified by prefixing with 0x (case-insensitive) in all forms of arithmetic expressions, except as numeric arguments to the test built-in utility. Prefixing numbers with a sole digit zero (0) does not cause interpretation as octal (except in POSIX mode, as required by the standard), as that's unsafe. Prefixing with 10# forces interpretation as decimal, even with leading zeros. An unset or empty parameter evaluates to 0 in integer context. maths but make it fashion ๐Ÿ’…

okay so, numbers with base one?? Yes bestie, that's a real mksh thing, it's a special little mksh extension ๐Ÿ’…. It treats them as either (8-bit transparent) ASCII or Universal Coded Character Set codepoints, and which one you get depends on the shell's [utf8-mode flag] (current setting), obviously. The AT&T UNIX ksh93 syntax of 'x' instead of 1#x is also supported โ€” legacy icons stay winning โœจ.

important though!!! NUL bytes (integral value of zero) cannot be used. Periodt. And if x isn't comprised of exactly one valid character, the behaviour is undefined (usually, the shell aborts with a parse error, but rarely, it succeeds, e.g. on the sequence C2 20). So if you're a user of this feature (as opposed to read -a), you must validate the input first. Check โ€œCAVEATSโ€ for UTF-8 mode handling, bestie. Also base-1 integers don't work well with a number of other shell features, such as reentry-safe output; use print -A or read -a if possible. ๐Ÿ™ˆ

โœจ the operators, explained โœจ

The vibes here are simple: the operators are evaluated as follows:

  • unary + โ€” Result is the argument (included for completeness). It's giving "we did this for symmetry" ๐Ÿ’€
  • unary - โ€” Negation.
  • ! โ€” Logical NOT; the result is 1 if argument is zero, 0 if not.
  • ~ โ€” Arithmetic (bit-wise) NOT.
  • ++ โ€” Increment; must be applied to a parameter (not a literal or other expression). The parameter is incremented by 1. When used as a prefix operator, the result is the incremented value of the parameter; when used as a postfix operator, the result is the original value of the parameter.
  • -- โ€” Similar to ++, except the parameter is decremented by 1.
  • , โ€” Separates two arithmetic expressions; the left-hand side is evaluated first, then the right. The result is the value of the expression on the right-hand side.
  • = โ€” Assignment; the variable on the left is set to the value on the right.
  • += -= *= /= %= <<= >>= ^<= ^>= &= ^= |= โ€” Assignment operators. <var><op>=<expr> is the same as <var>=<var><op><expr>, with any operator precedence in <expr> preserved. For example, var1 *= 5 + 3 is the same as specifying var1 = var1 * (5 + 3). ๐Ÿงฎ
  • || โ€” Logical OR; the result is 1 if either argument is non-zero, 0 if not. The right argument is evaluated only if the left argument is zero.
  • && โ€” Logical AND; the result is 1 if both arguments are non-zero, 0 if not. The right argument is evaluated only if the left argument is non-zero.
  • | โ€” Arithmetic (bit-wise) OR.
  • ^ โ€” Arithmetic (bit-wise) XOR (exclusive-OR).
  • & โ€” Arithmetic (bit-wise) AND.
  • == โ€” Equal; the result is 1 if both arguments are equal, 0 if not.
  • != โ€” Not equal; the result is 0 if both arguments are equal, 1 if not.
  • < โ€” Less than; the result is 1 if the left argument is less than the right, 0 if not.
  • <= > >= โ€” Less than or equal, greater than, greater than or equal. See <.
  • << >> โ€” Shift left (right); the result is the left argument with its bits arithmetically (signed operation) or logically (unsigned expression) shifted left (right) by the amount given in the right argument.
  • ^< ^> โ€” Rotate left (right); the result is similar to shift, except that the bits shifted out at one end are shifted in at the other end, instead of zero or sign bits. ๐ŸŒ€
  • + - * / โ€” Addition, subtraction, multiplication and division.
  • % โ€” Remainder; the result is the symmetric remainder of the division of the left argument by the right. To get the mathematical modulus of a mod b, use the formula (a % b + b) % b.
  • <arg1>?<arg2>:<arg3> โ€” If <arg1> is non-zero, the result is <arg2>; otherwise the result is <arg3>. The non-result argument is not evaluated.

๐Ÿฆ‹ Co-processes ๐Ÿฆ‹

A co-process (which is a pipeline created with the |& operator) is an asynchronous process that the shell can both write to (using print -p) and read from (using read -p). The input and output of the co-process can also be manipulated using >&p and <&p redirections, respectively. Once a co-process has been started, another can't be started until the co-process exits, or until the co-process's input has been redirected using an exec n>&p redirection. If a co-process's input is redirected in this way, the next co-process to be started will share the output with the first co-process, unless the output of the initial co-process has been redirected using an exec n<&p redirection. ๐Ÿ‘ฏ

Some notes concerning co-processes:

  • The only way to close the co-process's input (so the co-process reads an end-of-file) is to redirect the input to a numbered file descriptor and then close that file descriptor:
exec 3>&p; exec 3>&-
  • In order for co-processes to share a common output, the shell must keep the write portion of the output pipe open. This means that end-of-file will not be detected until all co-processes sharing the co-process's output have exited (when they all exit, the shell closes its copy of the pipe). This can be avoided by redirecting the output to a numbered file descriptor (as this also causes the shell to close its copy). Note that this behaviour is slightly different from the original Korn shell which closes its copy of the write portion of the co-process output when the most recently started co-process (instead of when all sharing co-processes) exits. ๐Ÿ˜ญ
  • print -p will ignore SIGPIPE signals during writes if the signal is not being trapped or ignored; the same is true if the co-process input has been duplicated to another file descriptor and print -un is used.

๐Ÿ’– Functions ๐Ÿ’–

Functions are defined using either Korn shell function function-name syntax or the Bourne/POSIX shell function-name() syntax (see below for the difference between the two forms). Functions are like .-scripts (i.e. scripts sourced using the . built-in) in that they are executed in the current environment. However, unlike .-scripts, shell arguments (i.e. positional parameters $1, $2, etc.) are never visible inside them. When the shell is determining the location of a command, functions are searched after special built-in commands, before builtins and the PATH is searched. ๐Ÿ”

An existing function may be deleted using unset -f function-name. A list of functions can be obtained using typeset +f and the function definitions can be listed using typeset -f. The autoload command (which is an alias for typeset -fu) may be used to create undefined functions: when an undefined function is executed, the shell searches the path specified in the FPATH parameter for a file with the same name as the function which, if found, is read and executed. If after executing the file the named function is found to be defined, the function is executed; otherwise, the normal command search is continued (i.e. the shell searches the regular built-in command table and PATH). Note that if a command is not found using PATH, an attempt is made to autoload a function using FPATH (this is an undocumented feature of the original Korn shell). ๐Ÿคซ

Functions can have two attributes, โ€œtraceโ€ and โ€œexportโ€, which can be set with typeset -ft and typeset -fx, respectively. When a traced function is executed, the shell's xtrace option is turned on for the function's duration. The โ€œexportโ€ attribute of functions is currently not used.

Since functions are executed in the current shell environment, parameter assignments made inside functions are visible after the function completes. If this is not the desired effect, the typeset command can be used inside a function to create a local parameter. Note that AT&T UNIX ksh93 uses static scoping (one global scope, one local scope per function) and allows local variables only on Korn style functions, whereas mksh uses dynamic scoping (nested scopes of varying locality). Note that special parameters (e.g. $$, $!) can't be scoped in this way.

The exit status of a function is that of the last command executed in the function. A function can be made to finish immediately using the return command; this may also be used to explicitly specify the exit status. Note that when called in a subshell, return will only exit that subshell and will not cause the original shell to exit a running function (see the while...read loop FAQ). ๐Ÿ‘‘

Functions defined with the function reserved word are treated differently in the following ways from functions defined with the () notation:

  • The $0 parameter is set to the name of the function (Bourne-style functions leave $0 untouched).
  • OPTIND is saved/reset and restored on entry and exit from the function so getopts can be used properly both inside and outside the function (Bourne-style functions leave OPTIND untouched, so using getopts inside a function interferes with using getopts outside the function).
  • Shell options (set -o) except -p (-o privileged) have local scope, i.e. changes inside a function are reset upon its exit. โœจ

In the future, the following differences may also be added:

  • A separate trap/signal environment will be used during the execution of functions. This will mean that traps set inside a function will not affect the shell's traps and signals that are not ignored in the shell (but may be trapped) will have their default effect in a function.
  • The EXIT trap, if set in a function, will be executed after the function returns.

๐ŸŒˆ Command execution ๐ŸŒˆ

After evaluation of command-line arguments, redirections and parameter assignments, the type of command is determined: a special built-in command, a function, a normal builtin or the name of a file to execute found using the PATH parameter. The checks are made in the above order. Special built-in commands differ from other commands in that the PATH parameter is not used to find them, an error during their execution can cause a non-interactive shell to exit, and parameter assignments that are specified before the command are kept after the command completes. Regular built-in commands are different only in that the PATH parameter is not used to find them. ๐Ÿ’…

POSIX special built-in utilities:

., :, break, continue, eval, exec, exit, export, readonly, return, set,
shift, times, trap, unset

Additional mksh commands keeping assignments:

source, typeset

All other builtins are not special; these are at least:

[, alias, bg, bind, builtin, cd, command, echo, false, fc, fg, getopts, jobs, kill, let, print, pwd, read, realpath, rename, suspend, test, true, ulimit, umask, unalias, wait, whence

okay so the moment the shell has figured out exactly what kind of command we're dealing with, any command-line parameter assignments get performed and exported for as long as that command runs. no cap. ๐Ÿ’…

Now for the main event โ€” here's the full rundown of the special and regular built-in commands and the builtin-adjacent reserved words, plus a couple of optional utilities that tagged along. Buckle up, bestie ๐ŸŽ€


. *file* [*arg* ...]

(keeps assignments, special) bestie this is THE "dot" command โœจ It executes the commands inside file in the current environment โ€” no subshell, we're staying right here, we're comfortable. The shell goes looking for file in the directories listed in PATH. If arguments are given, the positional parameters can be used to grab them while file is busy running. And if you give it no arguments? Then the positional parameters are just whatever the environment the command was used in already had. ๐Ÿ˜Œ

: [...]

(keeps assignments, special) The null command. That's it, that's the whole vibe. It does nothing and the exit status is set to zero. A little icon of emptiness ๐Ÿ‘‘

Lb64decode [string]

(dot.mkshrc function) Decodes string, or standard input, into binary. ๐Ÿงš

Lb64encode [string]

(dot.mkshrc function) Encodes string, or standard input, as base64. ๐Ÿ”ฅ

Lbafh_init

Lbafh_add [string]

Lbafh_finish

(dot.mkshrc functions) Okay these three together implement the Better Avalanche for Jenkins Hash (IV=1) ๐Ÿซถ and yes, clever girl, it's the exact same hash mksh currently uses internally. The ritual goes like this: call Lbafh_init first, then call Lbafh_add as many times as you need until all your input is read, then call Lbafh_finish โ€” which writes the result into the unsigned integer Lbafh_v variable for you to consume. Slay. ๐Ÿ’–

Lstripcom [file ...]

(dot.mkshrc function) Literally cat(1) but with a glow-up: it strips out any empty lines and comments (comments being anything from a # character onwards, and no, there are no escapes โ€” sorry bestie) and squashes any amount of whitespace down to a single space character. Tidy โœจ

[ expression ]

(regular) See test. That's the whole note, go look at test. ๐Ÿ™ˆ

alias [-d | -t [-r] | -+x] [-p] [+] [name[=value] ...]

(regular) With no arguments, alias lists all the aliases. For any name without a value, the existing alias for it gets listed. And any name with a value defines an alias โ€” see "Aliases" above for the whole lore.

The characters [][A-Za-z0-9_!%+,.@:-] are all valid in names, with two little caveats: they can't begin with a plus or a hyphen-minus, and [[ is not a valid alias name. Noted. ๐Ÿ“

When you're listing aliases, one of two formats shows up. Normally aliases are listed as name=value, where value is quoted however it needs to be. But if the options were preceded with +, or a lone + is given on the command line, only name gets printed.

  • -d โ€” makes it so directory aliases (the ones used in tilde expansion) are listed or set โ€” see "Tilde expansion" above.
  • -p โ€” each alias gets listed with the string alias prefixed onto it.
  • -t โ€” says we're listing/setting tracked aliases (any values you give with the command are ignored for tracked aliases, btw).
  • -r โ€” says all tracked aliases are getting reset.
  • -x โ€” sets (and +x clears) the export attribute of an alias, or, if you don't give any names, lists the aliases that have the export attribute. (Heads up: exporting an alias has no effect. It just vibes there.)

autoload

(built-in alias) See "Functions" above. ๐Ÿฆ‹

bg [job ...]

(regular, needs job control) Resumes the stopped job(s) you specify, in the background. No jobs given? %+ is assumed. See "Job control" below for the deets. ๐Ÿ’œ

bind -l

(regular) Lists the names of the editing commands that strings can be bound to. See "Emacs editing mode" for more. โŒจ๏ธ

bind [string ...]

Lists the current bindings โ€” for string if you gave one, otherwise all of them. Note: default prefix bindings (1=Esc, 2=^X, 3=NUL) are assumed. ๐Ÿ“‹

bind string=[editing-command] [...]

bind -m string=substitute [...]

To string โ€” which should consist of a control character, optionally preceded by one of the three prefix characters and optionally followed by a tilde โ€” the editing-command gets bound, so that future input of that string will immediately invoke that editing command. If a tilde postfix is given, a tilde trailing the control character is ignored. And if -m (macro) is given, future input of string gets replaced by the given NUL-terminated substitute string, wherein prefix/control/tilde characters that are mapped to editing commands (but not those mapped to other macros) will be processed. ๐Ÿ˜ตโ€๐Ÿ’ซ

The entire argument can also be written using extended caret notation: ^Z represents Ctrl-Z; ^+Z represents UTF-8 Meta-Ctrl-Z; and both ^!Z and \x9A represent ASCII Meta-Ctrl-Z. Otherwise, a backslash escapes the next character, which removes the special meaning from backslashes, carets and (for the string part) equals signs. (Those backslashes obviously have to be quoted for the shell, bestie.) And note: even though only three prefix characters โ€” usually Esc, ^X and NUL โ€” are usable, some multi-character sequences can still be supported. ๐Ÿ’…

break [level]

(keeps assignments, special) Exits out of the levelth inner-most for, select, until or while loop. level defaults to 1. Escape the loop, periodt. ๐Ÿšช

builtin [--] command [arg ...]

(regular) Executes the built-in command command. Simple as that. โœจ

\builtin command [arg ...]

(regular, decl-forwarder) Same as builtin. But additionally it acts as a declaration utility forwarder โ€” meaning it counts as a declaration utility (see "Tilde expansion") if and only if command is a declaration utility. Sneaky but iconic. ๐Ÿซถ

cd [-L] [dir]

cd -P [-e] [dir]

chdir [-eLP] [dir]

cd [-L] [dir]
cd -P [-e] [dir]
chdir [-eLP] [dir]

(regular) Sets the working directory to dir. If CDPATH is set, it hands you the search path for the directory containing dir; an unset or empty path means the current directory. If dir is found in some component of the CDPATH search path that isn't an unset or empty path, the name of the new working directory gets written to standard output. If dir is missing, the home directory HOME is used. And if dir is -? Then the previous working directory is used (see the OLDPWD parameter). ๐Ÿ”„

Use the -L option (logical path), or leave the physical option unset (see the set command below), and references to .. in dir are relative to the path you took to get to the directory. Use -P (physical path), or have the physical option set, and .. is relative to the filesystem directory tree instead. The PWD and OLDPWD parameters get updated to reflect the current and old working directory respectively. And if -e is set for physical filesystem traversal and PWD couldn't be set, the exit code is 1; greater than 1 if an error occurred, 0 otherwise. ๐Ÿ“

cd [-eLP] old new

chdir [-eLP] old new

(regular) Swaps the string new in for old in the current directory, and then the shell tries to change to that brand-new directory. Glow-up complete โœจ

cls

(dot.mkshrc alias) Reinitialise the display (a hard reset). ๐Ÿงผ

command [-pVv] cmd [arg ...]

(regular, decl-forwarder) If neither -v nor -V is given, cmd is executed exactly as if command hadn't been specified at all โ€” with two little exceptions. First: cmd can't be a shell function. Second: special built-in commands lose their specialness, i.e. redirection and utility errors do not cause the shell to exit, and command assignments are not permanent. ๐Ÿ‘€

  • -p โ€” a default search path is used instead of the current PATH; the actual value of that path is system-dependent.
  • -v โ€” instead of executing cmd, info about what would be executed is given for each argument. For builtins, functions and keywords, their names are simply printed; for aliases, a command that defines them is printed; for utilities found by searching the PATH parameter, the full path of the command is printed. If no command is found (i.e. the path search fails), nothing is printed and command exits with a non-zero status.
  • -V โ€” like -v, but more verbose. Extra chatty. ๐Ÿ’ฌ

continue [level]

(keeps assignments, special) Jumps right back to the beginning of the levelth inner-most for, select, until or while loop. level defaults to 1. Round two, bestie ๐Ÿ”

dirs [-lnv]

(dot.mkshrc function) Prints the directory stack. ๐Ÿ—‚๏ธ

  • -l โ€” makes tilde expansion occur in the output.
  • -n โ€” makes it wrap lines before 80 columns.
  • -v โ€” makes it a numbered vertical output.

doch

(dot.mkshrc alias) Executes the last command with sudo(8). ๐Ÿ‘‘

echo [-Een] [arg ...]

(regular) Warning: this utility is not portable โ€” use the standard Korn shell built-in utility print in new code instead. ๐Ÿ™…โ€โ™€๏ธ

It prints the arguments to standard output, separated by spaces and followed by a newline. That newline gets suppressed if any of the arguments contain the backslash sequence \c. See the print command below for a list of the other backslash sequences that get recognised.

The options exist purely for compatibility with BSD shell scripts. -E suppresses backslash interpretation, -e enables it (that's normally the default), -n suppresses the trailing newline, and anything else causes the word to be printed as an argument instead.

If the posix or sh option is set, or this is a direct builtin call, or print -R, then only the first argument is treated as an option โ€” and only if it is exactly -n. Backslash interpretation is disabled. ๐Ÿ’€

enable [-anps] [name ...]

(dot.mkshrc function) Hides and unhides built-in utilities, aliases and functions, plus the ones defined in dot.mkshrc. ๐Ÿ™ˆ

  • No name, or -p โ€” builtins get printed (behind the string enable , followed by -n if the builtin is currently disabled). Otherwise they get disabled (if -n is given) or re-enabled.
  • -a โ€” prints all builtins, not just the enabled ones. (When printing, only enabled builtins show up by default.)
  • -n โ€” prints only the disabled builtins instead.
  • -s โ€” limits the list to POSIX special builtins.

eval command ...

(keeps assignments, special) The arguments all get concatenated together, with a space between each one, forming a single string โ€” which the shell then parses and executes in the current execution environment. Chaotic, powerful, iconic. ๐Ÿช„

exec (keeps assignments, special) ๐Ÿ’…

exec [-a argv0] [-c] [command [arg ...]]

okay so bestie, this one is intense. The command (with its args) gets executed without forking, fully replacing the shell process. Like, the shell literally becomes the command. This is absolute โ€” exec never returns, even if the command is not found. Not "usually doesn't return". Never. ๐Ÿ‘‘

The -a option lets you set a different argv[0] value, and -c clears the environment before executing the child process, except for the _ parameter and direct assignments.

In โ€œPOSIX modeโ€, a failed exec does return to an interactive shell prompt. But heads up โ€” the execution environment may still contain traces of the attempt to pass control and is not fully restored. So it's giving "we tried our best" energy. ๐Ÿ˜ญ

If no command is given except for I/O redirection, the I/O redirection is permanent and the shell is not replaced. Any file descriptors greater than 2 which are opened or dup(2)'d in this way are not made available to other executed commands (i.e. commands that are not built-in to the shell). Note that the Bourne shell differs here; it does pass these file descriptors on.


exit (keeps assignments, special) ๐Ÿšช

exit [status]

The shell or subshell exits with the specified errorlevel (or the current value of the $? parameter).


export (keeps assignments, special, decl-util) ๐ŸŽ€

export [-p] [parameter[=value]]

Sets the export attribute of the named parameters. Exported parameters are passed in the environment to executed commands. If values are specified, the named parameters are also assigned. This is a declaration utility.

If no parameters are specified, all parameters with the export attribute set are printed one per line: either their names, or, if a โ€œ-โ€ with no option letter is specified, name=value pairs, or, with the -p option, export commands suitable for re-entry. Iconic behaviour honestly. โœจ


extproc (OS/2)

Null command required for shebang-like functionality. That's it. That's the whole job. ๐Ÿซถ


false (regular)

A command that exits with a non-zero status. Yes she's simple. Yes she's essential. ๐Ÿ’€


fc (regular) ๐Ÿ“œ

fc [-e editor | -l [-n]] [-r] [first [last]]

first and last select commands from the history. Commands can be selected by history number (negative numbers go backwards from the current, most recent, line) or a string specifying the most recent command starting with that string. The -l option lists the command on standard output, and -n inhibits the default command numbers. The -r option reverses the order of the list. Without -l, the selected commands are edited by the editor specified with the -e option or, if no -e is specified, the editor specified by the FCEDIT parameter (if this parameter is not set, /bin/ed is used), and the result is executed by the shell.

fc (the other form)

fc -e - | -s [-g] [old=new] [prefix]

Re-execute the selected command (the previous command by default) after performing the optional substitution of old with new. If -g is specified, all occurrences of old are replaced with new. The meaning of -e - and -s is identical: re-execute the selected command without invoking an editor. This command is usually accessed with the predefined:

alias r='fc -e -'

fg (regular, needs job control) ๐Ÿ”ฅ

fg [job ...]

Resume the specified job(s) in the foreground. If no jobs are specified, %+ is assumed. See โ€œJob controlโ€ below for more information.


functions (built-in alias)

functions [name ...]

Display the function definition commands corresponding to the listed, or all defined, functions.


getopts (regular) ๐Ÿงš

getopts optstring name [arg ...]

Used by shell procedures to parse the specified arguments (or positional parameters, if no arguments are given) and to check for legal options. Options that do not take arguments may be grouped in a single argument. If an option takes an argument and the option character is not the last character of the word it is found in, the remainder of the word is taken to be the option's argument; otherwise, the next word is the option's argument.

optstring contains the option letters to be recognised. If a letter is followed by a colon, the option takes an argument.

Each time getopts is invoked, it places the next option in the shell parameter name. If the option was introduced with a โ€˜+โ€™, the character placed in name is prefixed with a โ€˜+โ€™. If the option takes an argument, it is placed in the shell parameter OPTARG.

When an illegal option or a missing option argument is encountered, a question mark or a colon is placed in name (indicating an illegal option or missing argument, respectively) and OPTARG is set to the option letter that caused the problem. Furthermore, unless optstring begins with a colon, a question mark is placed in name, OPTARG is unset and a diagnostic is shown on standard error.

getopts records the index of the argument to be processed by the next call in OPTIND. When the end of the options is encountered, getopts returns a non-zero exit status. Options end at the first argument that does not start with a โ€˜-โ€™ (non-option argument) or when a โ€œ--โ€ argument is encountered.

Option parsing can be reset by setting OPTIND to 1 (this is done automatically whenever the shell or a shell procedure is invoked).

โš ๏ธ Warning: Changing the value of the shell parameter OPTIND to a value other than 1 or parsing different sets of arguments without resetting OPTIND may lead to unexpected results. Like, chaos. Not the cute kind. ๐Ÿ’€

hash (built-in alias)

hash [-r] [name ...]

Without arguments, any hashed executable command paths are listed. The -r option causes all hashed commands to be removed from the cache. Each name is searched as if it were a command name and added to the cache if it is an executable command.


hd (dot.mkshrc alias or function) ๐Ÿฆ‹

hd [file ...]

Hexdump stdin or arguments legibly.


history (built-in alias)

history [-nr] [first [last]]

Same as fc -l (see above).


integer (built-in alias)

integer [flags] [name[=value] ...]

Same as typeset -i (see below).


jobs (regular) ๐Ÿ‘‘

jobs [-lnp] [job ...]

Display information about the specified job(s); if no jobs are specified, all jobs are displayed.

  • -n โ€” causes information to be displayed only for jobs that have changed state since the last notification.
  • -l โ€” if used, the process ID of each process in a job is also listed.
  • -p โ€” causes only the process group of each job to be printed.

See โ€œJob controlโ€ below for the format of job and the displayed job.


kill (regular) ๐Ÿ”ช

kill [-s signame | -signum | -signame] { job | pid | pgrp } ...

Send the specified signal to the specified jobs, process IDs or process groups. If no signal is specified, the TERM signal is sent. If a job is specified, the signal is sent to the job's process group. See โ€œJob controlโ€ below for the format of job.

kill -l (regular)

kill -l [exit-status ...]

Print the signal name corresponding to exit-status. If no arguments are specified, a list of all the signals with their numbers and a short description of each are printed.


let (regular) ๐Ÿ“

let [expression ...]

Each expression is evaluated (see โ€œArithmetic expressionsโ€ above). If all expressions evaluate successfully, the exit status is 0 (1) if the last expression evaluated to non-zero (zero). If an error occurs during the parsing or evaluation of an expression, the exit status is greater than 1.

Since expressions may need to be quoted, (( expr )) is syntactic sugar for:

{ \\builtin let 'expr'; }

local (built-in alias)

local [flags] [name[=value] ...]

Same as typeset (see below).


mknod (optional) ๐ŸŒˆ

mknod [-m mode] name b|c major minor
mknod [-m mode] name p

Create a device special file. The file type may be one of b (block type device), c (character type device) or p (named pipe, FIFO). The file created may be modified according to its mode (via the -m option), major (major device number), and minor (minor device number).

This is not normally part of mksh; however, distributors may have added this as builtin as a speed hack.


nameref (built-in alias)

nameref [flags] [name[=value] ...]

Same as typeset -n (see below).


popd (dot.mkshrc function) ๐Ÿ’œ

popd [-lnv] [+n]

Pops the directory stack and returns to the new top directory. The flags are as in dirs (see above). A numeric argument +n selects the entry in the stack to discard.


print [-AcelNnprsu[n] | -R [-n]] [argument ...]

Print the specified argument(s) on the standard output, separated by spaces, terminated with a newline. The escapes mentioned in โ€œBackslash expansionโ€ above, as well as โ€œ\cโ€, which is equivalent to using the -n option, are interpreted.

The options are as follows:

  • -A โ€” Each argument is arithmetically evaluated; the character corresponding to the resulting value is printed. Empty arguments separate input words. No backslash expansion.
  • -c โ€” The output is printed columnised, top to bottom then left to right, similar to how tab completion (control character escaping excepted), the kill -l built-in utility, the select statement and the rs(1) utility do.
  • -e โ€” Restore backslash expansion after a previous -r.
  • -l โ€” Change the output word separator to newline.
  • -N โ€” Change the output word and line separator to ASCII NUL.
  • -n โ€” Do not print the trailing line separator.
  • -p โ€” Print to the co-process (see โ€œCo-processesโ€ above).
  • -r โ€” Inhibit backslash expansion.
  • -s โ€” Print to the history file instead of standard output.
  • -u[n] โ€” Print to the file descriptor n (defaults to 1 if omitted) instead of standard output.

The -R option mostly emulates the BSD echo(1) command which does not expand backslashes and interprets its first argument as option only if it is exactly โ€œ-nโ€ (to suppress the trailing newline). ๐ŸŒธ

printf ๐ŸŒธ

printf format [arguments ...]

(optional, defer always) If it got compiled in, this one formats and prints the arguments, supporting the bare POSIX-mandated minimum and not a crumb more. And here's the tea: if an external utility of the same name is found, it gets deferred to โ€” unless you run it as a direct builtin call or from the builtin utility. ๐Ÿ’…

pushd ๐ŸŒ€

pushd [-lnv]

(dot.mkshrc function) Rotates the top two elements of the directory stack. The options are literally the same as for dirs (see above, bestie), and after acting, pushd changes to the topmost directory stack entry.

pushd [-lnv] +n

(dot.mkshrc function) Rotates the element number n all the way to the top. main character energy honestly โœจ

pushd [-lnv] name

(dot.mkshrc function) Pushes name on top of the stack.

pwd ๐Ÿ 

pwd [-LP]

(regular) Prints the present working directory. If you give it no options at all, pwd behaves as if the -P option (print physical path) was used when the physical shell option is set โ€” and as if -L (print logical path) was used otherwise.

The logical path is the path you used to cd to the current directory. The physical path is determined from the filesystem, by following .. directories to the root directory. No cap. ๐Ÿฆ‹

r ๐Ÿ”

r [-g] [old=new] [prefix]

(built-in alias) Same as fc -e - (see above). That's it, that's the whole vibe.

read ๐Ÿ“–

read [-A | -a] [-d x] [-N z | -n z] [-p | -u[n]] [-t n] [-rs] [p ...]

(regular) Reads a line of input, splits the input into fields using the IFS parameter (see โ€œSubstitutionโ€ above) or other specified means, and assigns each field to the specified parameters p.

If you specify no parameters, the REPLY parameter catches the result instead. If there are more parameters than fields, the extra parameters get set to the empty string or 0. If there are more fields than parameters, the last parameter gets assigned the remaining fields โ€” word separators and all. ๐Ÿ˜Œ

The options are as follows:

  • -A โ€” Store the result into the parameter p (or REPLY) as an array of words. Only no parameter or one parameter is accepted.
  • -a โ€” Store the result, without applying IFS word splitting, into the parameter p (or REPLY) as an array of characters (wide characters if the utf8-mode option is enacted, octets otherwise). The codepoints are encoded as decimal numbers by default. Only no parameter or one parameter is accepted.
  • -d x โ€” Use the first byte of x, NUL if empty, instead of the ASCII newline character to delimit input lines.
  • -N z โ€” Instead of reading till end-of-line, read exactly z bytes. Upon EOF, a partial read is returned with exit status 1. After a timeout, a partial read is returned with an exit status as if SIGALRM were caught.
  • -n z โ€” Instead of reading till end-of-line, read up to z bytes but return as soon as any bytes are read, e.g. from a slow terminal device, or if EOF or a timeout occurs.
  • -p โ€” Read from the currently active co-process (see โ€œCo-processesโ€ above for details) instead of from a file descriptor.
  • -u[n] โ€” Read from the file descriptor number n (defaults to 0, i.e. standard input). โš ๏ธ The argument must immediately follow the option character.
  • -t n โ€” Interrupt reading after n seconds (specified as a positive decimal value with an optional fractional part). The exit status of read is the same as if SIGALRM were caught if the timeout occurred, but partial reads may still be returned.
  • -r โ€” Normally, read strips backslash-newline sequences and any remaining backslashes from input. This option enables raw mode, in which backslashes are retained and ignored.
  • -s โ€” The input line is saved to the history. ๐Ÿ•ฐ๏ธ

If the input is a terminal, both the -N and -n options set it into raw mode; they read an entire file if -1 is passed as the z argument.

The first parameter may have a question mark and a string appended to it, in which case the string is used as a prompt (printed to standard error before any input is read) if the input is a tty(4). Like, for example: read nfoo?'number of foos: ' ๐Ÿ“

If no input is read, or a timeout occurred, read exits with a non-zero status. ๐Ÿ’€

readonly ๐Ÿ”’

readonly [-p] [parameter[=value] ...]

(keeps assignments, special, decl-util) Sets the read-only attribute of the named parameters. If values are given, the parameters are assigned these values before disallowing writes. And once a parameter is made read-only, it cannot be unset and its value cannot be changed. It's locked, periodt. ๐Ÿ‘‘

If you specify no parameters, the names of all parameters with the read-only attribute are printed, one per line โ€” unless the -p option is used, in which case readonly commands defining all read-only parameters, including their values, are printed.

realpath ๐Ÿ›ค๏ธ

realpath [--] name

(defer with flags) Resolves an absolute pathname corresponding to name. If the resolved pathname either exists or can be created immediately, realpath returns 0 and prints the resolved pathname. Otherwise, or if an error occurs, it issues a diagnostic and returns nonzero.

Oh and heads up: if name ends with a slash (/), resolving to an extant non-directory is also treated as an error. ๐Ÿ™ˆ

rename ๐Ÿ“›

rename [--] from to

(defer always, needs rename(2)) Renames the file from to to. Both pathnames must be on the same device. This one is intended for emergency situations (like when /bin/mv becomes unusable); it's a thin syscall wrapper, no extras. ๐Ÿšจ

return โ†ฉ๏ธ

return [status]

(keeps assignments, special) Returns from a function or . script with errorlevel status. If no status is given, the exit status of the last executed command is used. If you use it outside of a function or . script, it has the same effect as exit. ๐Ÿ’…

Note: mksh treats both profile and ENV files as . scripts, while the original Korn shell only treated profiles as . scripts.

rot13 ๐Ÿ”

(dot.mkshrc alias) ROT13-encrypts/-decrypts stdin to stdout. Yes bestie, that's the whole thing. Iconic.

set ๐ŸŽ›๏ธ

set [-+abCefhkmnpsUuvXx] [-+o option] [-+A name] [--] [argument ...]
set -- [argument ...]
set -+o

(keeps assignments, special) The set command can be used to show all shell parameters (like typeset -), set (-) or clear (+) shell options, set an array parameter, or set the positional parameters.

Options can be changed using the -+o option syntax, where option is the long name of an option, or using the -+letter syntax, where letter is the option's single letter name (heads up: not all options have both names). Here's the whole table โ€” short and long names (if extant) plus what each option actually does: โœจ

  • -A name โ€” Sets the elements of the array parameter name to argument ...

If -A is used, the array is reset (i.e. emptied) first; if +A is used, the first N elements are set (where N is the number of arguments) and the rest are left untouched. If name ends with a +, the array is appended to instead.

An alternative syntax for the command set -A foo -- a b c; set -A foo+ -- d e โ€” which is compatible with GNU bash and also supported by AT&T UNIX ksh93 โ€” is: foo=(a b c); foo+=(d e)

  • -a | -o allexport โ€” Make all variables assigned to while enabled as exported.
  • -b | -o notify โ€” Print job notification messages asynchronously instead of just before the prompt. Only used with job control (-m).
  • -C | -o noclobber โ€” Prevent > redirection from overwriting existing files; >| must be used to force overwriting instead.
Note: This is not safe to use for creation of temporary files or lockfiles due to a TOCTOU in a check allowing one to redirect output to /dev/null or other device files even in noclobber mode. Be careful bestie ๐Ÿ˜ญ
  • -c โ€” Commands are read from an argument string. Can only be used when the shell is invoked.
  • -e | -o errexit โ€” Exit (after executing the ERR trap) as soon as an error occurs or a command fails (i.e. exits with a non-zero status). This does not apply to commands whose exit status is explicitly tested by a shell construct such as !, if, until or while statements. For &&, || and pipelines (but mind -o pipefail), only the status of the last command is tested.
  • -f | -o noglob โ€” Do not expand file name patterns.
  • -h | -o trackall โ€” Create tracked aliases for all executed commands (see โ€œAliasesโ€ above). Enabled by default for non-interactive shells.
  • -i | -o interactive โ€” The shell is an interactive shell. This option can only be used when the shell is invoked. See above for details.
  • -k | -o keyword โ€” Parameter assignments are recognised anywhere in a command.
  • -l | -o login โ€” The shell is a login shell. This option can only be used when the shell is invoked. See above for what this means.
  • -m | -o monitor โ€” Enable job control (default for interactive shells).
  • -n | -o noexec โ€” Do not execute any commands. Useful for checking the syntax of scripts. Ignored if reading commands from a tty.
  • -p | -o privileged โ€” The shell is a privileged shell. It is set automatically if, when the shell starts, the real UID or GID does not match the effective UID (EUID) or GID (EGID), respectively. See above for a description of what this means.

If the shell is privileged, setting this flag after startup file processing lets it go full setuid and/or setgid. Clearing the flag makes the shell drop privileges. Changing this flag resets the supplementary groups vector.

  • -r | -o restricted โ€” The shell is a restricted shell. This option can only be used when the shell is invoked. See above for what this means.
  • -s | -o stdin โ€” If used when the shell is invoked, commands are read from standard input. Set automatically if the shell is invoked with no arguments.

When -s is used with the set command it causes the specified arguments to be sorted ASCIIbetically before assigning them to the positional parameters (or to array name, with -A).

  • -U, -o utf8-mode โ€” turns ON UTF-8 support in โ€œEmacs editing modeโ€ and in the internal string handling functions ๐Ÿ’–. This flag is off by default, but you can switch it on right there on the shell command line. It also flips itself on automatically for interactive shells when the POSIX locale uses the UTF-8 codeset, or โ€” if there's no POSIX locale to speak of โ€” when the LC_ALL, LC_CTYPE or LANG environment variables either case-insensitively equal โ€œUTF-8โ€ or โ€œutf8โ€, or carry that as a codeset modifier. โœจ

This build of the shell does baroque locale tracking, meaning set -+U gets changed whenever one of the POSIX locale-related environment variables changes. It's giving watcher energy, honestly. ๐Ÿ‘€

  • -u, -o nounset โ€” referencing an unset parameter is treated as an error, except for โ€œ$@โ€ or โ€œ$*โ€, and unless one of the โ€˜-โ€™, โ€˜+โ€™ or โ€˜=โ€™ modifiers is in play. no cap, touching unset vars is a whole crime here ๐Ÿšซ
  • -v, -o verbose โ€” write shell input to standard error as it's read. she's narrating her own life ๐Ÿ“
  • -X, -o markdirs โ€” mark directories with a trailing โ€˜/โ€™ during globbing โœจ
  • -x, -o xtrace โ€” print commands when they're executed, preceded by PS4. the receipts, bestie ๐Ÿงพ
  • -o asis โ€” when quoting output, if you're not in EBCDIC mode and utf8-mode is disabled, show C1 control characters โ€œas isโ€ โ€” like, don't escape them. Use this with codepages where the range 0x80..0x9F contains printable characters (such as 437, 850, 1252, etc. โ€” but not the ISO 8859 series, for example). ๐Ÿ’…
  • -o bgnice โ€” background jobs get run with lower priority ๐ŸŒ
  • -o braceexpand โ€” enable brace expansion. On by default, queen behavior ๐Ÿ‘‘
  • -o emacs โ€” enable BRL emacs-like command-line editing (interactive shells only); see โ€œEmacs editing modeโ€. On by default ๐Ÿ’–
  • -o gmacs โ€” enable gmacs-like command-line editing (interactive shells only). Currently identical to emacs editing except that transpose-chars (^T) acts slightly differently. that's it, that's the whole difference ๐Ÿ™ˆ
  • -o ignoreeof โ€” the shell will not (easily) exit when end-of-file is read; you have to use exit. And to avoid infinite loops, the shell will exit if EOF is read 13 times in a row. thirteen. thirteen chances bestie ๐Ÿ˜ญ๐Ÿ’€
  • -o inherit-xtrace โ€” do not reset -o xtrace upon entering functions (default).
  • -o nohup โ€” do not kill running jobs with a SIGHUP signal when a login shell exits. Currently enabled by default.
  • -o nolog โ€” no effect. In the original Korn shell, this prevented function definitions from being stored in the history file. so... it's a vibe, not a feature ๐Ÿซ 
  • -o physical โ€” makes the cd and pwd commands use โ€œphysicalโ€ (i.e. the filesystem's) โ€œ..โ€ directories instead of โ€œlogicalโ€ directories (i.e. the shell handles โ€œ..โ€, which lets the user stay blissfully oblivious to symbolic links to directories). Clear by default. Note that setting this option does not affect the current value of the PWD parameter; only the cd command changes PWD. See cd and pwd above for more details. ๐Ÿฆ‹
  • -o pipefail โ€” make the exit status of a pipeline the rightmost non-zero errorlevel, or zero if all commands exited with zero. finally, pipeline truth ๐Ÿซถ
  • -o posix โ€” behave closer to the standards (see โ€œPOSIX modeโ€ for details). Automatically enabled if the shell invocation basename, after โ€˜-โ€™ and โ€˜rโ€™ processing, begins with โ€œshโ€ โ€” and (often used for the lksh binary) this autodetection feature is compiled in. As a side effect, setting this flag turns off the braceexpand flag, which you can turn back on manually, and (unless both are set in the same command) sh mode. โš ๏ธ side effects abound, keep an eye on her
  • -o sh โ€” enable kludge /bin/sh compatibility mode (see โ€œSH modeโ€ below for details). Automatically enabled if the basename of the shell invocation, after โ€˜-โ€™ and โ€˜rโ€™ processing, begins with โ€œshโ€ and this autodetection feature is compiled in (rather uncommon). As a side effect, setting this flag turns off the braceexpand flag, which can be turned back on manually, and posix mode (unless both are set in the same command). ๐Ÿ’ซ
  • -o vi โ€” enable vi(1)-like command-line editing (interactive shells only). See โ€œVi editing modeโ€ for documentation and limitations. ๐ŸŒˆ
  • -o vi-esccomplete โ€” in vi command-line editing, do command and file name completion when Esc (^[) is entered in command mode. ๐Ÿงš
  • -o vi-tabcomplete โ€” in vi command-line editing, do command and file name completion when Tab (^I) is entered in insert mode (default). โœจ
  • -o viraw โ€” no effect. In the original Korn shell, unless viraw was set, the vi command-line mode would let the tty(4) driver do the work until Esc was entered. mksh is always in viraw mode. iconic, she was born that way ๐Ÿ’…

These options can also be used upon invocation of the shell. The current set of options with single letter names can be found in the parameter โ€œ$-โ€. set -o with no option name will list all the options and whether each one is on or off; set +o prints a command to restore the current option set, using the internal set -o .reset construct, which is an implementation detail โ€” these commands are transient (only valid within the current shell session). ๐Ÿซง

A lone โ€œ-โ€ clears both the -v and -x options (obsolete); it (or a lone โ€œ+โ€) terminates option processing and is otherwise ignored. ๐Ÿ’€

Remaining arguments, if any, are assigned (in order, unless -s is given) to name (with -A) or the positional parameters (i.e., $1, $2, etc). Use -- if the first argument begins with plus or dash. If options end with โ€œ--โ€ and there are no remaining arguments, all positional parameters are cleared. If no options or arguments are given, the values of all parameters are printed (suitably quoted). ๐Ÿ“‹

๐Ÿ’– setenv ๐Ÿ’–

setenv [name [value]]

(dot.mkshrc function) Without arguments, display the names and values of all exported parameters. Otherwise, set name's export attribute, and its value to value (empty string if none given). she's export with a glow-up โœจ

๐Ÿ’– shift ๐Ÿ’–

shift [number]

(keeps assignments, special) The positional parameters number+1, number+2, etc. (number defaults to 1) are renamed to 1, 2, etc. sliding down the list like ๐Ÿ’…

๐Ÿ’– smores ๐Ÿ’–

smores [file ...]

(dot.mkshrc function) Simple pager: Enter next; โ€˜qโ€™+Enter quit. yes, it's named after the snack, she contains multitudes ๐Ÿ“

๐Ÿ’– source ๐Ÿ’–

source file [arg ...]

(keeps assignments) Like . (โ€œdotโ€), except that the current working directory is appended to the search path. (GNU bash extension) ๐ŸŒธ

๐Ÿ’– suspend ๐Ÿ’–

(needs job control and getsid(2)) Stops the shell as if it had received the suspend character from the terminal. ๐Ÿ˜ด

It is not possible to suspend a login shell unless the parent process is a member of the same terminal session but is a member of a different process group. As a general rule, if the shell was started by another shell or via su(1), it can be suspended. ๐ŸŽ€

๐Ÿ’– test ๐Ÿ’–

test expression
[ expression ]

(regular) test evaluates the expression and exits with status code 0 if true, 1 if false, or greater than 1 if there was an error. It is often used as the condition command of if and while statements. All file expressions, except -h and -L, follow symbolic links. ๐Ÿ”

The following basic expressions are available:

  • -a file โ€” file exists.
  • -b file โ€” file is a block special device.
  • -c file โ€” file is a character special device.
  • -d file โ€” file is a directory.
  • -e file โ€” file exists.
  • -f file โ€” file is a regular file.
  • -G file โ€” file's group is the shell's effective group ID.
  • -g file โ€” file's mode has the setgid bit set.
  • -H file โ€” file is a context dependent directory (only useful on HP-UX).
  • -h file โ€” file is a symbolic link.
  • -k file โ€” file's mode has the sticky(7) bit set.
  • -L file โ€” file is a symbolic link.
  • -O file โ€” file's owner is the shell's effective user ID.
  • -p file โ€” file is a named pipe (FIFO).
  • -r file โ€” file exists and is readable.
  • -S file โ€” file is a unix(4)-domain socket.
  • -s file โ€” file is not empty.
  • -t fd โ€” File descriptor fd is a tty(4) device.
  • -u file โ€” file's mode has the setuid bit set.
  • -w file โ€” file exists and is writable.
  • -x file โ€” file exists and is executable.
  • file1 -nt file2 โ€” file1 is newer than file2, or file1 exists and file2 does not.
  • file1 -ot file2 โ€” file1 is older than file2, or file2 exists and file1 does not.
  • file1 -ef file2 โ€” file1 is the same file as file2.
  • string โ€” string has non-zero length.
  • -n string โ€” string is not empty.
  • -z string โ€” string is empty.
  • -v name โ€” The shell parameter name is set.
  • -o option โ€” Shell option is set (see the set command above for a list of options). As a non-standard extension, if the option starts with a โ€˜!โ€™, the test is negated; the test always fails if option doesn't exist (so [ -o foo -o -o !foo ] returns true if and only if option foo exists). The same can be achieved with [ -o ?foo ] like in AT&T UNIX ksh93. option can also be the short flag prefixed with either โ€˜-โ€™ or โ€˜+โ€™ (no logical negation), for example โ€œ-xโ€ or โ€œ+xโ€ instead of โ€œxtraceโ€. ๐Ÿ”ฅ
  • string = string โ€” the two strings are equal, periodt โœจ. In double brackets, pattern matching happens (R59+ using extglobs) if the right-hand string isn't quoted.
  • string == string โ€” same thing as =, but deprecated bestie, so use = instead ๐Ÿ’….
  • string != string โ€” nope, not equal ๐Ÿ™…. See = above for the whole pattern matching situation.
  • string > string โ€” first string operand is greater than the second one.
  • string < string โ€” first string operand is less than the second one.
  • number -eq number โ€” numbers compare equal ๐Ÿ’–.
  • number -ne number โ€” numbers compare not equal.
  • number -ge number โ€” numbers compare greater than or equal.
  • number -gt number โ€” numbers compare greater than.
  • number -le number โ€” numbers compare less than or equal.
  • number -lt number โ€” numbers compare less than.

All those basic expressions above? Unary operators have precedence over binary operators, and you can vibe them together with these (listed in increasing order of precedence, so the strongest one goes last):

  • expr -o expr โ€” Logical OR.
  • expr -a expr โ€” Logical AND.
  • ! expr โ€” Logical NOT.
  • ( expr ) โ€” Grouping.

Lowkey important: a number can literally be an arithmetic expression, like a mathematical term or the name of an integer variable ๐Ÿง :

x=1; [ "x" -eq 1 ]      evaluates to true

Also note that some special rules kick in (courtesy of POSIX, of course ๐Ÿ™„) when the number of arguments to test or inside the brackets [ ... ] is less than five: if leading ! arguments can be stripped such that only one to three arguments remain, then the lowered comparison is executed; (thanks to XSI) parentheses \( ... \) lower four- and three-argument forms to two- and one-argument forms, respectively; three-argument forms ultimately prefer binary operations, followed by negation and parenthesis lowering; two- and four-argument forms prefer negation followed by parenthesis; the one-argument form always implies -n. To assume this is not necessarily portable ๐Ÿ‘€.

Note: a super common mistake is using if [ $foo = bar ], which fails if the parameter foo is empty or unset, if it has embedded spaces (i.e. IFS octets), or if it's a unary operator like ! or -n. Use tests like if [ x"$foo" = x"bar" ] instead, or the double-bracket construct (see [[ above): if [[ $foo = bar ]] or, to avoid pattern matching, if [[ $foo = "$bar" ]]. The [[ ... ]] construct is not only more secure to use but also often faster ๐Ÿ”ฅ. Similarly, operators need to be quoted as usual for test / [.

time [-p] [pipeline]

(reserved word) ๐Ÿ•ฐ๏ธ If you hand it a pipeline, the times used to execute that pipeline get reported. If you hand it nothing, then the user and system time used by the shell itself โ€” plus all the commands it has run since it started โ€” get reported.

The times reported are the real time (elapsed time from start to finish), the user CPU time (time spent running in user mode), and the system CPU time (time spent running in kernel mode) ๐Ÿ’ซ.

Times are reported to standard error, and the format of the output is:

0m0.03s real     0m0.02s user     0m0.01s system

If the -p option is given (which is only permitted if pipeline is a simple command), the output is slightly longer:

real     0.03
user     0.02
sys      0.01

Simple redirections of standard error do not affect time's output, bestie:

$ time sleep 1 2>afile
$ { time sleep 1; } 2>afile

Times for the first command do not go to afile, but those of the second command do. Main character behaviour tbh ๐ŸŒธ.


times

(keeps assignments, special) Print the accumulated user and system times (see above โฌ†๏ธ) used both by the shell and by processes that the shell started which have exited. The format of the output is:

0m0.01s 0m0.00s
0m0.04s 0m0.02s

trap n [signal ...]

(keeps assignments, special) If the first operand is a decimal unsigned integer, this resets all specified signals to the default action โ€” i.e. it's the same as calling trap with a dash (-) as the handler, followed by the arguments (interpreted as signals).

trap handler signal ...

(keeps assignments, special) Sets a trap handler that gets run when any of the specified signals are received ๐Ÿšจ. handler is either an empty string, meaning the signals are to be ignored, a dash (-), meaning that the default action is to be taken for the signals (see signal(3)), or a string made of shell commands to be executed at the first opportunity (i.e. when the current command completes or before printing the next PS1 prompt) after one of the signals is received. signal is the name, possibly prefixed with SIG, of a signal (e.g. PIPE, ALRM or SIGINT) or the number of the signal (see the kill -l command above) ๐Ÿ’–.

There are two special signals: EXIT (also known as 0), which runs when the shell is about to exit, and ERR, which runs after an error occurs; an error is something that would cause the shell to exit if the set -e or set -o errexit option were set. EXIT handlers are executed in the environment of the last executed command. The original Korn shell's DEBUG trap and handling of ERR and EXIT in functions are not yet implemented ๐Ÿ‘‘.

Note that, for non-interactive shells, the trap handler cannot be changed for signals that were ignored when the shell started. That's just how it is ๐Ÿ™ˆ.

trap [-p [signal ...]]

With no arguments, the current state of the traps that have been set since the shell started is shown as a series of trap commands โœจ. With -p, the state of all traps (or all listed traps) is shown, not just modified traps; this is useful to save and later restore the current state of traps.

Heads up: the output of trap cannot be easily captured or piped to another process (an artifact of the fact that traps are cleared when subprocesses are created ๐Ÿ˜ญ). Using ksh93-style funsubs works though:
save_traps=${ trap -p;}
# ...
eval "$save_traps"

SIGKILL, SIGSTOP and some OS-specific signals (such as 32 and 33 on GNU/Linux) cannot be trapped ๐Ÿšซ. Attempts are normally silently ignored; the output of trap -p does not necessarily reflect this.


true

(regular) A command that exits with a zero status. Just vibes, no output, immaculate โœ….


type name ...

(built-in alias) Reveal how name would be interpreted as a command ๐Ÿ”.


typeset [-+aglpnrtUux] [-L[n] | -R[n] | -Z[n]] [-i[n]] [name[=value] ...]

typeset -f [-tux] [name ...]

(keeps assignments, decl-util) Display or set attributes of shell parameters or functions ๐ŸŽ€. With no name arguments, parameter attributes are shown; if no options are used, the current attributes of all parameters are printed as typeset commands; if an option is given (or - with no option letter), all parameters and their values with the specified attributes are printed; if options are introduced with + (or + alone), only names are printed.

If any name arguments are given, the attributes of the so named parameters are set (-) or cleared (+); inside a function, this will cause the parameters to be created (and set to "" if no value is given) in the local scope (except if -g is used). Values for parameters may optionally be specified. For name[*], the change affects all elements of the array, and no value may be specified โœจ.

When -f is used, typeset operates on the attributes of functions ๐Ÿ’…. As with parameters, if no name arguments are given, functions are listed with their values (i.e. definitions) unless options are introduced with +, in which case only the names are displayed.

  • -a โ€” Indexed array attribute.
  • -f โ€” Function mode. Display or set shell functions and their attributes, instead of shell parameters.
  • -g โ€” "global" mode. Do not cause named parameters to be created in the local scope when called inside a function ๐Ÿ‘‘.
  • -i[n] โ€” Integer attribute. n specifies the base to use when stringifying the integer (if not specified, the base given in the first assignment is used). Parameters with this attribute may be assigned arithmetic expressions for values.
  • -L[n] โ€” Left justify attribute. n specifies the field width. If n is not specified, the current width of the parameter (or the width of its first assigned value) is used. Leading whitespace (and digit zeros, if used with the -Z option) is stripped. If necessary, values are either truncated or padded with space to fit the field width.
  • -l โ€” Lower case attribute. All upper case ASCII characters in values are converted to lower case. (In the original Korn shell, this parameter meant "long integer" when used with the -i option ๐Ÿซ .)
  • -n โ€” Create a bound variable (name reference): any access to the variable name will access the variable value in the current scope (this is different from AT&T UNIX ksh93!) instead ๐Ÿ˜ณ. Also different from AT&T UNIX ksh93 is that value is lazily evaluated at the time name is accessed. This can be used by functions to access variables whose names are passed as parameters, instead of resorting to eval.
  • -p โ€” Print complete typeset commands that can be used to re-create the attributes and values of parameters.
  • -R[n] โ€” Right justify attribute. n specifies the field width. If n is not specified, the current width of the parameter (or the width of its first assigned value) is used. Trailing whitespace is stripped. If necessary, values are either stripped of leading characters or padded with space to fit the field width.
  • -r โ€” the read-only attribute ๐Ÿ’… parameters rockin' this one cannot be assigned to and cannot be unset. And once you turn it on?? That's it, bestie, it is never coming off. no takebacks.
  • -t โ€” the tag attribute โœจ for parameters it literally means nothing to the shell, it's just there so your app can use it.

For functions tho, -t is the trace attribute ๐Ÿฆ‹ โ€” when a function with the trace attribute runs, the -o xtrace (-x) shell option gets temporarily turned on. it's giving "watch me work".

  • -U โ€” the unsigned integer attribute. integers get printed as unsigned values (it's combined with the -i option).
  • -u โ€” the upper case attribute ๐ŸŒธ all lower case ASCII characters in values get converted to UPPER CASE. (fun fact: in the original Korn shell this parameter meant "unsigned integer" when used with -i, which meant upper case letters would never be used for bases greater than 10. see -U above, she's the upgraded version.)

For functions, -u is the undefined attribute, used with FPATH. check "Functions" above for what that actually implies ๐Ÿ‘€

  • -x โ€” the export attribute. parameters get dropped into the environment of any executed commands. functions cannot be exported tho, for security reasons ("shellshock") ๐Ÿ’€
  • -Z[n] โ€” the zero fill attribute. if it's not combined with -L, it's the same as -R, except zero padding is used instead of space padding. for integers, it's the number that gets padded, not the base โญ
okay important one: if any of the -i, -L, -l, -R, -U, -u or -Z options are changed, all the others from that set get cleared โ€” unless you also gave them on the same command line. so don't come crying to me when half your attributes ghosted you ๐Ÿ™ˆ

๐Ÿ’– ulimit ๐Ÿ’–

ulimit [-aBCcdefHilMmnOPpqrSsTtVvwx] [value]

(regular) Display or set process limits ๐Ÿ‘‘ If you don't use any options, the file size limit (-f) is assumed. value, if you give one, can be either an arithmetic expression or the word "unlimited". These limits affect the shell and any processes the shell creates after a limit is imposed. heads up: systems may not let you increase some limits once they're set ๐Ÿ˜ญ Also note that which limits even exist is system dependent โ€” some systems have only the -f limit, or not even that, or can only set the soft limits, etc. so vibe check your OS first.

  • -a โ€” display all limits (soft limits unless you use -H).
  • -B n โ€” set the socket buffer size to n kibibytes.
  • -C n โ€” set the number of cached threads to n.
  • -c n โ€” impose a size limit of n blocks on the size of core dumps. silently ignored if the system doesn't support this limit ๐Ÿคทโ€โ™€๏ธ
  • -d n โ€” limit the size of the data area to n kibibytes. on some systems, read-only maximum brk(2) size minus etext.
  • -e n โ€” set the maximum niceness to n.
  • -f n โ€” impose a size limit of n blocks on files written by the shell and its child processes (any size may be read).
  • -H โ€” set the hard limit only (the default is to set both hard and soft limits). with -a, display all hard limits.
  • -i n โ€” set the number of pending signals to n.
  • -l n โ€” impose a limit of n kibibytes on the amount of locked (wired) physical memory.
  • -M n โ€” set the AIO locked memory to n kibibytes.
  • -m n โ€” impose a limit of n kibibytes on the amount of physical memory used.
  • -n n โ€” impose a limit of n file descriptors that can be open at once. on some systems attempts to set this are silently ignored ๐Ÿ™ˆ
  • -O n โ€” set the number of AIO operations to n.
  • -P n โ€” limit the number of threads per process to n.

this option mostly matches AT&T UNIX ksh93's -T; on AIX, check -r as used by its ksh tho.

  • -p n โ€” impose a limit of n processes that can be run by the user (uid) at any one time.
  • -q n โ€” limit the size of POSIX message queues to n bytes.
  • -R n โ€” (Linux) limit the CPU time slice a real-time process can use before performing a blocking syscall to n milliseconds.
  • -r n โ€” (AIX) limit the number of threads per process to n. (Linux) set the maximum real-time priority to n.
  • -S โ€” set the soft limit only (the default is to set both hard and soft limits). with -a, display soft limits (that's the default).
  • -s n โ€” limit the size of the stack area to n kibibytes.
  • -T n โ€” impose a time limit of n real seconds ("humantime") to be used by each process.
  • -t n โ€” impose a time limit of n CPU seconds spent in user mode to be used by each process.
  • -V n โ€” set the number of vnode monitors on Haiku to n.
  • -v n โ€” impose a limit of n kibibytes on the amount of virtual memory (address space) used.
  • -w n โ€” limit the amount of swap space used to at most n kibibytes.
  • -x n โ€” set the maximum number of file locks to n.
and as far as ulimit is concerned?? a block is 512 bytes. periodt.

๐ŸŽ€ umask ๐ŸŽ€

umask [-S] [mask]

(regular) Display or set the file permission creation mask, aka the umask (see umask(2)). if you use the -S option, the mask you display or set is symbolic; otherwise it's an octal number.

Symbolic masks are like the ones chmod(1) uses ๐Ÿ’… When you use them, they describe what permissions may be made available (as opposed to octal masks, where a set bit means the corresponding bit is to be cleared). for example, ug=rwx,o= sets the mask so files will not be readable, writable or executable by "others", and it's equivalent (on most systems) to the octal mask 007. iconic behavior honestly.


๐ŸŒˆ unalias ๐ŸŒˆ

unalias [-adt] [name ...]

(regular) The aliases for the given names get removed ๐Ÿซถ if you use the -a option, all aliases get removed. if you use the -t or -d options, the indicated operations are carried out on tracked or directory aliases, respectively.


๐Ÿฆ‹ unset ๐Ÿฆ‹

unset [-fv] parameter ...

(keeps assignments, special) Unset the named parameters (-v, the default) or functions (-f). with parameter[*], attributes are retained and only values get unset. the exit status is non-zero if any of the parameters are read-only, zero otherwise โ€” and btw that part is not portable ๐Ÿ‘€


โœจ wait โœจ

wait [job ...]

(regular) Wait for the specified job(s) to finish โณ the exit status of wait is that of the last specified job; if the last job is killed by a signal, the exit status is 128 + the signal number (see kill -l exit-status above); if the last specified job can't be found (because it never existed or had already finished), the exit status is 127. see "Job control" below for the format of job. wait will return if a signal for which a trap has been set is received, or if a SIGHUP, SIGINT or SIGQUIT signal is received.

If no jobs are specified, wait waits for all currently running jobs (if any) to finish and exits with a zero status. if job monitoring is enabled, the completion status of jobs gets printed โ€” and no, that is not the case when jobs are explicitly specified ๐Ÿ™…โ€โ™€๏ธ


๐Ÿ‘‘ whence ๐Ÿ‘‘

whence [-pv] [name ...]

(regular) Without the -v option, it's the same as command -v, except aliases are printed as their definition only. with the -v option, it's exactly identical to command -V. either way, with the -p option the search is restricted to the (current) PATH.


๐Ÿ“ which ๐Ÿ“

which [-a] [name ...]

(dot.mkshrc function) Without -a, it behaves like whence -p (does a PATH search for each name, printing the resulting pathname if found); with -a, matches in all PATH components are printed, i.e. the search isn't stopped after a match ๐Ÿ”ฅ if no name was matched, the exit status is 2; if every name was matched, it's zero; otherwise it's 1. and no diagnostics are produced on failure to match. silent treatment fr fr.


๐Ÿ’œ Job control ๐Ÿ’œ

Job control refers to the shell's ability to monitor and control jobs, which are processes or groups of processes created for commands or pipelines. At a minimum, the shell keeps track of the status of the background (i.e. asynchronous) jobs that currently exist; you can display this info with the jobs commands ๐Ÿ“‹ if job control is fully enabled (using set -m or set -o monitor), like it is for interactive shells, the processes of a job get placed in their own process group. foreground jobs can be stopped by typing the suspend character from the terminal (normally ^Z); jobs can be restarted in either the foreground or background using the commands fg and bg ๐Ÿƒโ€โ™€๏ธ

Note that only commands that create processes (e.g. asynchronous commands, subshell commands and non-built-in, non-function commands) can be stopped; commands like read cannot be. sorry bestie, that one's just not stoppable ๐Ÿ˜ญ

When a job is created, it's assigned a job number. for interactive shells, this number is printed inside "[...]", followed by the process IDs of the processes in the job, whenever an asynchronous command is run. A job may be referred to in the bg, fg, jobs, kill and wait commands either by the process ID of the last process in the command pipeline (as stored in the $! parameter) or by prefixing the job number with a percent sign (%). other percent sequences can also be used to refer to jobs:

  • %+ | %% | % โ€” the most recently stopped job or, if there are no stopped jobs, the oldest running job. main character of the job list ๐Ÿ’…
  • %- โ€” the job that would be the %+ job if the latter did not exist. (so, the runner-up ๐ŸŒธ)
  • %n โ€” the job with job number n.
  • %?string โ€” the job with its command containing the string string (an error occurs if multiple jobs are matched).
  • %string โ€” the job with its command starting with the string string (an error occurs if multiple jobs are matched).

When a job changes state (e.g. a background job finishes or a foreground job is stopped), the shell prints the following status information:

[number] flag status command

where...

number โ€” is the job number of the job;

flag โ€” is the + or - character if the job is the %+ or %- job, respectively, or a space if it's neither;

status โ€” indicates the current state of the job and can be:

  • Done [number] โ€” the job exited. number is the exit status of the job, which is omitted if the status is zero.
  • Running โ€” the job has neither stopped nor exited. (And bestie, heads up: "running" does not necessarily mean it's consuming CPU time โ€” the process could be blocked, waiting for some event.) ๐Ÿซถ
  • Stopped [signal] โ€” the job was stopped by the signal it names. If no signal is given there, then the job was stopped by SIGTSTP.
  • signal-description ["core dumped"] โ€” the job was killed by a signal (memory fault, hangup, that whole genre); use kill -l for a list of signal descriptions. The "core dumped" message means the process created a core file. ๐Ÿ’€
  • command is the command that created the process. If there are multiple processes in the job, each process will have a line showing its command and possibly its status, if it is different from the status of the previous process.

When you try to exit the shell while there are jobs in the stopped state, the shell warns you that there are stopped jobs and does not exit. But if you immediately try to exit again? The stopped jobs get sent a SIGHUP and the shell exits. ๐Ÿ˜ญ Same energy with login shells: if the nohup option is not set and there are running jobs when you attempt to exit a login shell, the shell warns you and does not exit โ€” and if you immediately try again, those running jobs get a SIGHUP and the shell exits.

๐Ÿ’ป Terminal state

The state of the controlling terminal can be modified by a command executed in the foreground, whether or not job control is enabled โ€” but the modified terminal state is only kept past the job's lifetime, and used for later command invocations, if the command exits successfully (i.e. with an exit status of 0). When such a job is momentarily stopped or restarted, the terminal state is saved and restored, respectively, but it will not be kept afterwards. And in interactive mode, when line editing is enabled, the terminal state is saved before being reconfigured by the shell for the line editor, then restored before running a command. Obsessed with the tidiness actually. โœจ

๐Ÿงพ POSIX mode

Entering set -o posix mode will cause mksh to behave even more POSIX compliant in places where the defaults or opinions differ. Note that mksh will still operate with unsigned 32-bit arithmetic; use lksh if arithmetic on the host long data type โ€” complete with ISO C Undefined Behaviour โ€” is required; refer to the lksh(1) manual page for details. Most other historic, AT&T UNIX ksh-compatible or opinionated differences can be disabled by using this mode; these are:

  • The incompatible GNU bash I/O redirection &>file is not supported.
  • File descriptors created by I/O redirections are inherited by child processes.
  • Numbers with a leading digit zero are interpreted as octal.
  • The echo builtin does not interpret backslashes and only supports the exact option -n.
  • Alias expansion with a trailing space only reruns on command words.
  • Tilde expansion follows POSIX instead of Korn shell rules.
  • The exit status of fg is always 0.
  • kill -l only lists signal names, all in one line.
  • getopts does not accept options with a leading โ€˜+โ€™.
  • exec skips builtins, functions and other commands and uses a PATH search to determine the utility to execute. Interactive shells do not exit on failure (note: the execution environment may be tainted).

๐Ÿš SH mode

Compatibility mode; intended for use with legacy scripts that cannot easily be fixed; the changes are as follows:

  • The incompatible GNU bash I/O redirection &>file is not supported.
  • File descriptors created by I/O redirections are inherited by child processes.
  • The echo builtin does not interpret backslashes and only supports the exact option -n, unless built with -DMKSH_MIDNIGHTBSD01ASH_COMPAT.
  • The substitution operations ${x#pat}, ${x##pat}, ${x%pat}, and ${x%%pat} wrongly do not require a parenthesis to be escaped and do not parse extglobs.
  • The getopt construct from lksh(1) passes through the errorlevel.
  • sh -c eats a leading -- if built with -DMKSH_MIDNIGHTBSD01ASH_COMPAT.

๐ŸŽ€ Interactive input line editing

The shell supports three modes of reading command lines from a tty(4) in an interactive session, controlled by the emacs, gmacs and vi options (at most one of these can be set at once). The default is emacs. Editing modes can be set explicitly using the set built-in. If none of these options are enabled, the shell simply reads lines using the normal tty(4) driver. If the emacs or gmacs option is set, the shell allows emacs-like editing of the command; similarly, if the vi option is set, the shell allows vi-like editing of the command. These modes are described in detail in the following sections.

In these editing modes, if a line is longer than the screen width (see the COLUMNS parameter), a โ€˜>โ€™, โ€˜+โ€™ or โ€˜<โ€™ character is displayed in the last column indicating that there are more characters after, before and after, or before the current position, respectively. The line is scrolled horizontally as necessary. ๐ŸŒˆ

Completed lines are pushed into the history, unless they begin with an IFS octet or IFS white space or are the same as the previous line.

๐ŸŒธ Emacs editing mode

When the emacs option is set, interactive input line editing is enabled. Warning: This mode is slightly different from the emacs mode in the original Korn shell. In this mode, various editing commands (typically bound to one or more control characters) cause immediate actions without waiting for a newline. Several editing commands are bound to particular control characters when the shell is invoked; these bindings can be changed using the bind command.

The following is a list of available editing commands. Each description starts with the name of the command, suffixed with a colon; a [n] (if the command can be prefixed with a count); and any keys the command is bound to by default, written using caret notation (e.g. the ASCII Esc character is written as ^[) or terminal-specific indications. A count prefix for a command is entered using the sequence ^[n, where n is one or more digits. Unless otherwise specified, if a count is omitted, it defaults to 1.

Bigwords, as used below, are separated by spaces or tabs; words consist of alphanumerics, underscore (โ€˜_โ€™) or dollar sign (โ€˜$โ€™) characters.

Note that editing command names are used only with the bind command. Furthermore, many editing commands are useful only on terminals with a visible cursor. The user's tty(4) characters (e.g. ERASE) are bound to reasonable substitutes and override the default bindings; their customary values are shown in parentheses below. The default bindings were chosen to resemble corresponding Emacs key bindings: ๐Ÿ’…
  • abort: INTR (^C), ^G โ€” Abort the current command, save it to the history, empty the line buffer and set the exit state to interrupted.
  • auto-insert: [n] โ€” (Most ordinary characters are bound to this command.) Simply causes the character to appear as literal input.
  • backward-bigword: [n] ^[B โ€” Moves the cursor backward to the beginning of the bigword.
  • backward-char: [n] ^B, ^XD, ANSI-CurLeft, PC-CurLeft โ€” Moves the cursor backward n characters.
  • backward-word: [n] ^[b, ANSI-Ctrl-CurLeft, ANSI-Alt-CurLeft โ€” Moves the cursor backward to the beginning of the word.
  • beginning-of-history: ^[< โ€” Moves to the beginning of the history.
  • beginning-of-line: ^A, ANSI-Home, PC-Home โ€” Moves the cursor to the beginning of the edited input line.
  • capitalise-bigword: [n] ^[C โ€” Uppercase the first character in the next n bigwords as below.
  • capitalise-word: [n] ^[c โ€” Uppercase the first ASCII character in the next n words, leaving the cursor past the end of the last word.
  • clear-screen: ^[^L โ€” Prints a compile-time configurable sequence to clear the screen and home the cursor, redraws the last line of the prompt string and the currently edited input line. The default sequence works for almost all standard terminals.
  • comment: ^[# โ€” If the current line does not begin with a comment character, one is added at the beginning of the line and the line is entered (as if return had been pressed); otherwise, the existing comment characters are removed and the cursor is placed at the beginning of the line. Does not update the mark.
  • complete: ^[^[ โ€” Automatically completes as much as is unique of the command name or the file name containing the cursor. If the entire remaining command or file name is unique, a space is printed after its completion, unless it is a directory name in which case โ€˜/โ€™ is appended. If there is no command or file name with the current partial word as its prefix, a bell character is output (usually causing a beep to be sounded).
  • complete-command: ^X^[ โ€” Automatically completes as much as is unique of the command name having the partial word up to the cursor as its prefix, as in the complete command above.
  • complete-file: ^[^X โ€” Automatically completes as much as is unique of the file name having the partial word up to the cursor as its prefix, as in the complete command described above.
  • complete-list: ^I, ^[= โ€” Complete as much as is possible of the current word and list the possible completions for it. If only one completion is possible, match as in the complete command above. Note that ^I is usually generated by the Tab (tabulator) key.
  • delete-bigword-backward: [n] ^[H โ€” Deletes n bigwords before the cursor.
  • delete-bigword-forward: [n] ^[D โ€” Deletes characters after the cursor up to the end of n bigwords.
  • delete-char-backward: [n] ERASE (^H), ^?, ^H โ€” Deletes n characters before the cursor.
  • delete-char-forward: [n] ANSI-Del, PC-Del โ€” Deletes n characters after the cursor.
  • delete-word-backward: [n] Pfx1+ERASE (^[^H), WERASE (^W), ^[^?, ^[^H, ^[h โ€” Deletes n words before the cursor.
  • delete-word-forward: [n] ^[d โ€” Deletes characters after the cursor up to the end of n words.
  • down-history: [n] ^N, ^XB, ANSI-CurDown, PC-CurDown โ€” Scrolls the history buffer forward n lines (later). Each input line originally starts just after the last entry in the history buffer, so down-history is not useful until either search-history, search-history-up or up-history has been performed.
  • downcase-bigword ([n] ^[L) โ€” lowercases the next n bigwords. no shouting bestie ๐Ÿ’…
  • downcase-word ([n] ^[l) โ€” lowercases the next n words.
  • edit-line ([n] ^Xe) โ€” internally runs fc -e "${VISUAL:-${EDITOR:-vi}}" -- n against a temp script file so you can edit line n by hand (or the current line if you don't give an n), and then โ€” unless the editor you launched exits nonzero โ€” it runs the resulting script as if you'd typed it right there on the command line. note that this happens even if the script didn't actually change, and both the edited/resulting line AND the original line get dropped onto history. girl keeps the receipts ๐Ÿ“
  • end-of-history (^[>) โ€” teleports to the end of the history. the very end. periodt.
  • end-of-line (^E, ANSI-End, PC-End) โ€” cursor to the end of the input line.
  • eot (^_) โ€” acts as an end-of-file. lowkey useful because edit-mode input disables normal terminal input canonicalisation. โœจ
  • eot-or-delete ([n] EOF (^D)) โ€” if you're all alone on a line, it's the same as eot; otherwise it's delete-char-forward.
  • error (not bound) โ€” errors out. aka rings the bell. ๐Ÿ’€
  • evaluate-region (^[^E) โ€” evaluates the text between the mark and the cursor position (or the entire line if there's no mark) as function substitution. if it can't be parsed, the editing state stays exactly as it was and the bell rings to tell you it's mad. $? gets updated accordingly. ๐Ÿ‘‘
  • exchange-point-and-mark (^X^X) โ€” puts the cursor where the mark is and sets the mark to where the cursor used to be. swapping besties ๐Ÿซถ
  • expand-file (^[*) โ€” appends a * to the current word and replaces the word with whatever you get from file globbing on it. if nothing matches the pattern: bell. ๐Ÿ””
  • forward-bigword ([n] ^[F) โ€” cursor forward to the end of the *n*th bigword.
  • forward-char ([n] ^F, ^XC, ANSI-CurRight, PC-CurRight) โ€” cursor forward n characters.
  • forward-word ([n] ^[f, ANSI-Ctrl-CurRight, ANSI-Alt-CurRight) โ€” cursor forward to the end of the *n*th word.
  • goto-history ([n] ^[g) โ€” goes straight to history number n. skipping the line fr.
  • kill-line (KILL (^U)) โ€” deletes the entire input line and unsets the mark.
  • kill-region (^[^W) โ€” deletes the input between the cursor and the mark. note: this used to be bound to ^W like in Emacs, except ^W is usually taken by WERASE, so it got moved. it's giving relocation era ๐Ÿ“ฆ
  • kill-to-eol ([n] ^K) โ€” with no n, deletes the input from the cursor to the end of the line. with an n, it deletes the characters between the cursor and column n.
  • list (^[?) โ€” prints a sorted, columnated list of the command names or file names (if any) that could complete the partial word containing the cursor. directory names get a / tacked onto them.
  • list-command (^X?) โ€” prints a sorted, columnated list of the command names (if any) that could complete the partial word containing the cursor.
  • list-file (^X^Y) โ€” prints a sorted, columnated list of the file names (if any) that could complete the partial word containing the cursor. file type indicators get appended just like under list above.
  • newline (^J, ^M) โ€” causes the current input line to be processed by the shell. your cursor can be anywhere on the line, no stress.
  • newline-and-next (^O) โ€” processes the current input line with the shell, and then the next line from history becomes the current line. this is only useful after an up-history, search-history or search-history-up. quotidian queen ๐Ÿ‘‘
  • no-op (QUIT (^\)) โ€” does absolutely nothing. main character energy of just existing โœจ
  • prefix-1 (^[) โ€” introduces a 2-character command sequence.
  • prefix-2 (^X, ^[[, ^[O) โ€” introduces a multi-character command sequence.
  • prefix-3 (^@) โ€” introduces a PC keyboard scancode.
  • prev-hist-bigword ([n] ^[., ^[_) โ€” with no count, the last bigword from the previous line gets inserted at the cursor; with a count, it's the *(n+1)*th bigword, and the mark is set to the beginning of the inserted word. invoke it over and over and the inserted text gets replaced by the matching bigword from the second-last, third-last, etc. line. time travel but make it text ๐Ÿฆ‹
  • quote (^^, ^V) โ€” the next character is taken literally instead of being read as an editing command.
  • quote-region (^[Q) โ€” escapes the text between the mark and the cursor position (or the entire line if there's no mark) into a shell command argument.
  • redraw (^L) โ€” reprints the last line of the prompt string and the current input line, on a new line.
  • search-character-backward ([n] ^[^]) โ€” search backward in the current line for the *n*th occurrence of the next character you type.
  • search-character-forward ([n] ^]) โ€” search forward in the current line for the *n*th occurrence of the next character you type.
  • search-history (^R) โ€” enter incremental search mode. the internal history list is searched backwards for commands matching your input. an initial ^ in the search string anchors the search at the beginning of the line. the escape key leaves search mode. other commands โ€” including sequences of escape as prefix-1 followed by a prefix-1 or prefix-2 key โ€” get executed after leaving search mode. the abort (^G) command restores the input line from before the search started. successive search-history commands keep searching backward to the next previous occurrence of the pattern. heads up: the history buffer only retains a finite number of lines, and the oldest ones get discarded as necessary. ๐Ÿ’€
  • search-history-down (ANSI-PgDn, PC-PgDn) โ€” search forwards (this command is only useful after an up-history, search-history-up or search-history) through the history buffer for commands whose beginning matches the portion of the input line before the cursor. on an empty line it does the same thing as down-history.
  • search-history-up (ANSI-PgUp, PC-PgUp) โ€” search backwards through the history buffer for commands whose beginning matches the portion of the input line before the cursor. on an empty line it does the same thing as up-history.
  • set-arg (^[0 .. ^[9) โ€” mapped to begin prefixing a count to a command. numbers era ๐Ÿ”ข
  • set-mark-command (^[ then Space) โ€” set the mark at the cursor position.
  • transpose-chars (^T) โ€” if you're at the end of line, or if the gmacs option is set, this exchanges the two previous characters; otherwise it exchanges the previous and current characters and moves the cursor one character to the right. swappy ๐Ÿ’…
  • up-history ([n] ^P, ^XA, ANSI-CurUp, PC-CurUp) โ€” scrolls the history buffer backward n lines (earlier).
  • upcase-bigword ([n] ^[U) โ€” uppercases the next n bigwords. SCREAMING ๐Ÿ’–
  • upcase-word ([n] ^[u) โ€” uppercases the next n words.
  • version (^[^V) โ€” displays the version of mksh. the current edit buffer is restored as soon as you press a key. that restoring keypress gets processed, unless it's a space.
  • vt100-hack (^[[1) โ€” mapped to internally represent some longer key sequences.
  • yank (^Y) โ€” inserts the most recently killed text string at the current cursor position.
  • yank-pop (^[y) โ€” immediately after a yank, replaces the inserted text string with the next previously killed text string.

tab completion escapes characters the same way as the following code:

print -nr -- "${x@/[\"-\$\&-*:-?[\\\`\{-\~${IFS-$' \t\n'}]/\\$KSH_MATCH}"

๐Ÿฆ‹ Vi editing mode ๐Ÿฆ‹

note: the vi command-line editing mode has not yet been brought up to the same quality and feature set as the emacs mode. it is 8-bit clean, but it specifically does not support UTF-8 or MBCS. bestie, it's giving work-in-progress ๐Ÿ’€

the vi command-line editor in mksh has basically the same commands as the vi(1) editor, with the following exceptions:

  • you start out in insert mode.
  • there are file name and command completion commands: =, \, *, ^X, ^E, ^F and, optionally, Tab and Esc.
  • the _ command is different (in mksh it's the last argument command; in vi(1) it goes to the start of the current line).
  • the / and G commands move in the opposite direction to the j command.
  • commands which don't make sense in a single line editor aren't available (e.g. screen movement commands and ex(1)-style colon (:) commands).

like vi(1), there are two modes: โ€œinsertโ€ mode and โ€œcommandโ€ mode. in insert mode, most characters are just simply put in the buffer at the current cursor position as you type them; but some characters are treated specially. in particular, the following characters come from the current tty(4) settings (see stty(1)) and keep their usual meaning (normal values in parentheses): kill (^U), erase (^?), werase (^W), eof (^D), intr (^C) and quit (^\). on top of all that, these characters are also treated specially in insert mode:

  • ^E โ€” command and file name enumeration (see below).
  • ^F โ€” command and file name completion (see below). used twice in a row, the list of possible completions gets displayed; used a third time, the completion is undone.
  • ^H โ€” erases previous character.
  • ^J | ^M โ€” end of line. the current line is read, parsed and executed by the shell.
  • ^V โ€” literal next. the next character typed isn't treated specially (great for inserting the very characters being described here ๐Ÿซถ).
  • ^X โ€” command and file name expansion (see below).
  • <Esc> โ€” puts the editor in command mode (see below).
  • <Tab> โ€” optional file name and command completion (see ^F above), enabled with set -o vi-tabcomplete.

in command mode, every character is interpreted as a command. characters that don't correspond to commands, illegal combinations of commands, or commands that can't be carried out โ€” all of them cause beeps. ๐Ÿ”” in the following command descriptions, an [n] means the command may be prefixed by a number (e.g. 10l moves right 10 characters); if no number prefix is used, n is assumed to be 1 unless otherwise specified. the term โ€œcurrent positionโ€ refers to the position between the cursor and the character preceding the cursor. a โ€œwordโ€ is a sequence of letters, digits and underscore characters, or a sequence of non-letter, non-digit, non-underscore and non-whitespace characters (e.g. ab2*&^ contains two words), and a โ€œbig-wordโ€ is a sequence of non-whitespace characters.

โœจ Special mksh vi commands โœจ

the following commands are not in, or are different from, the normal vi file editor:

  • [n]_ โ€” drops a space plus the nth big-word from the last command in your history right at the current position, then yeets you into insert mode โœจ if n isn't given, it's the last word that gets inserted.
  • # โ€” puts the comment character (#) at the start of the current line and hands the line back to the shell (it's literally the same as I#^J). periodt.
  • [n]g โ€” like G, except when n isn't specified it goes to the most recent remembered line.
  • [n]v โ€” internally runs this command, bestie:
fc -e "${VISUAL:-${EDITOR:-vi}}" -- n

on a temporary script file, so you interactively edit line n (or the current line if n isn't specified); then โ€” unless whatever editor got invoked exits nonzero, but even if the script wasn't changed โ€” it executes the resulting script as if you'd typed it straight onto the command line; both the edited (resulting) and original lines get added onto history. ๐Ÿ’…

  • * and ^X โ€” command or file name expansion gets applied to the current big-word (with a * appended if the word has no file globbing characters in it) โ€” and the big-word gets replaced with the resulting words. Okay so here's the vibe check: if the current big-word is the first one on the line, or it follows one of the characters ;, |, &, ( or ), and it does not contain a slash (/), then command expansion is what happens; otherwise it's file name expansion. Command expansion matches the big-word against all aliases, functions and built-in commands, plus any executable files it finds by searching the directories in the PATH parameter. File name expansion matches the big-word against the files in the current directory. After expansion, the cursor is placed just past the last word and the editor is in insert mode. iconic behavior ๐Ÿซถ
  • [n]\, [n]^F, [n]<Tab>, and [n]<Esc> โ€” command/file name completion. Replaces the current big-word with the longest unique match obtained after performing command and file name expansion. <Tab> is only recognised if the vi-tabcomplete option is set, while <Esc> is only recognised if the vi-esccomplete option is set (see set -o). If n is specified, the *n*th possible completion is selected (as reported by the command/file name enumeration command).
  • = and ^E โ€” command/file name enumeration. Lists all the commands or files that match the current big-word. no cap ๐Ÿ’€
  • ^V โ€” displays the version of mksh. The current edit buffer is restored as soon as a key is pressed. The restoring keypress is ignored.
  • @c โ€” macro expansion. Executes the commands found in the alias _c.

๐Ÿ›ผ Intra-line movement commands

  • [n]h and [n]^H โ€” move left n characters.
  • [n]l and [n]<space> โ€” move right n characters.
  • 0 โ€” move to column 0.
  • ^ โ€” move to the first non-whitespace character.
  • [n]| โ€” move to column n.
  • $ โ€” move to the last character.
  • [n]b โ€” move back n words.
  • [n]B โ€” move back n big-words.
  • [n]e โ€” move forward to the end of the word, n times.
  • [n]E โ€” move forward to the end of the big-word, n times.
  • [n]w โ€” move forward n words.
  • [n]W โ€” move forward n big-words.
  • % โ€” find match. The editor looks forward for the nearest parenthesis, bracket or brace and then moves the cursor to the matching parenthesis, bracket or brace. matching energy fr fr ๐ŸŽ€
  • [n]fc โ€” move forward to the nth occurrence of the character c.
  • [n]Fc โ€” move backward to the nth occurrence of the character c.
  • [n]tc โ€” move forward to just before the nth occurrence of the character c.
  • [n]Tc โ€” move backward to just before the nth occurrence of the character c.
  • [n]; โ€” repeats the last f, F, t or T command.
  • [n], โ€” repeats the last f, F, t or T command, but moves in the opposite direction.

๐Ÿ“œ Inter-line movement commands

  • [n]j, [n]+, and [n]^N โ€” move to the *n*th next line in the history.
  • [n]k, [n]-, and [n]^P โ€” move to the *n*th previous line in the history.
  • [n]G โ€” move to line n in the history; if n isn't specified, the number of the first remembered line is used.
  • [n]g โ€” like G, except if n isn't specified, it goes to the most recent remembered line.
  • [n]/string โ€” search backward through the history for the nth line containing string; if string starts with ^, the rest of the string has to appear at the start of the history line for it to match.
  • [n]?string โ€” same as /, except it searches forward through the history.
  • [n]n โ€” search for the *n*th occurrence of the last search string; the direction of the search is the same as the last search.
  • [n]N โ€” search for the *n*th occurrence of the last search string; the direction of the search is the opposite of the last search.
  • ANSI-CurUp, PC-PgUp โ€” take the characters from the beginning of the line to the current cursor position as a search string and do a history search, backwards, for lines beginning with this string; keep the cursor position. Heads up: this works only in insert mode and keeps it enabled.
  • ANSI-CurDown, PC-PgDn โ€” take the characters from the beginning of the line to the current cursor position as a search string and do a history search, forwards, for lines beginning with this string; keep the cursor position. This too only works in insert mode and keeps it enabled.

โœ๏ธ Edit commands

  • [n]a โ€” append text n times; goes into insert mode just after the current position. The append is only replicated if command mode is re-entered, i.e. <Esc> is used.
  • [n]A โ€” same as a, except it appends at the end of the line.
  • [n]i โ€” insert text n times; goes into insert mode at the current position. The insertion is only replicated if command mode is re-entered, i.e. <Esc> is used.
  • [n]I โ€” same as i, except the insertion is done just before the first non-blank character.
  • [n]s โ€” substitute the next n characters (so: delete the characters and go into insert mode).
  • S โ€” substitute whole line. All characters from the first non-blank character to the end of the line are deleted and insert mode is entered.
  • [n]cmove-cmd โ€” change from the current position to the position resulting from n move-cmds (so: delete the indicated region and go into insert mode); if move-cmd is c, the line starting from the first non-blank character is changed.
  • C โ€” change from the current position to the end of the line (so: delete to the end of the line and go into insert mode).
  • [n]x โ€” delete the next n characters.
  • [n]X โ€” delete the previous n characters.
  • D โ€” delete to the end of the line.
  • [n]dmove-cmd โ€” delete from the current position to the position resulting from n move-cmds; move-cmd is a movement command (see above) or d, in which case the current line is deleted.
  • [n]rc โ€” replace the next n characters with the character c.
  • [n]R โ€” replace. Enters insert mode but overwrites existing characters instead of inserting before existing characters. The replacement is repeated n times.
  • [n]~ โ€” change the case of the next n characters.
  • [n]ymove-cmd โ€” yank from the current position to the position resulting from n move-cmds into the yank buffer; if move-cmd is y, the whole line is yanked.
  • Y โ€” yank from the current position to the end of the line.
  • [n]p โ€” paste the contents of the yank buffer just after the current position, n times.
  • [n]P โ€” same as p, except the buffer is pasted at the current position.

๐ŸŽ€ Miscellaneous vi commands

  • ^J and ^M โ€” the current line is read, parsed and executed by the shell.
  • ^L and ^R โ€” redraw the current line.
  • [n]. โ€” redo the last edit command n times.
  • u โ€” undo the last edit command.
  • U โ€” undo all changes that have been made to the current line.
  • PC Home, End, Del and cursor keys โ€” they move as expected, both in insert and command mode.
  • intr and quit โ€” the interrupt and quit terminal characters cause the current line to be removed to the history and a new prompt to be printed.

โœจ FILES โœจ

  • ~/.mkshrc โ€” user mkshrc profile (for non-privileged interactive shells); see "Startup files." The location can be changed at compile time (like, for embedded systems); AOSP Android builds use /system/etc/mkshrc.
  • ~/.profile โ€” user profile (for non-privileged login shells); see "Startup files" near the top of this manual.
  • /etc/profile โ€” system profile (for login shells); see "Startup files."
  • /etc/shells โ€” shell database.
  • /etc/suid_profile โ€” privileged shells' profile (sugid); see "Startup files."
Note: on Android, /system/etc/ contains the system and suid profile. ๐Ÿซถ

๐ŸŒˆ SEE ALSO ๐ŸŒˆ

awk(1), cat(1), ed(1), getopt(1), lksh(1), sed(1), sh(1), stty(1), dup(2), execve(2), getgid(2), getuid(2), mknod(2), mkfifo(2), open(2), pipe(2), rename(2), wait(2), getopt(3), nl_langinfo(3), setlocale(3), signal(3), system(3), tty(4), shells(5), environ(7), script(7), utf-8(7), mknod(8)

The FAQ at http://www.mirbsd.org/mksh-faq.htm or in the /usr/share/doc/mksh/FAQ.htm file. ๐Ÿ’–

http://www.mirbsd.org/ksh-chan.htm โ€” go read it, it's giving lore.

Morris Bolsky, The KornShell Command and Programming Language, Prentice Hall PTR, xvi + 356 pages, 1989, ISBN 978-0-13-516972-8 (0-13-516972-0). ๐Ÿ“š

Morris I. Bolsky and David G. Korn, The New KornShell Command and Programming Language (2nd Edition), Prentice Hall PTR, xvi + 400 pages, 1995, ISBN 978-0-13-182700-4 (0-13-182700-6).

Stephen G. Kochan and Patrick H. Wood, UNIX Shell Programming, Sams, 3rd Edition, xiii + 437 pages, 2003, ISBN 978-0-672-32490-1 (0-672-32490-3).

IEEE Inc., IEEE Standard for Information Technology โ€” Portable Operating System Interface (POSIX), IEEE Press, Part 2: Shell and Utilities, xvii + 1195 pages, 1993, ISBN 978-1-55937-255-8 (1-55937-255-9).

Bill Rosenblatt, Learning the Korn Shell, O'Reilly, 360 pages, 1993, ISBN 978-1-56592-054-5 (1-56592-054-6).

Bill Rosenblatt and Arnold Robbins, Learning the Korn Shell, Second Edition, O'Reilly, 432 pages, 2002, ISBN 978-0-596-00195-7 (0-596-00195-9).

Barry Rosenberg, KornShell Programming Tutorial, Addison-Wesley Professional, xxi + 324 pages, 1991, ISBN 978-0-201-56324-5 (0-201-56324-X).

โœจ AUTHORS โœจ

okay so bestie, the MirBSD Korn Shell was developed by mirabilos (formerly The MirOS Project.) ๐Ÿ’œ

This shell is built on the public domain 7th edition Bourne shell clone by Charles Forsyth, who very kindly agreed to โ€” in countries where the Public Domain status of the work may not be valid โ€” grant a copyright licence to the general public to deal in the work without restriction, plus permission to sublicence derivatives under the terms of any OSI-approved Open Source licence. ๐Ÿซถ And it also includes parts of the BRL shell by Doug A. Gwyn, Doug Kingston, Ron Natalie, Arnold Robbins, Lou Salkind and others.

The first release of pdksh was created by Eric Gisin, and it was subsequently maintained by John R. MacMillan, Simon J. Gerraty and Michael Rendell. The effort by Debian, OpenBSD, and other contributors โ€” including our users โ€” to improve the shell is appreciated. ๐Ÿ’– See the documentation, website and the CVS repository (source code) for details.

  • mksh-os2 is developed by KO Myung-Hun <komh@chollian.net>.
  • mksh-w32 is developed by Michael Langguth <mail@michael-langguth.de>.
  • mksh/z/OS is contributed by Daniel Richard G. <skunk@iSKUNK.ORG>.

The BSD daemon is Copyright ยฉ Marshall Kirk McKusick. The complete legalese is at: http://www.mirbsd.org/TaC-mksh.txt


๐Ÿ’€ CAVEATS ๐Ÿ’€

ngl bestie, mksh provides a consistent, clear interface normally. But listen โ€” this may deviate from POSIX in historic or opinionated places. ๐Ÿ™ˆ set -o posix (see โ€œPOSIX modeโ€ for details) will make the shell more conformant, but mind the FAQ (see โ€œSEE ALSOโ€), especially regarding locales. ๐Ÿ‘‘

mksh (but not lksh) provides a consistent 32-bit integer arithmetic implementation, both signed and unsigned, with sign of the result of a remainder operation and wraparound defined, even (defying POSIX) on 36-bit and 64-bit systems. โœจ

mksh currently uses OPTU-16 internally, which is the same as UTF-8 and CESU-8 with 0000..FFFD being valid codepoints; raw octets map to U+EF80..U+EFFF. Future compatibility note: mksh R60 will use full 21-bit UTF-8 and map raw octets to U-001BBB80..U-001BBBFF instead.


๐Ÿ› BUGS ๐Ÿ›

Suspending (using ^Z) pipelines like the one below will only suspend the currently running part of the pipeline; in this example, โ€œfubarโ€ is immediately printed on suspension (but not later after an fg). ๐Ÿ˜ญ

$ /bin/sleep 666 && echo fubar

The truncation process involved when changing HISTFILE does not free old history entries (leaks memory) and leaks old entries into the new history if their line numbers are not overwritten by same-number entries from the persistent history file; truncating the on-disc file to HISTSIZE lines has always been broken and prone to history file corruption when multiple shells are accessing the file; the rollover process for the in-memory portion of the history is slow, should use memmove(3).

bestie this is the part where you don't @ me about the history behaviour, it's a known thing ๐Ÿ’€

This document attempts to describe mksh R59-CURRENT and up, with vendor patches from Debian, compiled without any options impacting functionality, such as MKSH_SMALL, when not called as /bin/sh which, on some systems only, enables set -o posix or set -o sh automatically (whose behaviour differs across targets), for an operating environment supporting all of its advanced needs.

Please report bugs in mksh in the #!/bin/mksh channel, on IRC; note the information at: http://www.mirbsd.org/mksh-faq.htm#contact


okay that's the whole page bestie, you read the entire MKSH(1) manpage and that's iconic of you tbh ๐Ÿ‘‘ this copy is from MirBSD, dated December 23, 2025, so it's fresh fresh โœจ go forth and script, mwah ๐Ÿ’…๐Ÿ’–

๐Ÿ“ฆ where this page comes from
  • ubuntu resolute: mksh 59c-43
  • ubuntu stonking: mksh 59c-43
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms