-
Notifications
You must be signed in to change notification settings - Fork 34
/
ssh-dump
executable file
·75 lines (65 loc) · 2.44 KB
/
ssh-dump
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/bin/bash
set -eo pipefail
export LC_ALL=C
err=0
ssh_cmd=( /export terse )
suff_fmt='%Y-%m-%d_%H%M%S'
n=9
rm_empty=t
usage() {
bin=$(basename $0)
echo >&2 "Usage: $bin [-x] [-e] [-n N] [-s suffix-fmt] dst-file user@host [command...]"
echo >&2
echo >&2 "Run command on a remote host non-interactively, dumping output to dst-file."
echo >&2
echo >&2 "dst-file is rotated (N copies preserved) and can have compression-type suffix."
echo >&2 "E.g. using backup.gz will use gzip to compress it, same with xz, lz4, zst."
echo >&2 "Empty resulting files are silently dropped, unless -e option is specified."
echo >&2 "Rotated files will have date/time suffix, use -n 0 to disable rotation entirely."
echo >&2
echo >&2 "Default command (if not specified): ${ssh_cmd[@]}"
echo >&2 "Default rotation count (N, 0 - disable): $n"
echo >&2 "Default date/time suffix format (used for alphanumeric sort): $suff_fmt"
exit ${1:-0}
}
while getopts ":hn:s:xe" opt
do case "$opt" in
h) usage 0 ;;
n) [[ $OPTARG =~ ^[0-9]+$ ]] && n=$OPTARG \
|| { echo >&2 -e "ERROR: Invalid -n value: $OPTARG\n"; usage 1; } ;;
s) suff_fmt=$OPTARG ;;
e) rm_empty= ;;
x) set -x ;;
*) { echo >&2 -e "ERROR: Invalid option: -$OPTARG\n"; usage 1; } ;;
esac; done
shift $((OPTIND-1))
[[ $# -ge 2 ]] || usage 1
file=$1 host=$2
shift; shift
[[ $# -le 0 ]] || ssh_cmd=( "$@" )
ssh_opts=(
-nT -oControlPath=none -oControlMaster=no
-oConnectTimeout=180 -oServerAliveInterval=6 -oServerAliveCountMax=10
-oBatchMode=yes -oPasswordAuthentication=no -oNumberOfPasswordPrompts=0 )
[[ "${file%.tmp}" = "$file" ]] || { echo >&2 "ERROR: dst filename cannot end with .tmp"; exit 1; }
file_dst="$file.$(date +"$suff_fmt")" file_tmp="$file_dst".tmp
ssh "${ssh_opts[@]}" "$host" -- "${ssh_cmd[@]}" >"$file_tmp" \
|| { rm -f "$file_tmp"; echo >&2 "ERROR: ssh dump command failed"; exit 1; }
[[ -n "$rm_empty" && ! -s "$file_tmp" ]] || {
declare -A compress_opts=( [gz]=gzip [xz]=xz [lz4]=lz4 [zst]=zstd )
compress=${compress_opts[${file##*.}]}
if [[ -z "$compress" ]]
then mv "$file_tmp" "$file_dst"
else "$compress" <"$file_tmp" >"$file_dst" || { mv "$file_tmp" "$file_dst"
echo >&2 "ERROR: failed to compress resulting dump-file using $compress"; err=1; }
fi; }
rm -f "$file_tmp"
[[ $n -le 0 ]] || {
readarray -t files < <(ls -1 "$file".* | sort)
n_files=${#files[@]}
for p in "${files[@]}"; do
[[ $n_files -gt $n ]] || break
rm -f "$p"
[[ "${p%.tmp}" != "$p" ]] || (( n_files -= 1 ))
done; }
exit $err