Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

POSIX compliance

POSIX (Portable Operating System Interface) is a set of standards specified by the IEEE to ensure compatibility among Unix-like operating systems. It defines a standard operating system interface and environment, including command-line utilities, shell scripting, and system calls.

As of 2025, the latest version of POSIX is POSIX.1-2024. The requirements for shells are mainly documented in the Shell & Utilities volume. Yash-rs aims to comply with the POSIX standard, providing a consistent and portable environment for shell scripting and command execution.

The shell currently supports running shell scripts and basic interactive features in a POSIX-compliant manner. See the homepage for an overview of implemented features. Progress on POSIX conformance and feature implementation is tracked in GitHub Issues and the GitHub Project.

Many features of yash-rs are still under development, and some may not yet be fully compliant with the POSIX standard. Any non-conforming behavior is described in the Compatibility section of each feature’s documentation. These sections also clarify which aspects of the shell’s behavior are POSIX requirements and which are shell-specific extensions.

Maximizing POSIX compliance

Some behaviors of yash-rs prioritize convenience over POSIX compliance. The posixlycorrect option disables such features. When this option is set:

  • The shell no longer refuses to exit because of suspended jobs when the exit built-in is executed or end-of-file is reached in an interactive shell. (See Suspended jobs.)
  • Extension built-ins are ignored (treated as non-existing), so the shell falls through to searching for an external utility with the same name.

This list may be expanded in the future as more features are added to the shell.

Writing portable scripts

Even when yash-rs conforms to POSIX, it also implements extensions that POSIX does not specify. Such extensions are convenient, but scripts that rely on them may not run on other shells. The portable option helps you catch this: when set, the shell rejects or ignores non-portable features so that you can verify a script uses only portable constructs.

Unlike posixlycorrect, which changes how the shell behaves to maximize POSIX conformance, portable does not alter the behavior of POSIX-conformant constructs. It only restricts the shell to features that are portable across POSIX-conforming shells, reporting an error or ignoring a feature when a non-portable construct is used. The two options are independent and can be combined.

When the portable option is set, the shell rejects the following non-portable constructs:

  • (Since 3.3.0) The ;;& and ;| terminators in case commands.
  • (Since 3.3.0) The non-portable redirection operators >>| and <<<.
  • (Since 3.3.0) A number or {...} token immediately followed by < or > used as a redirection operand (for example, the 1 in < 1>file). Separate it with a space or quote it instead.
  • (Since 3.3.0) A reserved word that immediately follows a subshell or a redirection without a separator (see where reserved words are recognized). POSIX recognizes a reserved word only when it begins a command or follows another reserved word; a subshell ends with ) and a redirection ends with a word, so a clause-delimiting reserved word right after one is not recognized. Insert ; or a newline before it. This affects }, done, fi, then, elif, else, esac, and do (for example, write { ( foo ); } instead of { ( foo ) }, and for i in 1; do ( foo ); done instead of for i in 1; do ( foo ) done).
  • (Since 3.3.0) A non-portable escape sequence in a dollar-single-quoted string ($'…'): the \E, \?, \u, and \U escapes, the \c@ control escape, and \x followed by more than two hexadecimal digits.
  • (Since 3.3.0) A (( or !( at the beginning of a command. Other shells parse ((…)) as an arithmetic command and !(…) as an extended glob, neither of which yash-rs supports. Insert a space (( ( to nest subshells, or ! ( to negate one).
  • (Since 3.3.0) A command name ending with a : (for example, foo:). POSIX reserves words whose final character is a : for possible future use, so using one where a reserved word would be recognized produces unspecified results. The lone : (colon built-in) is not affected.
  • (Since 3.3.0) A for loop variable name that is quoted, contains an expansion, or starts with a digit. POSIX requires the name to be an unquoted word consisting solely of underscores, digits, and alphabetics from the portable character set, not starting with a digit.
  • (Since 3.3.0) A function name that is quoted, contains an expansion, or starts with a digit. POSIX requires the name to be an unquoted word consisting solely of underscores, digits, and alphabetics from the portable character set, not starting with a digit.
  • (Since 3.3.0) A function name that is the same as a special built-in utility name (for example, break or export). POSIX does not allow a function to have the same name as a special built-in. This does not apply to other built-ins, such as cd, or to source, which is not a POSIX-mandated special built-in.
  • (Since 3.3.0) An assignment name that contains a character other than underscores, digits, and alphabetics from the portable character set, or that starts with a digit.
  • (Since 3.3.1) An array assignment (name=(...)). Array assignment is a yash extension that POSIX does not specify.
  • (Since 3.3.0) A parameter expansion that uses a length or switch modifier with special parameter * or @ (for example, ${#*} or ${@:+word}), or a trim modifier with special parameter #, *, or @ (for example, ${#%word} or ${*#word}). POSIX leaves the results of these combinations unspecified.
  • (Since 3.3.1) Defining an alias with the alias built-in with a name that contains a character other than ASCII letters, digits, !, %, ,, -, @, or _.

The portable option is still under development, so this list will be expanded as more checks are implemented.