shell浅谈之八I/O重定向

来源:互联网 发布:玩游戏防闪退软件 编辑:程序博客网 时间:2024/04/27 14:37

一、简介

      I/O重定向用于捕获一个文件、命令、程序或脚本甚至代码块的输出,然后把捕获到的输出作为输入发送给另外一个文件、命令、程序或脚本等。I/O重定向最常用的方法是管道(管道符"|")。

二、详解

1、管道

(1)管道技术是Linux间的一种通信技术,利用先进先出排队模型来指挥进程间的通信(可当作连接两个实体的一个单向连接器)。Linux管道可用于应用程序之间、linux命令之间、应用程序与命令间的通讯。shell编程指利用管道进行Linux命令之间的通信。

      管道通信的格式:command1 | command2 | command3 | ... | commandn,command1执行后如果没有管道则输出结果直接显示在shell上,当shell遇到管道符"|"后会将command1的输出发送到command2作为command2的输入。

      例:ls -l | grep vi | wc -l,在三个命令之间建立两根管道,第一个命令ls -l的输出作为grep vi 的输入,第二个命令在管道输入下执行后的输出作为第三个命令wc -l的输入,第三个命令在管道输入下执行命令将结果输出到shell。这是一个半双工通信,因通信是单向的,则两个命令之间的具体工作是由linux内核来完成的。

 (2)sed、awk和管道

       sed、awk可以从文件读取输入数据,也可以从管道获得输入数据。

       sed命令的格式是:| sed [选项] 'sed命令',表示sed对管道输入的数据进行处理。例:ls -l | sed -n '1,5p',表示打印ls -l命令结果的第1~5行。例:cat passwd | sed -n '/root/p' | sed -n '/login/p',查找文件中包含root和login两个关键字的行。例:variable1="Hello world";variable2=`echo $variable1 | sed "s/world/Sir/g"`;echo $variable2,对变量中字符串进行替换。

       awk的命令格式是:| awk [-F 域分隔符] 'awk程序段'(可以用awk代替expr的使用),例:echo $string | awk '{print length($0)}',计算string字符串的长度。echo $string | awk '{print substr($0, 1, 8)}',抽取string字符串中第1~8个字符作为字串输出。

  

      注意:管道将字符串作为awk的输入数据时,awk将管道输入当作输入文件。awk可以解析变量名,但这必须要在该变量从管道输入的情况下,如上若字符串变量不从管道输入时,awk无法解析变量名。

       awk使用管道,例:awk -F ':' '{print $1 | "sort"}' /etc/passwd,awk将分隔符指定为冒号,将打印结果通过管道传输给sort命令进行排序,然后输出。特别注意的是awk中调用Linux命令时需要用双引号将这些命令引起来。

      awk处理shell命令输出,需要引入getline函数将shell命令的输出保存到变量中,awk再对该变量进行处理。例:awk 'BEGIN{while (("ls /usr" | getline data) > 0) print data}',awk在BEGIN字段中使用了while循环将ls /usr命令的结果逐个传给getline data,并打印data变量,直到ls /usr命令的结果全部处理结束。例:df -k | awk '$4 > 1000000',df -k列出文件系统控件信息,第四个域是剩余空间量,输出可用空间大于1GB的文件系统。

      对shell命令结果进行处理,必须将结果通过管道传给getline函数,如果结果较多则要使用循环。也可以不将shell命令放在awk内部,awk同样也可以对shell命令进行处理。

2、I/O重定向

(1)I/O重定向是一个过程,这个过程捕捉一个文件、命令、程序或脚本,甚至代码块的输出,然后把捕捉到的输出作为输入发送给另外一个文件、命令、程序或脚本。

(2)文件描述符

     文件描述符是从0开始到9的结束的整数,指明了与进程相关的特定数据流的源。当Linux系统启动一个进程(该进程可能用于执行shell命令)时,将自动为该进程打开三个文件:标准输入(文件标识符为0)、标准输出(1标识)和标准错误输出(2标识),若要打开其他的输入或输出文件则从整数3开始标识。默认情况下,标准输入与键盘输入相关联,标准输出与标准错误输出与显示器相关联。

     Shell从标准输入读取输入数据,将输出送到标准输出,如果该命令在执行过程中发生错误,则将错误信息输出到标准错误输出。

     tee命令将shell的输出从标准输出复制一份到文件中,tee命令加-a表示追加到文件的末尾。

