Shell 有时会引用使用文件描述符(fd)的文件。我们一般使用文件描述符(fd)的范围是数字 0~9。重定向时大于 9 的文件描述符要谨慎使用,因为它们可能与 Shell 内部使用的文件描述符冲突。
文件描述符可以包含多个数字位。例如,文件描述符 001 和 01 与文件描述符 1 是相同的。多种操作(例如,exec 命令)都可以将文件描述符与特定的文件联系起来。
有些文件描述符是在 Shell 启动时被建立的,这就是我们前面介绍的标准输入、标准输出和标注错误(0、1、2)文件描述符。
实例:使用 exec 命令
Bash 的内部命令 exec 的功能之一就是允许我们操作文件描述符。如果在 exec 之后没有指定命令,则 exec 命令之后的重定向将更改当前 Shell 的文件描述符。
例如,在命令 “exec 2> file” 之后运行的所有命令,都会将其产生的错误信息发送到文件 file 中,就像你的命令在脚本 myscript.sh 中,而你运行的是 “./myscript.sh >2 file”。
比如,如果你想记录脚本中的命令产生的错误信息,就可以在脚本的开头使用类似如下的命令:
exec 2> errors.log
下面我们来看一个脚本文件,在这个脚本中我们想要顺序地读取文件中的每一行,并在打印每一行之后,等待用户输入任意键后继续。
#! /bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 FILEPATH"
exit
fi
file=$1
while read -r line
do
echo $line
read -p "Press any key" -n 1
done < $file
从上面的输出结果我们可以看到,read 语句并没有执行:因为我们将指定的文件重定向到了 while 循环的标准输入(文件描述符 0),即我们指定的文件将被打开以用于标准输入的读取,而循环中的所有命令包括 read 命令都会继承这个文件描述符(这里是标准输入),因此 read 将从重定向后的标准输入读取,而不是从默认的标准输入设备(键盘)读取。
而此时,我们就可以使用 exec 命令对脚本稍加改动,来实现我们想要的功能,改动后的脚本将类似如下所示:
#! /bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 FILEPATH"
exit
fi
# 将脚本的第一个参数作为输入文件,并制定一个文件描述符 3
exec 3< $1
while read -u 3 line
do
echo $line
read -p "Press any key: " -n 1
done
# 关闭文件描述符 3
exec 3<&-
在上述脚本中,我们使用的 “read -u 3 line” 命令,为 read 指定从指定的描述符中读取数据。
上述脚本的运行效果
本文参考自 《Linux Shell命令行及脚本编程实例详解 》