shell浅谈之八I/O重定向

一、简介

      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命令可以通过文件描述符打开或关闭文件,也可将文件重定向到标准输入及将标准输出重定向到文件。

#使用exec将stdin重定向到文件
#!/bin/bash         

exec 8<&0           #FD 8是FD 0的副本,用于恢复标准输入
exec < file         #将标准输入重定向到file       
read a              #读取file的第一行
read b              #读取file的第二行

echo "----------------"
echo $a             #标准输出
echo $b             #标准输出

echo "close FD 8:"
#exec 0<&8 8<&-     #将FD 8复制到FD 0,恢复FD 0,并关闭FD 8,其他进程可以重复使用FD 8
echo -n "Enter Data:"
read c              #read从标准输入读取数据
echo $c

 
#exec将标准输出从定向到文件
#!/bin/bash
exec 8>&1                 #FD 8是FD 1的副本,用于恢复FD 1
exec > log                #将标准输出重定向到log,>符号等价于1>符号
echo "Output of date command:"
date                      #date和df命令
echo "Output of df command:"
df

exec 1>&8 8>&-            #FD 8复制到FD 0,FD 0恢复为标准输出,并关闭FD 8   
echo "--------------------------------"
cat log                   #查看log文件           
# &>file将stdout和stderr重定向到文件
#!/bin/bash
exec 8>&1 9>&2      #FD 1复制到FD 8,FD 2复制到FD 9
exec &> log         #&>符号将stdout和stderr重定向到文件log

ls z*               #错误写入文件log
date                #输出写入文件log
exec 1>&8 2>&9 8<&- 9<&-      #恢复关闭操作
echo "-----------------"
echo "Close FD 8 and 9:"
ls z*
date
(5)代码块重定向

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

#while循环的重定向
#!/bin/bash

ls /etc > log                    #将ls /etc的结果写到log文件中
 
while [ "$filename" != "rc.d" ]  #搜索log文件中第一次与rc.d匹配的行,并输出行数
do                               #不匹配时,执行while循环体
  read filename                  
  let "count +=1"
done < log                       #将while代码块的标准输入重定向到log文件

echo "$count times read"         #测试循环体外的标准输入是否被重定向
echo -n "-----Input Data:-----"
read test                        #最终是从标准输入获取数据
echo $test
      for循环的重定向:

#for循环的重定向
#!/bin/bash

ls /etc > log                  #将ls /etc的结果写到log文件中

maxline=$(wc -l < log)         #计算log文件的最大行数,赋给maxline          


for filename in `seq $maxline` #seq命令产生循环参数,相当于for filename in 1,2,...,maxline
do
 read filename                 #按行读取log文件数据

 if [ "$filename" = "rc.d" ]   #if指定跳出循环的条件
 then
   break
 else
   let "count +=1"             #不匹配,计数器count加1
 fi
done < log                     #for代码块中将标准输入重定向到log文件

echo "$count:times read"

echo -n "-----Input Data:-----" #测试for外标准输入是否被重定向
read test
echo $test
      if/then结构的重定向,命令格式是(重定向符号要放在fi关键字后面):

#if/then结构的输出重定向
#!/bin/bash
if [ -z "$1" ]                          #如果位置参数$1为空
then
 echo "Positional Parameter is NULL"    #将该语句重定向输入到log文件
fi > log                                #if/then代码块输出重定向到log文件

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具有特殊意义的符号,必须直接出现在命令行中。

#eval重新提交shell
#!/bin/bash

while read NAME VALUE     #第一列作为变量名,第二列作为变量值
do
  eval "${NAME}=${VALUE}" #第1轮变量替换,eval重新提交shell完成赋值操作
done < evalsource         #输入重定向
echo "var1=$var1"         #变量赋值
echo "var2=$var2" 
echo "var3=$var3" 
echo "var4=$var4" 
echo "var5=$var5

三、总结

(1)I/O重定向、管道、文件描述符、exec、eval等在shell编程中有很重要的地位,需加强练习。

(2)了解Shell处理命令行的流程有助于理解命令的执行方式,帮助写出高效率简单的脚本,理解其流程还需参看更多的文档。

(3)在shell编程中不断强化其中的概念,进一步消化。
  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Shell Lab中,实现了基本的shell功能和I/O重定向功能,但不支持管道功能。\[2\]eval()函数用于对外壳命令行进行求值,handler()函数用于处理SIGINT,SIGCHLD和SIGTSTP信号。\[2\]在实现I/O重定向时,可以使用exec命令来替代当前shell并重新启动一个shell,这样可以清除任何现有环境,但不会覆盖当前的shell环境。\[3\] #### 引用[.reference_title] - *1* [CSAPP:ShellLab实验](https://blog.csdn.net/qq_41140987/article/details/105330458)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [ICS shell lab总结](https://blog.csdn.net/PKU_ZZY/article/details/53310876)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [shell中的I/O redirect](https://blog.csdn.net/shuangchen/article/details/5855660)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦2号

博文不易,支持的请给予小小打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值