(3)I/O重定向符号

     I/O重定向符号分为:基本I/O重定向符号和高级I/O重定向符号(与exec命令有关)。

    基本I/O重定向符号及其意义如下:

       >|符号是强制覆盖文件的符号,如果noclobber选项开启(set -o noclobber),表示不允许覆盖任何文件,此时>|可强制将文件覆盖。n>> file、n>|file与n>file都是将FD为n的文件重定向到file文件中。

      <是I/O重定向的输入符号,它可将文件内容写到标准输入之中。wc -l < newfile,其中shell从命令行会“吞掉”<newfile并启动wc命令。

      <<delimiter(delimiter为分界符),该符号表明:shell将分界符delimiter之前的所有内容作为输入,cat > file << FIN,输入FIN后按回车键结束编辑,输入内容重定向到file文件中。其另一种形式:-<<delimiter,在<<前加一个负号,这样输入文本行所有开头的"Tab"键都会被删除,但开头的空格键却不会被删除,如cat > file -<< FIN。

     高级I/O重定向符号及其意义:


(4)exec命令

exec命令可以通过文件描述符打开或关闭文件,也可将文件重定向到标准输入及将标准输出重定向到文件。

[cpp] view plaincopy
  1. #使用exec将stdin重定向到文件  
  2. #!/bin/bash           
  3.   
  4. exec 8<&0           #FD 8是FD 0的副本,用于恢复标准输入  
  5. exec < file         #将标准输入重定向到file         
  6. read a              #读取file的第一行  
  7. read b              #读取file的第二行  
  8.   
  9. echo "----------------"  
  10. echo $a             #标准输出  
  11. echo $b             #标准输出  
  12.   
  13. echo "close FD 8:"  
  14. #exec 0<&8 8<&-     #将FD 8复制到FD 0,恢复FD 0,并关闭FD 8,其他进程可以重复使用FD 8  
  15. echo -n "Enter Data:"  
  16. read c              #read从标准输入读取数据  
  17. echo $c  
[cpp] view plaincopy
  1. #exec将标准输出从定向到文件  
  2. #!/bin/bash  
  3. exec 8>&1                 #FD 8是FD 1的副本,用于恢复FD 1  
  4. exec > log                #将标准输出重定向到log,>符号等价于1>符号  
  5. echo "Output of date command:"  
  6. date                      #date和df命令  
  7. echo "Output of df command:"  
  8. df  
  9.   
  10. exec 1>&8 8>&-            #FD 8复制到FD 0,FD 0恢复为标准输出,并关闭FD 8     
  11. echo "--------------------------------"  
  12. cat log                   #查看log文件             
[cpp] view plaincopy
  1. # &>file将stdout和stderr重定向到文件  
  2. #!/bin/bash  
  3. exec 8>&1 9>&2      #FD 1复制到FD 8,FD 2复制到FD 9  
  4. exec &> log         #&>符号将stdout和stderr重定向到文件log  
  5.   
  6. ls z*               #错误写入文件log  
  7. date                #输出写入文件log  
  8. exec 1>&8 2>&9 8<&- 9<&-      #恢复关闭操作  
  9. echo "-----------------"  
  10. echo "Close FD 8 and 9:"  
  11. ls z*  
  12. date  
(5)代码块重定向

      代码块重定向是指在代码块内将标准输入或标准输出重定向到文件,而在代码块之外还是保留默认状态。可以重定向的代码块可以是while、until、for等循环结构、可以是if/then测试结构、还可以是函数。代码块输入重定向符号是<,输出重定向符号是>。
      while循环的重定向:

[cpp] view plaincopy
  1. #while循环的重定向  
  2. #!/bin/bash  
  3.   
  4. ls /etc > log                    #将ls /etc的结果写到log文件中  
  5.    
  6. while [ "$filename" != "rc.d" ]  #搜索log文件中第一次与rc.d匹配的行,并输出行数  
  7. do                               #不匹配时,执行while循环体  
  8.   read filename                    
  9.   let "count +=1"  
  10. done < log                       #将while代码块的标准输入重定向到log文件  
  11.   
  12. echo "$count times read"         #测试循环体外的标准输入是否被重定向  
  13. echo -n "-----Input Data:-----"  
  14. read test                        #最终是从标准输入获取数据  
  15. echo $test  
      for循环的重定向:
