echo just 1 line before execution/set +-x

Suppose that you want to mostly not echo commands inside your script during execution (e.g. to not bog the user down with details that they do not care about) but that there is the occaisional script line that you would like to echo before you execute it.

Is there an elegant way to achieve this?

The ugly way to do this is to have code like

<do stuff>
...
set -x
<execute desired command>
set +x
...
<do more stuff>

Here, you have to place set +- pairs around each desired echo line which is annoying.

Furthermore, the final set +x line gets printed out--is there any way to suppress that? In dos bat files you can suppress that by putting a '@' char at the start of any line that you do not want echoed when echo is on. Does the bourne shell have an equivalent?

The function that I wrote below works for simple commands, but it fails (in both cygwin and linux) for really complex ones (e.g. a long Java program invocation with lots of arguments and a big classpath).

# Echoes the command first and then executes it.
# Any number of parameters may be supplied.
# Inside this function they will first be concatenated into a single string with a single space between each arg.
# This string must not be empty or all whitespace
echoThenExecute() {
	# concatenate all the individual args into a single string with a single space between the args:
	argsAsString=""
	while [ $# -ne 0 ]
	do
		argsAsString="$argsAsString ${1}"
		shift
	done

	if [ "$argsAsString" = "" ]; then
		echo "ERROR: empty command supplied"
		exit 1
	elif [ ! -n "`echo $argsAsString | sed 's/[[:space:]]*//g'`" ]; then	# note: the final g in the sed command makes it a global replacement, which is crucial; see http://www.grymoire.com/Unix/Sed.html
		echo "ERROR: all whitespace command supplied"
		exit 1
	fi

	echo "$argsAsString"
	bash -c "$argsAsString"
}

Maybe just invoke another shell with the trace on:

bash -xvc 'ls -l *'
 -or-
(set -x; ls -l *)

The problem with this being that it's a subshell and may act different. You also can't set values and expect them in the parent.

I frequently see things like this:

typeset CMD="cat | sed | grep | cut | awk -v foo.bar"
echo "$CMD"
eval $CMD

But that's really not much different from your "echoThenExecute()".

Thanks for the subshell idea. Its clever, but I agree that it is not a perfect solution.

Just out of curiosity, what is the purpose of the typeset in the code below?

Since you are giving no options to typeset, you could simply say

CMD="cat | sed | grep | cut | awk -v foo.bar"

correct?

With regard to the original question, I would simply change:
command
to be:
echo "command"
command

Low tech, I know. But sometimes that's the way to go. Depending on the quotes in the original command I might need to fiddle with the quoting.

And "typeset" in a function creates a local variable.

$ cat typesettest
#! /usr/bin/ksh


function xyzzy
{
        typeset CMD=two
        echo in xyzzy CMD = $CMD
        return 0
}


CMD=one
echo CMD is $CMD
xyzzy
echo CMD still is $CMD
exit 0
$
$
$ ./typesettest
CMD is one
in xyzzy CMD = two
CMD still is one
$