常见的 Shell 操作符

基本概念

标准输入、标准输出、标准错误

在 von Neumann 计算机体系结构中,程序通过输入设备获取输入数据,通过输出设备输出结果数据。对应地,在 Unix 中,也产生了输入流和输出流的概念。

在类 Unix 操作系统中,程序有三种预设的数据流:

  • 标准输入(C 标准库中的 stdin):程序从中读取输入数据的流,通常与键盘关联。
  • 标准输出(C 标准库中的 stdout):程序将正常输出数据写入的流,通常与终端关联。
  • 标准错误(C 标准库中的 stderr):程序将错误消息写入的流,通常也与终端关联。

其中,标准错误也属于输出流的一种,但它与标准输出是分开的,目的是为了让用户能够区分正常的输出和错误消息。

stdio.h 头文件中,可以看到如下定义:

/* Standard streams.  */
extern FILE *stdin;		/* Standard input stream.  */
extern FILE *stdout;		/* Standard output stream.  */
extern FILE *stderr;		/* Standard error output stream.  */
/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin
#define stdout stdout
#define stderr stderr

对于 Windows 命令行程序,上述概念同样适用。

现代的 Shell 一般都赋予上述数据流一个默认的文件描述符:

  • 标准输入:文件描述符 0
  • 标准输出:文件描述符 1
  • 标准错误:文件描述符 2
/* Standard file descriptors.  */
#define	STDIN_FILENO	0	/* Standard input.  */
#define	STDOUT_FILENO	1	/* Standard output.  */
#define	STDERR_FILENO	2	/* Standard error output.  */

在所有的类 Unix 操作系统中,当新进程创建时,这三个文件描述符都会被默认打开并关联到相应的设备文件上, 其文件描述符的编号都固定为上面列出的值

命令和程序

命令、程序是人们总是混淆的概念(与此类似的还有程序和进程)。当我们打开终端时,终端实际上帮我们启动了一个 Shell 程序,Shell 程序接受用用户的输入,并对其进行解析。用户输入的可能是 Shell 的一个内置命令(如 exit),也可能是一个可执行程序(如 ls)。一般来说,Shell 会先尝试将输入的内容视作内置命令进行解析,若其不是内置的命令,则在环境变量 PATH 列出的目录下查找对应的可执行程序并运行它。

在现代操作系统中,“终端”是通过一个图形化的终端模拟器程序来实现的。

输出重定向

> 是重定向操作符,用于将前面提到的三种数据流重定向到其他数据流中。其使用方法为 N> 其中 N 是文件描述符,可取 12,分别代表标准输出和标准错误。在缺省的情况下,> 等价于 1>> 在重定向输出时会覆盖目标文件的内容,若想要追加输出,可以使用 >> 操作符,其使用方式与 > 完全一致。

>(或 1>

主流的 Shell 都支持使用 > 将命令的标准输出重定向到文件中,例如:

echo "Hello, world!" > hello.txt

2>

在 Unix 的设计中,标准错误的流和标准输出的流是分开的,主流的 Shell 均支持使用 2> 将标准错误重定向到文件中。其中,2 代表标准错误的文件描述符。

>&

>& 的语义是将一个文件描述符重定向到另一个文件描述符。2>&1 表示将标准错误重定向到标准输出中。

示例

// test.c
#include <stdio.h>

int main(int argc, char *argv[])
{
    fprintf(stdout, "Trivial message.\n");
    fprintf(stderr, "Error message.\n");
}
clang test.c -o test
./test 1> out.txt # 将标准输出重定向到 out.txt 文件中,标准错误仍然输出到终端
./test 2> err.txt # 将标准错误重定向到 err.txt 文件中,标准输出仍然输出到终端
./test > all.txt 2>&1 # 将标准输出和标准错误都重定向到 all.txt 文件中

输入重定向

<

< 是输入重定向操作符。由于设计上的差异,目前 PowerShell 还不支持这种重定向方式。< 用于将文件的内容作为命令的标准输入流。例如:

echo 3 2 1 > numbers.txt
sort < numbers.txt

PowerShell 采取了面向对象的设计理念,其输入输出流传递的是对象而非文本流,对象通过管道在 Shell 的各个命令之间传递。当遇到可执行程序时,PowerShell 会将对象转换为文本流传递给可执行程序。上面的例子在 PowerShell 中可以这样写:

"3`n2`n1" | Out-File -FilePath numbers.txt
Get-Content -Path numbers.txt | Sort-Object

流的创建

为类 Unix 系统设计的 shells 支持通过下面的方式创建新的数据流:

exec 3> newout.txt
ls -l >&3 # 输出写入到 newout.txt 文件中
echo 3 2 1 > newin.txt
exec 4< newin.txt
sort <&4 # 从 newin.txt 文件中读取输入