I'm running shell scripts from Jenkins, which kicks off shell scripts with the shebang options #!/bin/sh -ex
.
According to Bash Shebang for dummies?, -x
, "causes the shell to print an execution trace", which is great for most purposes – except for echos:
echo "Message"
produces the output
+ echo "Message"
Message
which is a bit redundant, and looks a bit strange. Is there a way to leave -x
enabled, but only output
Message
instead of the two lines above, e.g. by prefixing the echo command with a special command character, or redirecting output?
Best Answer
The question is about
echo
, and yet the majority of the answers so far have focused on how to sneak aset +x
command in. There’s a much simpler, more direct solution:(I acknowledge that I might not have thought of the
{ …; } 2> /dev/null
if I hadn’t seen it in the earlier answers.)This is somewhat cumbersome, but, if you have a block of consecutive
echo
commands, you don’t need to do it on each one individually:Note that you don’t need semicolons when you have newlines.
You can reduce the typing burden by using kenorb’s idea of opening
/dev/null
permanently on a non-standard file descriptor (e.g., 3) and then saying2>&3
instead of2> /dev/null
all the time.The first four answers at the time of this writing require doing something special (and, in most cases, cumbersome) every time you do an
echo
. If you really want allecho
commands to suppress the execution trace (and why wouldn’t you?), you can do so globally, without munging a lot of code. First, I noticed that aliases aren’t traced:(Note that the following script snippets work if the shebang is
#!/bin/sh
, even if/bin/sh
is a link to bash. But, if the shebang is#!/bin/bash
, you need to add ashopt -s expand_aliases
command to get aliases to work in a script.)So, for my first trick:
Now, when we say
echo "Message"
, we’re calling the alias, which doesn’t get traced. The alias turns off the trace option, while suppressing the trace message from theset
command (using the technique presented first in user5071535’s answer), and then executes the actualecho
command. This lets us get an effect similar to that of user5071535’s answer without needing to edit the code at everyecho
command. However, this leaves trace mode turned off. We can’t put aset -x
into the alias (or at least not easily) because an alias only allows a string to be substituted for a word; no part of the alias string can be injected into the command after the arguments (e.g.,"Message"
). So, for example, if the script containsthe output would be
so you still need to turn the trace option back on after displaying message(s) — but only once after every block of consecutive
echo
commands:It would be nice if we could make the
set -x
automatic after anecho
— and we can, with a bit more trickery. But before I present that, consider this. The OP is starting with scripts that use a#!/bin/sh -ex
shebang. Implicitly the user could remove thex
from the shebang and have a script that works normally, without execution tracing. It would be nice if we could develop a solution that retains that property. The first few answers here fail that property because they turn tracing “back” on afterecho
statements, unconditionally, without regard to whether it was already on. This answer conspicuously fails to recognize that issue, as it replacesecho
output with trace output; therefore, all the messages vanish if tracing is turned off. I will now present a solution that turns tracing back on after anecho
statement conditionally — only if it was already on. Downgrading this to a solution that turns tracing “back” on unconditionally is trivial and is left as an exercise.$-
is the options list; a concatenation of the letters corresponding to all the options that are set. For example, if thee
andx
options are set, then$-
will be a jumble of letters that includese
andx
. My new alias (above) saves the value of$-
before turning tracing off. Then, with tracing turned off, it throws control over into a shell function. That function does the actualecho
and then checks to see whether thex
option was turned on when the alias was invoked. If the option was on, the function turns it back on; if it was off, the function leaves it off.You can insert the above seven lines (eight, if you include an
shopt
) at the beginning of the script and leave the rest alone.This would allow you
echo
, in which case it will be executed but not traced. (But even if command 5 is anecho
, command 6 still will be traced.)echo
, command 9 still will not be traced.x
.P.S. I have discovered that, on my system, I can leave out the
builtin
keyword in my middle answer (the one that’s just an alias forecho
). This is not surprising; bash(1) says that, during alias expansion, …Not too surprisingly, the last answer (the one with
echo_and_restore
) fails if thebuiltin
keyword is omitted1. But, oddly it works if I delete thebuiltin
and switch the order:__________
1 It seems to give rise to undefined behavior. I’ve seen
/dev/null: Bad address
error message, and