2

UPDATE:

Some Background:

zsh returns the line number inside a function when $LINENO is called inside a function. I need a way to get the line number in the file and to differentiate when zsh is giving me a file line number vs. a function line number.

I couldn't find a zsh environment variable to change this behavior to match other Bourne shells (e.g. bash always gives the file line number), so I was trying to see if I could create a function with logic that could always output the file line number regardless of context. This is why I was trying to determine the length of the function.

If anyone knows of a good way to get the file line number with $LINENO in zsh in all contexts, I'd appreciate it!


QUESTION:

I've searched this and this, but can't seem to find an answer. Is there a portable way to write the number of lines a function definition has? (Please see "Some Background" above.)

My initial thought was to capture the function contents and pipe it to wc -l.

Consider the following test file:

Test File:

#! /bin/sh
#
# test_file.sh

func1() { echo 'A one-liner'; }  # With a nasty comment at the end
func2 (){
  echo "A sneaky } included"
  # Or an actual code block
  {
    echo 'hi'
    echo 'there'
  }
}

func3() { echo "I'm on a line."; }; echo 'And so am I'

func4(){ echo "But I'm a \"stand-alone\" one-liner."; }

func5() {
  echo "I'm a nice function."
  echo "And you can too!"


}

echo "Can we do this?"

My initial attempt was to match corresponding pairs of {}'s with sed:

Solution Attempt:

#! /bin/sh
#
# function_length
#
# $1: string: absolute path to file
# $2: string: name of function (without ()'s)

fp=$(realpath "$1")
func_name="$2"

func_contents=$(cat "${fp}" |
  sed -E -n '
/'"${func_name}"' ?[(][)]/{
  :top
  /[}]/!{
    H
    d
  }
  /[}]/{
    x
    s/[{]//
    t next
    G
    b end
  }
  :next
  x
  b top
  :end
  p
  q
}')

echo "${func_contents}"
echo

func_len=$(echo "${func_contents}" | wc -l)

echo "Function Length: ${func_len}"

However, running this in zsh gives

$ ./function_length ./test_file.sh func1      

func1() { echo 'A one-liner'; }  # With a nasty comment at the end

Function Length: 2
$ ./function_length ./test_file.sh func2


Function Length: 1
$ ./function_length ./test_file.sh func3

func3() { echo "I'm on a line."; }; echo 'And so am I'

Function Length: 2
$ ./function_length ./test_file.sh func4

func4(){ echo "But I'm a \"stand-alone\" one-liner."; }

Function Length: 2
$ ./function_length ./test_file.sh func5


Function Length: 1

Does anyone know of a solution? Thank you!

7
  • 3
    What do you need the length for? What difference does it make? Please make sure there is no XY problem here. Edit the question if there is. Jun 13, 2021 at 21:36
  • @KamilMaciorowski This isn't that. I'm asking for any solution. I'm just trying to show that I've done some work before blindly asking for help. Jun 13, 2021 at 21:39
  • @KamilMaciorowski If it is proven there is no solution to the problem (getting the length of a function in shell), please provide evidence/reference to that as that would help as well. My understanding is some seem to only SUGGEST this is not possible. Jun 13, 2021 at 21:42
  • 2
    The help center states "You should only ask practical, answerable questions based on actual problems that you face". Because of this I hoped there was an actual problem that matters in practice. Now it seems there's none and the question is academic. Maybe it's not academic, maybe I'm missing something; therefore I asked what difference the length makes. Jun 13, 2021 at 21:59
  • 1
    @EdMorton Thank you. Yes, I certainly see that now. This is a nontrivial problem. Thanks again! Jun 15, 2021 at 15:17

1 Answer 1

8

There is no portable way to retrieve the content of a shell function. Some shells have one, some don't. The popular shell dash has no way do do anything about a function's body except evaluate it.

dash/src $ grep -F ndefun.body *.c
eval.c: evaltree(func->n.ndefun.body, flags & EV_TESTED);
parser.c:                               n->ndefun.body = command();

Further examination of the source code reveals there is no separate data structure containing the “length” of the function, whatever that means.

In shells that do have a way to print the definition of a function, it may be formatted differently from the source code. So the “length” is not a meaningful number.

$ bash -c 'f () { echo hello; echo world; }; typeset -f f'
f () 
{ 
    echo hello;
    echo world
}
$ ksh -c 'f () { echo hello; echo world; }; typeset -f f'; echo
f() { echo hello; echo world; };
$ mksh -c 'f () { echo hello; echo world; }; typeset -f f'
f() {
        \echo hello 
        \echo world 
} 
$ zsh -c 'f () { echo hello; echo world; }; typeset -f f'
f () {
        echo hello
        echo world
}
1
  • Thank you for your answer. This is very telling. Much appreciated! Jun 15, 2021 at 15:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .