Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- trim_string() {
- : "${1#"${1%%[![:space:]]*}"}"
- : "${_%"${_##*[![:space:]]}"}"
- printf '%s\n' "$_"
- }
- pathOrCmd() {
- if [[ "$*" =~ ^[[:blank:]]*\[(.+)\].*$ ]]; then
- echo $(eval "${BASH_REMATCH[1]}" | xargs)
- else
- echo "$*"
- fi
- }
- chownSudo() {
- if [ -n "$SUDO_USER" ]; then
- chown "${SUDO_USER}:${SUDO_USER}" "$*"
- fi
- }
- expandGlobs() {
- shopt -s nullglob
- eval printf "\"%s\n\"" "$*"
- shopt -u nullglob
- }
- printCSI() {
- csi="$1"
- shift
- printf "\e[${csi}%s\e[0m" "$*"
- }
- printRed() { printCSI '91m' "$*"; }
- printYellow() { printCSI '93m' "$*"; }
- printGreen() { printCSI '92m' "$*"; }
- printMagenta() { printCSI '95m' "$*"; }
- printDetail() {
- printGreen "$1"
- shift
- echo " $*"
- }
- printWarning() { printYellow "$*"; echo ""; }
- printError() { printError "$*"; echo ""; }
- printEv() {
- eval printf '"%s"' "\${$1+$1}\ ";
- }
- exampleProfileText="BackupName #first line, mandatory, bash-evaluated
- BackupSourceDir #optional (default is current dir), bash-evaluated
- Flags #optional
- # t - add timestamp
- # e - elevate with sudo and chmod with current user
- # s - follow symlinks
- # i - incremental [HARD-CODED-DISABLED]
- # optional blank lines
- # if you didn't noticed yet, this is a comment
- #include paths
- .config/program #literal path
- [find . -type f -iname '*.txt'] #command output (always automatically xarg-ed)
- # blank lines
- #optional exclude paths
- ./config/*.txt #path with wildcards (tar default behavior)
- [echo '.*'] #command output (always automatically xarg-ed)
- "
- # PARSE ARGS
- action="gen"
- dryrun=0
- modes=('' 1 '' '')
- POSITIONAL=()
- while [[ $# -gt 0 ]]; do
- key="$1"
- case "$key" in
- -h|--help)
- cat << EOF
- Backup generator script v1 - Microeinstein
- Generate backups by pre-made profiles
- Usage: $0 [options] [PROFILE]
- Options:
- -h --help Show this help message.
- -d --dry-run Do not execute the final command.
- -r --remove Remove any backup of this profile.
- -e --example Dump the example in "./example.profile"
- (ignores dry-run mode)
- +s -+symlinks (Force-mode) Follow symlinks.
- +t -+timestamp (Force-mode) Add current timestamp.
- Profile:
- A filename, otherwise <stdin> will be used.
- example.profile:
- ----------------
- $exampleProfileText
- EOF
- exit 0
- ;;
- -e|--example)
- echo "$exampleProfileText" > example.profile
- if [ $? = 0 ]; then
- echo "Example dumped."
- else
- echo "Cannot dump example, sorry."
- fi
- exit 0
- ;;
- -d|--dry-run)
- printWarning "[DryRun]"
- dryrun=1
- ;;
- -r|--remove)
- printWarning "[Remove]"
- action="rem"
- ;;
- # +i) modes[0]=1 ;;
- +s|-+symlinks) modes[1]= ;;
- +t|-+timestamp) modes[2]=1 ;;
- *)
- POSITIONAL+=("$1")
- ;;
- esac
- shift
- done
- set -- "${POSITIONAL[@]}"
- # PARSE PROFILE
- if [[ $# -eq 0 ]]; then
- echo "Reading from stdin..."
- fi
- initialDir="$PWD"
- parsepart=0
- name="unknown"
- dir="."
- include=""
- exclude=""
- while read -r line; do
- parseModes() {
- # if [[ "$1" == *i* ]]; then modes[0]=1; fi
- if [[ "$1" == *s* ]]; then modes[1]=; fi
- if [[ "$1" == *t* ]]; then modes[2]=1; fi
- if [[ "$1" == *e* ]]; then modes[3]=1; fi
- printDetail "Modes:" $({ \
- printf '%s ' "${modes[0]:+incremental}"; \
- printf '%s ' "${modes[1]:+keepSymlinks}"; \
- printf '%s ' "${modes[2]:+addTimestamp}"; \
- printf '%s ' "${modes[3]:+elevated}"; \
- } | xargs)
- }
- line=$(trim_string "$line")
- line=$(sed 's/#.*$//g' <<< "$line")
- case $parsepart in
- 0)
- name=$(eval echo "$line")
- printDetail "Name:" "$name"
- ((parsepart++))
- ;;
- 1)
- line=$(eval echo "$line")
- if [ -z "$line" ]; then
- printWarning "Using default directory... (unspecified)"
- printDetail "Directory:" "$dir"
- parsepart=3
- elif [ -d "$line" ]; then
- dir="$line"
- pushd "$line" >/dev/null 2>&1
- printDetail "Directory:" "$dir"
- ((parsepart++))
- else
- printWarning "Using default directory... (not a directory)"
- printDetail "Directory:" "$dir"
- parseModes "$line"
- parsepart=3
- fi
- ;;
- 2)
- parseModes "$line"
- ((parsepart++))
- ;;
- 3)
- if [[ $action == rem ]]; then
- parsepart=99
- fi
- if [ -z "$line" ]; then
- if [ -n "$include" ]; then
- ((parsepart++))
- fi
- else
- line=$(pathOrCmd "$line")
- include="${include} $line"
- printDetail "Include:" "$line"
- fi
- ;;
- 4)
- if [ -z "$line" ]; then
- if [ -n "$include" ]; then
- ((parsepart++))
- fi
- else
- line=$(pathOrCmd "$line")
- exclude="${include} $line"
- printDetail "Exclude:" "$line"
- fi
- ;;
- esac
- done < "${1:-/dev/stdin}"
- popd >/dev/null 2>&1
- echo ""
- case "$action" in
- gen)
- bkpIn=$(realpath ./backupInclude.txt)
- bkpEx=$(realpath ./backupExclude.txt)
- eval printf '"%s\n"' "$include" > "$bkpIn"
- eval printf '"%s\n"' "$exclude" > "$bkpEx"
- chownSudo "$bkpIn"
- chownSudo "$bkpEx"
- outFile="$name"
- if [ -n "${modes[0]}" ]; then
- lastIncr=$(find . -type f -iname "${name}_*.tgz" | sort -V | tail -n-1)
- num=1
- if [[ "$lastIncr" =~ _([[:digit:]]+) ]]; then
- num="${BASH_REMATCH[1]}"
- ((num++))
- fi
- outFile="${outFile}_${num}"
- fi
- if [ -n "${modes[2]}" ]; then
- ts=$(date -Iseconds | sed 's/T/_/g' | head -c-7 | xargs)
- outFile="${outFile}_${ts}"
- fi
- outFile=$(realpath "./$outFile.tgz")
- incrFile=$(realpath "./$name.gbkp")
- pushd "$dir" >/dev/null 2>&1
- args=(
- $(eval echo "\${modes[0]:+--listed-incremental='$incrFile'}")
- ${modes[1]:+--keep-directory-symlink}
- --exclude-from="\"$bkpEx\""
- --files-from="\"$bkpIn\""
- -f "\"$outFile\""
- )
- cmd="tar -czv ${args[@]}"
- ;;
- rem)
- arg=$(eval echo "${name}* backup{In,Ex}clude.txt")
- cmd="rm -f $arg"
- ;;
- esac
- if [ -n "${modes[3]}" ]; then
- cmd="sudo bash -c '${cmd} && chown \"${USER}:${USER}\" \"$outFile\"'"
- fi
- printMagenta "$cmd"; echo ""
- if [ $dryrun = 1 ]; then exit 0; fi
- printWarning "Executing..."
- eval "$cmd"
- err=$?
- if [ $err != 0 ]; then
- printError "Exit code: $err"
- exit $err
- fi
- if [ $action = gen ]; then
- chownSudo "$outFile"
- fi
- printWarning "Done."
Advertisement
Add Comment
Please, Sign In to add comment