| |

Bash Scripting 26 ๐Ÿง Logging & Auditing โ€” Writing formatted syslog and timestamped logs

โฑ๏ธ Estimated read: 14 min | |

A script that runs unattended has no terminal to print to. When it fails at 3 a.m., the only evidence of what happened is what it wrote to a log. Bash provides no built-in logging facility, but it does provide the logger command for writing to the system log and the date command for generating timestamps. Together with printf for formatting and a small wrapper function for consistency, these tools cover the two logging patterns that matter: structured entries in the system journal or syslog, and timestamped lines in a dedicated log file.

This chapter covers both patterns. It explains the logger command and its -p and -t options, the facility and priority selection that determines where entries land, the date format strings that produce ISO 8601 and other standard timestamps, and the wrapper function that combines timestamp, level, and message into a consistent line. It also covers the PIPESTATUS problem that occurs when piping a script’s output through logger, and the rsyslog rule that routes a custom facility to a dedicated file.

Key point: logger writes to the system log; echo or printf writes to a file. Use logger for events that belong in the system journal, and a timestamped wrapper for application-specific log files. Do not pipe a script’s output through logger without checking PIPESTATUS, or a failed script will appear to have succeeded.


Why these two logging paths exist

The system log integration problem. A script that runs as a system service, a cron job, or a systemd timer should write its messages to the same place as the rest of the system’s logs. logger does this by sending messages to the syslog socket or the systemd journal. The entries appear in /var/log/syslog, /var/log/messages, or journalctl, alongside kernel messages, service logs, and authentication events. This integration means an administrator does not have to know where each script writes its logs; everything is in one place .

The structured timestamp problem. A dedicated application log file needs each line to carry enough context to be useful after the fact: when it happened, how severe it was, and what the message is. logger relies on the syslog daemon or journald to add the timestamp, but a plain file written with echo has no timestamp at all. The date command generates the timestamp, and printf formats the line so that the timestamp, level, and message are consistently positioned .

The pipeline exit status problem. A common pattern is to pipe a script’s output through logger so that every line becomes a syslog entry. The problem is that the pipeline’s exit status is the exit status of logger, not the script. If the script fails but logger succeeds, the pipeline returns 0 and the failure is invisible to cron, systemd, or whatever invoked the script. The PIPESTATUS array preserves the exit status of each stage, and a wrapper script can log the real result .


a. The logger command

logger is the shell interface to the system log. It accepts a message, a priority, and a tag, and sends the result to the local syslog socket or journald.

# Basic log entry with default priority (user.notice)
logger "Script started"

# With a tag that identifies the script
logger -t myscript "Script started"

# With a specific facility and priority
logger -p local0.info -t myscript "Processing complete"

# With an error priority
logger -p local0.err -t myscript "Failed to open input file"

The -t option sets the tag, which appears in the log entry as the program name. The -p option sets the facility and priority as a facility.level pair. The default is user.notice . Common facilities for scripts are user (the default) and local0 through local7 (reserved for local use). Common priorities are info, notice, warning, err, and crit.

The -i option adds the PID of the logger process to each line. The -s option also writes the message to standard error, which is useful for debugging because the message appears both in the terminal and in the log .

# Log with PID and also print to stderr
logger -i -s -t myscript "Debug: entering main loop"

For remote logging, -n specifies a remote syslog server, and -P specifies the port. The --udp and --tcp options select the transport .

b. Timestamps with date and printf

When writing to a dedicated log file, the timestamp must be generated by the script. date produces the string, and printf places it at the start of the line.

# ISO 8601 timestamp
timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
printf '%s %s\n' "$timestamp" "Processing started" >> /var/log/myscript.log

The date format string is the key. %Y-%m-%d produces 2026-11-05, %H:%M:%S produces 14:30:00, and %z produces the timezone offset like +0100. The combination is an ISO 8601 timestamp that sorts lexicographically and is unambiguous across time zones .

# Human-readable timestamp for console output
printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "Operation complete"

# Syslog-style timestamp (month day time)
printf '%s %s\n' "$(date '+%b %d %H:%M:%S')" "Legacy format"

For a log file that will be parsed by other tools, consistency matters more than the specific format. Pick one format, use it in every logging call, and document it. ISO 8601 is the safest choice because it is sortable and unambiguous.

c. The logging wrapper function

A function that combines the timestamp, a level, and the message produces a consistent log line without repeating the formatting code at every call site.

#!/bin/bash
set -euo pipefail

