The definition of the LINENO variable (from 7 TC2 XCU section 2.5.3,
page 2352, lines 74935-7) says ...

        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.

but I have noticed a significant difference between how different
shell implementations have implemented that - in particular, the
"within a script or function" relating to "sequential line number
starting with 1".

ksh93 (and other ksh variants), bash, and yash (maybe others) just keep on
counting through function definitions, so if this e-mail body were a shell
script, and we were to define...

        f() { echo "${LINENO}"; }

and then invoke f those shells would say 17 (or something close to that
depending upon how well I counted the lines in this message, and whether
or not it gets reformatted before you see it...)

Other shells (zsh, all ash derived shells I think, NetBSD's FreeBSD's, dash)
instead output 1, as that echo command is a the first sequential line
within the function (starting with 1).

Now neither of those interpretations is better than the other, as if
the "other" value is wanted, it can easily be computed, by writing the
function more like ..

        BASE=${LINENO}; f() { echo "$((LINENO - BASE + 1))"; }

if the ksh/bash/yash interpretation is correct, and the other is needed, or as

        BASE=${LINENO}; f() { echo "$((LINENO + BASE - 1))"; }

if the zsh/ash interpretation is correct, and the ksh form is wanted.
(modify the "+/- 1" const offsets as required if I got those wrong.)

But we have to know which value we are getting do do that, so we would
really need something like...

BASE=${LINENO}; f() { echo $(( LINENO > 1 ? LINENO - BASE + 1 : LINENO )) ; }

if we want the zsh/ash type values, or as

BASE=${LINENO}; f() { echo $(( LINENO > 1 ? LINENO : LINENO + BASE - 1 )) ; }

if we want the ksh/bash type values (with modifications to correct
that when the reference is not made from the first line of the function,
probably by writing (just one example this time) it more like..

BASE=${LINENO}; f() { LINE1=${LINENO}
        #whatever stuff
                echo $(( LINE1 > 1 ? LINENO : LINENO + BASE - 1 ))
        # more stuff
}

and now this is getting decidely ugly...  (note that each function
needs its own private BASE global var, though when local vars of some
form or other exist, they can have private LINE1 using the same name).


My guess is that the zsh/ash implementations were done from reading the
standard (I know that implementing it that way requires a bunch of extra
work) and believing that there would have been no point at all in even
mentioning functions in the text quoted above there unless they were
intended to have a separate sequence starting from 1, for each function ...
after all, there is no mention of other things which could potentially
reset the numbering, like subshells, cmdsubs, heredocs, ...)

And I assume that bash/yash (etc) just copied what ksh was observed to do.

One final point before I ask the obvious question(s) ... everyone agrees
that dotscripts (. script) start a new LINENO sequence starting from 1
for that script, and in general, there are almost no differences between
dotscripts and functions, except that one is inline, and the other is
in an external file.   That kind of suggests that the zsh/ash interpretation
is (or should be) correct.

So, which interpretation is really intended - which is correct?

And does this mean that the wording of that section of the standard
needs improvement?  (Or is there perhaps an open issue, or even an approved
modification already prepared, that I have failed to find?)



Now that's done, if the wording of this is to be updated, please consider
also saying something about just what "the current      sequential line number"
really means - what does it refer to?   "current" when?   All we are told
that it is before it executes each command, which is not very precise.

There are many possibilities, for example, in a script like

# stuff...
{
        # more stuff...

        echo "

text goes here



${LINENO}

and more text

"

        # even more stuff
}

any of the following could be defended as being correct ...

The line number of the '{'

The line number of the '}'

The line number of the "echo" or perhaps the opening '"' (which are the
                same in this example, but need not be)

The line number of the closing '"'

The line number where ${LINENO} actually occurs (but then what about
$\
{\
L\
INEN\
O\
} ... which line would it be then?  The $, the { the L, the O or the }  ?)

The line number that the shell has read up until ("current sequential...")
                at the time the args for the "echo" command are expanded,
                just before echo is invoked.

something else?

I ask this, because in shells I have tested, almost all of those have
actually been observed.

Now in a little script like this, and for the common uses of ${LINENO}
this probably doesn't matter much, as all the values are fairly close, and
would be enough to locate the part of the script where ${LINENO} was
referenced.

But any of those #stuff comments might be replacing hundreds, or even
thousands, of lines of script, and even the string arg to echo could be
hundreds, or thousands of lines long.

In such a case, knowing (at least) in which direction to go hunting,
forward, or backward, starting with the reported value, to find the
reference, would be a big help.

Actually specifying (in the std) which value it should have is clearly
impossible, as there is clearly no standard in this area (everyone, almost,
is different) but we could at least have some implementation guidance to
suggest how it should be done, or a warning to users that the value of
${LINENO} isn't really of much use for anything (using it is not portable)
or perhaps some guidance as to how to use ${LINENO} so that it actually
does give a similar result in all (or most) shells ...  A simple
        X=${LINENO}   (or: echo "one line containing ${LINENO} and text" )
executed outside any kind of compound statement appears to do the same
thing (modulo line counting errors in the version of yash I tested)
everywhere, so that very simple usage appears to be safe.

kre

ps: in the copy of the PDF file I have, the index seems to omit all the
var names in section 2.5.3.  Is that intentional?   LINENO is not in the
index, nor is the (rather more important) IFS ... PATH is, but the index
omits its definition on page 2352 and refers only to (some?) other
occurrences.

Reply via email to