我有一个包含几个子目录的目录。有关于压缩文件的问题其中包含我根据自己的需要稍微修改过的答案。
for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done
然而,我遇到了一个奇怪的问题。对于不存在的第一组文件夹zips/<name>.zip
,我收到此错误:
zip error: Nothing to do! (zips/2014-10.zip)
zip warning: name not matched: 2014-11/*.csv
然而当我只是echo
zip 语句时:
for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
然后运行回显命令 ( zip zips/2014-10.zip 2014-10/*.csv
),它工作正常并压缩文件夹。那么有趣的部分是原始命令的后续运行将要实际上可以压缩第一次不起作用的文件夹!
要自己测试此行为:
cd /tmp
mkdir -p 2016-01 2016-02 2016-03 zips
for i in 2*/; do touch "$i"/one.csv; done
for i in 2*/; do touch "$i"/two.csv; done
zip zips/2016-03.zip 2016-03/*.csv
for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
您将看到 echo 打印这些语句:
zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv
然而,实际的 zip 命令会告诉你:
zip warning: name not matched: 2016-01/*.csv
zip error: Nothing to do! (zips/2016-01.zip)
zip warning: name not matched: 2016-02/*.csv
zip error: Nothing to do! (zips/2016-02.zip)
updating: 2016-03/one.csv (stored 0%)
updating: 2016-03/two.csv (stored 0%)
.csv
所以它实际上是用zip 文件所在的 s更新 zip 文件,但是不是创建 zip 文件时。如果您复制其中一个 zip 命令:
$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)
然后重新运行 zip-all-the-things:
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done
您会看到它针对2016-02
和进行了更新2016-03
。这是我的输出tree
:
.
├── 2016-01
│ ├── one.csv
│ └── two.csv
├── 2016-02
│ ├── one.csv
│ └── two.csv
├── 2016-03
│ ├── one.csv
│ └── two.csv
└── zips
├── 2016-02.zip
└── 2016-03.zip
另外,(不)令人惊讶的是,这工作得很好:
zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"
我在这里做错了什么? (注意,我使用 zsh 而不是 bash,如果这有什么区别的话)
答案1
通过外壳进行扩展
周围的引号"$i*.csv"
有所不同。使用引号,shell 将该字符串扩展为“2014-11/*.csv”。该确切文件不存在,并zip
报告错误。如果没有引号,*
也会扩展(通过文件名扩展/“globbing”),并且生成的zip
命令是匹配文件的完整列表,每个文件作为单独的参数。您可以通过以下方式在循环内获得第二个行为for
:
for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done
通过 zip 扩展
zip
也可以为自身扩展通配符,但并非在所有情况下都可以。来自拉链手册:
这压缩程序可以对中的名称进行相同的匹配压缩存档被修改,或者,如果是-X(排除)或-我(include) 选项,在要操作的文件列表上,通过使用反斜杠或引号告诉 shell 不要进行名称扩展。
在成功创建存档后,原始命令将在后续尝试中起作用,因为zip
它会尝试将通配符与现有存档的内容进行匹配。它们存在于那里,并且仍然存在于文件系统上,因此它们以updating:
.
要得到zip
通配符时创造存档,使用-r
(recurse) 选项递归到请求的目录,并-i
(include) 将其限制为与模式匹配的文件:
for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done