归档包含文件通配符问题:如何让Zip支持通配符匹配?
我之前也被这个问题卡过——rsync的--include-from能直接解析通配符,比如+ .*rc就能匹配主目录下所有rc开头的配置文件,但zip的包含文件功能(用-@参数读取文件名列表)却完全不处理通配符,得手动把每个文件名列出来,确实有点反直觉。这其实不算bug,是两个工具的设计逻辑不同:rsync自己实现了通配符解析,而zip的-@只是单纯读取每行作为文件名,不依赖shell来展开。
不过我们可以借助shell的能力来绕开这个限制,下面是几个实用的解决方案:
方案1:用Shell循环逐行展开通配符
如果你的包含文件里的通配符都是简单的(比如.*rc、*.txt这种),可以直接用while循环让shell帮你展开每个模式,再传给zip:
# 假设你的包含文件名叫inclusion-list while read -r pattern; do zip backup.zip "$pattern" done < inclusion-list
注意:如果文件名里包含空格或者特殊字符,一定要加上双引号
"$pattern",避免shell把文件名拆成多个参数。如果需要匹配子目录里的文件,可以先开启globstar选项(shopt -s globstar),这样就能用**/*.txt这种递归通配符了。
方案2:结合find命令处理复杂通配符
如果你的通配符模式比较复杂(比如需要排除某些目录,或者匹配特定类型的文件),用find命令来匹配会更可靠:
# 把包含文件里的每行转换成find的-name参数 find . -type f \( $(sed 's/^/-name "/; s/$/" -o /' inclusion-list | sed '$ s/ -o $//') \) -print0 | xargs -0 zip backup.zip
解释一下这个命令:
sed把包含文件里的每行(比如.*rc)转换成-name ".*rc",然后用-o连接所有条件-print0和xargs -0是为了处理带空格、换行符的特殊文件名,避免出错-type f确保只匹配文件,如果你需要包含目录,可以去掉这个参数
方案3:预先生成展开后的文件名列表
如果你想保留zip的-@参数用法,可以先把通配符展开成实际的文件名,写入一个临时文件,再让zip读取这个临时文件:
# 开启globstar支持递归匹配(可选) shopt -s globstar # 生成展开后的文件名列表 > temp-file-list while read -r pattern; do ls -1d "$pattern" >> temp-file-list done < inclusion-list # 用zip读取临时文件 zip backup.zip -@ < temp-file-list # 清理临时文件 rm temp-file-list
这个方案的好处是你可以先检查temp-file-list里的文件名是否正确,再执行zip命令,避免误操作。
总结
zip本身不支持解析包含文件里的通配符,是因为它把-@读取的每行都当作字面量文件名。我们的核心思路就是让shell或者find命令先帮我们把通配符展开成实际文件名,再传给zip。根据你的场景选择合适的方法就行,我个人更推荐方案2,尤其是处理复杂匹配规则的时候。
内容的提问来源于stack exchange,提问作者forthrin

