> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/man1/sh.1.md).

# sh(1)

`sh` — 命令解释器（shell）

## 名称

`sh`

## 概要

`sh [-/+abCEefhIilmnPpTuVvx] [-/+o longname] [script [arg ...]]`

`sh [-/+abCEefhIilmnPpTuVvx] [-/+o longname] -c string [name [arg ...]]`

`sh [-/+abCEefhIilmnPpTuVvx] [-/+o longname] -s [arg ...]`

## 描述

`sh` 实用程序是系统的标准命令解释器。当前版本的 `sh` 接近 IEEE Std 1003.1 ("POSIX.1") 关于 shell 的规范。它仅支持 POSIX 规定的特性，外加少量 Berkeley 扩展。本手册页不打算作为 shell 的教程或完整规范。

### 概述

shell 是一种命令，它从文件或终端读取行，解释它们，并通常执行其他命令。它是用户登录系统时启动的程序，但用户可以用 chsh(1) 命令选择不同的 shell。shell 实现了一种语言，该语言具有流控制结构、提供除数据存储之外多种功能的宏机制，以及内建的历史和行编辑能力。它集成了许多有助于交互使用的特性，并具有解释性语言同时适用于交互式和非交互式使用（shell 脚本）的优势。也就是说，命令可以直接键入到运行中的 shell，也可以放入文件中，由 shell 直接执行。

### 调用

如果不存在参数且 shell 的标准输入连接到终端（或设置了 `-i` 选项），则 shell 被视为交互式 shell。交互式 shell 通常在每个命令之前提示，并以不同方式处理编程和命令错误（如下所述）。首次启动时，shell 检查参数 0，如果它以连字符（`-`）开头，则 shell 还被视为登录 shell。这通常在用户首次登录时由系统自动完成。登录 shell 首先从 **/etc/profile** 文件中读取命令，然后从用户主目录中的 **.profile** 读取命令（如果存在）。如果环境变量 `ENV` 在进入 shell 时已设置，或在登录 shell 的 **.profile** 中设置，则 shell 对其值进行参数展开和算术展开，并从命名文件中读取命令。因此，用户应将仅在登录时执行的命令放在 **.profile** 文件中，将每个 shell 都执行的命令放在 `ENV` 文件中。用户可以通过在主目录的 **.profile** 文件中放置以下行来将 `ENV` 变量设置为某个文件，将 **.shrc** 替换为所需的文件名：

```sh
ENV=$HOME/.shrc; export ENV
```

命令行上指定的第一个非选项参数将被视为从中读取命令的文件名（shell 脚本），其余参数设置为 shell 的位置参数（`$1`、`$2` 等）。否则，shell 从标准输入读取命令。

与旧版本的 `sh` 不同，`ENV` 脚本仅在调用交互式 shell 时才被 source。这关闭了一个众所周知的、有时容易利用的安全漏洞，该漏洞与考虑不周的 `ENV` 脚本有关。

### 参数列表处理

`sh` 的所有单字母选项都有对应的长名称，但 `-c` 和 `-/+o` 除外。这些长名称在下面描述中的单字母选项旁边提供。选项的长名称可以作为 `sh` 的 `-/+o` 选项的参数指定。shell 运行后，选项的长名称可以作为 `set` 内建命令的 `-/+o` 选项的参数指定（在“内建命令”一节中描述）。用连字符（`-`）引入选项会启用该选项，而用加号（`+`）则禁用该选项。“`--`”或普通的 `-` 将停止选项处理，并强制将命令行上的其余字视为参数。`-/+o` 和 `-c` 选项没有长名称。它们接受参数，并在单字母选项之后描述。

```sh
sh -T -c "trap 'exit 1' 2 ; some-blocking-program"
```

**`-a`** `allexport` 在对变量赋值时将其标记为导出。

**`-b`** `notify` 启用后台作业完成的异步通知。（未实现）

**`-C`** `noclobber` 不用 `>` 覆盖现有文件。

**`-E`** `emacs` 启用内建的 emacs(1)（`ports/editors/emacs`）命令行编辑器（如果已设置 `-V` 选项则禁用它；在终端上交互时自动设置）。参见“命令行编辑”。

**`-e`** `errexit` 在非交互模式下，如果任何未测试的命令失败则立即退出。如果命令是用于控制 `if`、`elif`、`while` 或 `until` 的列表的一部分；如果命令是 “`&&`” 或 “`||`” 运算符的左操作数；或如果命令是前面带有 `!` 关键字的管道，则该命令的退出状态被视为已显式测试。如果执行 shell 函数且其退出状态被显式测试，则该函数的所有命令也视为已被测试。注意，许多命令返回非零值以传达错误以外的信息，这可能导致使用 `-e` 时程序意外终止。

**`-f`** `noglob` 禁用路径名展开。

**`-h`** `trackall` 为兼容 POSIX 而设的空操作选项。

**`-I`** `ignoreeof` 在交互模式下忽略输入中的 `EOF`。

**`-i`** `interactive` 强制 shell 以交互方式运行。

**`-l`** 强制 shell 像被调用为登录 shell 一样运行。

**`-m`** `monitor` 启用作业控制（参见“作业控制”）。交互时自动设置。在非交互式 shell 中，即使没有终端可用也可以设置此选项，这对于将进程放入单独的进程组很有用。

**`-n`** `noexec` 如果不是交互式的，读取命令但不执行它们。这对于检查 shell 脚本的语法很有用。

**`-P`** `physical` 将 `cd` 和 `pwd` 命令的默认值从 `-L`（逻辑目录布局）更改为 `-P`（物理目录布局）。

**`-p`** `privileged` 启用特权模式。如果有效用户或组 ID 不等于实际用户或组 ID，则在启动时启用此模式。关闭此模式会将有效用户和组 ID 设置为实际用户和组 ID。为交互式 shell 启用此模式时，将 source **/etc/suid\_profile** 而不是 **\~/.profile**（在 source **/etc/profile** 之后），并且忽略 `ENV` 变量的内容。

**`-s`** `stdin` 从标准输入读取命令（如果不存在文件参数则自动设置）。此选项在 shell 已经开始运行后设置（即用 `set` 命令设置）时无效。

**`-T`** `trapsasync` 等待子进程时，立即执行 trap。如果未设置此选项，则在子进程退出后执行 trap，如 IEEE Std 1003.2 ("POSIX.2") 所规定。此非标准选项适用于在阻塞信号的子进程周围放置保护 shell。外围 shell 可以杀死子进程，也可以仅将控制权返回给 tty 并让子进程单独运行，如下所示：

**`-u`** `nounset` 当尝试展开未设置的变量、位置参数或特殊参数 `!` 时向标准错误写入消息，如果 shell 不是交互式的则立即退出。

**`-V`** `vi` 启用内建的 [vi(1)](/man1/vi.1.md) 命令行编辑器（如果已设置 `-E` 则禁用它）。参见“命令行编辑”。

**`-v`** `verbose` shell 在读取输入时将其写入标准错误。用于调试。

**`-x`** `xtrace` 在执行每个命令之前，将其（前面加上经过参数展开和算术展开的 `PS4` 变量的值）写入标准错误。用于调试。

**`nolog`** 为兼容 POSIX 而设的另一个空操作选项。它仅有长名称。

**`pipefail`** 将管道的退出状态更改为管道中任何命令的最后非零退出状态（如果有）。由于因 `SIGPIPE` 退出算作非零退出状态，如果管道中诸如 [head(1)](/man1/head.1.md) 之类的命令在不完全读取其输入的情况下以状态 0 终止，此选项可能导致成功管道的非零退出状态。此选项仅有长名称。

**`verify`** 在 source 文件或加载配置文件时设置 `O_VERIFY`。

`-c` 选项使命令从 `string` 操作数而不是标准输入读取。请注意，此选项仅接受单个字符串作为其参数，因此多字字符串必须加引号。

`-/+o` 选项接受要启用或禁用的选项的长名称作为其唯一参数。例如，以下两次 `sh` 调用都启用内建的 emacs(1)（`ports/editors/emacs`）命令行编辑器：

```sh
set -E
set -o emacs
```

如果不带参数使用，`-o` 选项以人类可读的格式显示当前选项设置。如果不带参数使用 `+o`，则以适合重新输入 shell 的格式输出当前选项设置。

### 词法结构

shell 从文件按行读取输入，并在空白字符（空格和制表符）以及某些称为“运算符”的特殊字符序列处将其拆分为字。有两类运算符：控制运算符和重定向运算符（其含义稍后讨论）。以下是有效运算符的列表：

