# bc.1

BC(1)

BC(1)

General Commands Manual

BC(1)

## [名称](#__u540D___u79F0_)

bc - 任意精度十进制算术语言和计算器

## [概要](#__u6982___u8981_)

**bc** \[**-ghilPqsvVw**] \[**--global-stacks**] \[**--help**] \[**--interactive**] \[**--mathlib**] \[**--no-prompt**] \[**--quiet**] \[**--standard**] \[**--warn**] \[**--version**] \[**-e** *expr*] \[**--expression**=*expr*...] \[**-f** *file*...] \[**--file**=*file*...] \[*file*...]

## [描述](#__u63CF___u8FF0_)

bc(1) 是 POSIX 于 1991 年首次标准化的语言的交互式处理器。（当前标准在 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html)。>) 该语言提供无限精度的十进制算术，并且有点类似于 C，但存在差异。此类差异将在本文档中注明。

在解析和处理选项之后， bc(1) 读取命令行上给出的任何文件并在从 **stdin** 读取之前执行它们。

bc(1) 是 *任何* bc(1)的直接替代品，包括（尤其是）GNU bc(1)。除了其他实现之外，它还具有许多扩展和额外功能。

## [选项](#__u9009___u9879_)

以下是 bc(1) 接受的选项。

**-g**, **--global-stacks**

将全局变量 **ibase**、 **obase**、 **scale** 和 **seed** 转换为堆栈。

这样做的效果是，在每次函数调用时，所有四个当前值的副本都被压入堆栈，并在每个函数返回时弹出。这意味着函数可以分配给任何和所有全局变量，而不必担心更改会影响其他函数。因此，假设一个名为 **output(x,b)** 的函数，简单地输出以 **b** 为基数的 **x** ，可以这样写：

define void output(x, b) { obase=b x }

而不是这样：

define void output(x, b) { auto c c=obase obase=b x obase=c }

这使得编写函数变得更加容易。

(**注意**: 函数 **output(x,b)** 存在于扩展数学库中。参见 **库** 部分。)

但是，由于使用此标志意味着函数无法全局设置 **ibase**、 **obase**、 **scale** 或 **seed** ，因此这样做的函数将无法再工作。有两个可能的用例，每个都有一个解决方案。

首先，如果在启动时调用函数将 bc(1) 转换为数字转换器，则可以用各种 shell 别名替换该功能。例如：

alias d2o="bc -e ibase=A -e obase=8" alias h2b="bc -e ibase=G -e obase=2"

其次，如果一个函数的目的是为任何其他目的全局设置 **ibase**、 **obase**、 **scale** 或 **seed** ，则可以将其拆分为一到四个函数（基于它设置的全局变量的数量），并且每个函数都可以返回全局所需的值。

对于设置 **seed** 的函数，分配给 **seed** 的值不会传播到父函数。这意味着他们看到的伪随机数序列将与任何父函数看到的伪随机数序列不同。仅在设置 **seed** 后才会出现这种情况。

如果一个函数希望不影响其父函数的伪随机数序列，但希望使用相同的 **seed** ，则可以使用以下行：

seed = seed

如果每次运行 bc(1) 都需要此选项的行为，则用户可以确保定义 **BC\_ENV\_ARGS** 并包含此选项（有关详细信息，请参阅 **环境变量** 部分）。

如果使用 **-s**、 **-w** 或任何等效项，则忽略此选项。

这是一个 **不可移植的扩展**。

**-h**, **--help**

打印使用消息并退出。

**-i**, **--interactive**

强制交互模式。（请参阅 **交互模式** 部分。）

这是一个 **不可移植的扩展**。

**-l**, **--mathlib**

将 **scale** (参见 **语法** 部分）设置为 **20** ，并在运行任何代码（包括命令行上指定的任何表达式或文件）之前加载包含的数学库和扩展的数学库。

要了解库中的内容，请参阅 **库** 部分。

**-P**, **--no-prompt**

在 TTY 模式下禁用提示。（提示仅在 TTY 模式下启用。请参阅 **TTY MODE** 部分）这主要适用于那些不想要提示或不习惯在 bc(1) 中使用提示的用户。大多数用户希望将此选项放在 **BC\_ENV\_ARGS** （请参阅 **环境变量** 部分）。

这是一个 **不可移植的扩展**。

**-q**, **--quiet**

此选项是为了与 GNU bc(1) (<https://www.gnu.org/software/bc/>) 兼容；这是一个空操作。如果没有这个选项， GNU bc(1) 会打印一个版权标题。如果提供一个或多个 **-v**、 **-V** 或 **--version** 选项，则此 bc(1) 仅打印版权标头。

这是一个 **不可移植的扩展**。

**-s**, **--standard**

准确处理标准定义的语言 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html>) ，如果使用任何扩展，则会出错。

这是一个 **不可移植的扩展**。

**-v**, **-V**, **--version**

打印版本信息（版权标题）并退出。

这是一个 **不可移植的扩展**。

**-w**, **--warn**

像 **-s** 和 **--standard** 一样，除了为非标准扩展打印警告（而不是错误）并且继续正常执行。

这是一个 **不可移植的扩展**。

**-e** *expr*, **--expression**=*expr*

对 *expr* 求值。 如果给出多个表达式，则按顺序计算它们。如果还给出文件（见下文），则表达式和文件将按照给定的顺序进行求值。这意味着如果在表达式之前给出文件，则首先读入并对该文件求值。

如果此选项在命令行中给出（即不在 **BC\_ENV\_ARGS** 中，请参阅 **环境变量** 部分），则在处理完所有表达式和文件后， bc(1) 将退出，除非 **-** (**stdin**) 作为参数给出 **-f** 或 **--file** 至少一次，无论是在命令行还是在 **BC\_ENV\_ARGS** 中。但是，如果在给出 **-f-** 或等效项之后给出任何其他 **-e**、 **--expression**、 **-f** 或 **--file** 参数， bc(1) 将给出致命错误并退出。

这是一个 **不可移植的扩展**。

**-f** *file*, **--file**=*file*

读取 *file* 并逐行评估它，就好像它是通过 **stdin** 。取的一样。如果还给出表达式（见上文），则按照给定的顺序计算表达式。

如果此选项在命令行中给出（即不在 **BC\_ENV\_ARGS** 中，请参阅 **ENVIRONMENT VARIABLES** 部分），则在处理完所有表达式和文件后， bc(1) 将退出，除非 **-** (**stdin**) 作为参数给出至少一次到 **-f** 或 **--file** 。但是，如果在给出 **-f-** 或等效项之后给出任何其他 **-e**、 **--expression**、 **-f** 或 **--file** 参数， bc(1) 将给出致命错误并退出。

这是一个 **不可移植的扩展**。

所有长选项都是 **不可移植的扩展**。

## [标准输出](#__u6807___u51C6___u8F93___u51FA_)

任何非错误输出都会写入 **stdout** 。此外，如果启用历史记录（参见 **历史** 部分）和提示（参见 **TTY 模式** 部分），则两者都将输出到 **stdout**。

**注意**: 与其他bc(1)实现不同，如果bc(1)不能写入 **stdout** ，它将发出致命错误(参见 **退出状态** 部分），因此如果 **stdout** 关闭，如 **bc >&-** ，它将退出并报错。这样做是为了使 bc(1) 可以在 **stdout** 重定向到文件时报告问题。

如果有脚本依赖于其他 bc(1) 实现的行为，建议更改这些脚本以将 **stdout** 重定向到 **/dev/null**。

## [标准错误](#__u6807___u51C6___u9519___u8BEF_)

任何错误输出都会写入 **stderr**。

**注意**: 与其他 bc(1) 实现不同，如果 bc(1) 无法写入 **stderr** ，它将发出致命错误（请参阅 **退出状态** 部分），因此如果 **stderr** 已关闭，如 **bc 2>&-** 它将退出并报错。这样做是为了在将 **stderr** 重定向到文件时 bc(1) 可以退出并显示错误代码。

如果有脚本依赖于其他 bc(1) 实现的行为，建议更改这些脚本以将 **stderr** 重定向到 **/dev/null**。

## [语法](#__u8BED___u6CD5_)

bc(1) 程序的语法大多类似于 C，但有一些区别。此 bc(1) 遵循 POSIX 标准 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html>) ，这是 bc(1) 接受的语言的更全面的资源。本节旨在总结和列出该标准的所有扩展。

在下面的部分中，**E** 表示表达式， **S** 表示语句， **I** 表示标识符。

标识符 (**I**) 以小写字母开头，后跟任意数量的小写字母 (**a-z**)、数字 (**0-9**) 和下划线 (**\_**) （最多 **BC\_NAME\_MAX-1**) 。 正则表达式是 **\[a-z]\[a-z0-9\_]\***。 具有多个字符（字母）的标识符是 **不可移植的扩展**。

