Op 18-10-16 om 00:15 schreef Martijn Dekker: > Turns out you don't actually have to read ${.sh.subshell} twice: it is > the output redirection within a command substitution that kills > ${.sh.subshell}. This is finally starting to make some sense now. > > $ echo $( (: 1>&1; echo ${.sh.subshell}) ) > 0 > > (expected output: 2)
Annnd it turns out that ${.sh.subshell} is not read-only. You can actually assign a value to it! Very strange, but this makes the workaround obvious: save the value before doing output redirection, restore it afterwards. $ echo $( (save=${.sh.subshell}; : 1>&1; .sh.subshell=$save; echo ${.sh.subshell}) ) 2 In case anyone is interested, here's how I found the bug. First a little simplified background information. The cross-platform POSIX shell library I'm developing, "modernish" <https://github.com/modernish/modernish>, includes a feature for robust shell programming called "harden" that hardens a command against errors. It does this by setting a shell function under the command's name that first runs the real command, then automatically checks its exit status against a user-specified value indicating a fatal error. This is my attempt to provide something better than 'set -e' which is fundamentally flawed. If a fatal error is found, the function set by 'harden' calls another modernish function, 'die'. Fatal errors should always kill the program, so this function is designed to reliably halt program execution, even if the error occurred within a subshell. To do this, "die" first checks if we're currently in a subshell using a third function called "insubshell". If we're not in a subshell, it simply exits. If we are, it sends SIGTERM to the main shell ("$$") and then exits. The aforementioned "insubshell" function has several platform-specific versions; the correct one is automatically detected. For ksh93 I simply have this: insubshell() { ((.sh.subshell)) } (returning false if ${.sh.subshell} is zero, true otherwise). But this fails something in my test scripts: the hardened 'grep' (which, remember, is a shell function calling the real grep) fails to kill the program on error: https://github.com/modernish/modernish/blob/master/share/doc/modernish/testsuite/harden-test > harden -tp grep '> 1' # harden and trace grep, whitelisting SIGPIPE > # [...] > print "this file has $(grep -c '.*' /almost/certainly/a/non/existent/file) lines" > print "we should never make it to here, BAD" On ksh, 'die' fails to kill the main shell and the last line is printed, because 'insubshell' fails to detect that 'die' is in a subshell. And now I know why: since 'grep' was hardened with the -t option, the 'grep' shell function traces the command using output redirection before doing its thing -- and this ksh bug is triggered by using output redirection within a command substitution. Mystery solved. Now on to implementing the necessary bug test (BUG_KSHSUBVAR) and workarounds in modernish, so it once again becomes ksh compatible. - M. _______________________________________________ ast-developers mailing list ast-developers@lists.research.att.com http://lists.research.att.com/mailman/listinfo/ast-developers