首页 > 解决方案 > 使用记录数和特定命名约定压缩文件的 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 等。

标签: linuxbashshellunixsh

解决方案


这在 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。)-0to 的参数xargs是启用此功能的 GNU 扩展。该-r论点只是说如果没有输入(即目录中没有文件;可能shopt -s nullglob也是如此),则不做任何事情。

-n 200说一次最多限制输入 200 个文件,然后我们将这 200 个或更少的文件名传递给脚本sh -c

...它接收我们要创建的 zip 文件的基本名称$0(这只是一个技巧,以避免必须shift从它接收的参数列表中分离出一个参数;第一个参数sh -c通常是未使用的,所以我们使用走私这个值)。它使用一个简单的for循环来查找具有此前缀的第一个未使用的名称,第一个使用空字符串。

xxx.zip(也许改变这个 - 我认为你提出的约定有点令人困惑。如果集合中只有一个文件,我宁愿只拥有一个文件,当有多个文件时,我更愿意使用 , 等。xxx1.zipxxx2.zip

一旦我们确定了文件名,我们只需zip将我们作为参数接收的文件放入该文件中。

xargs负责将输入文件集分割成所需大小的块,并根据需要sh -c多次调用脚本。

一开始这可能有点吓人。在像 Python 这样的现代脚本语言中,这会更容易一些。


推荐阅读