**ibase** 是一个决定如何解释常数的全局变量。 它是 “input” 基数，或用于解释输入数字的数字基数。 **ibase** 最初是 **10**。 如果命令行上没有给出 **-s** (**--standard**) 和 **-w** (**--warn**) 标志，则 **ibase** 的最大允许值为 **36**。 否则为 **16**。 **ibase** 的最小允许值为 **2**。 可以在 bc(1) 程序中使用 **maxibase()** 内置函数查询 **ibase** 的最大允许值。

**obase** 是一个全局变量，决定如何输出结果。 它是 “output” 基数，或用于输出数字的基数。 **obase** 最初是 **10**。 **obase** 最大允许值为 **BC\_BASE\_MAX** ，可以在 bc(1) 程序中使用 **maxobase()** 内置函数进行查询。 **obase** 的最小允许值为 **0**。 如果 **obase** 为 **0** ，则以科学计数法输出值，如果 **obase** 为 **1** ，则以工程计数法输出值。 否则，以指定的基数输出值。

以科学和工程符号输出是 **不可移植的扩展**。

表达式的 *scale* 是小数点右边的表达式结果中的位数，而 **scale** 是一个全局变量，它设置任何运算的精度，但有例外。 **scale** 最初为 **0**。 **scale** 不能为负。 **scale** 的最大允许值为 **BC\_SCALE\_MAX** ，可以在 bc(1) 程序中使用 **maxscale()** 内置函数查询。

bc(1) 既有 *global* 变量， *local* 变量。 所有 *local* 变量都是函数的局部变量；它们由参数或在函数的 **auto** 列表引入（参见 **函数** 部分）。 如果访问的变量不是参数或在 **auto** 列表中，则假定它是 *global* 的。 如果父函数具有子函数认为 *global* 变量的 *local* 变量版本，则子函数中该 *global* 变量的值是父函数中变量的值，而不是实际\_global\_ 变量的值。

以上所有内容也适用于数组。

如果语句是一个表达式(即任何指定的表达式或操作数)，则会打印该语句的值，除非最低优先级操作符是赋值运算符 *and* ，表达式用括号括起来。

**last** 打印的值也分配给特殊变量。 单个点 (**.**) 也可以用作 **last** 的同义词。 这些是 **不可移植的扩展**。

分号或换行符都可以分隔语句。

### [注释](#__u6CE8___u91CA_)

有两种评论：

1.

块注释包含在 **/\*** 和 **\*/** 中。

2.

行注释从 **#** 开始，直到（不包括）下一个换行符。 这是一个 **不可移植的扩展**。

### [命名表达式](#__u547D___u540D___u8868___u8FBE___u5F0F_)

以下是 bc(1) 中的命名表达式：

1.

变量: **I**

2.

数组元素: **I\[E]**

3.

**ibase**

4.

**obase**

5.

**scale**

6.

**seed**

7.

**last** 或一个点 (**.**)

数字 6 和 7 是 **不可移植的扩展**。

**seed** 的含义取决于当前的伪随机数生成器，但除了新的主要版本外，保证不会改变。

该值的 *scale* 和符号可能很重要。

如果将先前使用的 **seed** 值分配给 **seed** 并再次使用，则保证伪随机数生成器产生与先前使用 **seed** 值时相同的伪随机数序列。

如果立即再次查询 **seed** ，则不能保证返回分配给 **seed** 的确切值。 但是，如果 **seed** *确实* 返回不同的值，则当分配给 **seed** 时，这两个值都保证产生相同的伪随机数序列。 这意味着分配给 **seed** 的某些值 *不会* 产生唯一的伪随机数序列。 使用 **rand()** 和 **irand(E)** 操作数后， **seed** 的值将发生变化（请参阅下面的 *运算符* 小节），除非传递给 **irand(E)** 的参数为 **0**、 **1** 或负数。

可以分配给 **seed** 的值的长度（有效小数位数）或 *scale* 没有限制。

变量和数组不干扰；用户可以将数组命名为与变量相同的名称。 这也适用于函数（参见 **函数** 部分），因此用户可以拥有一个变量、数组和函数，它们都具有相同的名称，并且它们不会相互影响，无论是否在函数内部。

命名表达式需要作为 **递增**/**递减** 运算符的操作数和 **赋值** 运算符的左侧（请参阅 *运算符* 小节）。

