Bash脚本中变量无法传递给rsync的问题排查
问题:rsync脚本中输入~作为路径变量时失效的原因
我写了一个用rsync复制文件的简单bash脚本:
#! /bin/bash read -p "enter src: " sdir read -p "enter target: " tdir read -p "enter name of log file: " lfile current_date=$(date +%Y-%m-%d) rsync_options="-avb --exclude Documents --exclude Desktop --exclude Music --exclude Downloads --exclude Videos --exclude Backgrounds --exclude Pictures --exclude .cache --exclude .cabal --backup-dir $tdir/current_date --delete" $(which rsync) $rsync_options $sdir $tdir/$current_date.$lfile >> /home/paul/Documents/logs/backup_$current_date.$lfile
运行脚本时出现以下错误:
paul on arch ~ took 8s 10:07 ❯ backup1.sh enter src: ~/ enter target: /run/media/paul/7b266343-ee12-4e6b-bff5-77307f299ba0/Vostro/ enter name of log file: home rsync: [sender] change_dir "/home/paul/~" failed: No such file or directory (2) rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.3.0]
直接把脚本里的$sdir替换成实际路径~/时,脚本能正常运行,但用变量传递就不行,其他变量却没问题。我试了在多个目录运行脚本,情况都一样。之后我改写了脚本,这个版本能正常运行:
#! /bin/bash if [ $# -ne 3 ] then echo "Usage: backup.sh <source_directory> <target_directory>" echo "Please try again." exit 1 fi if! command -v rsync > /dev/null 2>&1 then echo "This script requires rsync" exit 2 fi current_date=$(date +%Y-%m-%d) rsync_options="-avb --exclude Documents --exclude Desktop --exclude Music --exclude Downloads --exclude Videos --exclude Backgrounds --exclude Pictures --exclude .cache --exclude .cabal --backup-dir $2/current_date --delete" $(which rsync) $rsync_options $1 $2/$current_date.$3 >> /home/paul/Documents/logs/backup_$current_date.$3.log # run this script doing something like: backup.sh /home/paul /run/media/paul/7b266343-ee12-4e6b-bff5-77307f299ba0 paul
原因分析
问题核心在于波浪号~的解析时机:
- 当你在命令行直接输入
~/或者在脚本里硬写~/时,bash会在执行命令前自动把~解析为当前用户的家目录(即/home/paul/)。 - 但通过
read命令读取输入的~/时,~会被当作普通字符存储在变量sdir中,bash不会对变量内的~做二次解析。因此rsync收到的路径是~/,它会将其视为当前工作目录下的子目录~去查找,自然会出现找不到目录的报错。
而修改后的脚本用位置参数$1传递路径时,如果你在命令行输入~/作为参数,bash会在调用脚本前就完成~的解析,$1拿到的是已经转换好的/home/paul/,因此rsync能正常识别路径。
如果想保留交互式输入的版本,可以手动对变量中的~做解析,比如添加以下代码:
read -p "enter src: " sdir # 解析变量中的~为家目录路径 sdir=$(eval echo "$sdir")
这样用户输入~/时,eval会触发bash对字符串的解析,将~替换为实际家目录路径,变量sdir就能得到正确的路径值。
内容的提问来源于stack exchange,提问作者Matematleta
相关产品推荐
相关产品推荐