[cpp] view plaincopy
  1. #for循环的重定向  
  2. #!/bin/bash  
  3.   
  4. ls /etc > log                  #将ls /etc的结果写到log文件中  
  5.   
  6. maxline=$(wc -l < log)         #计算log文件的最大行数,赋给maxline            
  7.   
  8.   
  9. for filename in `seq $maxline` #seq命令产生循环参数,相当于for filename in 1,2,...,maxline  
  10. do  
  11.  read filename                 #按行读取log文件数据  
  12.   
  13.  if [ "$filename" = "rc.d" ]   #if指定跳出循环的条件  
  14.  then  
  15.    break  
  16.  else  
  17.    let "count +=1"             #不匹配,计数器count加1  
  18.  fi  
  19. done < log                     #for代码块中将标准输入重定向到log文件  
  20.   
  21. echo "$count:times read"  
  22.   
  23. echo -n "-----Input Data:-----" #测试for外标准输入是否被重定向  
  24. read test  
  25. echo $test  
      if/then结构的重定向,命令格式是(重定向符号要放在fi关键字后面):

[cpp] view plaincopy
  1. #if/then结构的输出重定向  
  2. #!/bin/bash  
  3. if [ -z "$1" ]                          #如果位置参数$1为空  
  4. then  
  5.  echo "Positional Parameter is NULL"    #将该语句重定向输入到log文件  
  6. fi > log                                #if/then代码块输出重定向到log文件  
  7.   
  8. echo "------Normal Stdout --------"     #代码块外的标准输出是否被重定向  
       代码块重定向在一定程度上增强了shell脚本处理文本文件的灵活性,它可以让一段代码很方便地处理一个文件(只要该文件输入重定向到该代码块)

3、命令行处理

(1)流程

      shell从标准输入或脚本读取的每一行称为管道(pipeline),每一行包含一个或多个命令,这些命令用管道符隔开,shell对每一个读取的管道处理流程如下(命令行的处理步骤是由shell自动完成。):

      例如在/root目录下输入:echo ~/i* $PWD `echo hello world` $((21*20)) > output,shell处理该命令步骤:

1)shell首先将命令行分割成令牌(令牌以元字符分隔),> output虽被识别但它不是令牌。

2)检测第一个单词echo是否为关键字,echo不是开放关键字,命令行继续。

3)检测echo是否为别名,echo不是,命令行继续。

4)扫描命令行是否需要花括号展开,该命令无花括号,则命令行继续处理。

5)扫描命令行是否需要波浪号展开,存在则展开:echo /root/i* $PWD `echo hello world` $((21*20))

6)扫描命令行中是否存在变量,若存在则替换,存在PWD,命令行变为:echo /root/i* /root`echo hello world` $((21*20))

7)扫描命令行中是否存在反引号,若存在则替换,存在则命令行变为:echo /root/i* /roothello world $((21*20))

8)执行命令行中的算术运算,则命令行变为:echo /root/i* /roothello world 420

9)shell对前面所有展开所产生的结果进行再次扫描,依据$IFS对结果进行单词分割。

10)扫描命令行中的通配符并展开,展开后命令行变为:echo /root/install.log /root/install.log.syslog /root hello world 420。

11)此时,shell已经准备执行命令了,它寻找echo(echo是内建命令)。

12)shell执行echo,此时执行>output的I/O重定向。

(2)eval命令

      上图中从执行命令步骤跳转到初始步骤,这正是eval命令的作用。eval命令将其参数作为命令行,让shell重新执行该命令行。eval在处理简单命令时,与直接执行该命令无区别。如果变量中包含任何需要shell直接在命令中看到的字符,就需要使用eval命令。命令结束符(;,|,&)、I/O重定向符(<和>)及引号这些对shell具有特殊意义的符号,必须直接出现在命令行中。

[cpp] view plaincopy
  1. #eval重新提交shell  
  2. #!/bin/bash  
  3.   
  4. while read NAME VALUE     #第一列作为变量名,第二列作为变量值  
  5. do  
  6.   eval "${NAME}=${VALUE}" #第1轮变量替换,eval重新提交shell完成赋值操作  
  7. done < evalsource         #输入重定向  
  8. echo "var1=$var1"         #变量赋值  
  9. echo "var2=$var2"   
  10. echo "var3=$var3"   
  11. echo "var4=$var4"   
  12. echo "var5=$var5 
0 0