### [运算符](#__u8FD0___u7B97___u7B26_)

以下是 bc(1) 中的有效操作数：

1.

数字（请参阅下面的 *数字* 小节）。

2.

数组索引 (**I\[E]**).

3.

**(E)**: **E** 的值（用于改变优先级）。

4.

**sqrt(E)**: **E** 的平方根。 **E** 必须是非负数。

5.

**length(E)**: **E** 中有效小数位数。

6.

**length(I\[])**: 数组 **I** 的元素个数。 这是一个 **不可移植的扩展**。

7.

**scale(E)**: **E** 的 *scale* 。

8.

**abs(E)**: **E** 的绝对值。 这是一个 **不可移植的扩展**。

9.

**I()**, **I(E)**, **I(E, E)** 等等，其中 **I** 是非 **空** 函数的标识符（请参阅 **函数** 部分的 *空函数* 小节）。 **E** 参数也可以是 **I\[]** 形式的数组，如果函数定义中的相应参数是数组引用，它将自动转换为数组引用（参见 **函数** 部分的 *数组引用* 小节）。

10.

**read()**: 从 **stdin** 读取一行并将其用作表达式。 该表达式的结果是 **read()** 操作数的结果。 这是一个 **不可移植的扩展**。

11.

**maxibase()**: 允许的最大 **ibase** 。 这是一个 **不可移植的扩展**。

12.

**maxobase()**: 允许的最大 **obase** 。 这是一个 **不可移植的扩展**。

13.

**maxscale()**: 允许的最大 **scale** 。 这是一个 **不可移植的扩展**。

14.

**rand()**: 介于 **0** （含）和 **BC\_RAND\_MAX** （含）之间的伪随机整数。 使用这个操作数会改变 **seed** 的值。 这是一个 **不可移植的扩展**。

15.

**irand(E)**: 介于 **0** （包括）和 **E** （不包括）值之间的伪随机整数。 如果 **E** 为负数或非整数（**E**’ 的 *scale* 不为 **0** ），则会引发错误，并且 bc(1) 会重置（请参阅 **重置** 部分），而 **seed** 保持不变。 如果 **E** 大于 **BC\_RAND\_MAX** ，则通过生成几个伪随机整数、将它们乘以 **BC\_RAND\_MAX+1** 的适当幂并将它们相加来实现上界。 因此，可以使用此操作数生成的整数大小是无限的。 使用这个操作数会改变 **seed** 的值，除非 **E** 的值是 **0** 或 **1** 。 在这种情况下，将返回 **0** ，并且 *不会* 更改 **seed** 。 这是一个 **不可移植的扩展**。

16.

**maxrand()**: **rand()** 返回的最大整数。 这是一个 **不可移植的扩展**。

**rand()** 和 **irand(E)** 生成的整数保证尽可能无偏，但受伪随机数生成器的限制。

**注意**: 使用 **rand()** 和 **irand(E)** 的伪随机数生成器返回的值 *不是* 加密安全的。这是使用种子伪随机数生成器的结果。 但是，它们 *保证* 可以使用相同的种子值重现。 这意味着来自 bc(1) 的伪随机数只应该在可重复的伪随机数流是 *必要* 的情况下使用。 在任何其他情况下，请使用非种子伪随机数生成器。

### [数字](#__u6570___u5B57_)

数字是由数字、大写字母和最多 **1** 个句点组成的字符串。 数字最多可以有 **BC\_NUM\_MAX** 个数字。大写字母等于 **9** + 它们在字母表中的位置（即 **A** 等于 **10** 或 **9+1**)。 如果数字或字母与 **ibase** 的当前值无关，则将它们设置为 **ibase** 中最高有效数字的值。

无论 **ibase** 的值如何，单字符数字（即单独的 **A** ）采用它们是有效数字时的值。 这意味着单独的 **A** 总是等于十进制的 **10** ，单独的 **Z** 总是等于十进制的 **35** 。

此外，bc(1) 接受科学计数法的数字。 它们的格式为 **e** 。 指数（ **e** 之后的部分）必须是整数。 例如 **1.89237e9** ，它等于 **1892370000** 。 也允许负指数，因此 **4.2890e-3** 等于 **0.0042890**。

如果分别给出 **-s** 或 **-w** 命令行选项（或等效项），则使用科学记数法是错误或警告。

**警告**: 科学计数法中的数字和指数均根据当前 **ibase** 进行解释，但无论当前 **ibase** 如何，该数字仍乘以 **10^exponent** 。 例如，如果 **ibase** 为 **16** ，并且 bc(1) 被赋予数字字符串 **FFeA** ，则生成的十进制数将为 **2550000000000** ，如果 bc(1) 被赋予数字字符串 **10e-4** ，则生成的十进制数将为 **0.0016** 。

接受输入作为科学记数法是 **不可移植的扩展**.

### [运算符](#__u8FD0___u7B97___u7B26__2)

可以使用以下算术和逻辑运算符。它们按优先级降序排列。同一组中的运算符具有相同的优先级

**++** **--**

类型：前缀和后缀

结合性：无

说明: **递增**、 **递减**

**-** **!**

类型：前缀

结合性：无

说明: **否定**、 **布尔非**

**$**

类型：后缀

结合性：无

说明: **截断**

**@**

类型：二进制

结合性：右

说明: **设置精度**

**^**

类型：二进制

结合性：右

说明: **幂**

**\*** **/** **%**

类型：二进制

结合性: 左

说明: **乘法**、 **除法**、 **取模**

**+** **-**

类型：二进制

结合性: 左

说明: **加法**, **减法**

**<<** **>>**

类型：二进制

结合性: 左

说明: **左移**, **右移**

**=** **<<=** **>>=** **+=** **-=** **\*=** **/=** **%=** **^=** **@=**

类型：二进制

结合性: 右

说明: **赋值**

**==** **<=** **>=** **!=** **<** **>**

类型：二进制

结合性: 左

说明: **关系**

**&&**

类型：二进制

结合性: 左

说明: **布尔值和**

**||**

类型：二进制

结合性: 左

说明: **布尔值或**

下面将更详细地描述运算符。

**++** **--**

前缀和后缀 **递增** 和 **递减** 运算符的行为与它们在 C 中的行为完全相同。它们需要一个命名表达式（参见 *命名表达式* 小节）作为操作数。

这些运算符的前缀版本更有效；尽可能使用它们。

**-**

如果用户尝试对任何值为 **0** 的表达式 **求反** ，则否定运算符返回 **0** 。否则，返回其符号求反的表达式副本。