控制运算符：

| `&`  | `&&` | `(` | `)`  | `<换行>` |
| ---- | ---- | --- | ---- | ------ |
| `;;` | `;&` | `;` | `\|` | `\|\|` |

重定向运算符：

| `<`  | `>`  | `<<`  | `>>`  | `<>` |
| ---- | ---- | ----- | ----- | ---- |
| `<&` | `>&` | `<<-` | `>\|` |      |

字符 `#` 如果在字的开头使用则引入注释。以 `#` 开头的字和行的其余部分被忽略。

ASCII `NUL` 字符（字符代码 0）不允许在 shell 输入中出现。

### 引用

引用用于移除某些字符或字对 shell 的特殊含义，如运算符、空白字符、关键字或别名。

有四种引用方式：匹配的单引号、美元单引号、匹配的双引号和反斜杠。

**`\a`** 警告（响终端铃声）

**`\b`** 退格

**`\c`**`c` 由 stty(1) 中 `^``c` 表示的控制字符。如果 `c` 是反斜杠，则必须加倍。

**`\e`** ESC 字符（ASCII 0x1b）

**`\f`** 换页

**`\n`** 换行

**`\r`** 回车

**`\t`** 水平制表符

**`\v`** 垂直制表符

**`\\`** 字面反斜杠

**`\'`** 字面单引号

**`\"`** 字面双引号

**`\`**`nnn` 八进制值为 `nnn` 的字节（一至三位数字）

**`\x`**`nn` 十六进制值为 `nn` 的字节（一位或多位数字，仅使用最后两位）

**`\u`**`nnnn` Unicode 码点 `nnnn`（四位十六进制数字）

**`\U`**`nnnnnnnn` Unicode 码点 `nnnnnnnn`（八位十六进制数字）

| `$` | `` ` `` | `"` | `\` | `<换行>` |
| --- | ------- | --- | --- | ------ |

**单引号** 用单引号括起字符可保留所有字符的字面含义（单引号本身除外，因此无法在单引号字符串中放入单引号）。

**美元单引号** 将字符括在 `$'` 和 `'` 之间可保留所有字符的字面含义，反斜杠和单引号除外。反斜杠引入 C 风格转义序列：Unicode 码点的序列目前仅在 UTF-8 区域设置中有用。它们拒绝码点 0 和 UTF-16 代理项。如果转义序列会产生值为 0 的字节，则该字节和字符串的其余部分直到匹配的单引号都被忽略。任何其他以反斜杠开头的字符串都是错误的。

**双引号** 用双引号括起字符可保留所有字符的字面含义，美元符号（`$`）、反引号（`` ` ``）和反斜杠（`\`）除外。双引号内的反斜杠在历史上表现奇特。它保持字面意义，除非它位于以下字符之前，此时它用于引用：

**反斜杠** 反斜杠保留其后字符的字面含义，换行符（`\n`）除外。反斜杠后跟换行符被视为行续接。

### 关键字

关键字或保留字是对 shell 具有特殊含义的字，在行首和控制运算符之后被识别。以下是关键字：

| `!`    | `{`    | `}`    | `case`  | `do`    |
| ------ | ------ | ------ | ------- | ------- |
| `done` | `elif` | `else` | `esac`  | `fi`    |
| `for`  | `if`   | `then` | `until` | `while` |

### 别名

别名是使用 `alias` 内建命令设置的名称和相应值。在简单命令的命令字可能出现的地方，并且在检查关键字之后（如果可能出现关键字），shell 检查该字是否与别名匹配。如果匹配，则用其值在输入流中替换它。例如，如果存在名为 “`lf`” 的别名，值为 “`ls -F`”，则输入

```sh
lf foobar
```

会变为

```sh
ls -F foobar
```

如果别名的值以空格或制表符结尾，则在该别名之后也会识别别名。例如，如果还存在名为 “`nohup`” 的别名，值为 “`nohup` ”，则输入

```sh
nohup lf foobar
```

会变为

```sh
nohup ls -F foobar
```

别名为新手用户提供了一种便捷方式来为命令创建简写，而无需学习如何创建带参数的函数。不建议在脚本中使用别名，因为定义别名的命令必须在解析使用它们的代码之前执行。这很脆弱且不可移植。

可以在命令行中对别名进行转义，使其不被别名值替换，方法是在别名内部或相邻处使用引用字符。最常见的做法是在别名前加反斜杠前缀，以执行同名的函数、内建命令或普通程序。参见“引用”小节。

### 命令

shell 根据一种语言解释它读取的字，该语言的规范不在本手册页的范围内（参见 IEEE Std 1003.2 ("POSIX.2") 文档中的 BNF）。但本质上，读取一行，如果该行（或控制运算符之后）的第一个字不是关键字，则 shell 已识别出简单命令。否则，可能已识别出复合命令或某些其他特殊构造。

### 简单命令

如果已识别出简单命令，shell 执行以下操作：

* 形式为 “`name=value`” 的前导字被剥离并分配给简单命令的环境（它们不影响展开）。重定向运算符及其参数（如下所述）被剥离并保存以供处理。
* 其余字按“字展开”一节中所述进行展开，第一个剩余的字被视为命令名，并定位该命令。其余字被视为命令的参数。如果没有产生命令名，则在 1) 中识别的 “`name=value`” 变量赋值影响当前 shell。
* 按下一节所述执行重定向。

### 重定向

重定向用于更改命令读取输入或发送输出的位置。通常，重定向打开、关闭或复制对文件的现有引用。用于重定向的总体格式为：

> \[`n` ]`redir-op file`]

`redir-op` 是前面提到的重定向运算符之一。以下给出这些运算符如何使用的示例。请注意，stdin 和 stdout 分别是标准输入和标准输出的常用缩写。

**\[`n`** ]`>` `file`] 将 stdout（或文件描述符 `n`）重定向到 `file`

**\[`n`** ]`>|` `file`] 同上，但覆盖 `-C` 选项

**\[`n`** ]`>>` `file`] 将 stdout（或文件描述符 `n`）追加到 `file`

**\[`n`** ]`<` `file`] 从 `file` 重定向 stdin（或文件描述符 `n`）

**\[`n`** ]`<>` `file`] 将 stdin（或文件描述符 `n`）重定向到和从 `file`

**\[`n1`** ]`<&``n2`] 从文件描述符 `n2` 复制 stdin（或文件描述符 `n1`）

**\[`n`** ]`<&-`] 关闭 stdin（或文件描述符 `n`）

**\[`n1`** ]`>&``n2`] 将 stdout（或文件描述符 `n1`）复制到文件描述符 `n2`

**\[`n`** ]`>&-`] 关闭 stdout（或文件描述符 `n`）

以下重定向通常称为 “here-document”。

```sh
[`n` ]`<<` `delimiter`]
`here-doc-text`
`...`
`delimiter`
```

直到分隔符的连续行上的所有文本都被保存，并通过标准输入或文件描述符 `n`（如果指定）提供给命令。如果初始行上指定的 `delimiter` 被引用，则 `here-doc-text` 被视为字面值，否则该文本将进行参数展开、命令替换和算术展开（如“字展开”一节中所述）。如果运算符是 “`<<-`” 而不是 “`<<`”，则 `here-doc-text` 中的前导制表符被剥离。

### 搜索与执行

有三种类型的命令：shell 函数、内建命令和普通程序。命令按该顺序（按名称）搜索。这三种类型的命令都以不同方式执行。

执行 shell 函数时，所有 shell 位置参数（`$0` 除外，它保持不变）都设置为 shell 函数的参数。显式放置在命令环境中的变量（通过在函数名之前放置对它们的赋值）成为函数的局部变量，并设置为给定的值。然后执行函数定义中给出的命令。命令完成时，位置参数恢复为原始值。这一切都在当前 shell 内发生。

Shell 内建命令在 shell 内部执行，不产生新进程。有两种内建命令：常规和特殊。特殊内建命令之前的赋值在执行完成后持续存在，赋值错误、重定向错误和某些操作数错误会导致脚本中止。特殊内建命令不能用函数覆盖。常规和特殊内建命令都能以普通程序无法做到的方式影响 shell。

否则，如果命令名不匹配函数或内建命令，则在文件系统中作为普通程序搜索该命令（如下一节所述）。执行普通程序时，shell 运行该程序，将参数和环境传递给程序。如果程序不是普通的可执行文件（即，如果不以 ASCII 表示为 “`#!`” 的 “magic number” 开头，导致 execve(2) 返回 `ENOEXEC` 错误值）但似乎是文本文件，则 shell 将运行 `sh` 的新实例来解释它。

