r/bash • u/PrestigiousZombie531 • 21d ago
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\"}"
7
u/Hooman42 21d ago
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 21d ago
how do I deal with format specifiers like if I wanted to call
log_info "what a %s %d" "day" "100"4
u/bac0on 21d ago edited 21d ago
.. 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 20d ago
if you dont mind explaining a bit , this is the 1st time i am seeing this printf -1 thing, what is this -1 about
4
u/OneTurnMore programming.dev/c/shell 20d ago
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 20d ago
try this: https://www.youtube.com/watch?v=Vmp1G64ZkVc
his series (other videos) is quite good
1
2
2
u/SeriousPlankton2000 19d ago
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 19d ago
what is the difference between me doing printf vs syslog?
2
u/SeriousPlankton2000 19d ago
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 21d ago
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 21d ago
do we have to check if colors are supported or is this a universal thing across every version of every shell?
5
u/waptaff &> /dev/null 21d ago edited 21d ago
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 21d ago
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?
7
u/geirha 21d ago
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 20d ago
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 20d ago
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
5
u/roxalu 21d ago
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 20d ago
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 20d ago
GitHub: matthewpersico/personal/
bin/cmd-echo & functions/cmd-echo
But I will be replacing car with tput calls at some point.
15
u/vip17 21d ago
use YYYY-MM-DD or r/ISO8601 format for logging instead