17

Is there a way to check which line number of a bash script is being executed "right now"?

Using bash -x script.sh looks promising; however, I need to get the current line number.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255

5 Answers5

21

Combine xtrace with PS4 inside the script:

$ cat test.sh 
#!/usr/bin/env bash
set -x
PS4='+${LINENO}: '

sleep 1m
sleep 1d
$ timeout 5 ./test.sh
+3: PS4='+${LINENO}: '
+5: sleep 1m

or in the parent shell:

$ cat test.sh 
sleep 1m
sleep 1d
$ export PS4='+${LINENO}: '
$ timeout 5 bash -x ./test.sh
+1: sleep 1m
l0b0
  • 51,350
11

Yes, there is a way.
There is an array of line numbers where a function has been called.

Define this function:

f(){ echo "${BASH_LINENO[-2]}"; }

And call f at any line that you want the line number, for example:

#!/bin/bash


f(){ echo "${BASH_LINENO[-2]}"; }

f

echo next1
f

echo next2
f

echo next 3
f

Will print:

6
next 1
9
next 2
12
next 3
15

It could be expanded to show the trail of functions called:

#!/bin/bash

f(){
    for ((i=${#BASH_LINENO[@]}-1;i>=0;i--)); do
    printf '<%s:%s> ' "${FUNCNAME[i]}" "${BASH_LINENO[i]}";
    done
    echo "$LINENO"
 }

SomeOtherFunction(){ echo -n "test the line numbering:  "; f; }

f

echo next 1
echo -n "    This line numbering:  "; f
SomeOtherFunction

echo next 2
echo -n "    This line numbering:  "; f
SomeOtherFunction

echo next 3
echo -n "    This line numbering:  "; f

Which will print:

$ ./script
<main:0> <f:12> 7
next 1
    This line numbering:  <main:0> <f:15> 7
test the line numbering:  <main:0> <SomeOtherFunction:16> <f:10> 7
next 2
    This line numbering:  <main:0> <f:19> 7
test the line numbering:  <main:0> <SomeOtherFunction:20> <f:10> 7
next 3
    This line numbering:  <main:0> <f:23> 7

Note that above the echo "$LINENO" output is always the same (7 in this case).

7

Here’s a solution that borrows parts of l0b0’s and DopeGhoti’s answers (and, to a lesser extent, sorontar’s).  Like those answers, mine uses $LINENO to discover the line number; unlike them, I use trap to trigger the reporting.  bash’s trap command is described in bash(1):

trap [-lp] [[arg] sigspec ...]

    The command arg is to be read and executed when the shell receives signal(s) sigspec.  … ⁠     ︙
    …  If a sigspec is DEBUG, the command arg is executed before every simple command, for command, case command, select command, every arithmetic for command, and before the first command executes in a shell function …

So this script:

$ cat -n myscript
     1  #!/bin/bash
     2  trap 'printf "%3d: " "$LINENO"' DEBUG
     3  date
     4  sleep 30
     5  date
     6  sleep \
     7        11
     8  date
     9
    10  ls -l
    11  for f in *
    12  do
    13          echo "$f"  &&
    14                         ls -ld "$f"
    15  done
    16
    17  for ((i=0; i<3; i++))
    18  do
    19          echo "i = $i"; date
    20  done
    21
    22  echo $((5+25+12))
$

runs the printf "%3d: " "$LINENO" command before every command in the script, and produces this output:

$ ./myscript
  3: Wed, Apr 05, 2017 10:16:17 AM
  4:   5: Wed, Apr 05, 2017 10:16:47 AM
  7:   8: Wed, Apr 05, 2017 10:16:58 AM
 10: total 4
-rwxr-xr-x 1 myusername mygroup 221 Apr  5 10:01 myscript
-rwxr-xr-x 1 myusername mygroup 252 Apr  5 10:01 myscript2
-rw-r--r-- 1 myusername mygroup 132 Apr  5 09:59 myscript2.log
-rw-r--r-- 1 myusername mygroup  45 Apr  5 08:34 other_file
 11:  13: myscript
 14: -rwxr-xr-x 1 myusername mygroup 221 Apr  5 10:01 myscript
 11:  13: myscript2
 14: -rwxr-xr-x 1 myusername mygroup 252 Apr  5 10:01 myscript2
 11:  13: myscript2.log
 14: -rw-r--r-- 1 myusername mygroup 132 Apr  5 09:59 myscript2.log
 11:  13: other_file
 14: -rw-r--r-- 1 myusername mygroup  45 Apr  5 08:34 other_file
 17:  17:  19: i = 0
 19: Wed, Apr 05, 2017 10:16:59 AM
 17:  17:  19: i = 1
 19: Wed, Apr 05, 2017 10:16:59 AM
 17:  17:  19: i = 2
 19: Wed, Apr 05, 2017 10:16:59 AM
 17:  17:  22: 42
$

Notes:

  • Like l0b0’s answer, this is minimally invasive — just add line 2.
  • Unlike l0b0’s answer, this doesn’t display the commands themselves — but you didn’t ask for it to do that.
  • The second sleep, which spans script lines 6 and 7, is reported as line 7.
  • Line 11 (for f in *) is reported once before each iteration of that for loop.
  • echo "$f" and ls -ld "$f" are correctly reported on their respective lines (13 and 14).
  • Line 17 (for ((i=0; i<3; i++))) is reported twice before each iteration of that for loop, and twice more after the last iteration.
  • Unlike set -x, LINENO and PS4 (which are specified by the POSIX standard), the DEBUG trap is a bash extension and will not work in all shells.
  • The DEBUG trap can run any command(s), and is not restricted to writing to the script’s standard output or standard error.

The question says, «check which line number of a bash script is being executed “right now”» without specifying a user interface.  Another approach is to continually write the current line number to a log file:

$ diff myscript myscript2
2c2
< trap 'printf "%3d: " "$LINENO"' DEBUG
---
> exec 6> myscript2.log  &&  trap 'printf "%3d\n" "$LINENO" >&6' DEBUG
$ ./myscript2
Wed, Apr 05, 2017 10:23:50 AM
Wed, Apr 05, 2017 10:24:20 AM
Wed, Apr 05, 2017 10:24:31 AM
total 4
-rwxr-xr-x 1 myusername mygroup 221 Apr  5 10:01 myscript
-rwxr-xr-x 1 myusername mygroup 252 Apr  5 10:01 myscript2
-rw-r--r-- 1 myusername mygroup  24 Apr  5 10:23 myscript2.log
-rw-r--r-- 1 myusername mygroup  45 Apr  5 08:34 other_file
myscript
-rwxr-xr-x 1 myusername mygroup 221 Apr  5 10:01 myscript
myscript2
-rwxr-xr-x 1 myusername mygroup 252 Apr  5 10:01 myscript2
myscript2.log
-rw-r--r-- 1 myusername mygroup  60 Apr  5 10:23 myscript2.log
other_file
-rw-r--r-- 1 myusername mygroup  45 Apr  5 08:34 other_file
i = 0
Wed, Apr 05, 2017 10:24:31 AM
i = 1
Wed, Apr 05, 2017 10:24:31 AM
i = 2
Wed, Apr 05, 2017 10:24:31 AM
42
$

We can monitor the execution of this script by monitoring the contents of the myscript2.log file from another terminal.  For example, during the second sleep,

$ tail myscript2.log
  3
  4
  5
  7
6

You can echo $LINENO in a script and it should output whatever line that command happens to be on.

#!/bin/bash
echo $LINENO

$ ./foo.sh
2
DopeGhoti
  • 76,081
-1
#!/bin/bash -x

Add that "-x" at the start of your script. Then every time you execute the script it will echo the line your script is executing. like an execution tree of your script.

muru
  • 72,889