Linux重定向和文件描述符

目录

概念

文件描述符

重定向

输入输出符号及作用

输出重定向实例

输入重定向实例

文件描述符操作

文件描述符的复制(duplicate)

重定向

改变当前shell环境的重定向目标

关闭文件描述符

打开文件

文件描述符的移动


概念

什么是重定向:

Linux中的重定向就是将原本要输出到屏幕中的数据信息,重新指向某个特定文件当中,或者定向到黑洞文件(/dev/null)中。

重定向的作用:

  1. 当屏幕输出的信息很重要,希望保存时
  2. 后台执行的程序一般都会有输出,不希望它输出干扰到终端
  3. 执行定时备份任务,希望将备份结果保留下来时
  4. 执行一些命令,会提示一些报错信息,可以直接将报错丢弃。
  5. 执行命令时希望将报错和正确内容区分在不同文件中时(日志)

文件描述符

文件描述符是IO重定向中的重要概念。文件描述符使用数字表示,它指明了数据的流向特征。

软件设计认为,程序应该有一个数据来源、数据出口和报告错误的地方。在Linux系统中,它们分别使用描述符0、1、2来表示,这3个描述符默认的目标文件(设备)分别是/dev/stdin、/dev/stdout、/dev/stderr,它们分别是各个终端字符设备的软链接,分别代表的是标准输入,标准输出与标准错误。

┌──(root)-[~]
└─# ll /dev/std*                                                                                        16 ⨯
lrwxrwxrwx 1 root root 15 Mar  4 12:58 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Mar  4 12:58 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Mar  4 12:58 /dev/stdout -> /proc/self/fd/1

┌──(root)-[~]
└─# ll /proc/self/fd/
total 0
lrwx------ 1 root root 64 Mar  4 13:01 0 -> /dev/pts/0
lrwx------ 1 root root 64 Mar  4 13:01 1 -> /dev/pts/0
lrwx------ 1 root root 64 Mar  4 13:01 2 -> /dev/pts/0
lr-x------ 1 root root 64 Mar  4 13:01 3 -> /proc/2602/fd
                                                                                                             

在Linux中,每一个进程打开时都会自动获取3个文件描述符0、1和2,分别表示标准输入、标准输出、和标准错误,如果要打开其他文件,则文件描述符必须从3开始标识。对于我们人为要打开的描述符,建议使用9以内的描述符,超过9的描述符可能已经被系统内部分配给其他进程。

类型文件描述符默认情况对应文件句柄位置
标准输入(standard input)0从键盘获得输入/proc/self/fd/0
标准输出(standard output)1输出到屏幕(即控制台)/proc/self/fd/1
错误输出(error output)2输出到屏幕(即控制台)/proc/self/fd/2
  • 当进程操作一个文件时:

    1. 首先程序是无法直接访问硬件,需要借助内核来访问文件
    2. 而内核kernel需要利用文件描述(file descriptor)来访问
  • 总结:进程使用文件描述符来管理打开的文件对应关系

  • 通常程序访问一个文件至少会打开三个标准文件,分别是标准输入,标准输出,错误输出

  • 进程将从标准输入中的到数据,将正常输出打印至屏幕终端,将错误的输出信息也打印至屏幕终端

重定向

输入输出符号及作用

名称符号作用
标准输入重定向<或者0<将符号右边的内容交给符号左边的命令
标准输出覆盖重定向>或者1>将原本要输出在屏幕上的正确内容,覆盖到重定向的文件中
标准输出追加重定向>>或者1>>将原本要输出在屏幕上的正确内容,追加到重定向的文件中
错误输出覆盖重定向2>将原本要输出在屏幕上的错误内容,覆盖到重定向的文件中
错误输出追加重定向2>>将原本要输出在屏幕上的错误内容,追加到重定向的文件中

输出重定向实例

我们使用>或者>>对输出进行重定向。符号的左边表示文件描述符,如果没有的话表示1,也就是标准输出,符号的右边可以是一个文件,也可以是一个输出设备(Linux中万物皆文件,即设备也是文件)。当使用>时,会判断右边的文件存不存在,如果存在的话就先删除,然后创建一个新的文件,不存在的话则直接创建。但是当使用>>进行追加时,则不会删除原来已经存在的文件。

#查看文件内容,1.txt  2.txt (其中2.txt文件不存在)
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt
this is a.txt
cat: 2.txt: No such file or directory
 
#标准输出与错误输出都显示在屏幕了,
#现在需要把标准输出写入到1.log中
# 1>可以省略,表示标准输出
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 1>1.log
cat: 2.txt: No such file or directory
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt >1.log
cat: 2.txt: No such file or directory
[root@sccprocddev02:/home/upro01]cat 1.log
this is a.txt
 
