shell脚本【符号2】I/O重定向相关符号

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

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

一.概念:

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

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

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

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

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

二.区分:

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

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

三.重定向过程:

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

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

        需要注意的是:在重定向的时候,就已经对文件进行了操作。          如“>file”:在重定向后,file的内容就已经清空了,这在重定向这一步已经发生,而此时,命令行还没有执行。(命令解析过程:点击打开链接

        你也可以对比下面的例①。当重定向写法:>OriginalFile  1>TestFile时,虽然最后把标准输出定向到了TestFile。但是在定向到TestFile之前,是先定向到OriginalFile,然后又一次重定向到的TestFile。所以,OriginalFile的内容已经先一步被清空。

四.如何输出

        提问:有没有办法,让输出信息既打印进文件,同时又输出到终端呢?

        答案:有。

        方法1:tee命令。

        例如:bash test | tee filename

        分析:bash test命令的输出结果,通过管道传递给tee命令。tee命令一边把标准输出打印进文件filename,一边就输出信息到屏幕。(注意的是,管道不能传递标准错误输出点击打开链接

        方法2:/dev/tty

        这种方法,如果重定向了脚本的标准输出和标准错误输出,通过>/dev/tty,可以将输出信息仍然能够送到用户的终端窗口,但是注意:输出到终端的这一句信息,不会再保存到文件内

        顺便讲一下命令:exec,这个命令有两种用法,我们今天只讲它重定向的功能。

        exec命令加在脚本中,可以使这个脚本中其随后的所有命令的标准输入、标准输出、标准错误输出重定向。

        脚本例子test:

                echo hello
                exec >filename 2>errfilename       #exec命令加在这里,只对下面的所有命令有效果
                echo chenhaojie >/dev/tty          #加了exec命令后,如果想输出中间的某一句,可以使用这种方式
                echo WUST                          #由于exec的作用,打印到filename文件,而不是终端

        执行:bash test

        终端屏幕输出;hello 

                              chenhaojie

        同时filename中的内容:WUST

        exec这个命令的方便之处在于:如果有个shell脚本是通过系统自动调用的,你并不知道这个脚本具体在哪被调用。所以,无法用添加“ | tee filename”这种方式来获得它的输出信息。如果你想获取这个脚本的输出信息,可以直接在这个脚本中进行修改,添加exec > filename 重定向命令,而无须知道,它是在哪个地方被调用。并且可以随意查看标准输出和标准错误输出信息。

        注意:加了>/dev/tty的命令,不会再把当句命令的输出信息保存到文件。

        方法1和方法2各有各的限制,方法1,无法保存错误信息,方法2,无法一边保存一边输出。

 

五.  本篇博客主要内容概述:

    接下来会举一个四个例子:例①、②、③、④

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

    对例①、②、③、④的一个概述

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

    例①: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  <test8
                        #调用函数,并且测试调用函数后的输出重定向
                        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文件。

     问题的出现:函数中的最后一条命令echo hello >>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 <test1

        执行命令: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 <test1

          执行命令: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 <test1

 

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

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

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

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页