My goal with the below piece of POSIX shell code was to address the more platforms the better with shell tput
colors. With this code, I now start all of my scripts, and so it's time to review it for some things I overlooked, did not think of too well, and such. Note: I start my scripts with set -u
, which is why I set all empty in unspecified cases. Thanks.
#!/bin/sh
set -u
if tput setaf > /dev/null 2>&1; then
# Linux-like
tput_number_of_colors=$(tput colors 2> /dev/null)
tput_bold=$(tput bold 2> /dev/null)
tput_reset=$(tput sgr0 2> /dev/null)
tput_cmd_set_fg_color='tput setaf'
elif tput AF > /dev/null 2>&1; then
# BSD-like
tput_number_of_colors=$(tput Co 2> /dev/null)
tput_bold=$(tput md 2> /dev/null)
tput_reset=$(tput me 2> /dev/null)
tput_cmd_set_fg_color='tput AF'
else
# Console-like
tput_number_of_colors=2
tput_cmd_set_fg_color=
tput_bold=
tput_reset=
fi
tput_test ()
{
[ -n "$tput_number_of_colors" ] && [ -n "$tput_bold" ] && [ -n "$tput_reset" ] &&
{ [ "$tput_number_of_colors" -ge 8 ] && printf '%s' "$tput_bold" && $tput_cmd_set_fg_color 1; } > /dev/null 2>&1
}
if tput_test; then
color_red=$tput_bold$($tput_cmd_set_fg_color 1)
color_green=$tput_bold$($tput_cmd_set_fg_color 2)
color_yellow=$tput_bold$($tput_cmd_set_fg_color 3)
color_blue=$tput_bold$($tput_cmd_set_fg_color 4)
color_magenta=$tput_bold$($tput_cmd_set_fg_color 5)
color_cyan=$tput_bold$($tput_cmd_set_fg_color 6)
color_white=$tput_bold$($tput_cmd_set_fg_color 7)
else
color_red=; color_green=; color_yellow=; color_blue=; color_magenta=; color_cyan=; color_white=
fi
2 Answers 2
Self-review
Since no one replied thus far, I decided to re-think and re-write the code myself this morning.
terminfo
(*nix) vs termcap
(*bsd)
I never really thought about how important to make note of this actually is. For searching info online, one needs to know these keywords. So, I added them as comments.
Structure into functions
I believe this code should be structured into suitable functions where available.
Find a way for an exit code from those functions
It's needed to simply chain (&&
) the commands like tput_bold=$(tput bold 2> /dev/null) && tput_reset=$(tput sgr0 2> /dev/null)
in order for the imagined functions to return a reliable exit code.
The most basic test missing
command -v tput
was missing in my code, which is remedied now.
No unset variables
Since I use set -u
in my scripts, it's necessary to set variables empty in case of failure. This feature has been enhanced.
Modified code
#!/bin/sh
set -u
tput_setup_nix ()
{
# terminfo
tput_cmd_set_fg_color='tput setaf'
tput_number_of_colors=$(tput colors 2> /dev/null) &&
tput_bold=$(tput bold 2> /dev/null) &&
tput_reset=$(tput sgr0 2> /dev/null)
}
tput_setup_bsd ()
{
# termcap
tput_cmd_set_fg_color='tput AF'
tput_number_of_colors=$(tput Co 2> /dev/null) &&
tput_bold=$(tput md 2> /dev/null) &&
tput_reset=$(tput me 2> /dev/null)
}
tput_setup_none ()
{
# no unset variables
tput_cmd_set_fg_color=
tput_number_of_colors=
tput_bold=
tput_reset=
}
if command -v tput > /dev/null 2>&1; then
if tput setaf > /dev/null 2>&1; then
if ! tput_setup_nix; then tput_setup_none; fi
elif tput AF > /dev/null 2>&1; then
if ! tput_setup_bsd; then tput_setup_none; fi
else
tput_setup_none
fi
else
tput_setup_none
fi
tput_capability_test ()
{
[ -n "$tput_cmd_set_fg_color" ] && [ -n "$tput_number_of_colors" ] && [ -n "$tput_bold" ] && [ -n "$tput_reset" ] &&
[ "$tput_number_of_colors" -ge 8 ] && { $tput_cmd_set_fg_color 1 && printf '%s' "$tput_bold$tput_reset"; } > /dev/null 2>&1
}
if tput_capability_test; then
color_red=$tput_bold$($tput_cmd_set_fg_color 1)
color_green=$tput_bold$($tput_cmd_set_fg_color 2)
color_yellow=$tput_bold$($tput_cmd_set_fg_color 3)
color_blue=$tput_bold$($tput_cmd_set_fg_color 4)
color_magenta=$tput_bold$($tput_cmd_set_fg_color 5)
color_cyan=$tput_bold$($tput_cmd_set_fg_color 6)
color_white=$tput_bold$($tput_cmd_set_fg_color 7)
else
color_red=
color_green=
color_yellow=
color_blue=
color_magenta=
color_cyan=
color_white=
fi
-
\$\begingroup\$ I don't see how the
command -v tput
adds anything, except complexity. If there's no availabletput
command, then we get error return from trying to execute it. Also, you can simplifyif ! tput_setup_nix; then tput_setup_none; fi
to simpletput_setup_nix || tput_setup_none
. \$\endgroup\$Toby Speight– Toby Speight2022年10月11日 15:45:39 +00:00Commented Oct 11, 2022 at 15:45 -
\$\begingroup\$ @TobySpeight Correct, command -v did not add anything. \$\endgroup\$Vlastimil Burián– Vlastimil Burián2022年10月12日 22:34:35 +00:00Commented Oct 12, 2022 at 22:34
2022 review
I found myself time for another self-review now, two years later.
Main features
Simplified code, better readability.
Removed unnecessary tests.
Shortened code, rather unimportant.
Modified code
# Linux uses terminfo; BSD uses termcap; the null command (:) ignores everything after
tput_init_linux () { set_fg_color='tput setaf'; reset_color=$(tput sgr0 2>/dev/null); }
tput_init_bsd () { set_fg_color='tput AF'; reset_color=$(tput me 2>/dev/null); }
tput_init_none () { set_fg_color=':'; reset_color=; }
# This routine prepares `tput` on Linux and BSD and no color console
if tput setaf >/dev/null 2>&1; then tput_init_linux || tput_init_none;
elif tput AF >/dev/null 2>&1; then tput_init_bsd || tput_init_none;
else tput_init_none; fi
no_color () { printf '%s' "$reset_color"; }
colorize ()
{
case "1ドル" in
(red) $set_fg_color 1 ;;
(green) $set_fg_color 2 ;;
(yellow) $set_fg_color 3 ;;
(blue) $set_fg_color 4 ;;
(magenta) $set_fg_color 5 ;;
(cyan) $set_fg_color 6 ;;
(white) $set_fg_color 7 ;;
(*) printf '%s\n' "This color ('1ドル') is not supported. Quitting!" >&2; exit 1 ;;
esac
}
color_red=$(colorize red)
color_green=$(colorize green)
color_yellow=$(colorize yellow)
color_blue=$(colorize blue)
color_magenta=$(colorize magenta)
color_cyan=$(colorize cyan)
color_white=$(colorize white)
color_none=$(no_color)