#标准输出不输出到屏幕,输出到1.log中
#错误输出不输出到屏幕,输出到2.log中
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 1>1.log 2>2.log
[root@sccprocddev02:/home/upro01]cat 1.log 2.log
this is a.txt
cat: 2.txt: No such file or directory
 
#将标准输出和错误输出分别追加到文件1.log和2.log中  “>>”追加操作符
#可以看到两个文件分别多出一行输出的内容
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 1>>1.log 2>>2.log
[root@sccprocddev02:/home/upro01]cat 1.log 2.log
this is a.txt
this is a.txt
cat: 2.txt: No such file or directory
cat: 2.txt: No such file or directory

 
#高级用法(具体讲解后面会提到)
#将错误输出信息关闭掉,控制台只打印了标准输出
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 2>&-
this is a.txt
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 2>/dev/null
this is a.txt
#&[n] 代表是已经存在的文件描述符,&1 代表输出 &2代表错误输出&-代表关闭与它绑定的描述符
#/dev/null 这个设备,是linux 中黑洞设备,什么信息只要输出给这个设备,都会给吃掉
  
#关闭所有输出
#关闭 1 ,2 文件描述符
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt 1>&- 2>&-
 
#将1,2 输出转发给/dev/null设备
[chengmo@centos5 shell]$ ls test.sh test1.sh  2>/dev/null 1>/dev/null
 
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt >/dev/null 2>&1
#将标准输出fd=1重定向到/dev/null文件,然后将fd=2重定向到fd=1所绑定的/dev/null文件。这种常用文件描述符前必须有个 &, 否则2>1就变成将错误输出输出到一个名为1的文件。
[root@sccprocddev02:/home/upro01]cat 1.txt 2.txt &>/dev/null
#&>代表将标准输出与标准错误重定向到/dev/null文件

输入重定向实例

我们使用<对输入做重定向,如果符号左边没有写值,那么默认就是0。
格式:

command-line [n] <文件

命令默认从键盘获得的输入,使用输入重定向改成从文件,或者其它打开文件以及设备输入。执行这个命令,将标准输入0,与文件或设备绑定,将由它进行输入。

[root@sccprocddev02:/home/upro01]cat > stdout.txt
this is stdout.txt
^C
[root@sccprocddev02:/home/upro01]cat stdout.txt
this is stdout.txt
#这里使用ctrl+d 或者ctrl+c退出输入
#从标准输入[键盘]获得数据,然后输出给stdout.txt文件
  
[root@sccprocddev02:/home/upro01]cat > stdout2.txt < stdout.txt
[root@sccprocddev02:/home/upro01]cat stdout2.txt
this is stdout.txt
#从stdout.txt获得输入数据,然后输出给文件stdout2.txt
  
  
[root@sccprocddev02:/home/upro01]cat > stdout3.txt << end
> first line
> second line
> end
[root@sccprocddev02:/home/upro01]#cat stdout3.txt
first line
second line
#<< 这个连续两个小符号, 他代表的是[结束的输入字符]的意思。这样当空行输入end字符时,自动退出输入,不需要使用ctrl+d或者ctrl+c退出

文件描述符操作

文件描述符的复制(duplicate)

文件描述符的复制表示复制文件描述符到另一个文件描述符中,也就是将该文件描述符重定向到另一个文件描述符所绑定的文件,使用”&”进行复制。

  • [n]<&[m]将文件描述符n复制于m代表的文件描述符。可以理解为文件描述符n重定向到m代表的文件描述符所绑定的文件,即m原来对应哪个文件,现在n也对应这个文件。n不指定则默认为0(标准输入就是0),表示标准输入也将输入到m所对应的文件中。

  • [n]>&[m]将文件描述符n复制于m代表的文件描述符。可以理解为文件描述符n重定向到m代表的文件描述符所绑定的文件,即m原来对应哪个文件,现在n也对应这个文件。n不指定则默认为1(标准输出就是1),表示标准输出也将输出到m所对应的文件中。

例如,3>&1表示fd=3复制于fd=1,而fd=1目前的重定向目标文件是/dev/stdout(fd=1指向与输出设备是默认的),因此fd=3也重定向到/dev/stdout,以后进程将数据写入fd=3的时候,将直接输出到屏幕。如果后面改变了fd=1的输出目标(如file1),由于fd=3的目标仍然是/dev/stdout,所以可以拿fd=3来还原fd=1使其目标变回/dev/stdout。

(fd=1) --> /dev/stdout
  |
 3>&1
  |
(fd=3) --> /dev/stdout

关于文件描述符的duplicate