**!**

如果表达式为 **0** ，则 **布尔非** 运算符返回 **1** ，否则返回 **0** 。

这是一个 **不可移植的扩展**。

**$**

**截断** 运算符返回给定表达式的副本，其中删除所有 *scale* 。

这是一个 **不可移植的扩展**。

**@**

**set precision** 运算符接受两个表达式并返回第一个表达式的副本，其 *scale* 等于第二个表达式的值。 这可能意味着返回的数字没有变化（如果第一个表达式的 *scale* 与第二个表达式的值匹配）、扩展（如果小于）或截断（如果大于）。

第二个表达式必须是整数（无 *scale* ）且非负数。

这是一个 **不可移植的扩展**。

**^**

**幂** 运算符（不是 C 中的 **exclusive or** 运算符）采用两个表达式并将第一个表达式提升到第二个值的幂。 结果的 *scale* 等于 **scale** 。

第二个表达式必须是整数（无 *scale* ），如果为负数，则第一个值必须非零。

**\***

**乘法** 运算符接受两个表达式，将它们相乘，然后返回乘积。 如果 **a** 是第一个表达式的 *scale* ， **b** 是第二个表达式的 *scale* ，则结果的 *scale* 等于 **min(a+b,max(scale,a,b))** 其中 **min()** 和 **max()** 返回的值。

**/**

**除法** 运算符接受两个表达式，将它们相除，然后返回商。 结果的 *scale* 应为 **scale** 值。

第二个表达式必须非零。

**%**

**取模** 运算符采用两个表达式 **a** 和 **b** ，并通过以下方式对它们进行评估：1) 将 **a/b** 计算为当前 **scale** ， 2) 使用步骤 1 的结果计算 **a-(a/b)\*b** 到 *scale* **max(scale+scale(b),scale(a))**.

第二个表达式必须非零。

**+**

**加法** 运算符接受两个表达式 **a** 和 **b** ，并返回总和，其 *scale* 等于 **a** 和 **b** 的 *scale* 最大值。

**-**

**减法** 运算符接受两个表达式 **a** 和 **b** ，并返回差值，其 *scale* 等于 **a** 和 **b** 的 *scale* 最大值。

**<<**

**左移** 运算符接受两个表达式 **a** 和 **b** ，并返回 **a** 值的副本，其小数点向右移动 **b** 位。

第二个表达式必须是整数（无 *scale* ）且非负数。

这是一个 **不可移植的扩展**。

**>>**

**右移** 运算符接受两个表达式 **a** 和 **b**，并返回 **a** 值的副本，其小数点向左移动 **b** 位。

第二个表达式必须是整数（无 *scale* ）且非负数。

这是一个 **不可移植的扩展**。

**=** **<<=** **>>=** **+=** **-=** **\*=** **/=** **%=** **^=** **@=**

**赋值** 运算符接受两个表达式 **a** 和 **b** 其中 **a** 是命名表达式（请参阅 *命名表达式* 小节）。

对于 **=** ,复制 **b** 并将结果分配给 **a** 。 对于所有其他， **a** 和 **b** 作为操作数应用于相应的算术运算符，并将结果分配给 **a** 。

对应于作为扩展的运算符的 **赋值** 运算符本身就是 **不可移植的扩展** 。

**==** **<=** **>=** **!=** **<** **>**

**关系** 运算符比较两个表达式 **a** 和 **b** ，如果关系成立，根据 C 语言语义，结果为 **1** ，否则为 **0** 。

请注意，与 C 不同，这些运算符的优先级低于 **赋值** 运算符，这意味着 **a=b>c** 被解释为 **(a=b)>c** 。

此外，与标准 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html>) 要求不同，这些运算符可以出现在可以使用任何其他表达式的任何地方。 这是一个 **不可移植的扩展**。

**&&**

**布尔 and** 运算符接受两个表达式，如果两个表达式都不为零，则返回**1** ，否则返回 **0** 。

这 *不是* 短路运算符。

这是一个 **不可移植的扩展**。

**||**

**布尔 or** 运算符接受两个表达式，如果其中一个表达式不为零，则返回 **1** ，否则返回 **0** 。

这 *不是* 短路运算符。

这是一个 **不可移植的扩展**。

### [声明](#__u58F0___u660E_)

以下项目是声明：

1.

**E**

2.

**{** **S** **;** ... **;** **S** **}**

3.

**if** **(** **E** **)** **S**

4.

**if** **(** **E** **)** **S** **else** **S**

5.

**while** **(** **E** **)** **S**

6.

**for** **(** **E** **;** **E** **;** **E** **)** **S**

7.

一个空的声明

8.

**break**

9.

**continue**

10.

**quit**

11.

**halt**

12.

**limits**

13.

一串字符，用双引号括起来

14.

**print** **E** **,** ... **,** **E**

15.

**I()**, **I(E)**, **I(E, E)** 等等，其中 **I** 是 **void** 函数的标识符（请参阅 **函数** 部分的 *空函数* 小节）。 **E** 参数也可以是 **I\[]** 形式的数组，如果函数定义中的相应参数是数组引用，它将自动转换为数组引用（参见 **函数** 部分的 *数组引用* 小节）。

数字 4、 9、 11、 12、 14 和 15 是 **不可移植的扩展**。

此外，作为 **不可移植的扩展** ，可以省略 for 循环头中的任何或所有表达式。 如果省略条件（第二个表达式），则假定为常数 **1** 。

**break** 语句使循环停止迭代并在循环后立即恢复执行。 这仅在循环中允许。

**continue** 语句使循环迭代提前停止并返回到循环的开头，包括测试循环条件。 这仅在循环中允许。 这仅在循环中允许。

**if** **else** 语句的作用与 C 中的相同。

**quit** 语句会导致 bc(1) 退出，即使它位于不会执行的分支上（它是编译时命令）。

