I was playing with getopts, which has a shell variable OPTERR
that influences its behavior. I wanted to change the value of OPTERR
and invoke getopts on the same line, to affect the behavior of getopts but restore OPTERR
to its default value thereafter. So, given that I'm a little fuzzy on the process of mixing a variable change a command invocation, I thought I'd experiment with just a humble echo
. Here is a series of shell commands with their outputs, some of which don't make sense to me.
$ echo OPTERR
1
$ OPTERR=0 echo $OPTERR
1
Now, let's stop there. Shouldn't the output for the second command be '0'? So before asking the question, I wracked my brains a bit and thought it must be because following command should be in a sub shell. So then I tried...
$ OPTERR=0 (echo $OPTERR)
-bash: syntax error near unexpected token `('
This was an even bigger surprise. How does one change the value of shell variable for the run of a sub shell, if the above construct is a syntax error? (I also tried putting a terminal ";" after the echo command with the same result). I also tried...
$OPTERR=0 bash -c 'echo $OPTERR'
1
So, no joy there either. I took a different tack...
$function sf() { echo $OPTERR }
$sf
1
$OPTERR=0 sf
0
$sf
1
Success!! But why? Functions are specifically not sub shells (I don't think) and this isn't really what I want (though, I could probably make it work for my application). Here's another formula that worked (sort of):
$function sf() { (OPTERR=0; echo $OPTERR;) }
$sf
0
$echo $OPTERR
1
This is PRETTY close to what I want, but of course the intended target for the actual script (not my trivial echo
) is getopts
, and getopts
has to change variables that the rest of the script can see. So, I changed my trivial echo
script to a simple variable set, as in ...
$function sf() { foo='bar'; (foo='check'; echo $foo;); echo $foo; }
$sf
check
bar
D'oh!! So I can call getopts
with the environment I want, but I can't get any values back out of the sub shell. I tried exporting foo and all kinds of things too embarrassing to list here, but here's my question: if you put all my experiments together, they must reveal a fundamentally wrong model of how all this should be working. What is the missing piece to my understanding, and what's the right thing to do?
Just to re-iterate, all I'm trying to do is build a function, that uses getopts
with OPTERR
set to 0, ideally without setting OPTERR
on the line where the function is invoked, and in a way that OPTERR
is restored outside the function, and most importantly in a way that allows me to actually process the output of getopts
. It sounds like a lot to ask, but it's pretty sensible and the existence of this OPTERR
variable suggests it should be possible.
local
inside the function; see this near-dupe answer. As for prefixing an assignment to a command, such assignments only apply within the execution of that command; withecho $OPTERR
, variable expansion happens beforeecho
is executed; with a function, bash basically sets it temporarily within the function. As for using it withbash -c
, the new shell resets it to default ("1") before executing the command. – Gordon Davisson Jul 02 '19 at 21:52It makes sense that it would be vastly simpler than most of my contorted experiments. Thanks!
– cycollins Jul 02 '19 at 22:14local
because they had some special status, such that they couldn't be covered by a local variable or if they were, thatgetopts
would still be influenced by the one from the calling shell. Another incorrect underlying model. – cycollins Jul 02 '19 at 22:24local
ized, but it seems to work! BTW, subshells do inherit environment variables, butOPTERR
is a special case because bash initializes it. Comparefoo=bar OPTERR=0 bash -c 'echo "foo=$foo, OPTERR=$OPTERR"'
(which prints "foo=bar, OPTERR=1") with the equivalent usingdash
instead ofbash
, which prints "foo=bar, OPTERR=0". – Gordon Davisson Jul 02 '19 at 22:39