objectif
Exécuter une commande, afficher sa sortie en direct, la stocker dans un fichier et retourner exactement son code retour (pas celui de tee), y compris avec stderr.
code minimal
# usage: run_logged LOGFILE -- commande args...
run_logged() {
local log="$1"; shift
set -o pipefail
"$@" 2>&1 | tee -a "$log"
local rc=${PIPESTATUS[0]}
return "$rc"
}
utilisation
# 1) build de data.pm avec log daté et statut fidèle
LOG="/var/log/data.pm/build-$(date -u +%Y-%m-%d).log"
mkdir -p "$(dirname "$LOG")"
if run_logged "$LOG" -- npm run build; then
echo "build ok"
else
echo "build ko" >&2; exit 1
fi
# 2) capturer stdout+stderr vers fichier ET syslog en même temps
LOG="/var/log/data.pm/deploy-$(date -u +%Y-%m-%d).log"
run_logged "$LOG" -- bash -lc 'set -Eeuo pipefail; ./deploy.sh' \
| tee >(:) >(logger -t data.pm -p user.info) >/dev/null
# (le code retour reste celui de ./deploy.sh via run_logged)
# 3) forcer l'affichage des couleurs quand l'outil détecte un TTY
# (de nombreux outils supportent --color=always ou variables d'env dédiées)
run_logged "$LOG" -- grep --color=always -RIn 'pattern' .
# 4) éviter le buffering des grosses sorties
run_logged "$LOG" -- stdbuf -oL -eL pytest -q
variante(s) utile(s)
# rediriger globalement un script: tout est tee'lé, codes retours préservés
LOG="/var/log/data.pm/task-$(date -u +%Y-%m-%d).log"
# ouvre deux pipes de process substitution, stdout et stderr séparés
exec > >(tee -a "$LOG") 2> >(tee -a "$LOG" >&2)
# ... vos commandes ici ...
# le code retour de chaque commande reste accessible via $?
# journaliser en JSONL minimal (timestamp + rc)
run_logged_json() {
local log="$1"; shift
local ts rc
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
set -o pipefail
"$@" 2>&1 | tee -a "$log"
rc=${PIPESTATUS[0]}
printf '{"ts":"%s","cmd":%q,"rc":%d}\n' "$ts" "$*" "$rc" >> "$log"
return "$rc"
}
# verrouiller l'écriture pour éviter des logs entremêlés (concurrence)
flock -n /tmp/data.pm.log.lock -c 'run_logged /var/log/data.pm/app.log -- ./job.sh'
notes
${PIPESTATUS[0]}capture le code retour du premier élément du pipeline (la commande exécutée), indépendamment detee.set -o pipefailfait échouer le pipeline si une étape échoue; utile pour ne pas masquer les erreurs.stdbufn’est pas toujours disponible par défaut sur macOS; à défaut, préférez des options d’outils qui flushent en ligne ouscript -q /dev/null -c "cmd".- protégez vos logs (droits 0640/0600) et utilisez des noms datés pour faciliter la rotation (
logrotateou équivalent).