如何在Shell/Bash脚本中用变量压缩多后缀文件?
问题根源:Shell扩展时机的坑
你碰到的这个问题本质是Shell的扩展顺序在搞鬼:当你把带{*.json,*.yaml,*.db}的字符串赋值给变量时,Shell不会自动解析这个大括号扩展和通配符,只会把它原封不动地当成一个单一的字符串参数传给tar——而这个字面路径显然不存在,所以报错。但直接写在tar命令里时,Shell会先完成大括号扩展和文件名匹配,把它转换成所有符合条件的文件路径列表,再传给tar,所以第二条命令能正常工作。
解决方法(按推荐程度排序)
1. 使用Bash数组(最安全,推荐)
Bash数组能在赋值时就完成大括号和通配符的扩展,并且能正确处理带空格的文件名(这是字符串变量做不到的)。修改后的脚本如下:
#!/bin/bash BackupPath="/root/upfOps/te" # 简化判断目录的写法 [ ! -d "$BackupPath" ] && mkdir -p "$BackupPath" # 用数组存储所有匹配的文件,赋值时自动完成扩展 MYSC_FILES=(/home/mysc/cmin02sms/etc/{*.json,*.yaml,*.db}) # 用数组展开语法传给tar,确保每个文件都是单独的参数 tar vczf "$BackupPath/t1.tar.gz" "${MYSC_FILES[@]}"
这里的"${MYSC_FILES[@]}"是关键:它会把数组里的每个元素作为独立的参数传递,即使文件名包含空格、特殊字符也不会出错。
2. 用eval强制扩展(不推荐,仅当路径绝对安全时用)
如果你坚持要用字符串变量,可以用eval命令强制Shell重新解析变量里的扩展语法。但要注意:如果路径里包含空格、引号、$等特殊字符,eval可能会执行意外的命令,有安全风险。示例:
#!/bin/sh BackupPath="/root/upfOps/te" if [ ! -d "$BackupPath" ]; then mkdir -p "$BackupPath" fi MYSC_JSON_YAML_DB="/home/mysc/cmin02sms/etc/{*.json,*.yaml,*.db}" # 用eval让Shell重新解析变量中的扩展语法 eval tar vczf "$BackupPath/t1.tar.gz" "$MYSC_JSON_YAML_DB"
3. 函数封装扩展(仅作参考)
你也可以用函数来返回扩展后的路径列表,但同样存在处理特殊文件名的问题,不如数组靠谱:
#!/bin/bash BackupPath="/root/upfOps/te" [ ! -d "$BackupPath" ] && mkdir -p "$BackupPath" # 函数返回匹配的文件路径 get_mysc_targets() { echo /home/mysc/cmin02sms/etc/{*.json,*.yaml,*.db} } # 命令替换获取扩展后的路径 tar vczf "$BackupPath/t1.tar.gz" $(get_mysc_targets)
总结
优先选择Bash数组的方案,它是处理这类多文件路径场景最稳妥的方式,能避免各种边界问题。eval尽量少用,除非你完全确定路径里没有任何危险字符。
内容的提问来源于stack exchange,提问作者sleepy2con
相关产品推荐
相关产品推荐