log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
    printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >> /var/log/myscript.log
}

log INFO "Script started"
log WARN "Configuration file not found, using defaults"
log ERROR "Failed to connect to database"

The output is consistent across every call:

2026-11-05T14:30:00+0100 [INFO] Script started
2026-11-05T14:30:01+0100 [WARN] Configuration file not found, using defaults
2026-11-05T14:30:02+0100 [ERROR] Failed to connect to database

The function can be extended to write to both a file and logger. The logger call uses the level to select the priority:

log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
    printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >> /var/log/myscript.log

    case "$level" in
        ERROR) logger -p local0.err -t myscript "$message" ;;
        WARN)  logger -p local0.warning -t myscript "$message" ;;
        *)     logger -p local0.info -t myscript "$message" ;;
    esac
}

This dual-write pattern gives the script a local log file for detailed review and a syslog entry for centralized monitoring.

d. The PIPESTATUS problem

A common pattern for capturing a script’s output as syslog entries is to pipe it through logger:

# This looks correct but has a hidden problem
/home/appuser/myjob.sh 2>&1 | logger -t myjob

The pipeline’s exit status is the exit status of logger, not of myjob.sh. If myjob.sh fails, logger still succeeds, and the pipeline returns 0. Cron, systemd, or any caller that checks the exit status will believe the job succeeded .

The fix is to capture PIPESTATUS immediately after the pipeline. The first element is the script’s status; the second is logger‘s status.

#!/bin/bash
/home/appuser/myjob.sh 2>&1 | logger -t myjob
results=("${PIPESTATUS[@]}")
logger -t myjob "event=finish status=${results[0]} logger_status=${results[1]}"
exit "${results[0]}"

The wrapper script logs both the script’s real status and the logger’s status, then exits with the script’s status so that the caller sees the failure .

e. Routing a custom facility to a dedicated file

By default, logger -p local0.info sends the message to the general system log. To route it to a dedicated file, add a rule to rsyslog.

# /etc/rsyslog.d/myscript.conf
local0.*    /var/log/myscript.log

After adding the rule, validate the configuration and restart rsyslog:

rsyslogd -N1
systemctl restart rsyslog

The local0.* selector matches all priorities from the local0 facility. The action writes them to /var/log/myscript.log instead of the default destination . The script continues to use logger -p local0.info -t myscript, and the messages land in the dedicated file.

The dedicated file needs a log rotation policy. Without one, it grows until the disk fills.

# /etc/logrotate.d/myscript
/var/log/myscript.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
}

Complete Example Session

# ============================================
# PART 1: BASIC LOGGER USAGE
# ============================================
logger -t myscript "Script started"
logger -p local0.info -t myscript "Processing complete"
logger -p local0.err -t myscript "Failed to open input file"

# Entries appear in /var/log/syslog or journalctl.
# ============================================
# PART 2: LOGGER WITH PID AND STDERR
# ============================================
logger -i -s -t myscript "Debug: entering main loop"

# -i adds the logger PID.
# -s also writes to stderr for immediate visibility.
# ============================================
# PART 3: TIMESTAMP WITH DATE
# ============================================
timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
printf '%s %s\n' "$timestamp" "Processing started"

# Output: 2026-11-05T14:30:00+0100 Processing started
# ============================================
# PART 4: THE LOGGING FUNCTION
# ============================================
log() {
    local level="$1"; shift
    local message="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
    printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >> /var/log/myscript.log
}

log INFO "Script started"
log ERROR "Database connection failed"
# ============================================
# PART 5: DUAL WRITE (FILE AND SYSLOG)
# ============================================
log() {
    local level="$1"; shift
    local message="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
    printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >> /var/log/myscript.log

    case "$level" in
        ERROR) logger -p local0.err -t myscript "$message" ;;
        WARN)  logger -p local0.warning -t myscript "$message" ;;
        *)     logger -p local0.info -t myscript "$message" ;;
    esac
}
# ============================================
# PART 6: THE PIPESTATUS PROBLEM
# ============================================
# Wrong: pipeline returns logger's status
# /home/appuser/myjob.sh 2>&1 | logger -t myjob

# Correct: capture PIPESTATUS
/home/appuser/myjob.sh 2>&1 | logger -t myjob
results=("${PIPESTATUS[@]}")
logger -t myjob "event=finish status=${results[0]}"
exit "${results[0]}"
# ============================================
# PART 7: ROUTE CUSTOM FACILITY TO FILE
# ============================================
sudo tee /etc/rsyslog.d/myscript.conf <<'EOF'
local0.*    /var/log/myscript.log
EOF

