本文是之前一篇文章的后续,关于trap ERR在函数调用,命令替换,命令列表()里的应用。
等等,一个trap ERR涉及的太多,以至于经常会片面地去理解并由此产生疑惑
前文提及一个朋友想将trap ERR继续到函数调用里去,并由此引出shell的-e参数(等效于shopt的errtrace)
然后另一个朋友提出了疑问,为何在()产生的子进程里,即使加了errtrace也无法继承trac ERR
为此重新查询了man ,bash文档,并且为此纠结了subshell与child process好长时间,
终于发现了问题答案所在:涉及到需要了解的知识点包括
1:bash shell 的trap ERR机制
2:subshell机制
3:shopt的 "errtrace" "extdebug"参数
4:bash的版本更新
等等,下边从这四点慢慢展开说明
首先,关于bash shell的trap ERR机制。trap本身是用于捕获信号的,
在信号列表里(kill -l ,或trap -l)里并没有ERR信号,
那么trap ERR是做什么用的,又是从什么时候开始的呢?
man bash和info bash文档里是这么描述trap ERR的:
If a SIGSPEC is `ERR', the command ARG is
executed whenever a simple command has a non-zero exit status,
subject to the following conditions.
联想到shell做为解释器的执行机制,再从trap做为bash的一个builtin来看,
ERR并不是传统的trap信号,而是bash shell一种特殊trap机制,
了解到这点并不能满足我们的好奇,并且带来了更多的疑惑:
紧接着问题便是,那trap ERR是否是bash特有的,继续查询文档:
从bash的更新文档(/usr/share/doc/bash-ver/NEWS)里可以看到这么一段:
the new features added to bash-2.05a since
the release of bash-2.05.
l. The ksh-like `ERR' trap has been added. The `ERR' trap will be run
whenever the shell would have exited if the -e option were enabled.
It is not inherited by shell functions.
说明,bash-2.05a正式采用了ksh的 trap ERR.这个版本的trap ERR是不具有函数继承的
但是在2.17版本bash下,man 文档与info 文档已经注意了,bash -E参数的作用为:
-E If set, any trap on ERR is inherited by shell functions, command substitutions, and commands
executed in a subshell environment. The ERR trap is normally not inherited in such cases.
使trap ERR可继承进函数与命令替换之中,包括subshell环境下
再看在apue第十章<信号>里的提及:
当一个进程调用f o r k时,其子进程继承父进程的信号处理方式。
相关的进程环境资料在info bash文档的3.7.3 Command Execution Environment 一章里有提及
The shell has an EXECUTION ENVIRONMENT, which consists of the following:
* current traps set by `trap'
并在接下来的一段中,提及subshell里的子进程在继承父进程时,对trap进行了如下的处理:
* traps caught by the shell are reset to the values inherited from
the shell's parent, and traps ignored by the shell are ignored
看来,默认的trap处理,包括trap ERR是不继承到子shell里的,-E的参数便是为了使shell不使用默认的处理方式
在bash的更新文档里也提到这么一点:
new features added to bash-3.0 since the release of bash-2.05b.
m. New `functrace' and `errtrace' options to `set -o' cause DEBUG and ERR
traps, respectively, to be inherited by shell functions. Equivalent to
`set -T' and `set -E' respectively. The `functrace' option also controls
whether or not the DEBUG trap is inherited by sourced scripts.
这便是上文的留言里一个朋友提到的疑惑,的确,我在bash3.1.17环境测试下,无法通过-E得到subshell环境下的trap ERR继承
程序如下:
trap 'echo ***trap erro*** >&2 ' ERR; trap; nocmd0 ( echo -e "/nuse ()" #subshell #出错的命令,测试trap ERR trap nocmd1 nocmd2 : ) echo -e "/nuse cmd substitude===" $(trap >&2;nocmd3; nocmd4;:); echo -e "/nuse tunnel" echo | while read line do trap; nocmd5; nocmd6; : done echo -e "/nuse fun call" sub() { trap; nocmd7; nocmd8; : } sub
运行结果:
[root@rac2 trap]# bash --version GNU bash, version 3.1.17(1)-release (i686-redhat-linux-gnu) Copyright (C) 2005 Free Software Foundation, Inc. [root@rac2 trap]# bash -E a.sh trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 5: nocmd0: command not found ***trap erro*** use () trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 12: nocmd1: command not found a.sh: line 13: nocmd2: command not found use cmd substitude=== trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 18: nocmd3: command not found a.sh: line 18: nocmd4: command not found use tunnel trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 24: nocmd5: command not found a.sh: line 25: nocmd6: command not found use fun call trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 33: nocmd7: command not found ***trap erro*** a.sh: line 34: nocmd8: command not found ***trap erro*** [root@rac2 trap]#
结果显示,-E只对函数调用产生效果。
命令替换,(),通道等产生subshell的方式里,尽管trap显示信息已经继承进subshell
但却没有实际产生作用。
问题在哪里?
至此开始产生疑惑,甚至于怀疑是bash的bug?
于是到官网上去bug说明文档,愣是没发现相关的问题。
最后想去下载个bash4,看看新版本的是否还有这样的问题。
升级bash4时顺带升级了一下bash3,从3.17升级到3.2.25
运行一下,结局是令人迷惑和兴奋的:
[root@rac1 trap]# sh -E c.sh trap -- 'echo ***trap erro*** >&2 ' ERR c.sh: line 5: nocmd0: command not found ***trap erro*** use () trap -- 'echo ***trap erro*** >&2 ' ERR c.sh: line 12: nocmd1: command not found ***trap erro*** c.sh: line 13: nocmd2: command not found ***trap erro*** use cmd substitude=== trap -- 'echo ***trap erro*** >&2 ' ERR c.sh: line 18: nocmd3: command not found ***trap erro*** c.sh: line 18: nocmd4: command not found ***trap erro*** use tunnel trap -- 'echo ***trap erro*** >&2 ' ERR c.sh: line 24: nocmd5.0: command not found ***trap erro*** c.sh: line 25: nocmd6.0: command not found ***trap erro*** use fun call trap -- 'echo ***trap erro*** >&2 ' ERR c.sh: line 33: nocmd7: command not found ***trap erro*** c.sh: line 34: nocmd8: command not found ***trap erro*** [root@rac1 trap]#
这说明在bash 3.2和3.1中,肯定更改了一些关于trap ERR的机制
于是,继续查看bash文档
终于在bash3.2的更新文档(/usr/share/doc/bash-3.2/CHANGES)上找着了这么一段:
This document details the changes between this version, bash-3.2-alpha,
and the previous version, bash-3.1-release.
z. The inheritence of the DEBUG, RETURN, and ERR traps is now dependent only
on the settings of the `functrace' and `errtrace' shell options, rather
than whether or not the shell is in debugging mode.
果然,在3.2之前的bash要对trap ERR进行继承,不仅需要-E参数,更需要使shell进入debuggin mode
啥是debugging模式呢?形式上讲,就是调用时用了--debug参数(或设置了shopt里的extdebug)使shell进入的一种模式。
为了验证上边这一点,我们在3.1.17上采debug模式调用脚本:
[root@rac2 trap]# bash --debugger -E a.sh trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 5: nocmd0: command not found ***trap erro*** use () trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 12: nocmd1: command not found ***trap erro*** a.sh: line 13: nocmd2: command not found ***trap erro*** use cmd substitude=== trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 18: nocmd3: command not found ***trap erro*** a.sh: line 18: nocmd4: command not found ***trap erro*** use tunnel trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 24: nocmd5: command not found ***trap erro*** a.sh: line 25: nocmd6: command not found ***trap erro*** use fun call trap -- 'echo ***trap erro*** >&2 ' ERR a.sh: line 33: nocmd7: command not found ***trap erro*** a.sh: line 34: nocmd8: command not found ***trap erro*** [root@rac2 trap]#
终于看到了期望的 trap erro
到此,答案揭晓
做个简单的总结:
1:ERR不是传统的信号,只是shell的一种处理机制(同类机制可以查看DEBUG,EXIT等trap)
2:bash从2.0bate版本开始,(参考的ksh)开始采用了trap ERR
3:一开始,bash里的trap ERR是不继承进函数的
4:-E参数是可以使bash 的trap ERR继承进函数,同样也继承进了子进程(subshell),
5:3.2版本之前的bash,虽然-E可以使subshell继承了trap ERR,但并不起作用,需要在调试模式下才能使trap ERR在subshell里起作用
6:3.2版本后,只需要-E,就可以使trap ERR继承进subshell,并起作用