linux的I/O重定向、管道、文件描述符、exec、eval等在shell编程中运用,及执行流程

19 篇文章 0 订阅
12 篇文章 0 订阅

前言

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

详解

管道

管道技术是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内核来完成的。

sed、awk和管道

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

sed命令

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的命令格式是:| awk [-F 域分隔符] 'awk程序段'(可以用awk代替expr的使用)。

  • awk可以解析变量名
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命令输出

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命令进行处理。

I/O重定向

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

文件描述符是从0开始到9的结束的整数,指明了进程相关的特定数据流的源。

当Linux系统启动一个进程(该进程可能用于执行shell命令)时,将自动为该进程打开三个文件:标准输入(文件标识符为0)、标准输出(1标识)标准错误输出(2标识),若要打开其他的输入输出文件从整数3开始标识。

默认情况下,标准输入与键盘输入相关联,标准输出与标准错误输出与显示器相关联。

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

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

[root@localhost ~]$ ping baidu.com | tee ping-baidu.log #输出到控制台的同时,将内容保存到ping-baidu.log文件中
PING baidu.com (220.181.57.216) 56(84) bytes of data.
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=1 ttl=128 time=30.6 ms
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=2 ttl=128 time=30.5 ms
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=3 ttl=128 time=30.2 ms
^C[mysql@localhost ~]$ cat ping-baidu.log  #检查文件内容是否和输出一致
PING baidu.com (220.181.57.216) 56(84) bytes of data.
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=1 ttl=128 time=30.6 ms
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=2 ttl=128 time=30.5 ms
64 bytes from 220.181.57.216 (220.181.57.216): icmp_seq=3 ttl=128 time=30.2 ms
[tang@Server shell]$ who | tee -a who.out
root     tty1         2015-11-14 22:55
tang     pts/0        2015-11-14 23:50 (192.168.1.105)
[tang@Server shell]$ ll
-rw-rw-r--. 1 tang tang  94 11月 15 04:30 who.out

I/O重定向符号

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

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

       >|符号是强制覆盖文件的符号,如果noclobber选项开启(set -o noclobber),表示不允许覆盖任何文件,此时>|强制将文件覆盖n>> filen>|filen>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重定向符号及其意义:

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:"
#将FD 8复制到FD 0,恢复FD 0,并关闭FD 8,其他进程可以重复使用FD 8
#exec 0<&8 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
#FD 8复制到FD 0,FD 0恢复为标准输出,并关闭FD 8 
exec 1>&8 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

代码块重定向

代码块重定向,是指在代码块内标准输入标准输出重定向到文件,而在代码块之外,还是保留默认状态

可以重定向的代码块可以是while、until、for等循环结构可以是if/then测试结构还可以是函数

代码块输入重定向符号是<输出重定向符号是>

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循环的重定向:

#!/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结构的输出重定向

if/then结构的重定向,命令格式是(重定向符号要放在fi关键字后面):

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

代码块重定向,在一定程度上,增强了shell脚本处理文本文件的灵活性,它可以让一段代码很方便地处理一个文件(只要该文件输入重定向到该代码块)。

命令行处理

流程

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重定向。

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编程中不断强化其中的概念,进一步消化。


 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿啄debugIT

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值