linux shell set e,科学网—Bash脚本笔记:set -e 和一个陷阱 - 余进的博文

在bash脚本中习惯了在脚本头部加上"set -e"内置命令,使得脚本里任何一行命令的退出状态码为非零时,shell立即退出。然而最近发现一个bug,排查了很久才找到原因,记录一下。

需求是在for循环中进行一系列由管道连接的操作,大致如下:#!/bin/bash

set -e

...

for id in `something`;do

cmd1 | cmd2 | grep sth

done

...

看起来很简单,实际也很简单。但是我忽略了,或者说我根本不知道的一点是:grep程序在匹配失败时,退出状态码是非零的,虽然它并不会给出任何STDERR信息。而set -e内置命令有一个特点,当它作用于由管道连接的命令组合时,仅检查该组合最后一个命令的退出状态码。在这个例子中,某一次grep失败,则整个for循环,以及整个脚本都会退出。而恰好在Marchantia的Swiss-prot功能注释结果中,地钱本身有一个基因在其中是找不到的,导致for循环进行到该基因时,grep失败,触发ERREXIT,脚本退出。

如果仅仅是这样的话,应该还是不难排查出来的。实际上并没有这么简单,上面的脚本是一个简化版本,真实的脚本是这样的:#!/bin/bash

set -e

for species in `something`;do

...

for id in `something`;do

cmd1 | cmd2 | grep sth

done | cmd3 | cmd4 > somefile

done

cmd5

这个脚本会对每一个物种(species)生成一个文件(somefile)。当内层循环中某一次迭代的grep匹配失败时,整个脚本会退出吗?答案是否定的。因为内层循环的"done"后面还有管道操作,前面说过,只有组合的最后一个命令可能触发ERREXIT,所以这里的某一次grep失败并不会导致脚本退出。那这个脚本问题到底在哪呢?实际上,内层循环某一次grep失败,会导致整个内层循环退出,而由于内层循环与后面的管道形成了一个整体,这个整体的最后一个命令(重定向到somefile文件)不会失败,所以这个整体不会触发ERREXIT。外层循环可以顺利运行,遍历整个列表,而内层循环则会在迭代至列表中导致grep失败的那个基因处触发内层循环的ERREXIT,导致内层循环异常退出(如果后面没有接管道操作,则会继续导致外层循环异常退出,进而触发整个脚本的ERREXIT),而该异常并不会触发整个脚本的ERREXIT,所以导致了Marchantia这个物种生成的文件很小。

该问题的发现纯属偶然。给我的感受是,以后使用bash、perl等工具时要时刻谨慎。转载本文请联系原作者获取授权,同时请注明本文来自余进科学网博客。

链接地址:http://blog.sciencenet.cn/blog-3414436-1211293.html

下一篇:Linux命令行选项的三种风格

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值