标准文件描述符
Linux将每个对象当作文件处理,包括输入和输出进程。Linux用文件描述符(file descriptor)来标识每个文件对象,文件描述符是一个非负整数,可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符,出于特殊目的考虑bash shell保留了前三个文件描述符(0,1,2)
文件描述符 | 缩写 | 描述 |
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
#在命令行中输入cat命令时,会从STDIN接受输入,输入一行,cat命令就会显示出一行
[root@localhost scripts]# cat
123
123
abc
abc
#通过STDIN重定向强制cat接受来自文件的输入
[root@localhost scripts]# cat test
123
123
456
789
[root@localhost scripts]# cat <test
123
123
456
789
STDOUT
STDOUT代表shell的标准输出,在终端界面上,标准输出就是显示器,shell的所有输出会被重定向到标准输出即显示器
[root@localhost scripts]# ls -l >testout
[root@localhost scripts]# cat testout
total 112
-rwxr-xr-x. 1 root root 956 Jan 17 06:23 countUsers.sh
-rwxr-xr-x. 1 root root 455 Jan 9 07:24 createNewUserNew.sh
-rwxr--r--. 1 root root 715 Jan 6 06:08 createNewUser.sh
-rwxr-xr-x. 1 root root 93 Jan 9 07:22 deluser.sh
-rwxr-xr-x. 1 root root 979 Jan 11 07:10 detimateExpire.sh
-rwxr-xr-x. 1 root root 180 Jan 16 07:09 helloeveryone.sh
...
#通过输出重定向符号,通常会将显示到显示器的所有输出重定向至指定的文件,也可能通过【>>】追加到某个文件
[root@localhost scripts]# who >>testout
#tac从后向前查看信息,功能同cat
[root@localhost scripts]# tac testout
root pts/0 2021-01-24 16:36 (192.168.22.140)
-rwxr-xr-x. 1 root root 119 Jan 16 06:53 test.sh
-rw-r--r--. 1 root root 0 Jan 24 16:43 testout
-rwxr-xr-x. 1 root root 250 Jan 14 05:38 testFileType.sh
-rw-r--r--. 1 root root 70 Jan 23 16:41 testfile
-rw-r--r--. 1 root root 156 Jan 23 18:06 test4.sh
...
STDERR
shell通过特殊的STDERR文件描述符来处理错误信息,shell中运行的脚本和程序出错时的信息都会发送到这个位置。
#badfile不存在
[root@localhost scripts]# ls -al badfile 2>testerror
[root@localhost scripts]# cat testerror
ls: cannot access badfile: No such file or directory
#deluser.sh存在,其它两个文件不存在
[root@localhost scripts]# ls -al deluser.sh badfile badtest 2>testerror
-rwxr-xr-x. 1 root root 93 Jan 9 07:22 deluser.sh
[root@localhost scripts]# cat testerror
ls: cannot access badfile: No such file or directory
ls: cannot access badtest: No such file or directory
重定向错误和数据
如果想重定向错误输出和正常输出,必须用两个重定向符号,需要在符号前面放上待重定向数据所对应的文件描述符,然后指向用于保存数据的输出文件。
#2>表示错误输出重定向,1>表示正常输出重定向
[root@localhost scripts]# ls -al deluser.sh badfile badtest 2>testerror 1>testout
[root@localhost scripts]# cat testout
-rwxr-xr-x. 1 root root 93 Jan 9 07:22 deluser.sh
[root@localhost scripts]# cat testerror
ls: cannot access badfile: No such file or directory
ls: cannot access badtest: No such file or directory
#通过&>将错误和正确输出重定向到同一个文件
[root@localhost scripts]# ls -al deluser.sh badfile badtest &>testout
[root@localhost scripts]# cat testout
ls: cannot access badfile: No such file or directory
ls: cannot access badtest: No such file or directory
-rwxr-xr-x. 1 root root 93 Jan 9 07:22 deluser.sh
#注:shell赋予了错误输出更高的优先级,查看时先显示错误输出
重定向输出
临时重定向输出
如果需要在脚本中生成错误信息,可以单独的一行输出重定向到STDERR,使用输出重定向符来将输出信息重定向到STDERR文件描述符。在重定向到文件描述符时,你必须在文件描述符数字之前加一个&
[root@localhost scripts]# echo "这是一个错误信息" >&2
这是一个错误信息
#默认情况下,Linux会将STDERR导向STDOUT,如果运行脚本时重定向了STDERR,脚本中所有的导向STDERR的文件都会被重定向
```bash
#!/bin/bash
#
echo "这是一个错误..." >&2
echo "这是一个标准输出"
[root@localhost scripts]# bash test5.sh 2>testerror
这是一个标准输出
[root@localhost scripts]# cat testerror
这是一个错误...
永久重定向脚本中的所有命令
如果脚本中有大量数据需要重定向,可以使用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符
#!/bin/bash
#
exec 1>testout
echo "This is a test of redirecting all output"
echo "from a script to another file."
echo "without having to redirect every individual line"
#上述文件中所有的标准输出都被重定向至testout中
[root@localhost scripts]# bash test2.sh
[root@localhost scripts]# cat testout
This is a test of redirecting all output
from a script to another file.
without having to redirect every individual line
#!/bin/bash
#重定向输出至不同位置
exec 2>testerror
echo "脚本开始"
echo "重定向输出至其它位置"
exec 1>testout
echo "输出至testout文件"
echo "输出至testerror文件" >&2
#没有定向重定向输出之前,信息被定向至默认输出(显示器),定向输出后,标准输出信息被输出至testout文件中
[root@localhost scripts]# bash test6.sh
脚本开始
重定向输出至其它位置
[root@localhost scripts]# cat testout
输出至testout文件
[root@localhost scripts]# cat testerror
输出至testerror文件
创建输出文件描述符
通过exec命令为输出分配文件描述符,和标准文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向则会一直有效,直到重新分配。
#!/bin/bash
# 创建输出文件描述符
#将文件描述符3重定向至testout
exec 3>testout
echo "显示到屏幕"
echo "这些将存储到文件" >&3
echo "下面将显示到屏幕下面"
重定向文件描述符
#!/bin/bash
#
#将文件描述符3重定向至标准输出文件描述符1
exec 3>&1
#将标准输出文件描述符1重定向至文件testout
exec 1>testout
echo "存储至输出文件"
echo "连同这一行"
#重新将标准输出重定向至文件描述符3,重新返回标准输出
exec 1>&3
echo "现在将返回标准输出"
[root@localhost scripts]# bash test7.sh
现在将返回标准输出
[root@localhost scripts]# cat testout
存储至输出文件
连同这一行
列出打开的文件描述符
lsof命令会列出整个Linux系统打开的所有文件描述符,该命令会产生大量输出。
-p 允许指定进程ID(PID)
-d 允许指定要显示的文件描述符编号
可能通过$$变量获得当前进程当前PID
[root@localhost scripts]# echo $$
1562
[root@localhost scripts]# ps -ef | grep 1562
root 1562 1558 0 17:08 pts/0 00:00:00 -bash
root 1616 1562 1 17:27 pts/0 00:00:00 ps -ef
root 1617 1562 0 17:27 pts/0 00:00:00 grep --color 1562
显示当前进程默认的文件描述符(0,1,2)
[root@localhost scripts]# lsof -a -p$$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 1562 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 1562 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 1562 root 2u CHR 136,0 0t0 3 /dev/pts/0
描述
列 | |
COMMAND | 正在运行命令的前9个字符 |
PID | 进程的PID |
USER | 进程属主的登录名 |
FD | 文件描述符号以及访问类型 |
TYPE | 文件的类型(CHR表示字符型,BLK表示块型,DIR代表目录,REG代表常规文件) |
DEVICE | 设备的设备号(主设备号和从设备号) |
SIZE | 如果存在表示文件的大小 |
NODE | 本地文件的节点号 |
NAME | 文件名 |
#!/bin/bash
#
exec 3> testfile1
exec 6> testfile2
exec 7< testfile
lsof -a -p $$ -d0,1,2,3,6,7
[root@localhost scripts]# bash test8.sh
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 1585 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 1585 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 1585 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 1585 root 3w REG 253,0 0 919920 /root/scripts/testfile1
bash 1585 root 6w REG 253,0 0 919936 /root/scripts/testfile2
bash 1585 root 7r REG 253,0 70 919917 /root/scripts/testfile
#创建了3个替代性文件描述符,两个作为输出(3和6),一个作为输入(7)