linux简单理解输入输出重定向

linux简单理解输入输出重定向

linux重定向可以分为输出重定向和输入重定向。在系统中除了标准输入、标准输出、错误输出以外,其他的重定向,都需要指定文件描述符id,也可以称为句柄。

linux中,可以通过查看/proc/进程pid号/fd/下的文件来查看该进程有多少个fd,比如:

bash 复制代码
# ls -l /proc/$$/fd/
total 0
lrwx------. 1 root root 64 Jun  3 22:22 0 -> /dev/pts/0
lrwx------. 1 root root 64 Jun  3 22:22 1 -> /dev/pts/0
lrwx------. 1 root root 64 Jun  3 22:22 2 -> /dev/pts/0
#

上述命令中$$表示该进程本身的pid,而该fd文件下,可以看到有012 三个文件,都指向/dev/pts/0,这是一个字符设备文件,表示当前的终端。

输出重定向原理

对于系统而言,任何一个程序都有2个输出,分别是:

  • 文件描述符为1的标准输出
  • 文件描述符为2的错误输出

所以,当执行一个命令输出重定向的时候,比如:

bash 复制代码
command >> filename

上述命令是将command命令执行的标准输出结果重定向到filename文件中,你可以理解为在执行该命令的时候,其进程标准输出文件描述符1由默认的/dev/pts/0指向了filename文件,为此可以来写一个程序来试试。

程序如下:

bash 复制代码
#!/bin/bash

echo "pid: $$" >&2

while true
do
        echo 123
        sleep 3
done

这个程序非常简单,首先现输出该脚本的pid到错误输出上,即文件描述符为2,而后定一个无限循环,每隔3秒输出123

在执行的时候,将该文件输出重定向到filename中。

bash 复制代码
# bash test.sh >> filename
pid: 42699

程序执行会卡主,这是正常的,这是因为有无限循环,每隔3秒会输出123字符串至标准输出上。

而后打开一个新的终端,查看42699fd文件信息。

bash 复制代码
# ls -l /proc/42699/fd/
total 0
lrwx------. 1 root root 64 Jun  3 23:41 0 -> /dev/pts/0
l-wx------. 1 root root 64 Jun  3 23:41 1 -> /root/fd/filename
lrwx------. 1 root root 64 Jun  3 23:41 2 -> /dev/pts/0
lr-x------. 1 root root 64 Jun  3 23:41 255 -> /root/fd/test.sh
#

通过上面执行的结果可以发现,其标准输出,文件描述符为1已经指向一个文件:/root/fd/filename,而非终端字符设备。所以说,在上面案例中,重定向后,实际上是将句柄给改写到文件中,所以屏幕没有输出任何数据。基于此,标准输入和错误输出的原理也是这样的。

输出重定向

输出重定向一般格式为:[n]>[|]filename,其中n是文件描述符,>|表示覆盖保护绕过机制,不过一般不用,即常用的写法为:[n]>filename

比如,将字符串abcdef的标准输出重定向到filename中:

bash 复制代码
echo "abcdef" 1> filename

默认情况下,1>可以缩略写为>,这也是常用的写法:

bash 复制代码
echo "abcde" > filename

将命令dasdsada的错误结果写入到文件filename中:

bash 复制代码
dasdsada 2> filename

因为没有这个命令,所以一定会报错-bash: dasdsada: command not found

对于该重定向而言,它在执行前会清理掉原始内容,从而写入新的内容。

除此之外,还有追加重定向,其格式为[n]>>filename,其中n是文件描述符,>>表示追加,它不会清理掉原有内容。比如将abc追加到文件filename中。

bash 复制代码
echo 123 1>> filename

同样的,也可以将1>>缩略写为>>

将命令aaa的错误结果追加写入到文件filename中:

bash 复制代码
aaa 2>> filename

输入重定向

linux系统中,任何一个程序的标准输入文件描述符id0,而输入重定向一般格式为[n]<filename,其中n表示文件描述符。

所以,如下命令:

bash 复制代码
cat 0< filename	

它的意思是将filename文件的内容作为标准输入提供给cat调用,结果是打印了filename的内容,上述命令中的0<可以缩略写为<

不仅如此,还能在循环中,搭配read,将文件的信息按行读取。

比如有以下文件内容:

bash 复制代码
# cat filename
aa
bb
cc
dd
#

有脚本内容如下:

bash 复制代码
#!/bin/bash

while read ll
do
        echo read_at: ${ll}
done 0< filename

上述脚本表示将filename文件作为输入源,供while调用,按照的方式是read命令按行调用,将行内容赋值到ll变量上,这个变量名称可以自定义。

