diff --git a/docs/shells/bash.md b/docs/shells/bash.md index 47a3933..958ff50 100644 --- a/docs/shells/bash.md +++ b/docs/shells/bash.md @@ -1,5 +1,60 @@ # Bash +## stdout/stderr (console and logs) + +``` bash +#!/usr/bin/env bash + +# Environment overrides +LOG_LEVEL="${LOG_LEVEL:-INFO}" # TRACE, DEBUG, INFO, WARN, ERROR +LOG_INCLUDE_TS=0 # 0 or 1 = include timestamp +LOG_INCLUDE_TYPE=1 # 0 or 1 = include [LEVEL] + +# When set to 1, all log messages are sent to stderr instead of splitting +# INFO/DEBUG/TRACE to stdout and WARN/ERROR to stderr. Useful for scripts +# where stdout must remain clean for pipelines, JSON output, or data export. +LOG_ALL_TO_STDERR=0 + +# Level mapping +declare -A LEVELS=([TRACE]=0 [DEBUG]=1 [INFO]=2 [WARN]=3 [ERROR]=4) + +# Helpers +_log_ts() { [ "$LOG_INCLUDE_TS" = "1" ] && printf "[%s]" "$(date -Iseconds)"; } +_log_type() { [ "$LOG_INCLUDE_TYPE" = "1" ] && printf "[%s]" "$1"; } +_log_ok() { [[ ${LEVELS[$1]} -ge ${LEVELS[$LOG_LEVEL]} ]]; } + +# Output selector +_log_out() { + local level="$1" + shift + local msg="$*" + + if [ "$LOG_ALL_TO_STDERR" = "1" ]; then + printf "%s\n" "$msg" >&2 + else + case "$level" in + TRACE|DEBUG|INFO) printf "%s\n" "$msg" >&1 ;; + WARN|ERROR) printf "%s\n" "$msg" >&2 ;; + esac + fi +} + +# Log functions +log_trace() { _log_ok TRACE && _log_out TRACE "$(_log_ts)$(_log_type TRACE) $*"; } +log_debug() { _log_ok DEBUG && _log_out DEBUG "$(_log_ts)$(_log_type DEBUG) $*"; } +log_info() { _log_ok INFO && _log_out INFO "$(_log_ts)$(_log_type INFO) $*"; } +log_warn() { _log_ok WARN && _log_out WARN "$(_log_ts)$(_log_type WARN) $*"; } +log_error() { _log_ok ERROR && _log_out ERROR "$(_log_ts)$(_log_type ERROR) $*"; } + +# Examples +log_trace "trace example" +log_debug "debug example" +log_info "info example" +log_warn "warn example" +log_error "error example" +log_error "error example" +``` + ## arrays ``` bash