技术栈

主页 > 系统 >

shell脚本:介绍I/O重定向相关的符号和操作

技术栈 - 中国领先的IT技术门户

本篇博客用于介绍I/O重定向相关的符号和操作

I/O重定向符号:> < >> 2> 1> &1 &>

概念:

标准输入重定向:就是把命令的输入从默认的从键盘读取,重定向到一个文件:

标准输出重定向:就是把命令的标准输出保存到指定的文件内:>file >>file 1>file。

标准错误输出重定向:把命令的错误输出保存到指定文件内:2>file 2>>file。

标准错误输出和标准输出均重定向到同一个文件的操作:

1>file 2>&1 或 &>file 或 &>>file (其中>和>>的区别就是一个覆盖文件原内容,一个不覆盖。并且注意,是没有2>>&1这种写法的。)

区分:

标准输出和标准错误输出不是包含关系,是两个独立的输出。相当于一个输出正常信息,一个输出出错信息。

命令、shell脚本、判断(循环)语句块、shell函数等等,一切中间可以嵌套echo语句的或者本身就有输出的命令语句,都可以进行标准输入、标准输出、标准错误输出的I/O重定向。(可能这句话没有那么准确)

重定向过程:

系统会打开一个默认的文件,用于标准输入、标准输出、标准错误输出指向文件。

重定向后,系统会先关闭原指向文件,打开新文件,并把打开这个文件产生的指针重新装入进程文件表中。此时,文件描述符就更改了,更改为了指向新打开的这个文件的指针。所以,此时再用&1引用这个描述符,就是代表新打开的这个文件,而不是原系统默认文件。

实际脚本例子:

本篇博客会举一个例子:例①

然后针对例①出现的问题进行分析,并改进代码,例②③④都是对例①的精简和改进。例④最终会解决问题。

例①、②、③、④分别用的重定向和出现的问题:

例①:1>file 2>&1

问题:函数中有一句输出被覆盖

例②:1>>file 2>&1

问题:例①出现的覆盖没有出现。但是,不能自动删除文本中原数据。

例③:&>file

输出:和例①是一模一样的

例④:1>file &>>file (注意例④这种,和1>file 2>>file并不相等)

结果:例①出现的覆盖问题没有出现。并且,每次更新文件时,可以自动删除原数据

例 ① 脚本文件,利用1>test6 2>&1:

脚本功能:实现shell函数I/O重定向的检测

脚本文件:sum100

脚本代码结构说明:其实就只包含了 两个shell函数 和 一个while语句块。其中function_testTwo函数是对function_testOne函数中出现问题的一个对比分析检测。因为这两个函数在最终的输出重定向时有点不同,one函数是在语句块结束后紧跟着就重定向,而two函数是在调用函数时,才重定向。two函数就是为了排除,在这一点上的区别对最终输出造成的影响。虽然结果显示,这两种并没有任何区别。

代码如下:

number=1

sum=0

max=100

function_testOne()

{

read a b c #用于读取重定向到函数的文件内容

echo "输出测试${b}"

echo ${a}

echo chenhaojie>>test6 #这一句没有输出到文件

# echo chenhaojie> test6 #和上一句比较,这一种把前两句输出删了

echo ${*} #没有输出

echo ${1} #没有输出

ls –y #错误命令行

echo hello>>test6 #test6可以被写入,并且是在文本最后写入

#打开的文件,在写入时不会出问题吗,test6在函数运行期间没有一直打开吗

}>test2 1>test6 2>test4 2>&1

function_testTwo( ) #shell函数输入变量测试函数

{

echo `expr $1 + 8` >>test7 #没有输出,和前一个函数情况一样

echo $1

echo `expr $1 + 1` >>test7

#注意:是反引号,expr是求表达式的值,中间要加空格,至于不加空格的情况自己去查吧

}

while [ "$number" -le "$max" ]

do

let sum=$sum+$number

let number=$number+1

echo hello

done >test1 #while语句块的输出重定向

echo $sum

function_testOne >test3 2>test5

#调用函数,并且测试调用函数后的输出重定向

function_testTwo 921 >test7 #数值921参数传递

exit 0

执行命令:bash sum100

说明:最终会建立七个用于输出的文件:test1、test2、test3、test4、test5、test6、test7。

输入文件:test8,其内容为:1 2 3

分析:

test1文件:while语句块的输出重定向文件

内容:很多行hello

每循环一次,test1文件中就会在文本最后增加写入一行hello。

分析:后一次写入并没有覆盖前一次写入,这说明什么?test1文件,怎样才会被重新写入?应该是关闭了再打开一次,就重新写入。没有覆盖写入,说明在while语句块循环期间,test1文件一直是打开状态,直到循环结束。

其实从shell命令解析过程也可以看出来,while语句块是当做一个整体被读入的。而I/O重定向是在解析的第六步就进行了,并不是等待命令执行,或者是执行完才I/O重定向,而是在命令执行的前几步就进行了。具体参见命令行解析过程:(等待一个链接)

test2文件,没有内容,但是会被创建。

分析:先是>test2文件,所以,系统会先创建一个test2文件,打开这个test2文件,并把它的指针信息更新进进程文件表。但是,后一句: 1>test6,系统又把test2文件关闭,然后创建test6文件,打开它,并更新进程文件表信息。这里的文件描述符“1”更像是一个文件指针。&1用来指向test2文件,但是,这个指针随时都可以被改变(自我理解,不对,请指出^^)。

test3文件,没有内容,但是会被创建。

分析:之所以会被创建,理由和test2文件被创建的理由相当,命令被读入后,就会创建文件,并首先更新进程文件表。之所以没有内容,相比因为是,函数已经没有了输出。原因是,紧跟语句块后面的重定向已经把数据重定向,输出到文件了,所以,在后面调用函数时,就不会再有输出内容。

这里有一个概念,出口状态和标准输出的区别,别把return ,exit等输出的出口状态,和标准输出弄混了:点击打开链接

test4文件,没有内容,但是会被创建,同test2文件。

test5文件,没有内容。同test3。

test6文件,内容:函数function_testOne()的标准输出和标准错误输出都输出到了test6文件。

问题的出现:函数中的最后一条命令echohello >>test6标准输出,输出到了test6文件,在最后一句写入。中间的一句命令:echo chenhaojie>>test6没有输出,或者说输出被覆盖了。问题就是这两个。

基本分析:>test2,是标准输出重定向到了test2文件;1>test6,把标准输出重定向到了test6文件;2>test4,是标准错误输出重定向到test4文件;2>&1,是标准错误输出重定向到文件描述符1指向的文件test6。所以,最终,这四句相当于:1>test6 2>&1

test6文件要问的是:

问1:为什么echo hello >>test6,可以被写入,并且为什么是在文本最后写入,而不是文本最开始。这一句应该是对test6最开始的写入,除非,函数里面的内容是一边输出一边写入。

答:首先,为什么是在文本最后写入,而不是文本最开始。这里的疑惑的原因在于理解上有错误。

错误的理解,所有的输出都是在一个缓冲区,等待着函数块执行完,然后写入文件。所以,echo hello >>test6作为一句实际语句的命令输出,应该是最先被写入的。

然而根据命令行解析过程来看,最终的输出是按照命令执行的逻辑顺序依次输出的。也就是说,函数块中的语句是先执行,如果有输出,就有写入操作。所以,echo hello >>test6,最为最后执行的语句,当然是最后输出。

问2:为什么echo hello >>test6可以输出,虽然是在文件最后。而中间的一句命令echo chenhaojie >>test6 没有输出到文件test6文件中。

答:这一句应该也是有输出的,接在上一句的输出后面,只是被后一句给覆盖了。

如果用>test6来检测,会发现它把前两句给删掉了。

解释一下,为什么echo chenhaojie >test6这个命令会删掉原先数据。应该是,虽然文件已经被打开了,但是文件肯定是首先被,然后执行删除原数据操作。并非,文件打开了,就不能执行删除操作了。

而之所以被后一句覆盖了,应该是因为,他们的信息不对称导致的,在那一句命令独立输出后,函数指向并没有更新。或许上面的解释不准确,先留个坑吧,以后学Linux系统时,再回来看这里,先给一个多进程同时打开一个文件的分析博客:点击打开链接