其结果为:

bash 复制代码
# bash test.sh
read_at: aa
read_at: bb
read_at: cc
read_at: dd
#

输入重定向还有一种表达方式是here-documents。表示多行定义输入,其语法为:

bash 复制代码
command [n]<<word
here doc
word

其中n表示文件操作符,如果省略,则使用默认的文件操作符0,而后是关键字<<word是自定义的结束符,让后续读取到word后停止读取。

比如:

bash 复制代码
cat <<END
123
456
789
END

输出的结果为

bash 复制代码
123
456
789

上述命令中的END是可以替换为任何字符串的,只要后续出现该字符串就会停止输入,注意这个字符,一定是要在单独的一行出现的,不能有其他字符,包括空格。

here-documents会进行命令替换,变量替换等,比如有如下命令:

bash 复制代码
# x=123
# cat <<END
${x}
$(pwd)
aa
bb
cc
END

输出的结果会先进行命令替换,变量替换等,比如将${x}替换为123,将$(pwd)替换为当前目录。如果不想其被替换,则需要添加单引号进行转义。

重定向的顺序

注意看,如下有2条命令:

bash 复制代码
ls -l > filename 2>&1

bash 复制代码
ls -l 2>&1 > filename

所执行的结果是完全不一样的,第一条命令是将标准输出和错误输出都写入到filename中,而第二条则是将错误输出输出到屏幕上,而将标准输出写入到文件中,如果不是很明白,可以尝试拆解一下命令:

首先,命令的前提是:标准输出 和 错误输出 都在某个设备上,比如pty终端,假设设备为/dev/pts/0,所以文件描述符信息为:

  • 1 -> /dev/pts/0
  • 2 -> /dev/pts/0

然后第一条命令,ls -l > filename 其实是将文件描述符1给指向了文件filename,这个时候,文件描述符信息为:

  • 1 -> filename
  • 2 -> /dev/pts/0

这个时候再使用2>&1表示将错误消息也写入到文件描述符为1的文件中,即filename,所以最后的文件描述符为:

  • 1 -> filename
  • 2 -> filename

而第二条命令,一开始都是输出到某个设备上:

  • 1 -> /dev/pts/0
  • 2 -> /dev/pts/0

这个时候,它将错误输出指向2>&1,而文件描述符为1/dev/pts/0,所以这第一步,就没动过,而第二部则是将标准输出指向filename,即:

  • 1 -> filename
  • 2 -> /dev/pts/0

所以第二条命令,错误输出输出到屏幕上,而将标准输出写入到文件中。

总结

要理解输入输出重定向,一定要理解文件的描述符!对于每个进程,都会产生3个默认的文件描述符,分别是 标准输入、标准输出 和 错误输出,用文件描述符id012 来表示。

其重定向就是修改这些文件描述符的指向,将他们指向到文件中即可。

对于输入重定向而言,有一种表达方式是here-documents,它可以和输出重定向结合,将终端中输入的文件写入到文件中,比如:

bash 复制代码
cat 1>> filename 0<<END
a
b
c
d
e
END

表示将终端输入的ab... e字符提供给cat命令,而cat将内容重定向到文件filename中。可以发现上面有1>>表示将标准输出的内容重定向到某个文件,0<<表示将标准输入的内容重定向到某个文件,上述命令中的1>>可以缩写为>>,而0<<可以缩写为<<

相关推荐
dingdingfish2 天前
GNU Parallel 学习 - 第1章:How to read this book
bash·shell·gnu·parallel
似霰4 天前
Linux Shell 脚本编程——核心基础语法
linux·shell
似霰5 天前
Linux Shell 脚本编程——脚本自动化基础
linux·自动化·shell
偷学技术的梁胖胖yo6 天前
Shell脚本中连接数据库查询数据报错 “No such file or directory“以及函数传参数组
linux·mysql·shell
纵有疾風起15 天前
【Linux 系统开发】基础开发工具详解:软件包管理器、编辑器。编译器开发实战
linux·服务器·开发语言·经验分享·bash·shell
gis分享者17 天前
Shell 脚本中如何使用 here document 实现多行文本输入? (中等)
shell·脚本·document·多行·文本输入·here
柏木乃一17 天前
基础IO(上)
linux·服务器·c语言·c++·shell
angushine18 天前
CPU脚本并远程部署
shell
赵民勇22 天前
Linux/Unix中install命令全面用法解析
linux·shell
gis分享者23 天前
Shell 脚本中如何使用 trap 命令捕捉和处理信号(中等)
shell·脚本·信号·处理·trap·捕捉