请注意，本文档的早期版本和源代码本身误导性地、零散地将没有 magic number 的 shell 脚本称为 “shell procedure”。

### 路径搜索

定位命令时，shell 首先查看是否有该名称的 shell 函数。然后查找该名称的内建命令。如果未找到内建命令，则发生以下两种情况之一：

* 包含斜杠的命令名直接执行，不执行任何搜索。
* shell 依次在 `PATH` 变量的每个条目中搜索该命令。`PATH` 变量的值应为一系列以冒号分隔的条目。每个条目由一个目录名组成。当前目录可以通过空的目录名隐式指示，或通过单个句点显式指示。

### 命令退出状态

每个命令都有一个退出状态，可以影响其他 shell 命令的行为。范式是命令以零退出表示正常或成功，以非零退出表示失败、错误或假指示。每个命令的手册页应指明各种退出代码及其含义。此外，内建命令返回退出代码，执行的 shell 函数也是如此。

如果命令被信号终止，其退出状态大于 128。可以通过将退出状态传递给 `kill -l` 来找到信号名称。

如果没有命令字，退出状态是最后执行的命令替换的退出状态，如果命令不包含任何命令替换则为零。

### 复合命令

复合命令是简单命令与控制运算符或关键字的组合，共同创建更大的复合命令。更一般地，命令是以下之一：

* 简单命令
* 管道
* 列表或复合列表
* 复合命令
* 函数定义

除非另有说明，命令的退出状态是该命令执行的最后一个简单命令的退出状态，如果没有执行简单命令则为零。

### 管道

管道是一个或多个由控制运算符 `|` 分隔的命令序列。除最后一个命令外，所有命令的标准输出都连接到下一个命令的标准输入。最后一个命令的标准输出像往常一样从 shell 继承。

管道的格式为：

> \[`!` ]`command1` \[`|` `command2 ...`]]

`command1` 的标准输出连接到 `command2` 的标准输入。命令的标准输入、标准输出或两者被视为由管道在作为命令一部分的重定向运算符指定的任何重定向之前分配。

请注意，与其他一些 shell 不同，`sh` 在子 shell 环境中且作为 `sh` 进程的子进程执行具有多个命令的管道中的每个进程。

如果管道不在后台（稍后讨论），shell 等待所有命令完成。

如果关键字 `!` 不在管道之前，退出状态是如果未设置 `pipefail` 选项或所有命令返回零则为管道中指定的最后一个命令的退出状态，否则为管道中任何命令的最后非零退出状态。否则，退出状态是该退出状态的逻辑非。也就是说，如果该状态为零，退出状态为 1；如果该状态大于零，退出状态为零。

由于标准输入或标准输出或两者的管道分配发生在重定向之前，因此它可以被重定向修改。例如：

```sh
command1 2>&1 | command2
```

将 `command1` 的标准输出和标准错误都发送到 `command2` 的标准输入。

`;` 或换行符终止符使前面的 AND-OR 列表（在“短路列表运算符”一节中描述）按顺序执行；`&` 使前面的 AND-OR 列表异步执行。

### 后台命令（&）

如果命令由控制运算符与号（`&`）终止，shell 在子 shell 环境中（参见下面的“命令分组”）异步执行该命令；shell 不等待命令完成就开始执行下一个命令。

在后台运行命令的格式为：

> `command1` `&` \[`command2` `&` `...`]

如果 shell 不是交互式的，异步命令的标准输入设置为 **/dev/null**。

退出状态为零。

### 列表（概述）

列表是由换行符、分号或与号分隔的零个或多个命令的序列，并可选地由这三个字符之一终止。列表中的命令按其编写的顺序执行。如果命令后跟与号，shell 启动该命令并立即继续下一个命令；否则它等待该命令终止后再继续下一个。

### 短路列表运算符

“`&&`” 和 “`||`” 是 AND-OR 列表运算符。“`&&`” 执行第一个命令，然后如果第一个命令的退出状态为零则执行第二个命令。“`||`” 类似，但如果第一个命令的退出状态为非零则执行第二个命令。“`&&`” 和 “`||`” 具有相同的优先级。

### 流控结构（if、while、for、case）

`if` 命令的语法为：

`if` `list` `then` `list` \[`elif` `list`] `then` `list` ]`...` \[`else` `list`] `fi`

退出状态是所选 `then` 或 `else` 列表的退出状态，如果没有选择列表则为零。

`while` 命令的语法为：

`while` `list` `do` `list` `done`

当第一个列表的退出状态为零时，重复执行两个列表。`until` 命令类似，但用 `until` 代替 `while`，使其重复直到第一个列表的退出状态为零。

退出状态是第二个列表最后执行的退出状态，如果从未执行则为零。

`for` 命令的语法为：

`for` `variable` \[`in` `word ...`] `do` `list` `done`

如果省略 `in` 和后面的字，则使用 `in` `"$@"` 代替。展开这些字，然后重复执行列表，变量依次设置为每个字。`do` 和 `done` 命令可以用 `{` 和 `}` 替换。

`break` 和 `continue` 命令的语法为：

> `break` \[`num`]

> `continue` \[`num`]

`break` 命令终止 `num` 个最内层的 `for` 或 `while` 循环。`continue` 命令继续最内层循环的下一次迭代。这些被实现为特殊内建命令。

`case` 命令的语法为：

`case` `word` `in` `pattern`) `list` `;;` `...` `esac`

模式实际上可以是一个或多个模式（参见后面描述的“shell 模式”），由 `|` 字符分隔。对字应用波浪号展开、参数展开、命令替换、算术展开和引用移除。然后，依次使用波浪号展开、参数展开、命令替换和算术展开展开每个模式，并检查字的展开形式是否匹配。如果找到匹配项，则执行相应的列表。如果所选列表由控制运算符 `;&` 而不是 `;;` 终止，则继续执行下一个列表，继续直到以 `;;` 终止的列表或 `case` 命令的结尾。

### 命令分组

命令可以通过以下任一方式分组：

```sh
(`list` )
```

或

```sh
{ `list ; }`
```

第一种形式在子 shell 环境中执行命令。子 shell 环境有自己的副本：

* 由 `cd` 设置的当前工作目录。
* 由 `umask` 设置的文件创建掩码。
* 由 `ulimit` 设置的资源限制。
* 对打开文件的引用。
* 由 `trap` 设置的 trap。
* 已知作业。
* 位置参数和变量。
* shell 选项。
* shell 函数。
* shell 别名。

这些从父 shell 环境复制，但被 trap（但非被忽略）的信号重置为默认操作，已知作业被清除。任何更改都不影响父 shell 环境。

子 shell 环境可以作为子进程或以其他方式实现。如果在交互式 shell 中启用了作业控制（参见“作业控制”），则用括号分组的命令可以作为整体挂起和继续。

为了与其他 shell 兼容，两个连续的左括号应由空白字符分隔。

第二种形式从不 fork 另一个 shell，因此效率稍高。以这种方式将命令分组允许用户像重定向一个程序一样重定向它们的输出：

```sh
{ echo -n "hello"; echo " world"; } > greeting
```

### 函数

函数定义的语法为

> `name` `( )` `command`

函数定义是可执行语句；执行时它安装名为 `name` 的函数并返回退出状态零。`command` 通常是用 `{` 和 `}` 或 `(` 和 `)` 括起的列表。在后一种情况下，调用时函数将在子 shell 中执行。

可以使用 `local` 命令将变量声明为函数的局部变量：

> `local` \[`variable`\[=value ]]\[`-`]]]

`local` 命令被实现为内建命令。退出状态为零，除非命令不在函数中或变量名无效。

当变量变为局部变量时，它从周围作用域中的同名变量继承初始值以及导出和只读标志（如果存在）。否则，变量最初未设置，除非在 `local` 语句中赋值。shell 使用动态作用域，因此如果变量 `x` 变为函数 *f* 的局部变量，*f* 然后调用函数 *g*，则在 *g* 内部对变量 `x` 的引用将指向在 *f* 内部声明的变量 `x`，而不是名为 `x` 的全局变量。

唯一可以变为局部变量的特殊参数是 `-`。将 `-` 变为局部变量会导致通过函数内的 `set` 命令更改的任何 shell 选项（包括仅有长名称的选项）在函数返回时恢复为原始值。

`return` 命令的语法为

> `return` \[`exitstatus`]

它终止当前执行作用域，从最近嵌套的函数或 source 脚本返回；如果没有执行函数或 source 脚本，则退出 shell 实例。如果指定了 `exitstatus`，退出状态设置为其值，否则设置为最后执行的命令的退出状态。`return` 命令被实现为特殊内建命令。

