I am about ready to commit the changes (additions really) to the way that ${LINENO} works, which will have little effect on ${LINENO} iself, but which makes $((LINENO)) and export LINENO work.
In fact, I am going to commit 2 versions, one right after the other. (both so that we have a record of both ways this can be done, just imagine that the first was done, then several months later a better version came along...) The first is the one I had done last time I sent a message about this topic. The one pretty much literally implemented the POSIX spec... LINENO Set by the shell to a decimal number representing the current sequential line number (numbered starting with 1) within a script or function before it executes each command. That helps, but actually isn't perfect - but is perhaps a little expensive. The new (2nd) implementation defers setting LINENO until just before it is referenced, so the shell only does the work when it is needed (it still exports LINENO is requested, and if that is done, will need to set it before every external command is run, but there's no significant extra overhead if LINENO is not exported, unless you have a script that references it every line...) [The overhead compared with the rest of what the shell is doing is very small in either case, but every little bit helps.] Further, in both versions the old LINENO hack is still there, so ${LINENO} doesn't really count as a reference to LINENO for this purpose. I suspect now (with the second implementation) that we can probably do without the hack (just remove it completely) and barely notice the difference - but I haven't tested that yet, and removing it can certainly wait for later (maybe much later.) With the second implementation, but not the first, even this bizarre case works... case ${LINENO} in # or $((LINENO)) would work as well ( $((LINENO - 1)) ) echo "good";; ( * ) echo "bad";; esac which I have not been able to find another shell which produces anything other than "bad". The answer should be "good" (and will be for us) as, assumimg the "case" line is line 100, then the next line is 101, so LINENO there is 101, and 101 - 1 == 100. which is a match for ${LINENO} on line 100. The reason everyone else fails (including implementation #1 for us), is that LINENO is set before the "case" command is executed, and then not set again until the next command is ready (one of the two echo commands.) So when $((LINENO - 1)) is evaluated, LINENO is still 100, and 100-1 is 99, which is not 100, so ... Note it is probable that POSIX does not actually require this to work, and there's no practical use for it I can think of right now, but it is kind of nice, and demonstrates that LINENO really is doing what it should with the deferred evaluation. The are two questions I still would appreciate answers to: 1) from the quote above: (numbered starting with 1) within a script or function we have traditionally caused LINENO within a function to count from one, so... func() { echo "${LINENO}"; } is just a fancy way of doing "echo 1". Most other (major) shells either ignored that (forever) (or don't believe that the words in the standard actually mean how we read them as) or started the same as us, but then changed from that to simply keeping on counting lines from the enclosing script, so that function would tell us, in those shells, which line the function was defined on. Both variants have advantages - the ksh93/bash/... (keep counting) style means that if you have a long script, with functions, and you get an error message from the script (from the script code, not from the interpreter, the shell) something like detected failure at line 666 then you can just go to line 666 and see what is happening, if the error happened to be within a function, it would work just the same. With the other (NetBSD sh, and some others) interpretation, you would get detected failure at line 13 which would then also need to have appended something like in function foobar and the user would first need to find foobar, and then count down 13 lines to find the location of the problem. On the other hand, I use lots of functions in my normal interactive environment. They come from a bunch of different init files (each file has functions for a set of related purposes.) Those files keep getting changed (new functions added, implementations changed or enhanced, sometimes old unused functions deleted). But I only tend to re-read the init file (after it has changed) into one of my (many) interactive shells, if I need the changes in that shell, which usually means 1 or 2 of the shells, and the others just keep using things as they were before. If in one of "the others", using a shell which keeps couning line numbers, I run a function (which has not changed, or if I am using it in this shell I would have re-read the init file) and it tells me "error at line 123". Now I go look at the init file that contains the definition of that function, go to line 123, and find myself no-where near the function in question. And when I find the function, the line number doesn't even give me a clue where in there I should be looking. [This is just like trying to use gdb to debug a program, where the sources have changed, a lot, since the program was compiled ... pretty much impossible.] And on the other hand again, if I have function relative line numbers, I get told "error at line 27" (and probably also told in what function, as above ... but here I already know that) so I read the init file, find the function definition, go down 27 lines, and I am there. So, both variants are useful, depending upon circumstances. That's the obvious place where we add an option, so it can work either way. (It happens to be really cheap and easy to do this). So I have done that. For now the option is called "funclinereset" (I always accept suggestions for better names!) and has no short form. [ For reference for the future, the option needs to be set, before the shell starts reading the code that contains the function definition. That means, the option setting, and the function definition, cannot both be within the same compound statement - if they are the shell will have read, and parsed (though not yet defined) the function before the option setting takes effect. I doubt that restriction will make much difference to anyone though ... it is just the way the parser works. ] Now to the question... what should the default for this be? Set, so we retain compat (for scripts that do not set the option) with the old NetBSD shell, and other ash derived shells (like the FreeBSD shell, zsh, and dash) or reset, so we become compatible (by default) with bash, ksh93 (and other ksh variants, including our /bin/ksh), yash, and others ?? 2) - this is a technical question rather than philosophical, and for someone who knows (not me) there will probably be a simple answer... As part of implementing the 2nd LINENO implementation variant, I need to initialise an array of structs where the structs contain a union (of two members.) Until now, only the first member of the union existed (there was no union...) and the init of the array (which wants to be const, so it gets into text, rather than data) just happens as normal. Everything still works when the union is introduced, until I try and add my new element to the array, in which I need to init the 2nd element of the union, rather than the first. gcc complains that the data type is incorrect, and the compilation fails. Now I suspect that there is some syntax trick that will let me do what needs to be done - but I don't know what it is (I learned C way before anything like this was conceivable .. or perhaps, back when the compiler wouldn't care if the types didn't match, it just did what it was told, and as long as the programmer made no mistakes, it all just worked...) So, can someone give me an example of how to do this? For now I have it working my moving the array into data (removing "const") leaving out the init of the union in the static init, and doing the init of that one field at run time during the shell startup (I know how to write: array[N].union_name.element = value; !!!) While that lets me verify that the method works, it isn't ideal, and I'd like to go back to the "const" version with static init everywhere. So, please help! kre