From: u...@stderr.spb.ru (Valery Ushakov) Subject: Re: Add static local vars to sh(1) ? Date: Mon, 29 Jan 2018 13:38:25 +0000 (UTC)
| This doesn't seem to mention what happens when the | function is called recursively. Apologies. I think I have said before that I much prefer writing C over English... Assistance to make the man page better will be much appreciated. Either now, or perhaps better, later if/when we are closer to actually doing something with this, beyond just considering whether it is a reasonable thing to do. | I assume it does the right thing, Well, that depends upon what "the right thing" is in this context. But you can be sure that I won't commit something that I don't believe is working correctly. If we decide to incorporate this, then when the ATF tests appear (which will probably be just before the code, so I can verify that the tests correctly skip themselves when static locals are not supported) there will certainly be recursive functions included, and tested, to verify that the results are at least as I expect. Further, a little later (once I am using something better than "Mail" (on my MTA) again) I can certainly send a "cvs diff" patch to anyone who would like to review the code, and test it. In considering what the right thing is, please remember that sh is not C, its language is not C, and most especially, its variables are nothing like what exists in C (or other declarative programming languages). In particular, a static variable in sh will not (and cannot) be private to a function, the way it would be in C. So, for eaxmple VAR="hello" f() { echo $VAR; } f prints "hello" no surprise there. Then add g() { local VAR=goodbye; f; } g prints "goodbye" (from the "echo" command in f, called from g) - that's the way "local" has always worked, and needs to work (otherwise all kinds of useful stuff becomes either impossible, or unexplainable.) Making that instead be h() { local -S VAR="goodbye"; f; } h changes nothing (will not change anything) about the way that works. when f is run, it accesses the local static VAR from h (or g in the earlier example). Technically, all references to VAR access the global (the one and only) var of that name, it is just that when g (or h) is running, its value "hello" has been preserved in some anonymous place, and replaced by "goodbye", with the "hello" value being restored when g (or h) returns. So if we run f; g; h; f we get (in order, on a line each) hello goodbye goodbye hello This is why the doc is all about saving and restoring, as that's what local actually does - it does not create a new name space, just preserves and restores the values. That doesn't mean that the doc is currently good however, it can certainly be made better. That is fn() { local X; X=3; ... ; } is more or less the same as fn() { saveX=$X; X=3; ...; X=$saveX; } except that the script writer doesn't need to do all the bookkeeping. That is making sure to restore the external value before each and every return from the function - and remember sh has no "goto" so the C trick of "goto out;" isn't possible. It is possible to write a "restore" func and call it though, but that still needs be be inserted at every return point (care also needs to taken with the value of $? if the "return" has no operand). And second, local doesn't have the problem that by attempting (successfuly if the function not using "local" is written correctly) of avoiding damaging an external var X (it needs to be more clever than the above to cope with the possibility that X was unset before fn was called) but in order to do so, it ends up clobbering the external variable saveX instead (and that would make writing recursive funcs with pseudo-local variables without the "local" command, very hard.). So, the issue is when values get saved (which can only be when the local command is executed, or when the function exits) and when they get restored (same two possibilities). So that is what I attempted to convey. Probably not well... And while I am here, to answer another (private e-mail) query about the doc, in case anyone else has the same question - static vars will belong to a function definition. One particular fn definition. (Nb: not one function name - if you redefine the function all the old statics vanish, and a new set are created when "local -S" runs in that redefined func.) Other functions can have their own locals (static or not) (with the same names, if they like) - just the same as "local" (without -S) works. All that's really intended to change is what value the variable gets given when it is made local. local -N makes it unset (the way bash, and some otehr shells do). local -I (or just plain local in the NetBSD sh) makes it be inherited, (ie: local does not change it) the way that ash based shells (including NetBSD's) have always done. Both of those are overridden by an init on the local cmd (local X=1). Then local -S causes any of that init to only happen when the func is executed the first time (really, when the "local -S" is executed in this func for the very first time). After that you get whatever value the variable (the version of it available in this function after the local command) last had. It is essentially inherited from itself. Changing the var init on local that way, and arranging to preserve the return time value of a static local, so it is not lost like a normal local var's value would be, is really all that is happening here. kre ps: given everything in sh is dynamic, defining functions, and the local command included, and you can achieve some truly bizarre effects by exploiting that if so inclined. Eg: by only sometimes making a var static in the function... And lastly, apologies for being too classroomish, expecially given I know that most of you know all of this already.