How can I print only certain commands from a bash script as they are run?

Use a sub-shell, i.e:

( set -x; cmd1; cmd2 )

For example:

( set -x; echo "hi there" )

prints

+ echo 'hi there'
hi there

When I write more complex bash scripts, I use a little function to run commands that will also print the commands run into a logfile:

runthis(){
    ## print the command to the logfile
    echo "$@" >> $LOG
    ## run the command and redirect it's error output
    ## to the logfile
    eval "$@" 2>> $LOG 
}

Then, in my script, I run commands like this:

runthis "cp /foo/bar /baz/"

If you don't want a command printed, just run it normally.

You can either set the $LOG to a filename or just remove it and print to stdout or stderr.


I've seen methods used similar to @terdon's. It's the beginnings of what higher level programming languages call loggers, and offer as full blown libraries, such as log4J (Java), log4Perl (Perl) etc.

You can get something similar using set -x in Bash as you've mentioned but you can use it to turn up the debugging just a subset of commands by wrapping blocks of code with them like so.

$ set -x; cmd1; cmd2; set +x

Examples

Here's a one liner pattern you can use.

$ set -x; echo  "hi" ;set +x
+ echo hi
hi
+ set +x

You can wrap them like this for multiple commands in a script.

set -x
cmd1
cmd2
set +x

cmd3

Log4Bash

Most people are oblivious but Bash also has a log4* as well, Log4Bash. If you have more modest needs this might be worth the time to set it up.

log4bash is an attempt to have better logging for Bash scripts (i.e. make logging in Bash suck less).

Examples

Here are some examples of using log4bash.

#!/usr/bin/env bash
source log4bash.sh

log "This is regular log message... log and log_info do the same thing";

log_warning "Luke ... you turned off your targeting computer";
log_info "I have you now!";
log_success "You're all clear kid, now let's blow this thing and go home.";
log_error "One thing's for sure, we're all gonna be a lot thinner.";

# If you have figlet installed -- you'll see some big letters on the screen!
log_captains "What was in the captain's toilet?";

# If you have the "say" command (e.g. on a Mac)
log_speak "Resistance is futile";

Log4sh

If you want what I would classify as more of the full power of a log4* framework then I'd give Log4sh a try.

excerpt

log4sh was originally developed to solve a logging problem I had in some of the production environments I have worked in where I either had too much logging, or not enough. Cron jobs in particular caused me the most headaches with their constant and annoying emails telling me that everything worked, or that nothing worked but not a detailed reason why. I now use log4sh in environments where logging from shell scripts is critical, but where I need more than just a simple "Hello, fix me!" type of logging message. If you like what you see, or have any suggestions on improvements, please feel free to drop me an email. If there is enough interest in the project, I will develop it further.

log4sh has been developed under the Bourne Again Shell (/bin/bash) on Linux, but great care has been taken to make sure it works under the default Bourne Shell of Solaris (/bin/sh) as this happens to be the primary production platform used by myself.

Log4sh supports several shells, not just Bash.

  • Bourne Shell (sh)
  • BASH - GNU Bourne Again SHell (bash)
  • DASH (dash)
  • Korn Shell (ksh)
  • pdksh - the Public Domain Korn Shell (pdksh)

It's also been tested on several OSes, not just Linux.

  • Cygwin (under Windows)
  • FreeBSD (user supported)
  • Linux (Gentoo, RedHat, Ubuntu)
  • Mac OS X
  • Solaris 8, 9, 10

Using a log4* framework will take some time to learn but it is worth it if you have more demanding needs from your logging. Log4sh makes use of a configuration file where you can define appenders and control the formatting for the output that will appear.

Example

#! /bin/sh
#
# log4sh example: Hello, world
#

# load log4sh (disabling properties file warning) and clear the default
# configuration
LOG4SH_CONFIGURATION='none' . ./log4sh
log4sh_resetConfiguration

# set the global logging level to INFO
logger_setLevel INFO

# add and configure a FileAppender that outputs to STDERR, and activate the
# configuration
logger_addAppender stderr
appender_setType stderr FileAppender
appender_file_setFile stderr STDERR
appender_activateOptions stderr

# say Hello to the world
logger_info 'Hello, world'

Now when I run it:

$ ./log4sh.bash 
INFO - Hello, world

NOTE: The above configures the appender as part of the code. If you like this can be extracted out into its own file, log4sh.properties etc.

Consult the excellent documentation for Log4sh if you need further details.