#!/usr/bin/env bash insert_date() { while read do local date_value date_value="$(date +"%b %d %T %s.%N")" echo "${date_value} ${REPLY}" done } fatal_error_handler() { local result="$?" local p_message="$1" local pid="${BASHPID}" (exit "${result}") || { ( cd "${BASH_SOURCE[1]%/*}" echo -n "${FUNCNAME[0]}: $(echo | insert_date)${PWD}/${BASH_SOURCE[1]##*/}:${BASH_LINENO[0]} ${FUNCNAME[1]}() PID(${pid})" ) [ "${p_message}" != "" ] && echo -n " ${p_message}" echo return "${result}" } >&2 } execute_with_retry() { local p_max_try_count="$1" shift false local result="$?" local try_number=0 while ((try_number < p_max_try_count)) do echo "Execute command($@): try #$((try_number+1))/${p_max_try_count}" >&2 "$@" && return result="$?" sleep 1 ((++try_number)) done return "${result}" } get_own_process_group() { local ps_output ps_output=($(ps --no-heading -o pgrp "$$")) || fatal_error_handler || return [ "${#ps_output[@]}" = "1" ] || fatal_error_handler || return echo "${ps_output[0]}" }
#!/usr/bin/env bash cd "${0%/*}" || exit PATH+=":${PWD}" cd - &> /dev/null source "toolbox.sh" || exit set -o pipefail declare -r g_mode_read="READ" declare -r g_mode_write="WRITE" terminate_process_group() { kill -TERM -"$(get_own_process_group)" } exit_handler() { terminate_process_group } int_handler() { terminate_process_group } child() { local p_mode="$1" local p_file="$2" local p_log_file="$3" if [ "${p_mode}" = "${g_mode_write}" ] then dd if=/dev/zero of="${p_file}" bs=8M 2>&1 | stdbuf -o0 grep 'copied' | insert_date | tee "${p_log_file}" || fatal_error_handler elif [ "${p_mode}" = "${g_mode_read}" ] then dd if="${p_file}" of=/dev/null bs=8M 2>&1 | stdbuf -o0 grep 'copied' | insert_date | tee "${p_log_file}" || fatal_error_handler else fatal_error_handler fi terminate_process_group } get_dd_pid() { local pid_container pid_container=($(pgrep -g "$(get_own_process_group)" dd)) || fatal_error_handler || return [ "${#pid_container[@]}" = "1" ] || fatal_error_handler || return echo "${pid_container[0]}" } main() { local p_mode="$1" local p_file="$2" local p_log_file="$3" trap exit_handler EXIT trap int_handler SIGINT child "${p_mode}" "${p_file}" "${p_log_file}" & local dd_pid execute_with_retry 5 eval 'dd_pid="$(get_dd_pid)"' || fatal_error_handler || return while true do sleep 1 kill -USR1 "${dd_pid}" || fatal_error_handler || return done } LANG="C" main "$@" || fatal_error_handler