Is it possible to customize the Bash error messages attributes?
For example, is it possible to edit the .bash_profile
to get the following Bash error message
-bash: cd: foo: No such file or directory
in red?
Sadly, there is no PS
prompt variable to control how bash errors are presented.
stderred is a comprehensive solution for this type of problem, but it will not work without modification as it has a hard-coded exclude for bash
(read on for good reasons why). Also it's somewhat intrusive (DLL injection).
hilite requires you to use it as a wrapper, this won't work for bash builtin commands like cd
.
Your question asks specifically about bash error messages, these are send to stderr
, but stderr
is also shared with any child processes, i.e. any other commands. I'm not sure if you want to distinguish the two.
A hidden issue here is that bash
itself writes the prompt and your input (echo) to stderr
. To prove:
bash # start a sacrificial shell
exec 2> tmpfile # change stderr to a file
ls # you will see no prompt, and no command echo
...
exit # quit sacrificial shell
cat tmpfile # contains PS1 prompt, your commands (and any errors too)
Builtins call (or at least should call) the builtin_error()
internal function to print errors, this unconditionally calls fprintf()
to stderr
, so the options are few.
Without jumping through hoops, or patching bash
, a simple way of highlighting errors is:
function _t_debug()
{
if [ "${BASH_COMMAND:0:6}" != "_t_err" ]; then
_lastcmd="$BASH_COMMAND"
fi
}
function _t_err()
{
local rc=$1 nn _type _argv
#shift; pipe=($*)
#if [ ${#pipe[*]} -gt 1 ]; then
# for ((nn=1; nn<=${#pipe[*]};nn++));do
# rc=${pipe[$((nn-1))]}
# echo -n "[$nn]=$rc ";
# ((rc >=128)) && echo -n "($((rc-128))) "
# done
#fi
read -a _argv <<< ${_lastcmd}
_type=$(type -t "${_argv[0]}")
if [ -n "$_lastcmd" ]; then
tput setf 4
printf 'Error %s: "%s"' "${_type}" "${_lastcmd:-unknown command}"
tput sgr 0
printf "\n"
fi
((rc >=128)) && echo "[rc=$rc ($((rc-128)))]" ||
echo "[rc=$rc]"
}
trap '_t_err $? ${PIPESTATUS[*]}' ERR
trap '_t_debug' DEBUG
This uses the bash DEBUG
trap to cache each command line before execution, and the ERR
trap to output the return codes if non-zero. This won't though for certain bash builtins (specifically compound commands: while/case/for/if
and more, see man page).
I use a variation on this in my .profile
, though I use the pipe[]/PIPESTATUS[]
bit commented out above, it's not compatible with a DEBUG trap as presented above. If you comment out the trap DEBUG
then you can use it to show the return code of each command in a pipeline.
(Also, since it's referenced, the command_not_found
function hook is bash-4.0+.)
Below solution works for what you want:
# whatever_command 2> >(while read line; do echo -e "\e[01;31m$line\e[0m" >&2; done)
Basically, above command prints stderr in red.
Check below screen shot:
I do not know how to make this permanent. However, this should definitely give you some pointers.
Put this into your ~/.bashrc
or ~/.bash_profile
file for a permanent fix, or, if you want it to be temporary, just type this into bash
.
command_not_found_handle () {
tput bold;
tput setaf 1;
echo "$0: $1: command not found";
tput sgr0;
}
It will make bash
output the command not found error in bold, and red. Go ahead and take out tput bold;
to make it so that the error message is not bold, or change the tput setaf 1
to another number for a different color. Hope it helps!!
EDIT
Here is a new and improved version of the above:
command_not_found_handle () {
echo -e "\e[1;31m$0: $1: command not found\e[0;0m";
return 127; #return bash's error code for command not found
}
EDIT 2
If the name is neither a shell function nor a builtin, and contains no slashes, bash
searches each element of the PATH for a directory containing an executable file by that
name. Bash uses a hash table to remember the full pathnames of executable files (see hash
under SHELL BUILTIN COMMANDS below). A full search of the directories in PATH is performed
only if the command is not found in the hash table. If the search is unsuccessful, the
shell searches for a defined shell function named command_not_found_handle. If that
function exists, it is invoked with the original command and the original command's
arguments as its arguments, and the function's exit status becomes the exit status of the
shell. If that function is not defined, the shell prints an error message and returns an
exit status of 127.
Taken from bash's manpage
So if this does not work, maybe your shell is not true bash
, or it is a modified version of bash
.
command_not_found_handle () { echo "Error..." }
: I still get the basic -bash: foo: command not found
? I'm using a Mac OSX operating system.