24

In shell scripts I would like to echo some of the major (long running) commands for status and debug reason. I know I can enable an echo for all commands with set -x or set -v. But I don't want to see all the commands (specially not the echo commands). Is there a way to turn on the echo for just one command?

I could do like this, but that's ugly and echoes the line set +x as well:

#!/bin/sh

dir=/tmp
echo List $dir

set -x
ls $dir
set +x

echo Done!

Is there a better way to do this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Den
  • 547
  • 1
  • 6
  • 15
  • Why not just add some echos at various key point in the script? – John3136 Oct 04 '12 at 00:35
  • @John3136: Because it is fiddly and unreliable...in particular, you end up duplicating the command, writing it out twice, which becomes a maintenance liability. – Jonathan Leffler Oct 04 '12 at 00:49
  • That was my first idea, but the commands I actually run are several lines long - it will be a bigger mess... – Den Oct 04 '12 at 01:02
  • Related: http://stackoverflow.com/q/12231792/11543 http://stackoverflow.com/q/2853803/11543 – mjs Jun 03 '14 at 15:26
  • Another reason not to do that is because it willl with quotes and variable expansion and then you still don't know the exact command you're running. – Jannes Feb 12 '15 at 12:29

4 Answers4

32

At the cost of a process per occasion, you can use:

(set -x; ls $dir)

This runs the command in a sub-shell, so the set -x only affects what's inside the parentheses. You don't need to code or see the set +x. I use this when I need to do selective tracing.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 6
    The only time this technique causes trouble is if the commands to be traced need to set variables for use in the parent shell. Basically, the sub-shell can't modify the original shell's variables, so the modifications are strictly local to the code inside the parentheses. – Jonathan Leffler Oct 05 '12 at 06:21
  • 8
    Also, `{ set +x; } 2>/dev/null` - via [Bash set +x without it being printed - Stack Overflow](http://stackoverflow.com/a/19226038/277826) – sdaau Feb 28 '14 at 08:23
  • @sdaau: interesting — obvious once seen, but not until you've seen it. – Jonathan Leffler Feb 28 '14 at 13:58
5

How about using this function?

runtraced() {
    echo "$@"
    "$@"
}

dosomething
runtraced dosomethingelse
Jo So
  • 25,005
  • 6
  • 42
  • 59
  • That is quite neat — until you need to trace a pipeline. `runtraced ls | wc -l` doesn't do what you expect (it traces the `ls` but not the `wc -l`). Or other similar constructs. – Jonathan Leffler Oct 05 '12 at 06:20
  • True. Perhaps one working version would include an `eval "$1"` instead (use `runtraced "ls | wc -l"`). – Jo So Oct 05 '12 at 13:27
2

Based on Jonathan Leffler answer, this works the same way, just a little more clear because there is noting needed after the command. But you need to specify which shell should be used. This is a example for sh:

sh -xc ls $dir
Den
  • 547
  • 1
  • 6
  • 15
  • 1
    your version could need extra quoting and escaping with pipelines. For example (set -x; echo 'toronto"s rules' | wc -c) versus sh -xc 'echo '"'"'toronto"s rules'"'"' | wc -c' – iruvar Oct 05 '12 at 01:59
  • True, it doesn't work in all situations so well. Thanks for the hint! – Den Oct 05 '12 at 06:16
2

An easy way to do this is with a heredoc and an uninterpreted string. It is POSIX portable and fast:

...
% cmd='ls ${dir}'
% sh -x <<_EOF_
> ${cmd}
> _EOF_
...

You can build out entire scripts in this way, parsing and/or modifying them programmatically as needed, saving them to and calling them from shell variables, and running them all from within another script or shell function:

...
% script="$(cat </some/entire/script.sh)"
% script="$(pipeline | processing | on | ${script})"    
% sh -x <<_EOF_ 2>&1 | grep ${specific_cmds_Im_looking_for}
> ${script}
> _EOF_
<desired output>

In my answer to POSIX compliant way to see if a function is defined in an sh script I describe the hows and whys of this in greater detail. And at Stack Exchange I discuss pretty thoroughly how the heredoc can be used to solve some annoying quoting problems in answer to Is there a way to get actual (uninterpreted) shell arguments in a function or script?.

-Mike

Community
  • 1
  • 1
mikeserv
  • 694
  • 7
  • 9