### 变量和参数

shell 维护一组参数。由名称表示的参数（仅由字母、数字和下划线组成，并以字母或下划线开头）称为变量。启动时，shell 将所有具有有效名称的环境变量转换为 shell 变量。可以使用以下形式设置新变量

> `name`=`value`

参数也可以由数字或特殊字符表示，如下所述。

赋值与其他字的展开方式不同：等号之后和任何冒号之后也执行波浪号展开，用户名也由冒号终止，并且不执行字段分割和路径名展开。

这种特殊展开不仅适用于自身形成简单命令或在命令字之前的赋值，还适用于传递给具有此形式的 `export`、`local` 或 `readonly` 内建命令的字。为此，内建命令的名称必须是字面值（不是展开的结果），并且可以选择在前面加上一个或多个不带选项的字面 `command`。

### 位置参数

位置参数是由大于零的数字表示的参数。shell 最初将这些设置为 shell 脚本名称之后的命令行参数值。`set` 内建命令也可用于设置或重置它们。

### 特殊参数

特殊参数是由单个特殊字符或数字零表示的参数。它们在以下列表中显示，与用户键入的输入或 shell 脚本源中出现的完全相同。

```sh
"abc"   "def ghi"
```

**`$*`** 展开为位置参数，从 1 开始。当展开发生在双引号字符串内时，它展开为单个字段，每个参数的值由 `IFS` 变量的第一个字符分隔，如果 `IFS` 未设置则由空格分隔。

**`$@`** 展开为位置参数，从 1 开始。当展开发生在双引号内时，每个位置参数作为单独的参数展开。如果没有位置参数，`@` 的展开生成零个参数，即使 `@` 被双引号括起。这基本上意味着，例如，如果 `$1` 是 “`abc`” 且 `$2` 是 “`def ghi`”，则 `"$@"` 展开为两个参数：

**`$#`** 展开为位置参数的数量。

**`$?`** 展开为最近管道的退出状态。

**`$-`**（连字符）展开为当前选项标志（单字母选项名称连接成字符串），按调用、`set` 内建命令或 shell 隐式指定。

**`$$`** 展开为被调用 shell 的进程 ID。子 shell 保留与其父 shell 相同的 `$` 值。

**`$!`** 展开为从当前 shell 执行的最近后台命令的进程 ID。对于管道，进程 ID 是管道中最后一个命令的进程 ID。如果引用此参数，shell 将记住进程 ID 及其退出状态，直到 `wait` 内建命令报告该进程完成。

**`$0`**（零）展开为如果在命令行上传递的 shell 脚本名称，如果给定（使用 `-c`）则为 `name` 操作数，否则为传递给 shell 的参数 0。

### 特殊变量

以下变量由 shell 设置或对其有特殊含义：

**`\D{format}`** strftime(3) `format` 中的当前时间。大括号是必需的。空 `format` 等效于 %X，时间的国家表示。

**`\H`** 此系统的完全限定主机名（FQDN）。

**`\h`** 此系统的主机名。

**`\u`** 用户名。

**`\W`** 当前工作目录的最后组成部分。

**`\w`** 当前工作目录的完整路径。

**`\$`** 超级用户状态。普通用户为 “`$`”，超级用户为 “`#`”。

**`\\`** 字面反斜杠。

**`\[`** 非打印字符序列的开始（用于例如将 ANSI CSI 序列嵌入提示符）。

**`\]`** 非打印字符序列的结束。

**`\a`** 发出 ASCII BEL（0x07, 007）字符。

**`\e`** 发出 ASCII ESC（0x1b, 033）字符。

**`\r`** 发出 ASCII CR（0x0d, 015）字符。

**`\n`** 发出 CRLF 序列。

**`CDPATH`** 与 `cd` 内建命令一起使用的搜索路径。

**`EDITOR`** 与 `fc` 内建命令一起使用的后备编辑器。如果未设置，默认编辑器为 ed(1)。

**`FCEDIT`** 与 `fc` 内建命令一起使用的默认编辑器。

**`HISTFILE`** 用于持久历史存储的文件。如果未设置，将使用 **\~/.sh\_history**。如果设置但为空或 `HISTSIZE` 设置为 0，shell 将不加载和保存历史。

**`HISTSIZE`** 可访问的前一命令数。

**`HOME`** 用户的主目录，用于波浪号展开和作为 `cd` 内建命令的默认目录。

**`IFS`** 输入字段分隔符。这在启动时初始化为 、 和 ，顺序如此。如果 `IFS` 未设置也应用此值，但如果设置为空字符串则不适用。参见“空白分割”一节了解更多详情。

**`LINENO`** 脚本或函数中的当前行号。

**`MAIL`** 邮件文件的名称，将检查其是否有新邮件到达。被 `MAILPATH` 覆盖。

**`MAILPATH`** 以冒号（`:`）分隔的文件名列表，供 shell 检查传入邮件。此变量覆盖 `MAIL` 设置。一次最多可监视 10 个邮箱。

**`OPTIND`** `getopts` 要处理的下一个参数的索引。这在启动时初始化为 1。

**`PATH`** 可执行文件的默认搜索路径。参见“路径搜索”一节了解详情。

**`PPID`** 被调用 shell 的父进程 ID。这在启动时设置，除非此变量在环境中。父进程 ID 的后续更改不会反映。子 shell 保留相同的 `PPID` 值。

**`PS1`** 主提示字符串，默认为 “`$` ”，除非你是超级用户，在这种情况下默认为 “`#` ”。`PS1` 可以包含以下任何格式序列，这些序列由给定信息替换：以下特殊和非打印字符在非打印字符序列内受支持：

**`PS2`** 次提示字符串，默认为 “`>` ”。`PS2` 可以包含 `PS1` 中的任何格式序列。

**`PS4`** 跟踪输出的前缀（如果 `-x` 处于活动状态）。默认为 “`+` ”。

### 字展开

本子句描述对字执行的各种展开。并非所有展开都在每个字上执行，如下所述。

在单个字内发生的波浪号展开、参数展开、命令替换、算术展开和引用移除展开为单个字段。只有字段分割或路径名展开才能从单个字创建多个字段。此规则的唯一例外是双引号内特殊参数 `@` 的展开，如上所述。

字展开的顺序为：

* 波浪号展开、参数展开、命令替换、算术展开（这些都同时发生）。
* 除非 `IFS` 变量为空，否则对步骤 (1) 生成的字段执行字段分割。
* 路径名展开（除非 `-f` 选项生效）。
* 引用移除。

`$` 字符用于引入参数展开、命令替换或算术展开。

### 波浪号展开（替换用户主目录）

以未加引号的波浪号字符（`~`）开头的字进行波浪号展开。直到斜杠（`/`）或字末尾的所有字符被视为用户名，并替换为用户的主目录。如果用户名缺失（如 `~/foobar`），则波浪号替换为 `HOME` 变量的值（当前用户的主目录）。

### 参数展开

参数展开的格式如下：

> `${``expression``}`

其中 `expression` 由直到匹配 `}` 的所有字符组成。由反斜杠转义或在单引号或双引号字符串内的任何 `}`，以及嵌入式算术展开、命令替换和变量展开中的字符，在确定匹配 `}` 时不被检查。如果带有 `+`、`-`、`=` 或 `?` 的变体出现在双引号字符串内，作为扩展可能有未加引号的部分（通过展开内的双引号）；此类部分中的 `}` 在确定匹配 `}` 时也不被检查。

参数展开的最简单形式为：

> `${``parameter``}`

替换 `parameter` 的值（如果有）。

参数名或符号可以用大括号括起，大括号是可选的，但对于多于一位数字的位置参数或当参数后跟可能被解释为名称一部分的字符时除外。如果参数展开发生在双引号内：

* 不对展开结果执行字段分割，特殊参数 `@` 除外。
* 不对展开结果执行路径名展开。

此外，参数展开可以使用以下格式之一进行修改。

**`${`** `parameter``:-``word``}` 使用默认值。如果 `parameter` 未设置或为空，则替换 `word` 的展开；否则替换 `parameter` 的值。

**`${`** `parameter``:=``word``}` 赋默认值。如果 `parameter` 未设置或为空，则将 `word` 的展开赋给 `parameter`。在所有情况下，替换 `parameter` 的最终值。`word` 内的引用不阻止字段分割或路径名展开。只有变量，而非位置参数或特殊参数，可以以这种方式赋值。