test7文件,内容921 922。

分析:test7文件有内容,但是test3、test5文件没有内容。test3、test5文件没有内容的原因是function_testOne函数的输出已经被重定向到文件了,所以,在最后调用的时候,function_testOne函数已经没有输出了。

函数function_testTwo中一条语句的输出922,也是写在test7文件的最后,这和function_testOne函数中的一条语句的输出,写在test6文件最后是一致的。

test8文件,是输入文件。标准输入重定向文件。内容是1 2 3。通过read命令读入,并赋给参数a b c。

执行完命令后,输出于屏幕内容,只有语句echo $sum,输出到屏幕的内容:5050。

如果执行命令换成:bash sum100 >test1 (test1文件是while语句块的输出重定向文件)

输出结果test1中的内容(while语句块的输出)并不会清除,只会把最开始的两行hello替换成5050。

所以,为什么test1没有被清空的原因和上面在function_testOne函数中出现的情况应该是一样吧,但是,又有点不同,因为函数中把内容全部清掉了。

例②: 对例子①的精简,并且更改,利用1>>test6 2>&1

现在把上面的测试代码精简一下,并把原1>test6 2>&1,更改为1>>test6 2>&1:

sum101脚本:

function_testOne()

{

read a b c #用于读取输入重定向到函数的文件内容

echo "输出测试${a}"

echo ${b}

echo "string">>test6 #这一句没有输出到文件

ls -y #错误输出

echo chenhaojie

echo ${*} #没有输出

echo ${1} #没有输出

ls –y #错误命令行

echo "hello">>test6 #test6可以被写入,并是在文本最后写入

} 1>>test6 2>&1 #函数输出重定向到test6

function_testOne

执行命令:bash sum101

test6文件的输出:

输出测试1

2

string

ls: invalid option -- 'y'

Try 'ls --help' for more information.

chenhaojie

ls: cannot access '–y': No such file or directory

hello

分析:echo string >>test6 正常输出了,

问题:但有一个问题,test6文件原内容也会保留。如何使test6文件原内容删除,又使echo string >>test6输出正常呢?看例④

例③ :对例 ① 的等效更改,利用&>finlename

对原脚本进行更改:

function_testOne()

{

read a b c #用于读取输入重定向到函数的文件内容

echo "输出测试${a}"

echo ${b}

echo "string">>test6 #这一句没有输出到文件

ls -y #错误输出

echo chenhaojie

echo ${*} #没有输出

echo ${1} #没有输出

ls –y #错误命令行

echo "hello">>test6 #test6可以被写入,并是在文本最后写入

} &>test6 #函数输出重定向到test6

function_testOne

执行命令:bash sum101

test6文件的输出:

输出测试1

2

ls: invalid option -- 'y'

Try 'ls --help' for more information.

chenhaojie

ls: cannot access '–y': No such file or directory

hello

分析:和例①的结果是一样的。string字符串的输出被覆盖掉了。所以,这个&>test6的功能想必也猜到了,就是把标准输出stdout和标准错误输出stderr都重定向到test6文件。

&>test6等同于 1>test6 2>&1

例④ :对例②进行更改,利用1>filename &>>filename:

把函数的输出重定向语句1>>test6 2>&1更改为1>test6 &>>test6,问题解决。

脚本:

function_testOne()

{

read a b c #用于读取输入重定向到函数的文件内容

echo "输出测试${a}"

echo ${b}

echo "string">>test6 #这一句没有输出到文件

ls -y #错误输出

echo chenhaojie

echo ${*} #没有输出

echo ${1} #没有输出

ls –y #错误命令行

echo "hello">>test6 #test6可以被写入,并是在文本最后写入

} 1>test6 &>>test6 #函数输出重定向到test6

function_testOne

所以,1>test6 &>>test6是表示什么意思?中间做了什么?

虽然最后这个问题解决了,但是,这中间的过程我还是没有弄懂。

不过知道了一个很强的操作,就是1>filename &>>filename,这个操作,不会漏掉任何信息,相对于其它操作都比较安全。

责任编辑:admin  二维码分享:
本文标签: test6输出文件echo重定向函数