IljaBek
IljaBek

Reputation: 621

bash: override echo to print a custom prefix in a script

I have some lengthy scripts calling each other and I want their output to be more descriptive. The idea is to customize the echo command for each script with something like below.

My question is, how to make it non recursive, using echo?

this is script1.sh

#!/bin/bash
#Original version:
#function echo(){ echo $(basename $0 .sh): $1; }
#Version after fixes
function echo(){ builtin echo -n "$(basename $0 .sh): ">&2; builtin echo $@ ; } 
echo Info
./script2.sh

this is script2.sh

#!/bin/bash
#Original version:
#function echo(){ echo $(basename $0 .sh): $1; }
#Version after fixes
function echo(){ builtin echo -n "$(basename $0 .sh): ">&2; builtin echo $@ ; } 
echo Info
exit 0

so the output should be:

>./script1.sh
script1: Info
script2: Info

--- EDIT

Bonus:

>./script1.sh 2> /dev/null 
Info
Info

Upvotes: 4

Views: 6714

Answers (3)

Mikkel
Mikkel

Reputation: 1212

builtin only works for shell internal commands like echo. I know the original question is specifically related to echo, but as this was a popular Google hit for "bash override function", I thought I'd add my solution for other commands. I'm writing a wrapper to change terminal name when I run ssh, so I defined a function called ssh and need to call the actual binary from within. It may not be the best way, but it works to do:

function ssh {
    $(which ssh) $@
}

Upvotes: 2

Tim Friske
Tim Friske

Reputation: 2052

I came up with I/O redirection as a solution. The Bash Redirections Using Exec article inspired me. It has the following implications:

  • Redirect the script's standard or error output no matter which command prints to it, e.g. echo, printf, etc.
  • No repetitive work for subshells or sourced scripts.
  • Redirection extends to any executable that is being called by this script, a subscript or sourced script thereof.
  • Filters can be composed by creating several pipes where one pipe redirects its output into the input of the next, and so on.

In my bash shell scripts I use:

alias start_log_ctx="start_filter_by_pipe_regex 2 s/.*/${0##*/}\(pid=$$\): \0/"
function start_filter_by_pipe_regex {
  declare -r a="$(mktemp -u)" #1
  mknod "$a" p                #2
  sed <"$a" ">&$1" "$2" &     #3
  exec "$1">"$a"              #4
}

alias stop_log_ctx_by_job="stop_filter_by_pipe_job 2"
def stop_filter_by_pipe_job {
  exec "$1">&- #1
  wait "$2" #2
}

In essence the start_log_ctx alias uses sed to line-wise (.*) filter the script's I/O that goes to the error output (2) and prepends the script's base name (${0##*/}) and process id (pid=$$) to each line. The start_filter_by_pipe_regex function works as follows:

  1. Store the absolute path of but do not create (-U) a temporary file.
  2. Create a pipe under the absolute path of [1.].
  3. Start sed as a filter in the background (&). Let it read (<) from the pipe of [2.] and redirect its standard output (>) to the file descriptor given as the 1. parameter. Pass the filter given as 2. parameter to sed.
  4. Redirect (>) the file descriptor given as the 1. parameter to the pipe of [2.].

The stop_log_ctx alias closes the error output's file descriptor (2) and waits for the previously started sed to stop. It implicitly does so upon closing the file descriptor. The stop_filter_by_pipe_job function does the following:

  1. Close the file descriptor given as the 1. parameter thereby stopping sed.
  2. Wait for the background sed identified by the bash job given as the 2. parameter to stop.

At the beginning of my main function I usually do a declare -r a="$(start_log_ctx)" and finish with a stop_log_ctx_by_job "$a".

Upvotes: 0

Lesmana
Lesmana

Reputation: 27053

use the builtin keyword:

function echo(){ builtin echo $(basename $0 .sh): $1; }

here the help page:

$ help builtin
builtin: builtin [shell-builtin [arg ...]]
    Execute shell builtins.

    Execute SHELL-BUILTIN with arguments ARGs without performing command
    lookup.  This is useful when you wish to reimplement a shell builtin
    as a shell function, but need to execute the builtin within the function.

    Exit Status:
    Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is
    not a shell builtin..

Upvotes: 15

Related Questions