在操作系统(或C)中,对于实体文件的文件描述符来说,文件描述符是用来描述它所指向的实体文件的。例如fd=5指向文件a.txt。复制(duplicate)实际上是执行dup()函数,表示创建另一个文件描述符(例如fd=6),指向同一个底层对象,例如指向同一个实体文件。这时fd=5和fd=6都将指向a.txt。
在shell中,我们将文件描述符和实体文件的关联关系(或者称为指向的关系)称为重定向,其实用更底层的指向关系更容易理解。例如,”3>&1”表示复制fd=1,使得fd=3和fd=1都指向同一个对象,也就是stdout。

再例如,cat <&1表示fd=0复制于fd=1上,而此时fd=1的重定向文件是/dev/stdout,所以fd=0也指向这个/dev/stdout文件,而cat从fd=0中读取标准输入,于是/dev/stdout既是标准输入设备,也是标准输出设备,也就是说进程从/dev/stdout(屏幕)接受输入,输入后再直接输出到/dev/stdout。以下是结果:

┌──(root💀kali)-[~]
└─# cat <&1                                                                                            130 ⨯
uegwfwuggi
uegwfwuggi

重定向

>file 2>&12>&1 >file

想必很多人都知道>file 2>&1的作用,它等价于&>file,表示标准输出和标准错误都重定向到file中。那它和2>&1 >file有什么区别呢?

首先解释>file 2>&1。这里分两个过程:先打开file,再将fd=1重定向到file文件上,这样file文件就成了标准输出的输出目标;之后再将fd=2复制于fd=1,而fd=1此时已经重定向到file文件上,因此fd=2也重定向到file上。所以,最终的结果是标准输出重定向到file上,标准错误也重定向到file上。

再解释2>&1 >file。这里也分两个过程:先将fd=2复制于fd=1,而此时fd=1重定向的文件是默认的/dev/stdout,所以fd=2也重定向到/dev/stdout;之后再将fd=1重定向到file文件上。也就是说,这里的标准错误和标准输出仍然是分开输出的,只不过是使用/dev/stdout替代了/dev/stderr,使用file替代了/dev/stdout。所以,最终的结果是标准错误输出到/dev/stdout,即屏幕上,而标准输出将输出到file文件中。

可以使用下面的命令来测试2>&1 >file。第一个ls命令是正确的,结果输出到/tmp/fff.log中,第二个ls命令是错误的,结果将直接输出到屏幕上。

                                                                                                             
┌──(root💀kali)-[~]
└─# ls /boot 2>&1 >/tmp/fff.log                                                                          130 ⨯
┌──(root💀kali)-[~]
└─# ls haha 2>&1 >/tmp/fff.log
ls: cannot access 'haha': No such file or directory
                                                                                                             

最后需要说明的是一种特殊情况,如果是>&[word],且word不是一个数值,比如echo haha >&/tmp/fff.log,那么>&word&>word是等价的,都表示>word 2>&1,即标准错误和标准输出都重定向同一个目标。参考man bash的”Redirecting Standard Output and Standard Error”段落。

改变当前shell环境的重定向目标

如果在命令中直接改变重定向的位置,那么命令执行结束的时候描述符会自动还原。正如上面的ls /boot 2>&1 >/tmp/fff.log命令,在ls执行结束后,fd=2还原回默认的/dev/stderr,fd=1还原回默认的/dev/stdout。

但是我们可以通过exec程序直接在当前的shell环境下改变重定向目标,只有在当前shell退出的时候才会释放描述符的绑定。


Tips:shell的内建命令exec将并不启动新的shell,而是用要被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令后的其它命令将不再执行。
不过,要注意一个例外,当exec命令来对文件描述符操作的时候,就不会替换shell,而且操作完成后,还会继续执行接下来的命令。


例如:下面的命令将标准错误fd=2指向fd=3对应的文件上。

┌──(root💀kali)-[~]
└─# exec 2>&3                                                                                            1 ⨯

因此,我们可能在一段程序执行结束后,需要将描述符还原到原来的位置,并关闭不再需要的描述符。毕竟描述符也是资源,是有限的(ulimit -n)。

关闭文件描述符

  • [n]>&-
  • [n]<&-
    关闭文件描述符的方式是将[n]>&word[n]<&word中的word使用符号”-“,这表示释放fd=n描述符,且关闭其指向的文件。

打开文件

[n]<> filename:打开filename,并指定其文件描述符为n,该描述符是可读、可写的描述符。若不指定n则默认为0,若filename文件不存在,则先创建filename文件。

文件描述符的移动

文件描述符的移动表示将文件描述符1移动到描述符2上,同时关闭文件描述符1。

[n]>&digit-:将文件描述符digit代表的输出文件移动到n上,并关闭digit值的描述符。
[n]<&digit-:将文件描述符digit代表的输入文件移动到n上,并关闭digit值的描述符。

 参考:Linux重定向 - Gie - 博客园

https://www.jianshu.com/p/fc2df7c0adb6

Linux中的重定向_野心家love的博客-CSDN博客

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值