第七章:重定向¶
如果一个命令后跟着 &
且作业控制不生效,该命令默认的标准输入是空文件 /dev/null
。否则,命令的执行环境会包含 Shell 按照输入 / 输出规范修改的文件描述符。
以下的规范可出现在简单命令的任何位置或复杂命令前后。word 或 digit 除非下文有注解,在使用前会发生展开,如果 word 的展开结果包含多于一个的文件名,每个文件名都发生重定向。
<
word- 以读取模式打开 word 并作为标准输入。用这种方式打开不存在的文件会产生错误。
<>
word- 以读写模式打开 word 并作为标准输入。如果文件不存在则创建它。
>
word- 以写入模式打开 word 并作为标准输出。如果文件不存在则创建它。如果文件存在,且
CLOBBER
选项未被设定,产生错误;否则文件大小截断为零。 >|
word>!
word- 同
>
,除了无视CLOBBER
无条件在文件存在时将文件长度截断为零。 >>
word- 以追加模式打开 word 并作为标准输出。如果文件不存在,且
CLOBBER
和APPEND_CREATE
选项都未被设定,产生错误;否则创建文件。 >>|
word>>!
word- 同
>>
,除了无视CLOBBER
和APPEND_CREATE
无条件在文件不存在时创建文件。 <<
[-
] word- Shell 开始读取输入,直到读取到和 word 相同的一行,或直到读取到文件结束符。word 中不会发生参数展开,命令替换或文件名生成。产生的被称为 here-document 的文档作为标准输入。
如果 word 中任何字符被单引号,双引号或者\
引用(Quoted),文档中的字符不会被解析,否则会发生参数和命令替换,紧随换行的\
被移除,并且必须用\
来引用字符\
,$
,'
以及 word 的首字符。
注意 word 不会被 Shell 展开。word 中的反引号(`)不会发生作用,而是类似于双引号,除了反引号自身会被原封不动地传入。(该信息仅为完整性而提供,不建议使用反引号。)$'...'
形式的引用会产生展开反斜杠引用为特殊字符的标准效果。
如果使用<<-
,前面的所有 Tab 译注:是 Tab 而不是空格!会从 word 和文档中移除。 <<<
word- Shell 展开 word 并把结果作为标准输入。这被称为 here-string。和 Here-Document 中的 word 相比,前者的 word Shell 不展开。产生的结果结尾有换行。
<&
number>&
number- 标准输入 / 输出从以 number 表示的文件描述符(数字)复制(见 dup2(2))。
<& -
>& -
- 关闭标准输入 / 输出。
<& p
>& p
- 从协程的输入 / 到协程的输出移动到标准输入 / 输出。
>&
word&>
word- (除非
>& word
符合上述语法;可使用&>
避免歧义。)同时将标准输出和标准错误(文件描述符 2)以> word
的形式重定向。注意 Multios(见下)情况下它 不 产生和> word 2>&1
一样的效果。 >&|
word>&!
word&>|
word&>!
word- 同时将标准输出和标准错误(文件描述符 2)以
>| word
的形式重定向。 >>&
word&>>
word- 同时将标准输出和标准错误(文件描述符 2)以
>> word
的形式重定向。 >>&|
word>>&!
word&>>|
word&>>!
word- 同时将标准输出和标准错误(文件描述符 2)以
>>| word
的形式重定向。
如果以上任一语法前有数字,则使用的文件描述符是该数字指定的文件描述符而非默认的 0 或 1。指定重定向的顺序非常重要。Shell 按照(文件描述符,文件)关联的方式处理每个重定向。例如:
... 1>fname 2>&1
首先将文件描述符 1 与 fname 关联。然后将文件描述符 2 与文件描述符 1 关联的文件(fname)关联。若调换重定向顺序,文件描述符 2 将与终端关联(假定文件描述符 1 此时已经如此关联)且文件描述符 1 与文件 fname 关联。
-> 简单的命令与管道 中描述的分隔符 |&
是 2>&1 |
的简写。
进程替换的多种形式,即用于输入的 <(list)
和 =(list)
以及用于输出的 >(list)
,常与重定向一起使用。例如,如果输出重定向中的 word 是 >(list)
形式,输出被管道到 list 表示的命令。详见 %%。
用参数打开文件描述符¶
当 Shell 将参数解析成命令时,且 Shell 选项 IGNORE_BRACES
未被设置时,可以使用另一种形式的重定向:操作符前可以不是数字而是花括号{}表示的有效 Shell 标识符。Shell 会打开一个新的保证数字至少为 10 的文件描述符并将标识符指向的参数设定为打开的文件描述符。闭合花括号和重定向字符间不能有空格。例如:
... {myfd}>&1
这会打开一个新的文件描述符,其是文件描述符 1 的复制,并把参数 myfd
设定成该文件描述符的至少为 10 的数字。可以用 >&$myfd
语法写入新文件描述符。该文件描述符在子 Shell 和产生的外部可执行文件中保持打开状态。
{varid}>&-
语法,例如 {myfd}>&-
,可以用于关闭用这种方法打开的文件描述符。注意这种情况下 varid 指定的参数此前必须设定成文件描述符。
参数只读时用这种方法打开或关闭文件描述符会产生错误。然而如果 param 只读,用 <&$
param 或 >&$
param 读写文件描述符不产生错误。
如果未设定选项 CLOBBER
,用一个此前已经通过这种方式设定为打开的文件描述符的参数打开文件描述符会产生错误。在用该参数打开文件描述符前取消设定参数可避免这一错误。
注意这种方式只分配或关闭文件描述符;它不会执行任何从它或到它的重定向。使用用于 exec
的文件描述符前分配它通常会很方便。这个语法当用在像括号内子 Shell 或循环这样的复杂命令周围时无论如何都不会工作,因为此时打开的花括号被解析为当前 Shell 里执行的命令列表的一部分。
以下展示了一个通常的分配,使用,关闭文件描述符的例子:
integer myfd
exec {myfd}>~/logs/mylogfile.txt
print This is a log message. >&$myfd
exec {myfd}>&-
注意表达式 >&$myfd
中变量的展开在重定向打开时发生。这是在命令参数的展开以及此前的重定向被处理之后产生的。
Multios¶
如果用户尝试多次打开一个文件描述符用于写入,Shell 会把文件描述符打开为一个像 tee 一样复制输入到所有指定输出的管道,前提是选项 MULTIOS
被设定,而它默认被设定。因此:
date >foo >bar
向两个名为 foo
和 bar
的文件写入日期。注意管道隐含重定向;因此
date >foo | cat
将日期写入到文件 foo
,还把它管道到 cat。
注意 Shell 打开所有用于 Multio 进程的所有文件,而不是在它们要被写入时。
注意重定向永远按顺序展开,这是无视 MULTIOS
设定永远发生的事情,但该选项产生效果时会有额外的影响。例如,表达式 >&1
的含义会在上一个重定向后发生改变:
date >&1 >output
上一例子中,>&1
在这一行的开头指向标准输出;结果与命令 tee
类似,然而,考虑以下:
date >output >&1
由于重定向按顺序执行,当遇到 >&1
时标准输入已经设定为文件 output
从而另一份输出也写入到那个文件。这可能不是预期行为。
如果设定 MULTIOS
,重定向操作符后的单词还会发生文件名生成(Globbing)。因此
: > *
截断当前目录所有文件,假设至少有一个。(没有选项 MULTIOS
时会产生空文件 *
。)类似地,你可以
echo exit 0 >> *.sh
如果用户尝试多次打开一个文件描述符用于读取,Shell 会把文件描述符打开为一个复制所有指定输入到其输出的管道,前提是选项 MULTIOS
被设定。注意所有文件都被立即打开,而不是在它们要被读取时。这与 cat
行为不同,所以如果需要严格的标准行为,应该使用 cat
。
因此
sort <foo <fubar
甚至
sort <f{oo,ubar}
都与 cat foo fubar | sort
等效。
重定向参数的展开发生在重定向打开时,在上述描述的 >&$myfd
中变量展开时。
注意管道隐含重定向;因此
cat bar | sort <foo
等效于 cat bar foo | sort
(注意输入顺序)。
如果 没有 设置 MULTIOS
选项,每个重定向替换先前那个对应文件描述符的重定向。然而所有被重定向到的文件实际上都被打开,所以
echo Hello > bar > baz
在未设置 MULTIOS
时截断 bar
,向 baz
写入 Hello
。
输出 Multio 接入外部程序时有一个问题,一个简单的例子展示了这一点:
cat file >file1 >file2
cat file1 file2
这里,第二个 cat
有可能不能完整输出 file1
和 file2
的内容(即 file
原始内容重复两次)。
这种情况发生的原因是 Multios 在 cat
进程从父 Shell 中分叉时产生,所以父 Shell 不等待 Multios 写入完数据。这意味着上述命令可能在 file1
和 file2
完全写入前就退出。为绕过此问题,可以把 cat
进程运行为当前 Shell 的一个作业:
{ cat file } >file >file2
这里 {...}
作业等待两个文件都写入完毕。
无命令重定向¶
如果一个简单命令包含一个或多个重定向操作符以及零或多个参数赋值,但没有命令名,Zsh 有多种表现行为。
如果未设定参数 NULLCMD
或选项 CSH_NULLCMD
被设定,产生错误。这是 csh 默认行为且模拟 csh 时 CSH_NULLCMD
被默认设定。
如果设定选项 SH_NULLCMD
,内建命令 :
被插入为命令,继承给定重定向。这是模拟 sh 或 ksh 默认行为。
否则,如果设定参数 NULLCMD
,它的值作为命令,继承给定重定向。如果同时设定 NULLCMD
和 READNULLCMD
,如果重定向是输入则使用后者而非前者。NULLCMD
默认是 cat
而 READNULLCMD
默认是 more
。因此
< file
在标准输出上显示 file
内容,在终端时带分页。NULLCMD
和 READNULLCMD
可指向 Shell 函数。