如果执行 **halt** 语句，则 bc(1) 将退出。 (（与如果它位于未执行的 **if** 语句的分支上的 **quit** 不同，bc(1) 不会退出。）

**limits** 语句打印此 bc(1) 所受的限制。 这就像 **quit** 语句，因为它是一个编译时命令。

表达式本身被评估和打印，然后是换行符。

科学记数法和工程记数法都可用于打印表达式的结果。 科学记数法通过将 **0** 分配给 **obase** 来激活，而工程记数法通过将 **1** 分配给 **obase** 来激活。 要停用它们，只需为 **obase** 分配一个不同的值。

如果使用 **-s** 或 **-w** 命令行选项（或等效项）运行 bc(1)，则会禁用科学记数法和工程记数法。

以科学记数法和/或工程记数法打印数字是一种 **不可移植的扩展**。

### [打印声明](#__u6253___u5370___u58F0___u660E_)

**print** 语句中的 “expressions” 也可以是字符串。 如果是，则有专门解释的反斜杠转义序列。 这些序列是什么，以及它们导致打印的内容如下所示：

**\a**

**\a**

**\b**

**\b**

**\\\\**

**\\**

**\e**

**\\**

**\f**

**\f**

**\q**

**"**

反斜杠后面的任何其他字符都会导致反斜杠和字符按原样打印。

打印语句中的任何非字符串表达式都应分配给 **last** ，就像打印的任何其他表达式一样。

### [求值顺序](#__u6C42___u503C___u987A___u5E8F_)

一个语句中的所有表达式都是从左到右计算的，除非为了维护操作顺序。 这意味着，例如，假设 **i** 等于 **0** ，在表达式中

a\[i++] = i++

**a** 的第一个（或第 0 个）元素设置为 **1** ，并且 **i** 在表达式末尾等于 **2** 。

这包括函数参数。 因此，假设 **i** 等于 **0** ，这意味着在表达式中

x(i++, i++)

传递给 **x()** 的第一个参数是 **0** ，第二个参数是 **1** ，而在函数开始执行之前 **i** 等于 **2** 。

## [函数](#__u51FD___u6570_)

函数定义如下：

define I(I,...,I){ auto I,...,I S;...;S return(E) }

参数列表或 **auto** 列表中的任何 **I** 都可以替换为 **I\[]** 以使参数或 **auto** 成为数组，并且参数列表中的任何 **I** 都可以替换为 **\*I\[]** 以使参数成为数组引用。 采用数组引用的函数的调用者不应在调用中加上星号；它们必须像普通数组参数一样只用 **I\[]** 调用，并且会自动转换为引用。

作为 **不可移植的扩展**， **define** 语句的左大括号可能出现在下一行。

作为 **不可移植的扩展**， return 语句也可以是以下形式之一：

1.

**return**

2.

**return** **(** **)**

3.

**return** **E**

前两个，或者不指定 **return** 语句，等效于 **return (0)**, ，除非该函数是一个 **void** 函数（请参阅下面的 *空函数* 小节）。

### [空函数](#__u7A7A___u51FD___u6570_)

函数也可以是 **空** 函数，定义如下：

define void I(I,...,I){ auto I,...,I S;...;S return }

它们只能用作独立的表达式，这样的表达式将单独打印，除了在打印语句中。

void 函数只能使用上面列出的前两个返回语句。 他们也可以完全省略 return 语句。

“void” 一词不被视为关键字；仍然可以有名为 **void** 的变量、数组和函数。 “void” 这个词只在 **define** 关键字之后被特别对待。

这是一个 **不可移植的扩展**。

### [数组引用](#__u6570___u7EC4___u5F15___u7528_)

对于参数列表中的任意一个数组，如果该数组以形式声明

\*I\[]

这是一个 **参考**。 当函数返回时，函数中数组的任何更改都会反映到传入的数组中。

除此之外，所有函数参数都是按值传递的。

这是一个 **不可移植的扩展**。

## [库](#__u5E93_)

以下所有函数，包括扩展数学库中的函数（请参阅下面的 *扩展库* 小节），在给出 **-l** 或 **--mathlib** 命令行标志时可用，除了在给出 **-s** 选项、 **-w** 选项或等效选项的情况下，扩展数学库在不可用。

### [标准库](#__u6807___u51C6___u5E93_)

标准 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html>) 为数学库定义了以下函数：

**s(x)**

返回 **x** 的正弦值，假定为弧度。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**c(x)**

返回 **x** 的余弦，假定为弧度。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**a(x)**

返回 **x** 的反正切，以弧度为单位。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**l(x)**

返回 **x** 的自然对数。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**e(x)**

返回数学常数 **e** 的 **x** 次方。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**j(x, n)**

返回 **x** 的贝塞尔整数阶 **n** （截断）。

这是一个超越函数（参见下面的 *超越函数* 小节）。

### [扩展库](#__u6269___u5C55___u5E93_)

给出 **-s**/**--standard** 或 **-w**/**--warn** 选项时 *不会* 加载扩展库，因为它们不是标准定义的库的一部分 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html)。>

这是一个 **不可移植的扩展**。

**p(x, y)**

计算 **x** 的 **y** 次方，即使 **y** 不是整数，并将结果返回到当前 **scale** 。

如果 **y** 为负且 **x** 为 **0** ，则为错误。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**r(x, p)**

根据舍入模式返回 **x** 舍入到 **p** 小数位，从 **0** 舍入一半 (<https://en.wikipedia.org/wiki/Rounding#Round\\_half\\_away\\_from\\_zero)。>

**ceil(x, p)**

根据舍入模式从 **x** 舍入返回 **p** 舍入到 **0** 位小数 (<https://en.wikipedia.org/wiki/Rounding#Rounding\\_away\\_from\\_zero)。>

**f(x)**

返回 **x** 的截断绝对值的阶乘。

**perm(n, k)**

如果 **n** ，则返回 **k** 的截断绝对值的 **k <= n** 的截断绝对值的排列。 如果不是，则返回 **0** 。

**comb(n, k)**

如果 **k <= n** ，则返回 **k** 的截断绝对值的 **n** 截断绝对值的组合。 如果不是，则返回 **0** 。

**l2(x)**

返回 **x** 的以 **2** 为底的对数。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**l10(x)**

返回 **x** 的以 **10** 为底的对数。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**log(x, b)**

返回 **x** 的以 **b** 为底的对数。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**cbrt(x)**

返回 **x** 的立方根。

**root(x, n)**

计算 **n**、 **r** 的截断值，并将 **x** 的第 **r** 根返回到当前 **scale** 。

如果 **r** 为 **0** 或负数，则会引发错误并导致 bc(1) 重置（请参阅 **重置** 部分）。 如果 **r** 为偶数且 **x** 为负数，它还会引发错误并导致 bc(1) 重置。

**pi(p)**

将 **pi** 返回到 **p** 个小数位。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**t(x)**

返回 **x** 的正切，假定为弧度。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**a2(y, x)**

返回 **y/x** 的反正切，以弧度为单位。 如果 **y** 和 **x** 都等于 **0** ，则会引发错误并导致 bc(1) 重置（请参阅 **重置** 部分）。 否则，如果 **x** 大于 **0** ，则返回 **a(y/x)**。 如果 **x** 小于 **0** ，并且 **y** 大于或等于 **0** ，则返回 **a(y/x)+pi** 。 如果 **x** 小于 **0** ，并且 **y** 小于 **0**, ，则返回 **a(y/x)-pi** 。 如果 **x** 等于 **0** ，并且 **y** 大于 **0**, 则返回 **pi/2** 。 如果 **x** 等于 **0** ，并且 **y** 小于 **0**，则返回 **-pi/2**。

此函数与许多编程语言中的 **atan2()** 函数相同。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**sin(x)**

返回 **x** 的正弦值，假定为弧度。

这是 **s(x)** 的别名。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**cos(x)**

返回 **x** 的余弦，假定为弧度。

这是 **c(x)** 的别名。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**tan(x)**

返回 **x** 的正切，假定为弧度。

如果 **x** 等于 **1** 或 **-1** ，则会引发错误并导致 bc(1) 重置（请参阅 **重置** 部分）。

这是 **t(x)** 的别名。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**atan(x)**

返回 **x** 的反正切，以弧度为单位。

这是 **a(x)** 的别名。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**atan2(y, x)**

返回 **y/x** 的反正切，以弧度为单位。如果 **y** 和 **x** 都等于 **0** ，则会引发错误并导致 bc(1) 重置（请参阅 **重置** 部分）。 否则，如果 **x** 大于 **0** ，则返回 **a(y/x)** 。 如果 **x** 小于 **0** ，并且 **y** 大于或等于 **0** ，则返回 **a(y/x)+pi** 。 如果 **x** 小于 **0** ，并且 **y** 小于 **0** ，则返回 **a(y/x)-pi** 。 如果 **x** 等于 **0** ，并且 **y** 大于 **0** , 则返回 **pi/2** 。 如果 **x** 等于 **0** ，并且 **y** 小于 **0**, ，则返回 **-pi/2** 。

此函数与许多编程语言中的 **atan2()** 函数相同。

这是 **a2(y, x)** 的别名。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**r2d(x)**

将 **x** 从弧度转换为度数并返回结果。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**d2r(x)**

将 **x** 从度数转换为弧度并返回结果。

这是一个超越函数（参见下面的 *超越函数* 小节）。

**frand(p)**

生成一个介于 **0** （含）和 **1** （不含）之间的伪随机数，小数点后的小数位数等于 **p** 的截断绝对值。 如果 **p** 不为 **0** ，则调用此函数将更改 **seed** 的值。 如果 **p** 为 **0** ，则返回 **0** ，并且**seed** *不* 变。

**ifrand(i, p)**

生成介于 **0** （含）和 **i** 的截断绝对值（不含）之间的伪随机数，小数点后的小数位数等于 **p** 的截断绝对值。 如果 **i** 的绝对值大于等于 **2**, 且 **p** 不为 **0**, 则调用该函数会改变 **seed** 的值；否则，返回 **0** 并且不更改 **seed** 。

**srand(x)**

返回 **x** ，其符号以 **0.5** 的概率翻转。 换句话说，它随机化 **x** 的符号。

**brand()**

返回一个随机布尔值 ( **0** 或 **1**)。

**ubytes(x)**

返回保存 **x** 的截断绝对值所需的无符号整数字节数。

**sbytes(x)**

返回保存 **x** 的截断值所需的有符号二进制补码整数字节数。

**hex(x)**

输出 **x** 的十六进制（以 **16** 为基数）表示。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**binary(x)**

输出 **x** 的二进制（以 **2** 为基数）表示。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**output(x, b)**

输出 **b** 为底 **x** 的表示。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uint(x)**

将 **x** 的二进制和十六进制表示形式输出为无符号整数，并以尽可能少的两个字节的幂。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或负数，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**int(x)**

将 **x** 的二进制和十六进制表示形式输出为带符号的二进制补码整数，并以尽可能少的两个字节的幂。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uintn(x, n)**

将 **x** 的二进制和十六进制表示形式输出为 **n** 字节的无符号整数。 两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数、为负数或无法容纳 **n** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**intn(x, n)**

将 **x** 的二进制和十六进制表示形式输出为 **n** 字节的有符号二进制补码整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或不能放入 **n** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uint8(x)**

将 **x** 的二进制和十六进制表示形式输出为 **1** 字节的无符号整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数、为负数或无法放入 **1** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**int8(x)**

将 **x** 的二进制和十六进制表示形式输出为 **1** 个字节中的有符号二进制补码整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或不能放入 **1** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uint16(x)**

将 **x** 的二进制和十六进制表示形式输出为 **2** 个字节的无符号整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数、为负数或不能容纳 **2** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**int16(x)**

将 **x** 的二进制和十六进制表示形式输出为 **2** 个字节的有符号二进制补码整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或不能容纳 **2** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uint32(x)**

将 **x** 的二进制和十六进制表示形式输出为 **4** 字节的无符号整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数、为负数或无法容纳 **4** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**int32(x)**

将 **x** 的二进制和十六进制表示形式输出为 **4** 字节的有符号二进制补码整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或不能容纳 **4** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**uint64(x)**

将 **x** 的二进制和十六进制表示形式输出为 **8** 字节的无符号整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数、为负数或不能容纳 **8** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**int64(x)**

将 **x** 的二进制和十六进制表示形式输出为 **8** 个字节的有符号二进制补码整数。两个输出都被分成由空格分隔的字节。

如果 **x** 不是整数或不能放入 **8** 个字节，则会打印一条错误消息，但不会重置 bc(1)（请参阅 **重置** 部分）。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**hex\_uint(x, n)**

将 **x** 的截断绝对值的表示形式输出为使用 **n** 字节的十六进制无符号整数。如果 **n** 太小，则不会输出所有值。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**binary\_uint(x, n)**

将 **x** 的截断绝对值的表示形式输出为使用 **n** 字节的二进制无符号整数。如果 **n** 太小，则不会输出所有值。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**output\_uint(x, n)**

使用 **n** 字节将 **x** 的截断绝对值的表示形式输出为当前 **obase** （请参阅 **语法** 部分）中的无符号整数。如果 **n** 太小，则不会输出所有值。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

**output\_byte(x, i)**

输出 **i** 的截断绝对值的字节 **x** ，其中 **0** 是最低有效字节， **number\_of\_bytes - 1** 是最高有效字节。

这是一个 **void** 函数（请参阅 **函数** 部分的 *Void 函数* 小节）。

### [超越函数](#__u8D85___u8D8A___u51FD___u6570_)

所有超越函数都可能返回稍微不准确的结果（最多 1 个 ULP (<https://en.wikipedia.org/wiki/Unit\\_in\\_the\\_last\\_place))。> 这是不可避免的，这篇文章 (<https://people.eecs.berkeley.edu/\\~wkahan/LOG10HAF.TXT>) 解释了为什么不可能也没有必要计算超越函数的精确结果。

由于可能存在不准确性，我建议用户调用这些函数时将精度 (**scale**) 设置为至少高于所需的 1。 如果 *absolutely* 需要精确的结果，用户可以将精度 (**scale**) 加倍，然后截断。

标准数学库中的超越函数是：

•

**s(x)**

•

**c(x)**

•

**a(x)**

•

**l(x)**

•

**e(x)**

•

**j(x, n)**

扩展数学库中的超越函数是：

•

**l2(x)**

•

**l10(x)**

•

**log(x, b)**

•

**pi(p)**

•

**t(x)**

•

**a2(y, x)**

•

**sin(x)**

•

**cos(x)**

•

**tan(x)**

•

**atan(x)**

•

**atan2(y, x)**

•

**r2d(x)**

•

**d2r(x)**

## [重置](#__u91CD___u7F6E_)

当 bc(1) 遇到错误或它具有非默认处理程序的信号时，它会重置。 这意味着发生了几件事。

首先，任何正在执行的函数都会停止并从堆栈中弹出。 这种行为与编程语言中的异常没有什么不同。 然后设置执行点，以便跳过任何等待执行的代码（在所有函数返回之后）。

因此，当 bc(1) 重置时，它会跳过任何剩余的等待执行的代码。 然后，如果它是交互模式，并且错误不是致命错误（参见 **退出状态** 部分），它会要求更多输入；否则，它会以适当的返回码退出。

请注意，此重置行为与 GNU bc(1) 不同，后者试图在导致错误的语句之后立即开始执行语句。

## [性能](#__u6027___u80FD_)

大多数 bc(1) 实现使用 **char** 类型一次计算 **1** 个十进制数字的值，但这可能很慢。这个 bc(1) 做了一些不同的事情。

它使用大整数一次计算多于 **1** 个十进制数字。如果在 **BC\_LONG\_BIT** （参见 **LIMITS** 部分）为 **64** 的环境中构建，则每个整数都有 **9** 个十进制数字。 如果在 **BC\_LONG\_BIT** 为 **32** 的环境中构建，则每个整数都有 **4** 个十进制数字。 这个值（每个大整数的小数位数）称为 **BC\_BASE\_DIGS**。

**BC\_LONG\_BIT** 和 **BC\_BASE\_DIGS** 的实际值可以通过 **limits** 语句查询。

此外，此 bc(1) 使用更大的整数进行溢出检查。此整数类型取决于 **BC\_LONG\_BIT** 的值，但始终至少是用于存储数字的整数类型的两倍。

## [极限](#__u6781___u9650_)

以下是 bc(1) 的限制：

**BC\_LONG\_BIT**

构建 bc(1) 的环境中 **long** t类型的位数。这决定在一个大整数中可以存储多少个十进制数字（请参阅性 **性能** 部分）。

**BC\_BASE\_DIGS**

每个大整数的小数位数（请参阅 **性能** 部分）。取决于 **BC\_LONG\_BIT**。

**BC\_BASE\_POW**

每个大整数可以存储的最大十进制数（参见 **BC\_BASE\_DIGS**) 加**1**。取决于 **BC\_BASE\_DIGS**。

**BC\_OVERFLOW\_MAX**

溢出类型（参见 **性能** 部分）可以容纳的最大数量。取决于 **BC\_LONG\_BIT**。

**BC\_BASE\_MAX**

最大输出基数。设置为 **BC\_BASE\_POW**。

**BC\_DIM\_MAX**

数组的最大大小。设置为 **SIZE\_MAX-1**。

**BC\_SCALE\_MAX**

最大 **scale**.设置为 **BC\_OVERFLOW\_MAX-1**。

**BC\_STRING\_MAX**

字符串的最大长度。设置为 **BC\_OVERFLOW\_MAX-1**。

**BC\_NAME\_MAX**

标识符的最大长度。设置为 **BC\_OVERFLOW\_MAX-1**。

**BC\_NUM\_MAX**

数字的最大长度（十进制数字），包括小数点后的数字。设置为 **BC\_OVERFLOW\_MAX-1**。

**BC\_RAND\_MAX**

**rand()** 运算符返回的最大整数（包括）。设置为 **2^BC\_LONG\_BIT-1**。

指数

最大允许指数（正或负）。设置为 **BC\_OVERFLOW\_MAX**。

变量数

变量/数组的最大数量。设置为 **SIZE\_MAX-1**。

实际值可以通过 **limits** 语句查询。

这些限制实际上是不存在的；限制是如此之大（至少在 64 位机器上）以至它们不成问题。 事实上，在达到这些限制之前，内存应该已经耗尽。

## [环境变量](#__u73AF___u5883___u53D8___u91CF_)

bc(1) 识别以下环境变量：

**POSIXLY\_CORRECT**

如果此变量存在（无论内容如何），bc(1) 的行为就如同给出 **-s** 选项一样。

**BC\_ENV\_ARGS**

这是为 bc(1) 提供命令行参数的另一种方法。它们应该与所有其他命令行参数的格式相同。这些总是首先处理，因此 **BC\_ENV\_ARGS** 中给出的任何文件都将在命令行中给出的参数和文件之前处理。 这使用户能够设置每次调用时使用的 “standard” 选项和文件。 此类文件包含的最有用的东西是用户每次运行 bc(1) 时可能想要的有用功能。

解析 **BC\_ENV\_ARGS** 的代码将正确处理带引号的参数，但它不理解转义序列。 例如，字符串 **“/home/gavin/some bc file.bc”** 将被正确解析，但字符串 **“/home/gavin/some "bc" file.bc”** 将包含反斜杠。

引号解析将处理任何一种引号， **’** 或 **“** 。 因此，如果您有一个文件名中包含任意数量的单引号，您可以使用双引号作为外部引号，如 **”some \`bc' file.bc"** ，反之亦然，如果您有一个带有双引号的文件。但是，由于解析的复杂性，不支持在 **BC\_ENV\_ARGS** 中处理具有两种引号的文件，尽管在由 shell 完成解析的命令行上仍然支持此类文件。

**BC\_LINE\_LENGTH**

如果此环境变量存在并且包含大于 **1** 且小于 **UINT16\_MAX** (**2^16-1**) 的整数，则 bc(1) 将输出该长度的行，包括反斜杠 (**\\**)。 默认行长为 **70**。

## [退出状态](#__u9000___u51FA___u72B6___u6001_)

bc(1) 返回以下退出状态：

**0**

没有错误。

**1**

出现数学错误。这遵循使用 **1** 表示预期错误的标准做法，因为数学错误将在正常执行过程中发生。

数学错误包括除以 **0** 、取负数的平方根、使用负数作为伪随机数生成器的界限、尝试将负数转换为硬件整数、将数字转换为硬件时溢出整数，并尝试在需要整数的地方使用非整数。

幂 (**^**)、位 (**@**)、左移 (**<<**) 和右移 (**>>**) 运算符及其对应的赋值运算符的第二个操作数转换为硬件整数。

**2**

发生解析错误。

解析错误包括意外的 **EOF** 、使用无效字符、未能找到字符串或注释的结尾、使用无效的标记、给出无效的表达式、给出无效的打印语句、给出无效的函数定义、尝试分配到一个不是命名表达式的表达式（参见 **语法** 部分的 *命名表达式* 小节），给出一个无效的 **auto** 列表，有一个重复的 **auto**/函数参数，找不到代码块的结尾，试图返回一个值来自 **void** 函数，尝试使用变量作为引用，并在给出选项 **-s** 或任何等效项时使用任何扩展。

**3**

发生运行时错误。

运行时错误包括将无效编号分配给 **ibase**、**obase** 或 **scale** ；给 **read()** 调用一个错误的表达式，在 **read()** 调用中调用 **read()** ，键入错误，将错误数量的参数传递给函数，尝试调用未定义的函数，以及尝试使用 **void** 函数调用作为表达式中的值。

**4**

发生致命错误。

致命错误包括内存分配错误、I/O 错误、无法打开文件、尝试使用不只有 ASCII 字符的文件（bc(1) 只接受 ASCII 字符）、尝试将目录作为文件打开以及给出无效的命令行选项。

退出状态 **4** 是特殊的；当发生致命错误时，无论 bc(1) 处于何种模式，bc(1) 总是退出并返回 **4** 。

仅当 bc(1) 未处于交互模式时才会返回其他状态（请参阅 **交互模式** 部分），因为 bc(1) 会重置其状态（请参阅 **重置** 部分）并在其中一个错误发生时接受更多输入交互模式。当\*\*-i\*\* 标志或 **--interactive** 选项强制交互模式时也是如此。

这些退出状态允许 bc(1) 在带有错误检查的 shell 脚本中使用，并且可以通过使用 **-i** 标志或 **--interactive** 选项强制其正常行为。

## [交互模式](#__u4EA4___u4E92___u6A21___u5F0F_)

根据标准 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html)，bc(1>) 具有交互模式和非交互模式。 当 **stdin** 和 **stdout** 都连接到终端时，交互模式会自动打开，但 **-i** 标志和 **--interactive** 选项可以在其他情况下打开它。

在交互模式下，bc(1) 会尝试从错误中恢复（请参阅 **重置** 部分），并且在正常执行中，一旦当前输入的执行完成，就会刷新 **stdout** 。

## [TTY 模式](#TTY___u6A21___u5F0F_)

如果 **stdin**、 **stdout** 和 **stderr** 都连接到 TTY，bc(1) 将打开 “TTY 模式”。

启用历史记录需要 TTY 模式（请参阅 **命令行历史** 部分）。还需要启用对 **SIGINT** 信号的特殊处理。

提示在 TTY 模式下启用。

TTY 模式与交互模式不同，因为在 bc(1) 规范 (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html>) 中需要交互模式，而交互模式只需要 **stdin** 和 **stdout** 即可连接到一个终端。

## [信号处理](#__u4FE1___u53F7___u5904___u7406_)

发送 **SIGINT** 将导致 bc(1) 停止执行当前输入。 如果 bc(1) 处于 TTY 模式（参见 **TTY 模式** 部分），它将重置（参见 **重置** 部分）。否则，它将清理并退出。

请注意， “current input” 可能意味着两件事之一。 如果 bc(1) 在 TTY 模式下处理来自 **stdin** 的输入，它将要求更多输入。 如果 bc(1) 正在以 TTY 模式处理来自文件的输入，它将停止处理该文件并开始处理下一个文件（如果存在），或者如果其他文件不存在则从 **stdin** 请求输入。

这意味着如果在执行文件时将 **SIGINT** 发送到 bc(1)，则 bc(1) 似乎没有响应该信号，因为它将立即开始执行下一个文件。这是设计使然；用户在与 bc(1) 交互时执行的大多数文件都有函数定义，可以快速解析。如果文件需要很长时间才能执行，则该文件中可能存在错误。其余文件仍然可以毫无问题地执行，允许用户继续。

**SIGTERM** 和 **SIGQUIT** 导致 bc(1) 清理并退出，并且它对所有其他信号使用默认处理程序。一个例外是 **SIGHUP** 。在这种情况下，当 bc(1) 处于 TTY 模式时， **SIGHUP** 将导致 bc(1) 清理并退出。

## [命令行历史](#__u547D___u4EE4___u884C___u5386___u53F2_)

bc(1) 支持交互式命令行编辑。如果 bc(1) 处于 TTY 模式（请参阅 **TTY 模式** 部分），则启用历史记录。可以使用箭头键调用和编辑以前的行。

**注意**: 制表符转换为 8 个空格。

## [区域设置](#__u533A___u57DF___u8BBE___u7F6E_)

此 bc(1) 支持为不同的语言环境添加错误消息，因此支持 **LC\_MESSAGES**。

## [参见](#__u53C2___u89C1_)

dc(1)

## [标准](#__u6807___u51C6_)

bc(1) 符合 IEEE Std 1003.1-2017 (“POSIX.1-2017”) (<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html)规范。标志> **-efghiqsvVw** 、所有长选项以及上面提到的扩展都是对该规范的扩展。

请注意，规范明确指出 bc(1) 仅接受使用句点 (**.**) 作为小数点的数字，而不管 **LC\_NUMERIC** 的值如何。

此 bc(1) 支持不同语言环境的错误消息，因此它支持 **LC\_MESSAGES**。

## [缺陷](#__u7F3A___u9677_)

没有一个是已知的。在 <https://git.yzena.com/gavin/bc> 报告错误。

## [作者](#__u4F5C___u8005_)

Gavin D. Howard <gavin@yzena.com> 和贡献者。

March 2021

Gavin D. Howard