sudo rsyslogd -N1
sudo systemctl restart rsyslog
# ============================================
# PART 8: VERIFY THE ROUTING
# ============================================
logger -p local0.info -t myscript "test message"
sleep 1
tail -n 1 /var/log/myscript.log

# Expected: Nov  5 14:30:00 hostname myscript: test message
# ============================================
# PART 9: LOG ROTATION
# ============================================
sudo tee /etc/logrotate.d/myscript <<'EOF'
/var/log/myscript.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
}
EOF
# ============================================
# PART 10: COMPLETE SCRIPT
# ============================================
#!/bin/bash
set -euo pipefail

log() {
    local level="$1"; shift
    local message="$*"
    local timestamp
    timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')
    printf '%s [%s] %s\n' "$timestamp" "$level" "$message" >> /var/log/myscript.log
    case "$level" in
        ERROR) logger -p local0.err -t myscript "$message" ;;
        WARN)  logger -p local0.warning -t myscript "$message" ;;
        *)     logger -p local0.info -t myscript "$message" ;;
    esac
}

log INFO "Script started"
# ... work ...
log INFO "Script finished"

The ten parts covered basic logger, PID and stderr options, timestamp generation, the logging function, dual write, PIPESTATUS, rsyslog routing, verification, log rotation, and the complete script.


Quick Reference

logger options

OptionPurposeExample
-t tagSet the program tag-t myscript
-p prioritySet facility and level-p local0.info
-iInclude logger PID-i
-sAlso write to stderr-s
-n serverRemote syslog server-n logserver
-P portRemote port-P 514

Common facilities and priorities

FacilityUse case
userDefault, general user messages
local0โ€“local7Custom script logging
daemonSystem daemons
PriorityMeaning
infoInformational
noticeNormal but significant
warningWarning
errError
critCritical

date format strings

FormatOutput
%Y-%m-%dT%H:%M:%S%z2026-11-05T14:30:00+0100
%Y-%m-%d %H:%M:%S2026-11-05 14:30:00
%b %d %H:%M:%SNov 05 14:30:00

PIPESTATUS indices

IndexValue
${PIPESTATUS[0]}First command’s status (the script)
${PIPESTATUS[1]}Second command’s status (logger)

Best Practices

โœ… Do This:

# Use a consistent timestamp format
timestamp=$(date '+%Y-%m-%dT%H:%M:%S%z')                   # โœ…
# Capture PIPESTATUS when piping through logger
results=("${PIPESTATUS[@]}")                               # โœ…
# Use local0 for script-specific logging
logger -p local0.info -t myscript "message"                # โœ…
# Configure log rotation for dedicated files
/etc/logrotate.d/myscript                                  # โœ…

โŒ Don’t Do This:

# Pipe a script through logger without checking status
myscript.sh 2>&1 | logger -t myscript                      # โŒ (hides failure)
# Use echo for timestamped logs without a format
echo "$(date) something happened" >> log                   # โŒ (inconsistent)
# Log to a dedicated file without rotation
local0.* /var/log/myscript.log  # no logrotate             # โŒ
# Use the same facility as system services
logger -p daemon.info "my script message"                  # โŒ (confusing)

Common Pitfalls

PitfallWhy It HappensFix
Script failure hidden by loggerPipeline returns logger’s statusCapture PIPESTATUS
Log file grows unboundedNo log rotation configuredAdd /etc/logrotate.d/ entry
Messages not in expected fileNo rsyslog rule for the facilityAdd local0.* /var/log/... rule
Timestamps inconsistentDifferent formats at different call sitesUse a single logging function
logger message truncatedLine length limit in syslogKeep messages under 1KB or split
logger not foundPackage not installedInstall util-linux or bsdutils

Real-World Examples

1. Basic logger call

logger -t myscript "Script started"

2. Error priority

logger -p local0.err -t myscript "Database connection failed"

3. ISO timestamp

date '+%Y-%m-%dT%H:%M:%S%z'

4. Timestamped line

printf '%s %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "message" >> log

5. Logging function

