linux - 使用记录数和特定命名约定压缩文件的 shell 脚本
问题描述
我们想压缩目录中的文件,其中包含记录数以及目录命名约定以遵循 zip 文件。
例如:我们有两个带有日期名称的目录(2021-10-01、2021-10-02,每个父目录都包含带有国家名称的子目录,这个国家目录包含文件数。
2021-10-01/USA, 2021-10-01/UK
2021-10-02/USA, 2021-10-02/USA
我们想压缩记录数量有限的国家/地区目录,并且 zip 文件应命名为parentdirectory_Countrydirectory.zip(2021-10-01_USA.zip)
.
我的脚本接受日期作为参数,并将其传递给 sql 查询,该查询将提取具有日期父目录结构的数据,数据中包含国家子目录和来自 DB 的文件,但我只是在这里跳过脚本的 sql 查询部分.
#!/bin/bash
startd=$1
endd=$2
compress () {
startd=$(date -d $startd +%Y%m%d)
endd=$(date -d $endd +%Y%m%d)
while [[ $startd -le $endd ]]
do
tempdate=$(date -d $startd +"%Y-%m-%d")
dirl+=" $tempdate"
startd=$(date -d"$startd + 1 day" +"%Y%m%d")
done
echo $dirl
for j in $dirl
do
if [ -d "$j" ]; then
cd $j
for d in *
do
zip ${j}_${d}.zip $d
mv ${j}_${d}.zip ../
done
else
echo "no data extracted on: $j"
fi
cd ..
done
}
我想压缩具有记录数量限制的文件,名称可以是 parentdirectory_subdirectory1.zip,并以相同的命名约定增加数量。
注意:记录数是指由 sql 查询提取的子目录中的文件,美国子目录可能包含数千个文件,所以我想将 zip 与子目录文件(如 200 个文件)拆分,然后创建具有相同命名约定的文件比如 2021-10-01_USA.zip 2021-10-01_USA1.zip 等。
解决方案
这在 Bash 中有点棘手,但您可以使用 egxargs
方便地将一长串项目拆分为可管理的块。接下来的挑战是为每个 zip 文件传入一个新的文件名。这是一个快速而肮脏的尝试。
compress () {
local startd=$(date -d "$1" +%Y%m%d)
local endd=$(date -d "$2" +%Y%m%d)
local mm
local j
local d
while [[ $startd -le $endd ]]
do
mm=${startd#??}
j="${startd%????}-${mm%??}-${mm#??}")
startd=$(date -d"$startd + 1 day" +"%Y%m%d")
if [ -d "$j" ]; then
for d in "$j"/*/; do
printf '%s\0' "$j"/"$d"/* |
xargs -r -0 -n 200 sh -c '
for ((i=0; i<=99; i++)); do
test -e "$0${i#0}.zip" || break
done
zip -j "$0${i#0}.zip" "$@"' ../"${j}_${d}"
done
else
echo "$0: no data extracted on: $j" >&2
fi
done
}
随机观察:
- 请尽量使用标准缩进;空白的随机变化让读者感到困惑,可能你自己也会感到困惑。
- 调用时应将参数传递给函数,而不是存储在全局变量中。
- 随机引用修复;另请参阅何时应该在 shell 变量周围加上引号?
对我们要循环的日期使用数组实际上,只是将日期一个一个处理然后忘记它们,而不是先将它们单独收集到内存中。- 与其再次调用以在数组中插入 yyyy-mm-dd 格式的破折号,不如
date
使用一系列参数扩展。这在代码方面有点乏味,但避免了调用外部进程来执行 shell 可以通过内部设施更快地完成的事情 - 直接在父目录中创建 zip 文件,而不是在完成后移动它们
- 我们使用
zip -j
从输入文件中删除目录名称,这样我们就不必cd
进入和退出每个目录。(如果您有目录符号链接,这有点容易出错。) - 将错误消息发送到标准错误
>&2
,并在消息本身中包含创建消息的脚本的名称。
真正的肉在稍微复杂的xargs
调用中。
我们printf
将文件名压缩为空分隔项,以便我们可以正确处理任意文件名。(有关详细信息,请参阅http://mywiki.wooledge.org/BashFAQ/020。)-0
to 的参数xargs
是启用此功能的 GNU 扩展。该-r
论点只是说如果没有输入(即目录中没有文件;可能shopt -s nullglob
也是如此),则不做任何事情。
-n 200
说一次最多限制输入 200 个文件,然后我们将这 200 个或更少的文件名传递给脚本sh -c
。
...它接收我们要创建的 zip 文件的基本名称$0
(这只是一个技巧,以避免必须shift
从它接收的参数列表中分离出一个参数;第一个参数sh -c
通常是未使用的,所以我们使用走私这个值)。它使用一个简单的for
循环来查找具有此前缀的第一个未使用的名称,第一个使用空字符串。
xxx.zip
(也许改变这个 - 我认为你提出的约定有点令人困惑。如果集合中只有一个文件,我宁愿只拥有一个文件,当有多个文件时,我更愿意使用 , 等。xxx1.zip
)xxx2.zip
一旦我们确定了文件名,我们只需zip
将我们作为参数接收的文件放入该文件中。
xargs
负责将输入文件集分割成所需大小的块,并根据需要sh -c
多次调用脚本。
一开始这可能有点吓人。在像 Python 这样的现代脚本语言中,这会更容易一些。
推荐阅读
- flutter - 如何在顶部附加新的 ListView 项目
- svelte - 历史推送状态功能后,Sapper后退按钮不起作用
- acumatica - 如何自定义费用报销屏幕上的审批按钮
- jenkins - 如何将值从 Jenkins 函数返回到构建阶段?
- excel - 从用户窗体的列表框中删除选定的行
- node.js - Invariant Violation:对象在 React App 中作为 React 子项无效
- compiler-errors - 编译 C 代码时编译器中的段错误
- javascript - Javascript calculate sum unless it is less than zero
- flutter - 在有状态小部件的状态下创建一个对象,该对象依赖于提供者
- google-cloud-automl - 如何获取谷歌 AutoML 模型系数