Skip to content

Instantly share code, notes, and snippets.

@taskie
Last active October 30, 2018 16:34
Show Gist options
  • Save taskie/19c71c907a3ac1ce76a60c78ec2a3a44 to your computer and use it in GitHub Desktop.
Save taskie/19c71c907a3ac1ce76a60c78ec2a3a44 to your computer and use it in GitHub Desktop.
diff then cp (bash)
#!/usr/bin/env bash
set -eu
PROGRAM="$(basename "$0")"
usage () {
cat <<EOF
${PROGRAM}: diff then cp
Usage:
${PROGRAM} [-f|-i] SRC DEST
Options:
-f, --force
-i, --interactive
--diff DIFF
--diffopts DIFFOPTS
-e, --error
-v, --verbose
-g, --debug
-h, --help
EOF
}
MODE=interactive
declare -i VERBOSITY=0
DIFF="${DCP_DIFF:-diff}"
DIFFOPTS="${DCP_DIFFOPTS:--u}"
declare -i ERROR=0
declare -i DEBUG=0
declare -a ARGS=()
parse_opts () {
local arg=
while (( $# > 0 )); do
arg="$1"
shift
case "$arg" in
-f|--force)
MODE=force
;;
-i|--interactive)
MODE=interactive
;;
--diff)
DIFF="$1"
shift
;;
--diffopts)
DIFFOPTS="$1"
shift
;;
-e|--error)
ERROR=1
;;
-v|--verbose)
(( VERBOSITY+=1 ))
;;
-g|--debug)
DEBUG=1
;;
-h|--help)
usage
exit 0
;;
--)
ARGS+=("$@")
shift $#
;;
-)
ARGS+=(-)
;;
-*)
usage
echo >&2
echo >&2 "invalid option: $arg"
exit 1
;;
*)
ARGS+=("$arg")
;;
esac
done
}
declare -a PREPARED_ARGS=()
function prepare_args () {
local arg= char=
PREPARED_ARGS=()
while (( $# )); do
arg="$1"
shift
case "$arg" in
--)
PREPARED_ARGS+=(-- "$@")
shift $#
;;
--*)
# long option
if [[ $arg =~ ^(--[^=]+)=(.*)$ ]]; then
PREPARED_ARGS+=("${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")
else
PREPARED_ARGS+=("$arg")
fi
;;
-)
PREPARED_ARGS+=(-)
;;
-*)
# short option
# https://stackoverflow.com/questions/10551981
while IFS= read -r -d '' -n 1 char; do
PREPARED_ARGS+=("-${char}")
done < <(printf "%s" "${arg#-}")
;;
*)
PREPARED_ARGS+=("$arg")
;;
esac
done
}
prepare_args "$@"
parse_opts "${PREPARED_ARGS[@]}"
if (( ${#ARGS[@]} != 2 )); then
usage
echo >&2
echo >&2 "you must specify just 2 arguments"
exit 1
fi
SRC="${ARGS[0]}"
DEST="${ARGS[1]}"
if (( DEBUG )); then
cat >&2 <<EOF
MODE=${MODE}
VERBOSITY=${VERBOSITY}
DIFF=${DIFF}
DIFFOPTS=${DIFFOPTS}
ERROR=${ERROR}
SRC=${SRC}
DEST=${DEST}
EOF
fi
if [[ $DEST = - ]]; then
echo >&2 "destination must not be -"
exit 1
fi
if [[ $MODE = interactive && ! -t 0 ]]; then
echo >&2 "stdin is not a tty. use -f (--force) option"
exit 1
fi
rm_tmp_src () {
[[ -e $SRC ]] && rm -f "$SRC" || :
}
if [[ $SRC = - ]]; then
SRC="$(mktemp "${PROGRAM}-XXXXXX")"
trap rm_tmp_src EXIT HUP INT TERM
cat >"$SRC"
fi
if [[ ! -e $DEST ]]; then
if (( ERROR )); then
echo >&2 "no such file: ${DEST}"
exit 10
fi
elif "$DIFF" $DIFFOPTS -- "$DEST" "$SRC"; then
if (( ERROR )); then
echo >&2 'no difference'
exit 11
else
exit 0
fi
fi
# has difference
case "$MODE" in
interactive)
cp -iT -- "$SRC" "$DEST"
;;
force)
cp -fT -- "$SRC" "$DEST"
;;
*)
echo >&2 "invalid mode: ${MODE}"
exit 1
;;
esac
if (( VERBOSITY >= 3 )); then
if command -v cowsay >/dev/null 2>&1; then
cowsay >&2 "completed."
else
echo >&2 "completed."
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment