diff options
| -rwxr-xr-x | alt-prune-rclone-rm.sh | 73 | ||||
| -rwxr-xr-x | backomp | 13 |
2 files changed, 86 insertions, 0 deletions
diff --git a/alt-prune-rclone-rm.sh b/alt-prune-rclone-rm.sh new file mode 100755 index 0000000..6e3eeb2 --- /dev/null +++ b/alt-prune-rclone-rm.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# Copyright 2023 David Vazgenovich Shakaryan + +qargs() { + local arg str + + for arg; do + [[ "${arg:-*}" =~ [^A-Za-z0-9%+./:=@_-] ]] && arg="${arg@Q}" + str+=" ${arg}" + done + printf '%s\n' "${str:1}" +} + +target="${1}" + +[[ "${target}" =~ ^(rclone://([^:]+):)(.+) ]] || exit 1 +prefix="${BASH_REMATCH[1]}" +remote="${BASH_REMATCH[2]}" +path="${BASH_REMATCH[3]}" + +conf="$(rclone config dump)" || exit 1 +read -rd '' JQ <<'EOF' +.[$k] +| select(.type == "crypt" or .type == "sftp") // error("not crypt or sftp") +| [.type, .remote] +| @tsv +EOF +IFS=$'\t' read -r r_type r_remote < <( + jq -rc --arg k "${remote}" "${JQ}" <<<"${conf}") || exit 1 + +if [[ "${r_type}" == 'crypt' ]]; then + crypt="${remote}" + + [[ "${r_remote}" =~ ^([^:]+):(.*) ]] || exit 1 + remote="${BASH_REMATCH[1]}" + base="${BASH_REMATCH[2]:+${BASH_REMATCH[2]}/}" + + upath=0 + while IFS=$'\t' read -r k v; do + [[ "${k% }" == "${path}" ]] && path="${base}${v# }" && upath=1 + done < <(rclone cryptdecode --reverse "${crypt}:" "${path}") + wait "$!" || exit 1 + ((upath)) || exit 1 +fi + +read -rd '' JQ <<'EOF' +.[$k] +| select(.type == "sftp") // error("not sftp") +| select(.key_file_pass | not) // error("key uses passphrase") +| [ + (.key_file | select(.) // error("missing key") + | "-i", (sub("^~"; env.HOME) | @sh)), + (.known_hosts_file | select(.) + | "-o", ("UserKnownHostsFile=\(sub("^~"; env.HOME))" | @sh)), + (.port | select(.) | "-p", @sh), + ("\(.user // error("missing user"))@\(.host // error("missing host"))" + | @sh) +] +| join(" ") +EOF +mapfile -td '' args < <( + jq -rc --arg k "${remote}" "${JQ}" <<<"${conf}" | \ + xargs -r printf '%s\0' + [[ "${PIPESTATUS[0]}" -eq 0 && "${PIPESTATUS[1]}" -eq 0 ]]) +wait "$!" || exit 1 + +ssh_cmd=(ssh -o BatchMode=yes "${args[@]}") +cmd=("${ssh_cmd[@]}" + "rm -rf $(printf '%q' "${path}")") +echo "* $(qargs "${cmd[@]}")" +"${cmd[@]}" || exit 1 +exit 0 @@ -348,6 +348,18 @@ prune() { else if ((DRY)); then echo "* DRY: prune ${__snaps[__i]}" + elif [[ -n "${opts['alt_prune']}" ]]; then + echo "* alt-prune ${__snaps[__i]}" + bash -c "$(declare -p opts \ + ); target=$(printf '%q' \ + "${__snaps[__i]}" \ + ); ${opts['alt_prune']}" < /dev/null + res="$?" + if [[ "${res}" -ne 0 ]]; then + err 'alt-prune command failed' \ + "(${res}):" \ + "${opts['alt_prune']}" + fi elif [[ "${__snaps[__i]}" == rclone://* ]]; then run rclone purge -- "${__snaps[__i]#*//}" elif [[ "${__snaps[__i]}" =~ ^([^:/]*):(.*) ]]; then @@ -419,6 +431,7 @@ declare -A def_opts=( ['rclone_copy_command']='copy' ['rclone_use_copy_dest']=1 ['pre_copy']= + ['alt_prune']= ) declare -A opts tainted_opts reset_opts() { |
