1.intruduction

exec 用来启动一个新shell来执行指定程序,它会清除现有shell环境,而不是开启子shell来执行命令。

exec的另一种作用是操作文件描述符,而此时exec不会覆盖你当前的 shell 环境 

2.sysopsis

exec 程序/命令

 

3.exec实现高级IO

IO的各种实现离不开对FD的操作,创建新的输入或输出文件描述符后,shell将在脚本退出时自动关闭它们,但有时也需要在脚本结束前手动关闭。

 

符号

意义

n>&m

FDm的输出复制到FDn的文件

n<&m

FDm的输入复制到FDn的文件

n>&-

关闭FDn的输出,>&-表示关闭标准输出

n<&-

关闭FDn的输入,<&-表示关闭标准输入

exec n<> filename

以读写方式打开文件

 

IO重定向其实就是让已创建的FD指向其它的文件(修改其链接的文件),可以使用ls -l /proc/$$/fd 来查看这些链接,而通过exec可以很容易的创建,复制,关闭FD,从而实现自定义的重定向操作。它对FD 0,1,2的操作也没有什么不同,只不过鉴于三个FD在系统中默认属性决定了脚本会经常对其进行重定向操作。

 

用户可以直接在终端反复执行ls -l /proc/$$/fd,来观察如下exec FD操作的效果,来更好的理解FD和重定向的原理。当前用户也可以自行编写脚本来观察对应进程的重定向操作(ls -l /proc/PID/fd,其中PID为脚本进程PID

 

 

[ade@h ~]$ exec 3>testout3               #创建FD3,

[ade@h ~]$ exec 4<>testout4           #exec 操作FD时,可以在一条语句中做多个操作,如上面创建的FD3,FD4,可以合并为一条语句exec 3>testout3 4<>testout4

[ade@h ~]$ ls -l /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan  9 20:04 0 -> /dev/pts/4

lrwx------. 1 ade ade 64 Jan  9 20:04 1 -> /dev/pts/4

lrwx------. 1 ade ade 64 Jan  9 20:04 2 -> /dev/pts/4

lrwx------. 1 ade ade 64 Jan  9 20:04 255 -> /dev/pts/4

lrwx------. 1 ade ade 64 Jan  9 20:04 3 -> /home/ade/testout3

lr-x------. 1 ade ade 64 Jan  9 20:04 4 -> /home/ade/testout4

[ade@h ~]$ echo "first line to FD3" >&3

[ade@h ~]$ cat testout

first line to FD3

[ade@h ~]$ echo "first line to FD4" >&4

[ade@h ~]$ cat testout2

first line to FD4

------------------------------------------------------------

[ade@h ~]$ exec 13>&3 14>&4     #复制FD3,FD4

[ade@h ~]$ ll /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan 10 10:41 0 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 1 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 13 -> /home/ade/testout3

lrwx------. 1 ade ade 64 Jan 10 10:41 14 -> /home/ade/testout4

lrwx------. 1 ade ade 64 Jan 10 10:41 2 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:42 255 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 3 -> /home/ade/testout3

lr-x------. 1 ade ade 64 Jan 10 10:41 4 -> /home/ade/testout4

[ade@h ~]$ echo "second line to FD3" >&13

[ade@h ~]$ cat testout3

first line to FD3

second line to FD3

-------------------------------------

[ade@h ~]$ exec 3>testout-rein     #重定向FD3

[ade@h ~]$ ll /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan 10 10:41 0 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 1 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 13 -> /home/ade/testout3

lrwx------. 1 ade ade 64 Jan 10 10:41 14 -> /home/ade/testout4

lrwx------. 1 ade ade 64 Jan 10 10:41 2 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:42 255 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 3 -> /home/ade/testout-rein

lr-x------. 1 ade ade 64 Jan 10 10:41 4 -> /home/ade/testout4

[ade@h ~]$ echo "redirect line to testout-rein" >&3

[ade@h ~]$ cat testout-rein

redirect line to testout-rein

[ade@h ~]$ exec 3>&13            #重定向后恢复FD 3

[ade@h ~]$ echo "third line to FD3" >&3

[ade@h ~]$ cat testout3

first line to FD3

second line to FD3

third line to FD3

[ade@h ~]$ ll /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan 10 10:41 0 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 1 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 13 -> /home/ade/testout3

lrwx------. 1 ade ade 64 Jan 10 10:41 14 -> /home/ade/testout4

lrwx------. 1 ade ade 64 Jan 10 10:41 2 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:42 255 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 3 -> /home/ade/testout3

lr-x------. 1 ade ade 64 Jan 10 10:41 4 -> /home/ade/testout4

 

---------------------------------------------------

[ade@h ~]$ exec 3>&- 4>&-  #关闭FD 3FD 4

[ade@h ~]$ ll /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan 10 10:41 0 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 1 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 13 -> /home/ade/testout3

lrwx------. 1 ade ade 64 Jan 10 10:41 14 -> /home/ade/testout4

lrwx------. 1 ade ade 64 Jan 10 10:41 2 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:42 255 -> /dev/pts/0

[ade@h ~]$ exec 13>&- 14>&-

[ade@h ~]$ ll /proc/$$/fd

total 0

lr-x------. 1 ade ade 64 Jan 10 10:41 0 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 1 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:41 2 -> /dev/pts/0

lrwx------. 1 ade ade 64 Jan 10 10:42 255 -> /dev/pts/0

------------------------------------------------------------------

[ade@h ~]$ lsof -a -p $$ -d 0,1,2,3,4                                #lsof查看所有打开的FD

COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME

bash    10016  ade    0r   CHR  136,0      0t0       3 /dev/pts/0

bash    10016  ade    1u   CHR  136,0      0t0       3 /dev/pts/0

bash    10016  ade    2u   CHR  136,0      0t0       3 /dev/pts/0

bash    10016  ade    3u   REG  253,1        0 1050063 /home/ade/testout3

bash    10016  ade    4r   REG  253,1       18 1050116 /home/ade/testout4

 

其它的FD操作都是同样的道理

exec 6<&0    #新建输入FD 6

exec 0<testin #重定向输入

exec 0<&6 #恢复默认输入

exec 3>&13 4>&14  13>&- 14>&- 恢复FD 3,FD 4, 关闭FD 13, FD 14. 同时4FD操作


 

示例1:输入重定向与恢复

cat execin.sh

#!/bin/bash

#FD 0复制到FD 8,用于恢复FD 0

exec 8<&0                         #创建了输入文件描述符8

exec < loggg                      #或者exec 0< loggg 脚本中重定向输入

read a

read b

echo "------------------------"

echo $a

echo $b

 

echo "close FD 8:"

# 0<&8 FD 8 复制到FD 0 ,即恢复

# 8<&- 关闭FD 8,以供其它进程使用

exec 0<&8 8<&-

 

echo -n " pls enter data:"

read c                                                #标准输出恢复成键盘输入

echo $c

 

示例2:输出重定向与恢复

这是临时重定向脚本输出后再将输出设置为普通设置的常见方式。

cat execout.sh

#!/bin/bash

#FD 1复制到FD 8,用于恢复FD 1

exec 8>&1 #创建了输出文件描述符8

exec > loggg                 #脚本中标准输出重定向

 

echo "output of date"

date

echo "output of df"

df

 

# 1>&8 FD 8复制到FD 1,即恢复

#8>&- 关闭FD 8

exec 1>&8 8>&-

 

echo "------------------------"

echo "output of date"

date

echo "output of df"

df

 

示例3:重定向标准错误输出与恢复

cat execerr.sh

#!/bin/bash

#FD 1复制到FD 8,FD 2复制到FD 9,以供重定向之后恢复

exec 8>&1 9>&2

#将标准输出与标准错误输出重定向到loggg文件

exec &> loggg

 

ls z*           #有错误输出

date

 

#恢复FD1,FD2,关闭FD8,FD9

exec 1<&8 2>&9 8>&- 9>&-

 

echo "----------------"

echo "closed FD 8 and 9:"

ls z*

date


示例4:exec 创建读取/写入文件描述符

文件打开进行读写操作时,有一个指针指向操作文件的位置,读写操作时应该注意指针位置的移动,否则易造成读写混乱或都写覆盖

#!/bin/bash

exec 3<> testfile

read line <&3

echo "read: $line"

echo " this is a test line " >&3

4.参考

关于IO重定向和文件描述符的理解,请参考IO重定向与文件描述符