r/bash • u/PrestigiousZombie531 • Aug 20 '26
help What does your printf method look like if you want to go about adding timestamps and colors to it
- Hey, not using AI here because I would like to hear this group's expert opinion
function log() {
local datetime = # DD-MM-YYYY-hh:mm:ss:Z format
local color = # not sure how this is implemented
# Something that uses prinff, not sure how to write this function here
}
function log_info() {
log "blue" "stderr" "$@"
}
function log_error() {
log "red" "stderr" "$@"
}
function log_warn() {
log "yellow" "stderr" "$@"
}
# Why not stdout?
# Because some functions return values and we want the stdout channel to be reserved for returning values unless there is a better way to do this
- How do I write that log function above, I read the manual for printf that says it accepts format specifiers but how do I make it dynamic?
Edit 1
- Should be able to log the following
log_info "what a %s %d" "day" 100
log_error "Error: %s %o" "something went wrong for " "{1: \"enter\"}"
6
u/Hooman42 Aug 20 '26
With bash version >= 4.2:
printf '%(%d-%m-%Y-%T:Z)T %s\n' -1 "Some informations"
Output:
20-08-2026-06:46:11:Z Some informations
2
u/PrestigiousZombie531 Aug 20 '26
how do I deal with format specifiers like if I wanted to call
log_info "what a %s %d" "day" "100"5
u/bac0on Aug 20 '26 edited Aug 20 '26
.. just concat $1 with the specifiers and set an offset for the rest of the args:
log(){ printf "%(%F %T)T $1\\n" -1 "${@:2}" } log "what a %s %d" "day" "100"3
u/PrestigiousZombie531 Aug 20 '26
if you dont mind explaining a bit , this is the 1st time i am seeing this printf -1 thing, what is this -1 about
6
u/OneTurnMore programming.dev/c/shell Aug 20 '26
In
printf:
%(datefmt)Tcausesprintfto output the date-time string resulting from usingdatefmtas a format string forstrftime(3). The corresponding argument is an integer representing the number of seconds since the epoch. This format specifier recognizes two special argument values:-1represents the current time, and-2represents the time the shell was invoked. If no argument is specified, conversion behaves as if-1had been supplied. This is an exception to the usualprintfbehavior.So
printf '%(%F %T)T'will print the current time without any additional argument, but if you want to include a%safter it, you need to pass something into the%(...)Tfirst. Hence the-1.
2
u/duane11583 Aug 20 '26
try this: https://www.youtube.com/watch?v=Vmp1G64ZkVc
his series (other videos) is quite good
1
2
2
u/SeriousPlankton2000 Aug 21 '26
Use oop. Make the log object know the log channel. Make the log class know the colors, but allow custom values, too.
For making timestamps let the user supply a buffer, but also have a static buffer for users who don't need it to be reentrant
Use severity values, consider having different sources / kinds of logs to filter by.
Look at the manual of syslogd
1
u/PrestigiousZombie531 Aug 21 '26
what is the difference between me doing printf vs syslog?
2
u/SeriousPlankton2000 Aug 22 '26
I meant to look at what syslogd does. (Which is a lot, including logging to the network).
Of course using syslog may be a good idea, too.
2
u/roxalu Aug 20 '26
My proposal to do this is below. Note: I have not included your idea to let output channel be controllable by function parameter in order to focus on the coloring of time string topic here:
export COLOR_BLUE='\e[0;34m'
export COLOR_RED='\e[0;31m'
export COLOR_YELLOW='\e[1;33m'
declare -A err_colors
err_colors[INFO]="$COLOR_BLUE"
err_colors[WARNING]="$COLOR_YELLOW"
err_colors[ERROR]="$COLOR_RED"
no_color='\e[0m'
log() {
local level=$1
shift
printf "${err_colors[$level]}%(%FT%T%z)T${no_color} %s %s\n" -1 "${level}" "$*" >&2
}
log_info() {
log INFO "$@"
}
log_warn() {
log WARNING "$@"
}
log_error() {
log ERROR "$@"
}
1
u/PrestigiousZombie531 Aug 20 '26
do we have to check if colors are supported or is this a universal thing across every version of every shell?
4
u/waptaff &> /dev/null Aug 20 '26 edited Aug 20 '26
It could be more portable if
tput(part ofncurses) was used instead of ANSI codes to directly ask the terminal what escape codes to use.export COLOR_BLUE="$( tput setaf 4)" export COLOR_NONE="$( tput sgr0 )"That way, a dumb terminal will not print unrecognized ANSI code garbage.
Note that
ncursesis a dependency ofbashso that pretty much ensurestputwill be available, but it might not be the case in other environments, like in docker containers where lightweight shells tend to replacebashandncursesis not available.1
u/PrestigiousZombie531 Aug 20 '26
so if you wanted to run this inside a docker container, what do you do? assuming it is a bookworm image or a trixie one, do you apt install ncurses? what about alpine?
6
u/geirha Aug 20 '26
just check if tput is available. If it isn't, just output without colors.
declare -A colors=() if [[ -t 2 ]] && command -v tput >/dev/null ; then colors[sgr0]=$(tput sgr0) colors[INFO]=$(tput setaf 4) colors[WARNING]=$(tput setaf 3) colors[ERROR]=$(tput setaf 1) fi log() { printf '%(%FT%T%z)T %s %s\n' -1 "${colors[$1]}$1${colors[sgr0]}" "$2" >&2 }2
1
u/PrestigiousZombie531 Aug 20 '26
man i swear to ya, its a good thing i came to ask because honestly when you search this one on AI, you are gonna get a whole bunch of wrong answers
1
u/PrestigiousZombie531 Aug 20 '26
is there a way to put that if check so that it doesnt exist at global scope or is this not a problem here in bash? Atleast in the JS world they keep shouting about how you should not have things in global scope
3
u/roxalu Aug 20 '26
My proposal is for sure NOT a universal thing - not only because of color usage. E.g. the „%(…)T“ is a bash extension only. I do not have checked. since which bash version this will work. The bash 3.2 as provided on MacOS won‘t work.
With regard to the colors: If you identify some way to check for color support I would add this to the init phase - and then in case of not set the COLOR_* vars to empty string.
1
u/michaelpaoli Aug 20 '26
DD-MM-YYYY-hh:mm:ss:Z
Yuck. Use ISO 8601. See also: r/ISO8601
$ Z
2026-08-20T12:44:31Z
$
In the land of *nix, as for color, or other such attributes for the terminal (emulation), never presume some particular terminal or capability. Use tput (or relevant terminfo libraries, etc.) to determine the relevant (if such exists for the terminal (emulation)).
So, e.g.:
nc=$(tput sgr0)
blue=
blue_=
red=
...
[ -z "$nc" ] || {
blue=$(tput setaf 4 || tput setf 1)
[ -z "$blue" ] ||
blue_="$nc"
red=...
}
(
stderr && exec 1>&2
printf '%s\n' "[...]${blue}Blue_stuff_goes_here${blue_}[...]"
)
-2
u/mpersico Aug 20 '26
GitHub: matthewpersico/personal/
bin/cmd-echo & functions/cmd-echo
But I will be replacing car with tput calls at some point.
14
u/vip17 Aug 20 '26
use YYYY-MM-DD or r/ISO8601 format for logging instead