log() { printf '%s [%s] %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$1" "${*:2}"; }

6. Dual write

logger -p local0.info -t myscript "$message"

7. PIPESTATUS capture

myscript.sh 2>&1 | logger -t myscript
status=${PIPESTATUS[0]}

8. rsyslog route

local0.* /var/log/myscript.log

9. Log rotation

/var/log/myscript.log { weekly rotate 4 compress }

10. Verify routing

logger -p local0.info -t test "hello" && tail /var/log/myscript.log

Visual

Two logging paths

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  SCRIPT                                                      โ”‚
โ”‚                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ logger              โ”‚    โ”‚ printf + date       โ”‚         โ”‚
โ”‚  โ”‚                     โ”‚    โ”‚                     โ”‚         โ”‚
โ”‚  โ–ผ                     โ”‚    โ–ผ                     โ”‚         โ”‚
โ”‚  syslog socket/journaldโ”‚    dedicated log file   โ”‚         โ”‚
โ”‚       โ”‚                โ”‚         โ”‚                โ”‚         โ”‚
โ”‚       โ–ผ                โ”‚         โ–ผ                โ”‚         โ”‚
โ”‚  /var/log/syslog       โ”‚    /var/log/myscript.logโ”‚         โ”‚
โ”‚  journalctl            โ”‚    (needs rotation)     โ”‚         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

PIPESTATUS

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  PIPELINE                                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  myscript.sh 2>&1 | logger -t myscript                       โ”‚
โ”‚       โ”‚                  โ”‚                                   โ”‚
โ”‚       โ–ผ                  โ–ผ                                   โ”‚
โ”‚  PIPESTATUS[0]      PIPESTATUS[1]                            โ”‚
โ”‚  (script status)    (logger status)                          โ”‚
โ”‚                                                              โ”‚
โ”‚  Pipeline exit = PIPESTATUS[1]                               โ”‚
โ”‚  If logger succeeds, pipeline returns 0                      โ”‚
โ”‚  even if script failed.                                      โ”‚
โ”‚                                                              โ”‚
โ”‚  Fix: exit "${PIPESTATUS[0]}"                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

rsyslog routing

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  logger -p local0.info -t myscript "message"                 โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  rsyslog                                                     โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ local0.* โ”€โ”€โ–บ /var/log/myscript.log                โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€ *.info   โ”€โ”€โ–บ /var/log/syslog (if no rule)          โ”‚
โ”‚                                                              โ”‚
โ”‚  The first matching rule wins.                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Logging function flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  log INFO "message"                                          โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  timestamp=$(date ...)                                       โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  printf '%s [%s] %s\n' "$timestamp" "$level" "$message"     โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€โ–บ /var/log/myscript.log                             โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  logger -p local0.info -t myscript "$message"                โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ””โ”€โ”€โ–บ syslog/journald                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
System log commandlogger
Tag option-t
Priority option-p facility.level
Default priorityuser.notice
Timestamp commanddate '+format'
ISO 8601 format%Y-%m-%dT%H:%M:%S%z
Pipeline statusPIPESTATUS array
Custom facility routingrsyslog rule in /etc/rsyslog.d/
Log rotation/etc/logrotate.d/

Key takeaways:

  • logger writes to the system log; printf writes to a file. Use logger for events that should appear in journalctl or /var/log/syslog. Use a timestamped wrapper for application-specific log files that need a consistent format .
  • The timestamp format must be consistent. Pick one date format string, use it in every logging call through a wrapper function, and document it. ISO 8601 is the safest choice because it sorts lexicographically and includes the timezone offset .
  • Piping through logger hides the script’s exit status. The pipeline returns logger‘s status, not the script’s. Capture ${PIPESTATUS[0]} and exit with it, or a failed script will appear to have succeeded .
  • A custom facility needs a rsyslog rule to land in a dedicated file. local0.* in /etc/rsyslog.d/ routes all local0 messages to the specified file. Without the rule, they go to the general system log .
  • Dedicated log files need rotation. A file that grows without bound will eventually fill the disk. A /etc/logrotate.d/ entry keeps the file size bounded and compresses old entries.
  • A logging function prevents inconsistency. The wrapper combines timestamp, level, message, and optional logger call in one place. When the format changes, it changes in one function, not at every call site.

Remember: Logging in Bash is two separate concerns. The first is where the log goes: the system journal via logger, or a dedicated file via redirection. The second is what the line looks like: timestamp, level, and message in a consistent format. The logging wrapper function handles the second concern. The logger command and the rsyslog rule handle the first. The one mistake that causes the most trouble is piping a script through logger without checking PIPESTATUS, because it turns a failure into a success. Check the status, log the real result, and exit with the script’s status, not logger‘s.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!