Skip to content

Instantly share code, notes, and snippets.

@Joker-vD
Created September 28, 2026 06:51
Show Gist options
  • Select an option

  • Save Joker-vD/6eb213a72d0c626eb0772aee6a749a99 to your computer and use it in GitHub Desktop.

Select an option

Save Joker-vD/6eb213a72d0c626eb0772aee6a749a99 to your computer and use it in GitHub Desktop.
Create a backup copy of a file, with a timestamp in its name
#!/bin/sh
errecho() {
>&2 printf '%s: %s\n' "%0" "$*"
}
if [ "$#" != 1 ] || [ -z "$1" ]
then
errecho 'Requires a single filename as the argument'
exit 1
fi
add_suffix_to_filename() {
# because of course the output of dirname can't be blindly joined with slash and the basename
DIRNAME=$(dirname -- "$1")
if [ "$DIRNAME" = / ]
then
DIRNAME=
fi
FILENAME=$(basename -- "$1")
# because of course extracting the filename's extension is not supported out of the box
case "$FILENAME" in
.*.* )
EXTENSION=.${FILENAME#.*.}
BASENAME=$(printf '%s' "$FILENAME" | cut -c-$((${#FILENAME} - ${#EXTENSION})) )
;;
.* )
EXTENSION=
BASENAME=$FILENAME
;;
*.[^.]* )
BASENAME=${FILENAME%%.*}
EXTENSION=${FILENAME#*.}
;;
* )
BASENAME=${FILENAME%%.*}
EXTENSION=$(printf '%s' "$FILENAME" | cut -c$((${#BASENAME} + 1))- )
;;
esac
if [ "$FILENAME" != "$BASENAME$EXTENSION" ] || [ -z "$BASENAME" ]
then
errecho 'Failed to properly split the extension from the filename:' "$FILENAME"
return 1
fi
printf '%s/%s%s%s' "$DIRNAME" "$BASENAME" "$2" "$EXTENSION"
}
ORIGINAL=$1
if [ "${ORIGINAL#./}" = "$ORIGINAL" ] && [ "${ORIGINAL#/}" = "$ORIGINAL" ]
then
ORIGINAL=./"$ORIGINAL"
fi
TIMESTAMP=$(date --utc --date=@"$(stat --format %Y "$ORIGINAL")" +'_%Y-%m-%d_%H%M%S')
if [ -z "$TIMESTAMP" ]
then
errecho 'Failed to get the timestamp of the file: ' "$ORIGINAL"
exit 1
fi
NEW_FILENAME=$(add_suffix_to_filename "$ORIGINAL" "$TIMESTAMP")
if [ "$?" != 0 ]
then
exit 1
fi
if [ "$NEW_FILENAME" = "$ORIGINAL" ]
then
errecho 'Somehow the name for the backup is the same as the original file:' "$NEW_FILENAME"
exit 1
fi
# this preserves the timestamps somewhat better
if ! mv --no-clobber --no-copy -- "$ORIGINAL" "$NEW_FILENAME"
then
errecho 'Failed to backup the file: ' "$ORIGINAL" "$NEW_FILENAME"
exit 1
fi
if ! cp -- "$NEW_FILENAME" "$ORIGINAL"
then
errecho 'Failed to backup the file: ' "$ORIGINAL" "$NEW_FILENAME"
mv --no-clobber --no-copy -- "$NEW_FILENAME" "$ORIGINAL"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment