linux shell脚本转移,SHELL(bash)脚本编程三:重定向

在这一篇中,我们介绍了一点关于输入输出重定向和管道的基础知识,本篇将继续重定向的话题。

在开始前,先说一说shell中的引用。

引用

和许多编程语言一样,bash也支持字符的转义,用来改变字符的原有含义,使得一些元字符(如&)可以出现在命令中。

bash中有三种类型的引用,相互之间稍有不同:

第一种是反斜线(\),用来转义紧随其后的一个字符

[root@centos7 temp]# echo \$PATH

$PATH

[root@centos7 temp]#

第二种是单引号(''),它禁止对包含的文本进行解析。

第三种是双引号(""),它阻止部分解析,但是允许一些单词(word)的展开。

在双引号中仍保持特殊含义的字符包括:

$ ` \ !

#其中$(扩展符:变量扩展,数学扩展,命令替换)和`(命令替换)保持它们的特殊意义;

#双引号中反斜线\只有在其后跟随的是如下字符时才保持其特殊意义:$ ` " \ ! ;

#默认时,感叹号!(历史扩展,下篇叙述)只用在交互式shell中,脚本中无法进行历史记录和扩展。

# 如第一篇所述,双引号中位置变量和数组变量使用@和*时,含义有所区别:

# "$@"和"${array[@]}"扩展之后每一个元素都是单独的单词

# "$*"和"${array[*]}"扩展之后是一个整体

bash中还有一种特殊的引用:$'string'。其中字符串string内反斜线转义的字符有特殊含义,遵循ANSI C标准,部分解释见这里

例子:

[root@centos7 ~]# echo $'\u4f60\u597d\uff0c\u4e16\u754c\uff01'

你好,世界!

[root@centos7 ~]#

重定向

在以下的描述中如果数字n省略,第一个重定向操作符号是,则此重定向指标准输出(文件描述符1)。

跟在重定向操作符后面的word会经过扩展。

1、输入重定向

[n]

2、输出重定向

[n]>word

word的扩展结果文件会被命令的输出所覆盖(文件不存在会被创建)。通过内置命令set设置了noclobber选项的bash进程在使用重定向操作符>时,不会覆盖后面的文件。使用操作符>|可以强制覆盖。

3、追加输出重定向

[n]>>word

4、重定向标准输出和标准错误

&>word

>&word

两种写法同理,相当于>word 2>&1。

5、追加重定向标准输出和标准错误

&>>word

相当于>>word 2>&1

6、以读写的方式打开文件

[n]<>word

以上的重定向中word的扩展结果不能为多个,且只能是文件。一条命令中多个重定向出现的先后顺序很重要,但某个重定向处于命令的位置是无关紧要的。

#!/bin/bash

#多个重定向出现的顺序有时会影响结果

#标准输出和标准错误都重定向至文件file

ls hello file >file 2>&1

#标准错误输出至终端,标准输出重定向至文件

ls hello file 2>&1 >file

#重定向出现的位置无关紧要。下面三条命令等价:

head -1 >newfile

>>newfile head -1

head>newfile -1

#查看验证

cat newfile

执行:

[root@centos7 ~]# ./test.sh

ls: 无法访问hello: 没有那个文件或目录

root:x:0:0:root:/root:/bin/bash

root:x:0:0:root:/root:/bin/bash

root:x:0:0:root:/root:/bin/bash

7、Here Documents

<

here-document

delimiter

此处的word不能扩展,如果word中有任何字符被引用(如前引用部分),delimiter是word去除引用后剩余的字符,并且here-document中的词都不会被shell解释。如果word没有被引用,here-document中的词可以经历变量扩展、命令替换和数学扩展(和双引号的情况类似)。

如果重定向操作符是<

8、Here Strings

<<

这里word的扩展结果会作为字符串被重定向。

脚本举例:

#!/bin/bash

VAR='hello'

#Here Documents

cat <file

#文档内容不会被作为注释

不被引用时变量可以在文档内被扩展:

$VAR world

EOF

cat file

#Here Strings

echo ${parameter:=$[`tr "," "+" <<

#变量临时作用域

IFS=':' read aa bb cc <<

echo -e "$aa $bb $IFS $cc"

执行结果:

[root@centos7 ~]# ./test.sh

#文档内容不会被作为注释

不被引用时变量可以在文档内被扩展:

hello world

6

11 22

33

[root@centos7 ~]#

9、复制文件描述符

[n]

[n]>&word #复制输出文件描述符

这里的word扩展后的值必须为数字,表示复制此文件描述符到n,如果word扩展的结果不是文件描述符,就会出现重定向错误。如果word的值为-,则表示关闭文件描述符n。

[n]>&word这里有一个特殊情况:如果n省略且word的结果不是数字,则表示重定向标准错误和标准输出(如前所述)。

10、转移文件描述符

[n]

[n]>&digit- #转移输出文件描述符

这两种表示移动文件描述符digit到文件描述符n,移动后文件描述符digit被关闭。

由于bash中重定向只在当前命令中有效,命令执行完毕后,重定向被撤销。可以使用内置命令exec使重定向在整个脚本有效。

脚本举例:

#!/bin/bash

#打开输入文件描述符3,并关联文件file

exec 3

#先将文件描述符复制给标准输入,cat命令从标准输入读取到文件file的内容

cat

#关闭文件描述符3

exec 3

#打开3号和4号描述符作为输出,并且分别关联文件。

exec 3>./stdout

exec 4>./stderr

#转移标准输出到3号描述符,关闭原来的1号文件描述符。

exec 1>&3-

#转移标准错误到4号描述符,关闭原来的2号文件描述符。

exec 2>&4-

#命令的标准输出将写入文件./stdout,标准错误写入文件./stderr

ls file newfile

#关闭两个文件描述符

exec 3>&-

#关闭的时候重定向符号是>还是

exec 4

#定义远端主机及端口

host=10.0.1.251

port=80

#以读写的方式打开文件描述符5并关联至文件(此文件代表一条到远端的TCP链接)

if ! exec 5<>/dev/tcp/$host/$port

then

exit 1

fi

#测试链接可用性

echo -e "GET / http1.1\n" >&5

#获取输出

cat

#关闭文件描述符

exec 5

执行结果:

[root@centos7 ~]# ./test.sh

#我是文件file的内容

余下部分是http响应信息

...

[root@centos7 ~]#

[root@centos7 ~]# cat stderr

ls: 无法访问newfile: 没有那个文件或目录

[root@centos7 ~]# cat stdout

file

[root@centos7 ~]#

coproc

上一篇中我们描述了coproc命令的语法,这里给出用例:

#!/bin/bash

#简单命令

#简单命令使用不能通过NAME指定协进程的名字

#此时进程的名字统一为:COPROC。(也预示着同一时间只能有一个简单命令的协进程)

coproc cat file

#协进程PID

echo $COPROC_PID

#转移协进程的输出文件描述符到标准输入,并供cat命令使用:

cat

#复合命令

#对于命名协进程,其后的命令必须是复合命令

coproc ASYNC while read line

do

if [ "$line" == "break" ];then

break

else

awk -F: '{print $1}' <<

fi

done

#传递数据到异步程序(sed命令在文件底部追加了字符串"break")

sed '$abreak' /etc/passwd >&${ASYNC[1]}

#获得输出

while read -u ${ASYNC[0]} user_name

do

echo $user_name

done

执行结果:

[root@centos7 ~]# ./test.sh

28653

命令的标准输出和标准输入通过双向管道分别连接到当前shell的两个文件描述符,

然后文件描述符又分别赋值给了数组元素NAME[0]和NAME[1]

root

bin

daemon

...

[root@centos7 ~]#

管道

管道是进程间通信的主要手段之一。linux管道分为两种:匿名管道和命名管道。

通过控制操作符|或|&连接命令时所创建的管道都是匿名管道。匿名管道只能用于具有亲缘关系的进程之间。

命名管道可以用在两个不相关的进程之间,可以使用命令mknod或mkfifo来创建命名管道。

我们已经见过很多匿名管道的例子,这里举一个利用命名管道控制并发进程数的例子:

#!/bin/bash

#进程个数

NUM=10

tmpfile="$$.fifo"

#生成临时命名管道

[ -e $tmpfile ] && exit || mkfifo $tmpfile

#以读写的方式打开文件描述符5,并关联至命名管道

exec 5<>$tmpfile

#删除临时命名管道文件

rm $tmpfile

#写入指定数量的空行供read使用

while((NUM-->0))

do

echo

done >&5

for IP in `cat ip.list`

do

#read命令每次读取一行输入,保证了同一时间有10个如下复合命令在运行

read

{

#统计IP在日志文件access.log出现的次数

grep -c $IP access.log >>result.txt

echo

#命令运行结束后仍写入一个空行至文件描述符5

#结尾的符号&保证此复合命令在后台运行

} >&5 &

done

#内置命令wait的作用是等待子进程的结束

wait

#关闭文件描述符5

exec 5>&-

执行略。

当然,这里的for循环中执行的复合命令可以替换为任意需要并发执行的任务。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值