简体   繁体   中英

Bash: highlight command before execution (set -x)

I have a bash script which executes about 20 commands and for debugging purposes I find myself scrolling through the output a lot. Unfortunately bash doesn't tell me which part of the output is part of what command. When I use "set -x" in the script it at least prints some information on what it just executed, but I don't really like the output it generates.

For instance, if I have this script:

#!/bin/bash

set -x
echo "foo"
if [ "$ASD" == "QWE" ] ; then
    echo "bar"
fi

I would like the output to be something like this:

echo "foo"
foo
echo "bar"
bar

Or maybe:

echo "foo"
foo
if [ "value_of_ASD" == "QWE" ] ; then
echo "bar"
bar
fi

Instead of printing the commands in bold, highlighting with a color would also be okay. But I don't just want to have "+" characters in front of the commands and I also don't like the if statements showing up like '[' value_of_ASD == QWE ']' .

How can I accomplish that with bash?

At the moment the output looks like this btw:

+ echo foo
foo
+ '[' value_of_ASD == QWE ']'
+ echo bar
bar

Edit:

One idea I had was to write a script that I would source in the very beginning of the main script and then let the sourced script parse the main one. Something like this:

source_me.sh

#!/bin/bash
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/$(basename $0)"
FORMAT_SET_BOLD='\033[0;1m'
FORMAT_RESET='\033[0m'

cat $SCRIPT_PATH | tail -n "+$START_LINE" | while read line; do
    printf "${FORMAT_SET_BOLD}${line}${FORMAT_RESET}\n"
    eval "${line}"
done

exit 0;

main.sh

#!/bin/bash
START_LINE=$((LINENO+1)) source ./source_me.sh

echo "Foo"
echo "Bar"
echo "+Hello"

The output in that case is:

echo "Foo"
Foo
echo "Bar"
Bar
echo "+Hello"
+Hello

But this method will fail if I use more complex code that goes over multiple lines (if statements, loops etc):

#!/bin/bash
START_LINE=$((LINENO+1)) source ./source_me.sh

echo "Foo"

if [ "$FOOBAR" == "" ] ; then
    echo "Bar"
fi

echo "+Hello"

In this case I get:

echo "Foo"
Foo

if [ "$FOOBAR" == "" ] ; then
./source_me.sh: eval: line 9: syntax error: unexpected end of file
echo "Bar"
Bar
fi
./source_me.sh: eval: line 8: syntax error near unexpected token ´fi'
./source_me.sh: eval: line 8: ´fi'

echo "+Hello"
+Hello

您可以通过设置PS4+更改为您想要的字符串,例如:

PS4="# "; set -x

Note that set -x has no innate knowledge of your terminal, which would be required to send the correct characters to create bold or coloured text.

If you can capture all output through a single file handle, you can probably do this with a pipe:

$ /path/to/your/script 2>&1 | sed "/^+ /s/.*/$(tput bold)&$(tput sgr0)/"

You might want to man tput for more information about the tool we're using to get bold type.

The viability of this depends on many factors I don't know about your environment. YMMV, may contain nuts.


Note:

You might think it was better to capture stderr separately with a line like this:

$ /path/to/your/script 2> >(sed "/^+ /s/.*/$(tput bold)&$(tput sgr0)/")

Alas, this doesn't behave consistently, because stderr passing through the pipe may not be submitted to the terminal before subsequent stdout from the script itself. Note also that this solution, because it uses process substitution , is a bashism and is not portable to POSIX.

I would like to extend rubo77's answer with a few examples that I think deserve a separate answer:

Plain text prefix

So the basic example is to set PS4 to some plain text, eg:

PS4="# "; set -x

Which will result in:



Color & extra line text prefix

But because you can use special characters and ANSI escape codes you can for example add a new line before each new command and print the prefix in a color, eg:

PS4="\n\033[1;33m>>>\033[0m "; set -x

Result:



Dynamic color prefix

Finally you can make the command prefix call other programs with each use , which you can use to add a timestamp, eg:

# yes, there have to be single quotes below, not double!
PS4='\033[1;34m$(date +%H:%M:%S)\033[0m '; set -x

Result:

What you are looking for is set -v .

-v prints out the line the interpreter just read.

-x prints out the post-parsing results of the line that the interpreter just read.

file x :

set -vx
foo=1
echo $foo
set +vx

Executing:

$: . x
foo=1
++ foo=1
echo $foo
++ echo 1
1
set +vx
++ set +vx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM