需要执行一个包含多个 rsync 命令的脚本,但是每次执行 rsync 命令都要输入密码,使用 AI 写了一个,只需要执行脚本,首次输入密码,之后同一轮任务内都复用同一连接。
提示词
写一个 Debian12 下运行的脚本 `job.sh`,运行 `job.sh` 输入一次密码可以执行脚本内配置的多行 rsync 完整命令,即不用每次都需要输入密码。
要求:
1. 执行脚本 `job.sh` 只需要输入一次密码,可以执行多次 rsync 命令
2. 脚本运行时间不确定,脚本中断、或者异常退出、或者 `Ctrl+C` 主动退出,就整个脚本也退出,包括但不限于连接
3. rsync 命令中输出的日志,实时在终端输出
4. 简单英文注释,禁止中文注释
5. 保持脚本简洁明了,不做过多比如命令不存在等检查——执行脚本,调用命令执行即可
伪代码如下
```bash
# 定义
REMOTE_USER="root"
REMOTE_HOST="192.168.1.115"
REMOTE_PORT="22"
# 配置
JOBS=[
rsync -e "ssh -p${REMOTE_PORT}" -avpz --exclude 'softs/*' --delete /mnt/hgfs/e/a/ ${REMOTE_USER}@${REMOTE_HOST}:/opt/backup/a/
rsync -e "ssh -p${REMOTE_PORT}" -avpz --exclude 'softs/*' --delete /mnt/hgfs/e/b/ ${REMOTE_USER}@${REMOTE_HOST}:/opt/backup/b/
]
# 执行逻辑
输出 任务开始
执行 JOBS[0]
执行 JOBS[1]
输出 任务完成
```
写完整的脚本,复制可使用
最终版本
使用 gpt 输入提示词得到的结果基本可以实现,根据需要小修改,已经满足需求。
JOBS
的配置直接写完整参数的,写法感觉累赘,继续优化写成一个函数,最终用的以下完整代码:
#!/usr/bin/env bash
set -euo pipefail
# Config
REMOTE_USER="root"
REMOTE_HOST="192.168.1.115"
REMOTE_PORT="22"
# SSH master connection
CONTROL_PATH="${HOME}/.ssh/cm-${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
SSH_OPTS=(
-p "${REMOTE_PORT}"
-o ControlMaster=auto
-o ControlPersist=60m
-o ControlPath="${CONTROL_PATH}"
-o StrictHostKeyChecking=accept-new
-o ServerAliveInterval=20
-o ServerAliveCountMax=3
)
# Reuse the same SSH master connection
RSYNC_SSH=(ssh "${SSH_OPTS[@]}")
# Factor common rsync options
RSYNC_BASE="rsync -e \"${RSYNC_SSH[*]}\" -avpz --delete --timeout=600"
# helpers
# 把每个参数目录转成:
# --include='dir/' --include='dir/***'
# 用 printf %q 做安全转义(空格、通配符都可)
make_inc() {
local d
for d in "$@"; do
printf -- "--include=%q --include=%q " "$d/" "$d/***"
done
}
# jobs
# 说明:
# 1) $(make_inc fastadmin) 会在数组定义时展开成
# --include='fastadmin/' --include='fastadmin/***'
# 2) 你可以一次写多个目录:$(make_inc fastadmin uploads assets)
JOBS=(
"${RSYNC_BASE} $(make_inc python) --exclude='*' \
/mnt/hgfs/e/workspace/ \"${REMOTE_USER}@${REMOTE_HOST}:/opt/backup/workspace/\""
"${RSYNC_BASE} $(make_inc fastadmin) --exclude='*' \
/mnt/hgfs/e/www/ \"${REMOTE_USER}@${REMOTE_HOST}:/opt/backup/www/\""
)
cleanup() {
echo "cleanup"
ssh -O exit "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" >/dev/null 2>&1 || true
}
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM
trap 'cleanup' EXIT
echo "SSH connection to ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PORT}"
ssh -fN "${SSH_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}"
echo "++++++++++++++++ Tasks Start ++++++++++++++++"
for i in "${!JOBS[@]}"; do
echo -e "\n///////////////////// $((i+1))"
# rsync -e "ssh -p 22 -o ControlMaster=auto -o ControlPersist=60m -o ControlPath=/root/.ssh/[email protected]:22 -o StrictHostKeyChecking=accept-new -o ServerAliveInterval=20 -o ServerAliveCountMax=3" -avpz --delete --include='python/' --include='python/***' --exclude='*' /mnt/hgfs/e/workspace/ "[email protected]:/opt/backup/workspace/"
# echo "${JOBS[$i]}"
# sleep 3
# Use bash -lc instead of eval to execute the full command line safely
bash -lc "${JOBS[$i]}"
done
echo "++++++++++++++++ Tasks Done ++++++++++++++++"