使用 zip/unzip 可以压缩/解压zip格式的压缩包,类似的还有 tar ,gunzip, bunzip2等命令。
而如果一个压缩包内含有很多文件,而我们只需要解压一部分我们所需要的文件出来,此时我们不难想到使用正则匹配对应文件的文件名然后将其提取出来。(
遗憾的是,unzip不支持使用正则匹配文件,不过我们可以通过其他linux命名组合来完成这一过程。
使用zip/unzip
为了测试,这里我们先介绍一下dd命令,用于生成我们的测试文件。
dd 命令
dd 是device driver 的缩写, 复制文件并对原文件的内容进行转换和格式化处理。dd - 复制文件并对原文件的内容进行转换和格式化处理
通常可以使用它创建一些文件,例如:
# 生成1M大小文件# bs 代表字节为单位的块大小。count 代表被复制的块数。# if 代表输入文件。如果不指定if,默认就会从stdin中读取输入。# of 代表输出文件。如果不指定of,默认就会将stdout作为默认输出。# /dev/zero 是一个字符设备,会不断返回0值字节(\0)。dd if=/dev/zero of=sun.txt bs=1M count=1
使用dd命令生成指定大小的文件
将压缩包中符合要求的文件提取出来
首先,需要准备一些文件,这里为了测试方便写了一个生成文件的脚本
#!/bin/bashprofix='abcd'suffix='xyz'for i in {1..20} dofile_name=${profix}_${i}# 如果是奇数则加上后缀 if [ $(($RANDOM%2)) -eq 1 ];then file_name=${file_name}_${suffix}fi# echo ${file_name}dd if=/dev/zero of=./${file_name} bs=1k count=$[RANDOM%10+1] 2>> log done
运行脚本后,可以发现已经生成20个文件了。区别为中间的序号,与有无后缀。
使用 zip 命令压缩文件。命令zip 压缩包名 文件列表
可以通过 unzip -v 命令查看压缩包内文件。
可以发现unzip自带一部分通配选项,例如解压指定后缀 xyz 结尾的文件
unzip的通配选项只有*
,/
,?
这几种,因此对于以上文件,我们只能用通配符区分【有后缀xyz】与【无后缀xyz】这两种文件。
可以发现,虽然unzip自带通配选项,但却不太符合我们特定场景的使用。
测试unzip的通配符匹配
基于以上文件,我们将其文件名做一些改变,其中包含:
电话号码、IP地址、日期、身份证号、邮箱、域名
unzip解压的时候可以选择可解压的文件列表(-x 可以指定不处理的文件列表),并且支持一部分通配符(等同于shell中支持的通配样式),但是不支持正则。
# man unzip"*.c" matches "foo.c" but not "mydir/foo.c""**.c" matches both "foo.c" and "mydir/foo.c""*/*.c" matches "bar/foo.c" but not "baz/bar/foo.c""??*/*" matches "ab/foo" and "abc/foo"but not "a/foo" or "a/b/foo"
即:*
代表0个或多个任意字符,/ 可用于匹配目录(且只能匹配一层),?匹配一个字符(??* 表示至少长度为2的文件名) 。
由于无法使用正则,我们只能使用自带的通配符解压上述几种特殊文件。首先对于邮箱我们可以指定后缀,例如*@*.com
而对于电话号码,或者身份证号,IP地址等只能通过长度限定,由于不能限定字符为数字或字母,匹配的结果是不精确的。
例如对于日期和IP地址,由于我们的测试样例不多,因此提取结果是正确的。
而对于我们要提取的域名来说,他和邮件存在相同的 .com 后缀。而域名的特征使用通配符表示为 *.com 。可以看到在提取的结果中,并不只有域名,还有邮箱。
使用正则表达式
使用正则提取压缩包内指定文件
通过 unzip 自带的通配符我们可以对一些简单的文件进行过滤提取,而对于一些比较复杂的就不行了。
因此,我们需要借助一些其他命令来实现正则匹配提取。
首先,-l 参数可以简要列出内部所含文件
可以看到从第4列开始,第4行至倒数第3行是文件名,我们可以借助awk工具提取出文件名。(这里的NR表示当前读取到的行号,类似的还有NF列号,FNR每打开一个文件从0计数当前的行号,在单文件中等于NR)
通过初步尝试,awk+管道+xargs 确实可以办到指定unzip要解压的文件。
使用grep过滤文件列表,unzip提取
grep通过匹配结果只包含数字的项。
通过 xargs 组合 unzip 最终达到一个匹配正则解压文件的效果。
根据我们这些文件及对应的正则,结合这个组合命令便可以针对需求提取文件了。
例如:
电话号码:\d{3}-\d{8}|\d{4}-\d{7}
下面我们将这个过程编写为一个脚本,使之执行跟方便。
脚本:通过正则式解压文件
#!/bin/bash# 默认unzip无参数opt=''if [ $# -lt 2 ]; thenecho "参数太少:至少两个参\n参考:\n"echo "\t sh unzip.sh 压缩包名 正则式 [unzip参数]"echo "\t sh unzip.sh test.zip \"^\\d+\$\" -v"elseif [ -z"$3" ]; thenopt=$3fiecho "\n执行命令:unzip -l ${1} | awk 'NR>3 {print \$4}' | grep -P '${2}' | xargs unzip ${opt} ${1}\n"echo "------------------------------------------------------------"unzip -l ${1} | awk 'NR>3 {print $4}' | grep -P ${2} | xargs unzip ${opt} ${1}fi
IP地址:^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$
通过脚本执行:
日期:^\d{4}-\d{1,2}-\d{1,2}
身份证号:^((\d{18})|([0-9x]{18})|([0-9X]{18}))$
邮箱:^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
域名+ip:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
域名:^([a-zA-Z0-9]([a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,11}$'