--- /dev/null
+#!/bin/bash
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+append() {
+ cat >> "$1"
+}
+log-once() {
+ local help="Usage: log-once [OPTION]... LOG_NAME [LOG_MESSAGE]
+
+For cronjobs, email only once for repeated failures, and for success after failure.
+
+Meant for use in cronjobs where LOG_MESSAGE or STDIN represents an error,
+but we only want to output that to STDOUT if we've seen this type of
+error ERRORS(default 3) number of times in a row, then we don't
+want to output anything again until we've seen a success (an empty LOG_MESSAGE).
+
+Logs LOG_MESSAGE or STDIN to ~/.cron_errors/LOG_NAME, and keeps
+state in the same directory.
+
+-e ERRORS: ERRORS is the number of errors to accumulate before outputing the error"
+ local cbase c c1 c2 log x i out file
+ errors=3
+ while true; do
+ if [[ $1 == --help ]]; then
+ echo "$help"
+ return
+ elif [[ $1 == -[0-9]* ]]; then
+ errors={$1#-}
+ shift
+ elif [[ $1 == -- ]]; then
+ shift
+ break
+ else
+ break
+ fi
+ done
+ log_name=$1
+ # todo, make option & make them overridable based on command line or env variable
+ cbase=$HOME/.cron-errors
+ [[ -d $cbase ]] || mkdir -p $cbase
+ c=$cbase/$log_name
+ # http://stackoverflow.com/questions/2456750/detect-presence-of-stdin-contents-in-shell-script
+ log=false
+ if [[ $2 ]]; then
+ log=true
+ # read stdin for anything which is not just a newline
+ elif [[ ! -t 0 ]]; then
+ while read -r x; do
+ output+=( $x )
+ [[ $x ]] && log=true
+ done
+ fi
+ if $log; then
+ file=
+ if [[ -f $c$((errors-1)) ]]; then
+ out="tee -a"
+ else
+ out=append
+ fi
+ for ((i=errors; i>=1; i--)); do
+ if [[ -f $c$((i-1)) ]]; then
+ file=$c$i
+ mv $c$((i-1)) $file
+ break
+ fi
+ done
+ if [[ ! $file ]]; then
+ if [[ -f $c$errors ]]; then
+ file=$c$errors
+ else
+ file=${c}1
+ fi
+ fi
+ $out $file <<<"log-once: $(date "+%A, %B %d, %r")"
+ if [[ $2 ]]; then
+ $out $file <<<"$2"
+ else
+ $out $file <<<"${output[@]}"
+ $out $file
+ fi
+ return 1
+ elif [[ -f $c$errors ]]; then
+ echo "log-once success after failure for $c"
+ rm -f $c$errors
+ else
+ rm -f $c[0-9]* # assuming no one is putting crazy files names, as this is not exact
+ fi
+ return 0
+}
+log-once "$@"