Suppress execution trace for echo command?

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?

Answer

When you are up to your neck in alligators,
it’s easy to forget that the goal was to drain the swamp.  
                — popular saying

The question is about echo,
and yet the majority of the answers so far
have focused on how to sneak a set +x command in. 
There’s a much simpler, more direct solution:

{ echo "Message"; } 2> /dev/null

(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:

{
  echo "The quick brown fox"
  echo "jumps over the lazy dog."
} 2> /dev/null

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 saying 2>&3 instead of 2> /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 all echo 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:

$ myfunc()
> {
>     date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016  0:00:00 AM           # Happy Halloween!
$ myfunc
+ myfunc                                # Note that function call is traced.
+ date
Mon, Oct 31, 2016  0:00:01 AM
$ myalias
+ date                                  # Note that it doesn’t say  + myalias
Mon, Oct 31, 2016  0:00:02 AM

(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 a shopt -s expand_aliases command
to get aliases to work in a script.)

So, for my first trick:

alias echo='{ set +x; } 2> /dev/null; builtin echo'

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 the set command
(using the technique presented first in user5071535’s answer),
and then executes the actual echo command. 
This lets us get an effect similar to that of user5071535’s answer
without needing to edit the code at every echo command. 
However, this leaves trace mode turned off. 
We can’t put a set -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 contains

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date

the output would be

+ date
Mon, Oct 31, 2016  0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016  0:00:04 AM           # Note that it doesn’t say  + date

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:

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date

It would be nice if we could make the set -x automatic
after an echo — 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 the x 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 after echo statements,
unconditionally, without regard to whether it was already on. 
This answer conspicuously fails to recognize that issue,
as it replaces echo 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 an echo 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.

alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
        builtin echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}

$- is the options list; a concatenation of the letters
corresponding to all the options that are set. 
For example, if the e and x options are set,
then $- will be a jumble of letters that includes e and x
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 actual echo
and then checks to see whether the x 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

  1. to use any of the following shebang lines:
    #!/bin/sh -ex
    #!/bin/sh -e
    #!/bin/sh –x

    or just plain

    #!/bin/sh

    and it should work as expected.

  2. to have code like
    (shebang)
    command1
    command2
    command3
    set -x
    command4
    command5
    command6
    set +x
    command7
    command8
    command9

    and

    • Commands 4, 5, and 6 will be traced — unless one of them is an echo,
      in which case it will be executed but not traced. 
      (But even if command 5 is an echo, command 6 still will be traced.)
    • Commands 7, 8, and 9 will not be traced. 
      Even if command 8 is an echo, command 9 still will not be traced.
    • Commands 1, 2, and 3 will be traced (like 4, 5, and 6)
      or not (like 7, 8, and 9) depending on whether the shebang includes 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 for echo). 
This is not surprising; bash(1) says that, during alias expansion, …

… a word that is identical to an alias being expanded
is not expanded a second time. 
This means that one may alias ls to ls -F, for instance,
and bash does not try to recursively expand the replacement text.

Not too surprisingly, the last answer (the one with echo_and_restore)
fails if the builtin keyword is omitted1
But, oddly it works if I delete the builtin and switch the order:

echo_and_restore() {
        echo "$*"
        case "$save_flags" in
         (*x*)  set -x
        esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'

__________
1 It seems to give rise to undefined behavior. 
I’ve seen

  • an infinite loop (probably because of unbounded recursion),
  • a /dev/null: Bad address error message, and
  • a core dump.

Attribution
Source : Link , Question Author : Christian , Answer Author : G-Man Says ‘Reinstate Monica’

Leave a Comment