dspjm
dspjm

Reputation: 5830

How to show line number when executing bash script

I have a test script which has a lot of commands and will generate lots of output, I use set -x or set -v and set -e, so the script would stop when error occurs. However, it's still rather difficult for me to locate which line did the execution stop in order to locate the problem. Is there a method which can output the line number of the script before each line is executed? Or output the line number before the command exhibition generated by set -x? Or any method which can deal with my script line location problem would be a great help. Thanks.

Upvotes: 134

Views: 117787

Answers (7)

Kev
Kev

Reputation: 16321

You can use this shebang line at the top of individual scripts to enable extensive, colour-coded tracing of each command:

#!/usr/bin/env -S PS4='\\033[0;33m+(${BASH_SOURCE:-$0}:${LINENO}):\\033[0m ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' bash -x

This combines the comments from @Ulysse BN and @nikhilweee on this answer — and addresses @majorgear's comment on the same — with the arguably somewhat improved method of using env instead of bash directly in the shebang line.

The -S parameter on env allows the -x to be passed to bash.

Upvotes: 1

devnull
devnull

Reputation: 123448

You mention that you're already using -x. The variable PS4 denotes the value is the prompt printed before the command line is echoed when the -x option is set and defaults to : followed by space.

You can change PS4 to emit the LINENO (The line number in the script or shell function currently executing).

For example, if your script reads:

$ cat script
foo=10
echo ${foo}
echo $((2 + 2))

Executing it thus would print line numbers:

$ PS4='Line ${LINENO}: ' bash -x script
Line 1: foo=10
Line 2: echo 10
10
Line 3: echo 4
4

http://wiki.bash-hackers.org/scripting/debuggingtips (alternate: web archive link) gives the ultimate PS4 that would output everything you will possibly need for tracing:

export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

Upvotes: 252

Peter Kionga-Kamau
Peter Kionga-Kamau

Reputation: 7068

If you're using $LINENO within a function, it will cache the first occurrence. Instead use ${BASH_LINENO[0]}

Upvotes: 1

Luc
Luc

Reputation: 121

PS4 with value $LINENO is what you need,

E.g. Following script (myScript.sh):

       #!/bin/bash -xv
       PS4='${LINENO}: '
       echo "Hello"
       echo "World"

Output would be:

  ./myScript.sh
  +echo Hello
  3 : Hello
  +echo World
  4 : World

Upvotes: 10

kklepper
kklepper

Reputation: 813

Workaround for shells without LINENO

In a fairly sophisticated script I wouldn't like to see all line numbers; rather I would like to be in control of the output.

Define a function

echo_line_no () {
    grep -n "$1" $0 |  sed "s/echo_line_no//" 
    # grep the line(s) containing input $1 with line numbers
    # replace the function name with nothing 
} # echo_line_no

Use it with quotes like

echo_line_no "this is a simple comment with a line number"

Output is

16   "this is a simple comment with a line number"

if the number of this line in the source file is 16.

This basically answers the question How to show line number when executing bash script for users of ash or other shells without LINENO.

Anything more to add?

Sure. Why do you need this? How do you work with this? What can you do with this? Is this simple approach really sufficient or useful? Why do you want to tinker with this at all?

Want to know more? Read reflections on debugging

Upvotes: 5

Deqing
Deqing

Reputation: 14632

In Bash, $LINENO contains the line number where the script currently executing.

If you need to know the line number where the function was called, try $BASH_LINENO. Note that this variable is an array.

For example:

#!/bin/bash       

function log() {
    echo "LINENO: ${LINENO}"
    echo "BASH_LINENO: ${BASH_LINENO[*]}"
}

function foo() {
    log "$@"
}

foo "$@"

See here for details of Bash variables.

Upvotes: 51

hek2mgl
hek2mgl

Reputation: 157947

Simple (but powerful) solution: Place echo around the code you think that causes the problem and move the echo line by line until the messages does not appear anymore on screen - because the script has stop because of an error before.

Even more powerful solution: Install bashdb the bash debugger and debug the script line by line

Upvotes: 1

Related Questions