**`${`** `parameter``:?`\[`word` ]`}`] 如果为空或未设置则指示错误。如果 `parameter` 未设置或为空，则将 `word` 的展开（如果省略 `word` 则为指示其未设置的消息）写入标准错误，shell 以非零退出状态退出。否则替换 `parameter` 的值。交互式 shell 不必退出。

**`${`** `parameter``:+``word``}` 使用替代值。如果 `parameter` 未设置或为空，则替换为空；否则替换 `word` 的展开。

在前面显示的参数展开中，格式中使用冒号会导致测试未设置或为空的参数；省略冒号会导致仅测试未设置的参数。

`word` 从周围继承引用类型（未加引号、双引号或 here-document），但在引用移除期间引用右大括号的反斜杠被移除。

**`${#`** `parameter``}` 字符串长度。`parameter` 值的字符长度。

以下四种参数展开变体提供子串处理。在每种情况下，使用模式匹配表示法（参见“shell 模式”），而非正则表达式表示法来评估模式。如果 parameter 是特殊参数 `*` 或 `@` 之一，则展开结果未指定。将完整参数展开字符串用双引号括起不会导致以下四种模式字符被引用，而大括号内的引用字符具有此效果。

**`${`** `parameter``%``word``}` 移除最小后缀模式。展开 `word` 以产生模式。然后参数展开结果为 `parameter`，删除与模式匹配的最小后缀部分。

**`${`** `parameter``%%``word``}` 移除最大后缀模式。展开 `word` 以产生模式。然后参数展开结果为 `parameter`，删除与模式匹配的最大后缀部分。

**`${`** `parameter``#``word``}` 移除最小前缀模式。展开 `word` 以产生模式。然后参数展开结果为 `parameter`，删除与模式匹配的最小前缀部分。

**`${`** `parameter``##``word``}` 移除最大前缀模式。展开 `word` 以产生模式。然后参数展开结果为 `parameter`，删除与模式匹配的最大前缀部分。

### 命令替换

命令替换允许将命令的输出替换到命令名本身的位置。当命令如下括起时发生命令替换：

> `$(``command``)`

或反引号版本：

> `command`

shell 通过执行命令并用命令的标准输出替换命令替换来展开命令替换，移除替换末尾的一个或多个换行符序列。输出末尾之前的嵌入式换行符不被移除；但是，在字段分割期间，根据 `IFS` 的值和生效的引用，它们可能被转换为空格。命令在子 shell 环境中执行，但内建命令 `jobid`、`jobs` 和 `trap` 返回有关父 shell 环境的信息，如果 `times` 是命令替换中的唯一命令则返回有关同一进程的信息。

如果 `$(` 形式的命令替换以子 shell 开头，则 `$(` 和 `(` 必须由空白字符分隔，以避免与算术展开歧义。

### 算术展开

算术展开提供了一种评估算术表达式并替换其值的机制。算术展开的格式如下：

> `$((``expression``))`

`expression` 被视为如同在双引号中，但表达式内的双引号不被特殊处理。shell 对 `expression` 中的所有标记进行参数展开、命令替换、算术展开和引用移除。

允许的表达式是 C 表达式的子集，总结如下。

**值** 所有值都是 `intmax_t` 类型。

**常量** 十进制、八进制（以 `0` 开头）和十六进制（以 `0x` 开头）整数常量。

**变量** shell 变量可以读写并包含整数常量。

**一元**运算符 `! ~ + -`

**二元**运算符 `* / % + - << >> < <= > >= == != & ^ | && ||`

**赋值**运算符 `= += -= *= /= %= <<= >>= &= ^= |=`

**条件**运算符 `? :`

表达式的结果以十进制替换。

### 空白分割（字段分割）

在某些上下文中，参数展开、命令替换和算术展开之后，shell 扫描未在双引号中发生的展开和替换结果以进行字段分割，可能产生多个字段。

`IFS` 中是空白字符（、 和 ）的字符与 `IFS` 中的其他字符处理方式不同。

`IFS` 中在字开头或结尾的空白字符被丢弃。

随后，字段由以下之一分隔：

* `IFS` 中的非空白字符，周围有 `IFS` 中的任何空白字符，或
* `IFS` 中的一个或多个空白字符。

如果字以 `IFS` 中的非空白字符结尾，则此字符之后没有空字段。

如果没有分隔字段，则丢弃该字。特别是，如果字完全由未加引号的替换组成且替换结果为空，则即使 `IFS` 为空，它也会被字段分割移除。

### 路径名展开（文件名生成）

除非设置了 `-f` 选项，否则在字分割完成后执行文件名生成。每个字被视为一系列由斜杠分隔的模式。展开过程用所有现有文件的名称替换该字，这些文件的名称可以通过用匹配指定模式的字符串替换每个模式来形成。这有两个限制：首先，模式不能匹配包含斜杠的字符串；其次，模式不能匹配以句点开头的字符串，除非模式的第一个字符是句点。下一节描述用于路径名展开、子串处理的四种参数展开变体和 `case` 命令的模式。

### shell 模式

模式由匹配自身的普通字符和元字符组成。元字符是 `*`、`?` 和 `[`。如果这些字符被引用，它们失去特殊含义。当执行命令或变量替换且美元符号或反引号未被双引号括起时，扫描变量值或命令输出中的这些字符并将它们转换为元字符。

星号（`*`）匹配任何字符串。问号（`?`）匹配任何单个字符。左方括号（`[`）引入字符类。字符类的结尾由 `]` 指示；如果缺少 `]`，则 `[` 匹配 `[` 而非引入字符类。字符类匹配方括号之间的任何字符。可以使用减号指定依赖于区域设置的字符范围。可以通过用 `[:` 和 `:]` 括起名称来指定命名字符类（参见 wctype(3)）。例如，`[[:alpha:]]` 是匹配单个字母的 shell 模式。可以通过使感叹号（`!`）成为字符类的第一个字符来补充字符类。脱字符（`^`）具有相同效果但非标准。

要在字符类中包含 `]`，使其成为列出的第一个字符（在 `!` 或 `^` 之后，如果有）。要包含 `-`，使其成为列出的第一个或最后一个字符。

### 作业控制

作业是一组进程，包括 shell 管道（参见“管道”）及其所有后代进程，它们都在同一个进程组中。

作业控制设施允许用户选择性地挂起进程的执行（通过按 ‘Ctrl-Z’，如果未使用 stty(1) 调整），稍后继续其执行，并在前台或后台运行它们（使用内建命令 `fg` 和 `bg`）。

作业 ID 是用于引用作业的句柄。它可以采用以下任何形式：

**`%%`** 当前作业。

**`%+`** 当前作业。

**`%-`** 前一个作业。

**`%`** `n` 作业号 `n`。

**`%`** `string` 命令以 `string` 开头的作业。

**`%?`** `string` 命令包含 `string` 的作业。

作业控制内建命令为：`bg`、`fg`、`jobid` 和 `jobs`。此外，以下内建命令接受作业 ID 作为参数：`kill`、`wait`。参见下面的“内建命令”。

### 内建命令

本节列出内建命令。

**`:`** \[`arg` `...`] 返回 0（真）退出值的空命令。任何参数都被丢弃。

**`.`** `file` shell 读取并执行指定文件中的命令。`return` 命令可用于返回到 `.` 命令的调用者。如果 `file` 包含任何 `/` 字符，则按原样使用。否则，shell 在 `PATH` 中搜索该文件。如果在 `PATH` 中未找到，则在当前工作目录中查找。

**`[`** test(1) 的内建等价物。

**`alias`** \[`name`\[=`string` ]`...`]]]] 如果指定了 `name`=`string`，shell 定义别名 `name`，值为 `string`。如果仅指定 `name`，则打印别名 `name` 的值。不带参数时，`alias` 内建命令打印所有已定义别名的名称和值（参见 `unalias`）。别名值以适当的引用写入，以便适合重新输入 shell。另参见“别名”小节。

**`bg`** \[`job ...`] 在后台继续指定作业（如果未给出作业则为当前作业）。参见“作业控制”了解作业 ID 形式列表。

**`bind`** \[`-aeklrsv` ]\[`key` \[`command` ]]]]] 列出或更改行编辑器 editline(7) 的键绑定。此命令在 editrc(5) 中有文档说明。

**`break`** \[`num`] 参见“流控结构”小节。

**`builtin`** `cmd` \[`arg ...`] 执行指定的内建命令 `cmd`。当用户希望用与内建命令同名的 shell 函数覆盖该内建命令时，这很有用。

**`cd`** \[`-L | -P` ]\[`-e` ]\[`directory`]]]

**`cd`** `-` 切换到指定的 `directory`，如果未指定 `directory` 则切换到 `HOME` 环境变量中指定的目录，如果 `directory` 为 `-` 则切换到 `OLDPWD` 环境变量中指定的目录。如果 `directory` 不以 `/`、`.` 或 `..` 开头，则在 `CDPATH` 变量中列出的目录中搜索指定的 `directory`。如果 `CDPATH` 未设置，则搜索当前目录。`CDPATH` 的格式与 `PATH` 相同。在交互式 shell 中，如果使用了 `CDPATH` 机制或 `directory` 为 `-`，`cd` 命令将打印它实际切换到的目录名称。

如果指定了 `-P` 选项，`..` 物理处理，符号链接在处理 `..` 组件之前被解析。如果指定了 `-L` 选项，`..` 逻辑处理。这是默认值。

`-e` 选项使 `cd` 在无法可靠或完全确定新目录的完整路径名时返回退出状态 1。通常这不视为错误，尽管会打印警告。

如果更改目录失败，退出状态大于 1。如果目录已更改，退出状态为 0，如果给出了 `-e` 则也为 1。

**`chdir`** `cd` 内建命令的同义词。

**`command`** \[`-p` ]\[`utility` \[`argument ...`]]]

**`command`** \[`-p` ]`-v` `utility`]

**`command`** \[`-p` ]`-V` `utility`] 第一种调用形式执行指定的 `utility`，在搜索中忽略 shell 函数。如果 `utility` 是特殊内建命令，则像常规内建命令一样执行。如果指定了 `-p` 选项，则使用保证能找到所有标准实用程序的 `PATH` 默认值执行命令搜索。如果指定了 `-v` 选项，`utility` 不执行，但打印 shell 对其解释的描述。对于普通命令，输出是路径名；对于 shell 内建命令、shell 函数和关键字，仅写入名称。别名打印为 “`alias` `name`=`value`”。`-V` 选项与 `-v` 相同，但输出不同。它打印 “`utility` `is` `description`”，其中 `description` 是 `utility` 的路径名、特殊 shell 内建命令、shell 内建命令、shell 函数、shell 关键字或 `value` 的别名。

**`continue`** \[`num`] 参见“流控结构”小节。

**`echo`** \[`-e | -n` ]\[`string ...`]] 将以空格分隔的参数列表打印到标准输出并追加换行符。只能指定 `-e` 和 `-n` 选项之一。

**`-n`** 抑制尾随换行符的输出。

**`-e`** 处理 C 风格反斜杠转义序列。`echo` 命令理解以下字符转义：

**`\a`** 警告（响终端铃声）

**`\b`** 退格

**`\c`** 抑制尾随换行符（如果它不是最后一个字符，则具有截断该行的副作用）

**`\e`** ESC 字符（ASCII 0x1b）

**`\f`** 换页

**`\n`** 换行

**`\r`** 回车

**`\t`** 水平制表符

**`\v`** 垂直制表符

**`\\`** 字面反斜杠

**`\0`**`nnn`（零）八进制值为 `nnn` 的字符

```sh
$ echo -e "a\vb"
a
 b
$ echo -e a\\vb
a
 b
$ echo -e "a\\b"
a\b
$ echo -e a\\\\b
a\b
```

如果 `string` 未用引号括起，则反斜杠本身必须用反斜杠转义以保护它免受 shell 影响。例如

> `export` `name`=`value`

**\[`+`** ]`num`] 表示命令号的正数；命令号可以用 `-l` 选项显示。

**`-`** `num` 表示之前执行了 `num` 个命令的命令的负十进制数。例如，-1 是紧接的前一个命令。

**`string`** 表示以该字符串开头的最近输入命令的字符串。如果未用 `-s` 同时指定 `old`=`new` 操作数，则第一个操作数的字符串形式不能包含嵌入式等号。

**`-e`** `editor` 使用 `editor` 命名的编辑器编辑命令。`editor` 字符串是命令名，通过 `PATH` 变量进行搜索。未指定 `-e` 时，使用 `FCEDIT` 变量中的值作为默认值。如果 `FCEDIT` 为空或未设置，使用 `EDITOR` 变量的值。如果 `EDITOR` 为空或未设置，使用 ed(1) 作为编辑器。

**`-l`**（ell）列出命令而非对其调用编辑器。命令按 `first` 和 `last` 操作数指示的顺序写入（受 `-r` 影响），每个命令前面有命令号。

**`-n`** 用 `-l` 列出时抑制命令号。

**`-r`** 反转列出（用 `-l`）或编辑（既不用 `-l` 也不用 `-s`）的命令顺序。

**`-s`** 不调用编辑器重新执行命令。

**`first`**

**`last`** 选择要列出或编辑的命令。可访问的前一命令数由 `HISTSIZE` 变量的值确定。`first` 或 `last` 或两者的值是以下之一：

**`FCEDIT`** 用于历史编辑的编辑器名称。

**`HISTSIZE`** 可访问的前一命令数。

* 如果 `optstring` 的第一个字符不是冒号，则 `OPTARG` 未设置，`var` 设置为 `?`，并向 stderr 写入诊断消息。
* 如果 `optstring` 的第一个字符是冒号，则 `OPTARG` 设置为找到的选项字符，当缺少必需参数时 `var` 设置为 `:`，当 `optstring` 未指定该选项时 `var` 设置为 `?`，且不向 stderr 写入诊断消息。

```sh
[`job_number` ]`current` `state` `command`]
```

**`+`** 标识将用作 `fg` 或 `bg` 命令默认值的作业。

**`-`** 标识如果当前默认作业退出将变为默认值的作业。

**<`space`>** 用于所有既未标记 `+` 也未标记 `-` 的其他作业。

**Running** 指示作业未被信号挂起且未退出。

**Done** 指示作业已完成并返回退出状态零。

**Done(`code`)** 指示作业正常完成并以指定的非零退出状态 `code` 退出。

**Suspended** 指示作业被 `SIGTSTP` 信号中断。这通常是因为按了 ‘Ctrl-Z’。

**Suspended** (signal) 指示作业被 `SIGSTOP` 信号中断。

**Stopped** (tty input) 指示作业被 `SIGTTIN` 信号中断。这通常是因为命令在后台时尝试从终端读取。

**Stopped** (tty output) 指示作业被 `SIGTTOU` 信号中断。这通常是因为命令在后台时尝试更改终端设置或（如果 `stty tostop` 生效；参见 stty(1)）写入终端。

**`job_number`** 可用于向 `bg`、`fg`、`kill` 和 `wait` 命令标识进程组的数字。使用这些命令，可以通过在 `job_number` 前加 `%` 来标识作业。另参见“作业控制”。

**`current`** 以下字符之一：

**`state`** 以下字符串之一，描述当前作业状态：参见 signal(3) 了解上述信号含义的更多信息。

**`command`** 给 shell 的关联命令。

> `readonly` `name`=`value`

* 不带参数时，列出所有 shell 变量的值。
* 如果给出选项（短形式或使用长 “`-/+o` `longname`” 形式），则按“参数列表处理”一节中所述设置或清除指定选项。
* 如果指定了 “`--`” 选项，`set` 将用后续参数替换 shell 的位置参数。如果 “`--`” 选项后没有参数，则清除所有位置参数，等效于执行命令 “`shift $#`”。在指定用作位置替换参数的参数时可以省略 “`--`” 标志。不建议这样做，因为第一个参数可能以连字符（`-`）或加号（`+`）开头，`set` 命令会将其解释为启用或禁用选项的请求。

**`-b`** `sbsize` 套接字缓冲区使用的最大大小，以字节为单位。

**`-c`** `coredumpsize` 核心转储文件的最大大小，以 512 字节块为单位。将 `coredumpsize` 设置为 0 可防止创建核心转储文件。

**`-d`** `datasize` 进程数据段的最大大小，以千字节为单位。

**`-f`** `filesize` 文件的最大大小，以 512 字节块为单位。

**`-k`** `kqueues` 此用户 ID 的最大 kqueue 数（参见 kqueue(2)）。

**`-l`** `lockedmem` 进程可锁定内存的最大大小，以千字节为单位。

**`-m`** `memoryuse` 进程的最大常驻集大小，以千字节为单位。

**`-n`** `nofiles` 进程可打开的最大描述符数。

**`-o`** `umtxp` 此用户 ID 的最大进程共享锁数（参见 [pthread(3)](/pthread/pthread.3.md)）。

**`-p`** `pseudoterminals` 此用户 ID 的最大伪终端数。

**`-s`** `stacksize` 栈段的最大大小，以千字节为单位。

**`-t`** `time` 每个进程使用的最大 CPU 时间量，以秒为单位。

**`-u`** `userproc` 此用户 ID 的最大同时进程数。

**`-v`** `virtualmem` 进程的最大虚拟大小，以千字节为单位。

**`-w`** `swapuse` 为此用户 ID 预留或使用的最大交换空间量，以千字节为单位。

**`:`** \[`arg` `...`] 返回 0（真）退出值的空命令。任何参数都被丢弃。

**`.`** `file` shell 读取并执行指定文件中的命令。`return` 命令可用于返回到 `.` 命令的调用者。如果 `file` 包含任何 `/` 字符，则按原样使用。否则，shell 在 `PATH` 中搜索该文件。如果在 `PATH` 中未找到，则在当前工作目录中查找。

**`[`** test(1) 的内建等价物。

**`alias`** \[`name`\[=`string` ]`...`]]]] 如果指定了 `name`=`string`，shell 定义别名 `name`，值为 `string`。如果仅指定 `name`，则打印别名 `name` 的值。不带参数时，`alias` 内建命令打印所有已定义别名的名称和值（参见 `unalias`）。别名值以适当的引用写入，以便适合重新输入 shell。另参见“别名”小节。

**`bg`** \[`job ...`] 在后台继续指定作业（如果未给出作业则为当前作业）。参见“作业控制”了解作业 ID 形式列表。

**`bind`** \[`-aeklrsv` ]\[`key` \[`command` ]]]]] 列出或更改行编辑器 editline(7) 的键绑定。此命令在 editrc(5) 中有文档说明。

**`break`** \[`num`] 参见“流控结构”小节。

**`builtin`** `cmd` \[`arg ...`] 执行指定的内建命令 `cmd`。当用户希望用与内建命令同名的 shell 函数覆盖该内建命令时，这很有用。

**`cd`** \[`-L | -P` ]\[`-e` ]\[`directory`]]]

**`cd`** `-` 切换到指定的 `directory`，如果未指定 `directory` 则切换到 `HOME` 环境变量中指定的目录，如果 `directory` 为 `-` 则切换到 `OLDPWD` 环境变量中指定的目录。如果 `directory` 不以 `/`、`.` 或 `..` 开头，则在 `CDPATH` 变量中列出的目录中搜索指定的 `directory`。如果 `CDPATH` 未设置，则搜索当前目录。`CDPATH` 的格式与 `PATH` 相同。在交互式 shell 中，如果使用了 `CDPATH` 机制或 `directory` 为 `-`，`cd` 命令将打印它实际切换到的目录名称。如果指定了 `-P` 选项，`..` 物理处理，符号链接在处理 `..` 组件之前被解析。如果指定了 `-L` 选项，`..` 逻辑处理。这是默认值。`-e` 选项使 `cd` 在无法可靠或完全确定新目录的完整路径名时返回退出状态 1。通常这不视为错误，尽管会打印警告。如果更改目录失败，退出状态大于 1。如果目录已更改，退出状态为 0，如果给出了 `-e` 则也为 1。

**`chdir`** `cd` 内建命令的同义词。

**`command`** \[`-p` ]\[`utility` \[`argument ...`]]] 第一种调用形式执行指定的 `utility`，在搜索中忽略 shell 函数。如果 `utility` 是特殊内建命令，则像常规内建命令一样执行。如果指定了 `-p` 选项，则使用保证能找到所有标准实用程序的 `PATH` 默认值执行命令搜索。

**`continue`** \[`num`] 参见“流控结构”小节。

**`echo`** \[`-e | -n` ]\[`string ...`]] 将以空格分隔的参数列表打印到标准输出并追加换行符。只能指定 `-e` 和 `-n` 选项之一。

**`eval`** `string ...` 用空格连接所有参数。然后重新解析并执行命令。

**`exec`** \[`command` \[arg ...]] 除非省略 `command`，否则 shell 进程被指定程序替换（必须是真正的程序，而非 shell 内建命令或函数）。`exec` 命令上的任何重定向都标记为永久，因此它们在 `exec` 命令完成时不会被撤销。

**`exit`** \[`exitstatus`] 终止 shell 进程。如果给出了 `exitstatus`，它用作 shell 的退出状态。否则，如果 shell 正在执行 `EXIT` trap，则使用 trap 之前最后一个命令的退出状态；如果 shell 正在为信号执行 trap，则 shell 通过向自己重新发送信号来退出。否则，使用前面命令的退出状态。退出状态应为 0 到 255 之间的整数。

**`export`** `name ...`

**`export`** \[`-p`] 导出指定名称，使其出现在后续命令的环境中。取消导出变量的唯一方法是 `unset` 它。shell 允许在导出变量的同时设置其值，写入形式为：不带参数时，`export` 命令列出所有已导出变量的名称。如果指定了 `-p` 选项，已导出变量以 “`export` `name`=`value`” 行的形式打印，适合重新输入 shell。

**`false`** 返回非零（假）退出值的空命令。

**`fc`** \[`-e` `editor` ]\[`first` \[`last`]]]

**`fc`** `-l` \[`-nr` ]\[`first` \[`last`]]]

**`fc`** `-s` \[`old`=`new` ]\[`first`]] `fc` 内建命令列出、或编辑并重新执行先前输入到交互式 shell 的命令。

**`fg`** \[`job`] 将指定 `job` 或当前作业移至前台。参见“作业控制”了解作业 ID 形式列表。

**`getopts`** `optstring var` 解析命令行选项和参数。第一个参数 `optstring` 应为一系列字母，每个字母可能后跟冒号，表示该选项接受参数。指定的变量 `var` 设置为解析的选项。下一个参数的索引放入 shell 变量 `OPTIND`。如果选项接受参数，则放入 shell 变量 `OPTARG`。如果找到的字符未由 `optstring` 指定或缺少必需参数，则该选项被视为无效：`getopts` 在遇到选项结尾时返回假值 (1)。可以通过赋值 `OPTIND=1` 来解析新的参数集。POSIX `getopts` 命令弃用旧的 getopt(1) 命令。

**`hash`** \[`-rv` ]\[`command ...`]] shell 维护一个哈希表来记忆命令的位置。不带任何参数时，`hash` 命令打印此表的内容。带参数时，`hash` 命令从哈希表中移除每个指定的 `command`（除非它们是函数）然后定位它。使用 `-v` 选项，`hash` 在找到命令时打印它们的位置。`-r` 选项使 `hash` 命令删除哈希表中除函数外的所有条目。

**`jobid`** \[`job`] 打印指定 `job` 中进程的进程 ID。如果省略 `job` 参数，使用当前作业。参见“作业控制”了解作业 ID 形式列表。

**`jobs`** \[`-lps` ]\[`job ...`]] 打印有关指定作业的信息，如果未给出 `job` 参数则打印所有作业的信息。参见“作业控制”了解作业 ID 形式列表。如果未指定 `-l` 选项，每个作业的输出行如下：其中：如果指定了 `-l` 选项，作业的 PID 插入到 `state` 字段之前。如果指定了 `-p` 选项，仅打印进程组首长的进程 ID，每行一个。如果指定了 `-s` 选项，仅打印作业命令的 PID，每行一个。

**`kill`** [kill(1)](/man1/kill.1.md) 的内建等价物，另外支持通过将作业 ID 指定为参数来向作业发送信号。参见“作业控制”了解作业 ID 形式列表。

**`local`** \[`variable`\[=value ]]\[`-`]]] 参见“函数”小节。

**`printf`** printf(1) 的内建等价物。

**`pwd`** \[`-L | -P`] 打印当前目录的路径。内建命令可能与同名程序不同，因为内建命令记住当前目录是什么，而非每次重新计算。这使其更快。但是，如果当前目录被重命名，[pwd(1)](/man1/pwd.1.md) 的内建版本将继续打印该目录的旧名称。如果指定了 `-P` 选项，解析符号链接。如果指定了 `-L` 选项，打印 shell 对当前目录的认知（不解析符号链接）。这是默认值。

**`read`** \[`-p` `prompt` ]\[]] `-t` `timeout` ]\[`-er` ]`variable ...`] 如果指定了 `-p` 选项且标准输入是终端，则打印 `prompt`。然后从标准输入读取一行。从该行删除尾随换行符，并按上面“空白分割（字段分割）”一节中所述分割该行，各部分按顺序分配给变量。如果部分多于变量，剩余部分（连同分隔它们的 `IFS` 中的字符）分配给最后一个变量。如果变量多于部分，剩余变量分配空字符串。反斜杠被特殊处理，除非指定了 `-r` 选项。如果反斜杠后跟换行符，则反斜杠和换行符将被删除。如果反斜杠后跟任何其他字符，则反斜杠将被删除，后面的字符将被视为不在 `IFS` 中，即使它在。如果指定了 `-t` 选项且在提供完整输入行之前 `timeout` 过期，`read` 命令将返回如同被 `SIGALRM` 终止的退出状态，不分配任何值。`timeout` 值可选地后跟 `s`、`m` 或 `h` 之一，以显式指定秒、分钟或小时。如果未提供，则假定为 `s`。多个值-单位组可以串在一起，在这种情况下它们相加，例如 `1h30m15s` 总计 5,415 秒。`-e` 选项仅为与旧脚本向后兼容而存在。退出状态成功时为 0，文件结束时为 1，发生错误时在 2 到 128 之间，如果被 trap 的信号中断 `read` 则大于 128。

**`readonly`** \[`-p` ]\[`name ...`]] 每个指定的 `name` 被标记为只读，使其不能随后修改或取消设置。shell 允许在标记为只读的同时设置变量的值，使用以下形式：不带参数时，`readonly` 命令列出所有只读变量的名称。如果指定了 `-p` 选项，只读变量以 “`readonly` `name`=`value`” 行的形式打印，适合重新输入 shell。

**`return`** \[`exitstatus`] 参见“函数”小节。

**`set`** \[`-/+abCEefIimnpTuVvx` ]\[`-/+o` `longname`]] ]\[`--` `arg ...`] `set` 命令执行三种不同的功能：

**`setvar`** `variable value` 将指定的 `value` 赋给指定的 `variable`。`setvar` 命令用于在将值赋给名称作为参数传递的变量的函数中。通常最好写 “`variable`=`value`” 而非使用 `setvar`。

**`shift`** \[`n`] 将位置参数移位 `n` 次，如果未指定 `n` 则移位一次。移位将 `$1` 的值设置为 `$2` 的值，`$2` 的值设置为 `$3` 的值，依此类推，将 `$#` 的值减一。为可移植性起见，应避免在零个位置参数时移位，因为 shell 可能中止。

**`test`** test(1) 的内建等价物。

**`times`** 打印执行 shell 进程及其子进程所花费的时间。第一行输出显示 shell 进程本身的用户和系统时间，第二行包含子进程的用户和系统时间。

**`trap`** \[`action` ]`signal ...`]

**`trap`** `-l` 使 shell 在收到任何指定 `signal` 时解析并执行 `action`。信号由名称或编号指定。此外，伪信号 `EXIT` 可用于指定 shell 终止时执行的 `action`。`action` 可以是空字符串或连字符（`-`）；前者使指定信号被忽略，后者使采取默认操作。省略 `action` 并仅使用信号编号是请求默认操作的另一种方式。在子 shell 或实用程序环境中，shell 将被 trap（但非被忽略）的信号重置为默认操作。`trap` 命令对进入 shell 时已被忽略的信号无效。选项 `-l` 使 `trap` 命令显示有效信号名称列表。

**`true`** 返回 0（真）退出值的空命令。

**`type`** \[`name ...`] 将每个 `name` 解释为命令并打印命令搜索的解析结果。可能的解析结果为：shell 关键字、别名、特殊 shell 内建命令、shell 内建命令、命令、跟踪别名和未找到。对于别名，打印别名展开；对于命令和跟踪别名，打印命令的完整路径名。

**`ulimit`** \[`-HSabcdfklmnopstuvw` ]\[`limit`]] 设置或显示资源限制（参见 getrlimit(2)）。如果指定了 `limit`，将设置命名的资源；否则显示当前资源值。如果指定了 `-H`，将设置或显示硬限制。虽然允许任何人降低硬限制，但只有超级用户可以提高它。`-S` 选项指定软限制。显示限制时，只能给出 `-S` 或 `-H` 之一。默认为显示软限制，并设置硬限制和软限制。选项 `-a` 使 `ulimit` 命令显示所有资源。此模式下不接受 `limit` 参数。其余选项指定要显示或修改的资源值。它们互斥。

**`umask`** \[`-S` ]\[`mask`]] 将文件创建掩码（参见 umask(2)）设置为 `mask` 指定的八进制或符号（参见 [chmod(1)](/man1/chmod.1.md)）值。如果省略参数，打印当前掩码值。如果指定了 `-S` 选项，输出为符号形式，否则输出为八进制。

**`unalias`** \[`-a` ]\[`name ...`]] 移除指定的别名。如果指定了 `-a`，移除所有别名。

**`unset`** \[`-fv` ]`name ...`] 取消设置并取消导出指定的变量或函数。如果指定了 `-v` 选项或未给出选项，则 `name` 参数被视为变量名。如果指定了 `-f` 选项，则 `name` 参数被视为函数名。

**`wait`** \[`job ...`] 等待每个指定 `job` 完成，并返回最后指定 `job` 中最后一个进程的退出状态。如果指定的任何 `job` 对 shell 未知，则将其视为以退出状态 127 退出的已知作业。如果未给出操作数，等待所有作业完成并返回退出状态零。

### 命令行编辑

当 `sh` 从终端交互使用时，可以使用 `vi` 模式命令行编辑编辑当前命令和命令历史（参见“内建命令”中的 `fc`）。此模式使用类似于 [vi(1)](/man1/vi.1.md) 手册页中描述的子集的命令。命令 “`set -o vi`”（或 “`set -V`”）启用 `vi` 模式编辑并将 `vi` 置于 `vi` 插入模式。启用 `vi` 模式后，可以通过键入 在插入模式和命令模式之间切换。在命令模式下按 将该行传递给 shell。

类似地，“`set -o emacs`”（或 “`set -E`”）命令可用于启用 `emacs` 风格命令行编辑功能的子集。

`sh` 中的命令行编辑由 editline(7) 处理。

## 环境变量

以下环境变量影响 `sh` 的执行：

**`ENV`** 交互式 shell 的初始化文件。

**`LANG`** , `LC_*` 区域设置。这些由 shell 的子进程继承，并被 shell 自身以有限方式使用。

**`OLDPWD`** 前一个当前目录。这由 `cd` 使用和更新。

**`PWD`** 当前目录的绝对路径名，可能包含符号链接。这由 shell 使用和更新。

**`TERM`** shell 的默认终端设置。这由 shell 的子进程继承，并用于历史编辑模式。

此外，环境变量在启动时转换为 shell 变量，可能如“特殊变量”下所述影响 shell。

## 文件

**`~/.profile`** 用户登录配置文件。

**`~/.sh_history`** 默认 shell 历史。

**`~/.shrc`** 默认 shell 资源。

**`/etc/profile`** 系统登录配置文件。

**`/etc/shells`** shell 数据库。

**`/etc/suid_profile`** 特权 shell 配置文件。

## 退出状态

如果找不到 `script`，退出状态为 127；如果由于其他原因无法打开，退出状态为 126。shell 检测到的其他错误（如语法错误）将导致 shell 以非零退出状态退出。如果 shell 不是交互式 shell，shell 文件的执行将被中止。否则 shell 将返回最后执行的命令的退出状态，或者如果 `exit` 内建命令使用数字参数，则返回该参数。

## 参见

[builtin(1)](/man1/builtin.1.md), chsh(1), echo(1), ed(1), emacs(1) (`ports/editors/emacs`), [kill(1)](/man1/kill.1.md), printf(1), [pwd(1)](/man1/pwd.1.md), [test(1)](/man1/test.1.md), [vi(1)](/man1/vi.1.md), [execve(2)](/sys/execve.2.md), [getrlimit(2)](/sys/getrlimit.2.md), [umask(2)](/sys/umask.2.md), [signal(3)](/gen/signal.3.md), [wctype(3)](/locale/wctype.3.md), editrc(5), [shells(5)](/man5/shells.5.md), editline(7)

## 历史

`sh` 命令，即 Thompson shell，出现在 Version 1 AT\&T UNIX 中。它在 Version 7 AT\&T UNIX 中被 Bourne shell 取代，后者继承了 `sh` 名称。

此版本的 `sh` 于 1989 年在 BSD 许可下重写，源自 AT\&T System V Release 4 UNIX 的 Bourne shell，首次出现在 4.3BSD 中。

## 作者

此版本的 `sh` 最初由 Kenneth Almquist 编写。

## 缺陷

`sh` 实用程序不识别 UTF-8 以外的多字节字符。使用 `IFS` 分割不识别多字节字符。
