当前位置: 首页 > 知识库问答 >
问题:

从tar存档中删除重复项

管梓
2023-03-14

我正在尝试创建多个文本文件的存档。有时这些文件会更新,当这些文件更新时,我使用 tar 中的 --update 选项将这些文件附加到存档中。

假设我们有两个文件,test1.txt和test2.txt.这些文件被添加到存档test.tar.

用焦油-tf测试检查焦油.tar

我得到的如预期:

test1.txt
test2.txt

现在,如果我更新test2.txt,并使用< code > tar-f test.tar-u test 2 . txt 将其添加到存档中。

我希望运行tar-tftest.tar的输出是:

test1.txt
test2.txt

但相反,我得到:

test1.txt
test2.txt
test2.txt

那幺,如何摇动这个tar来删除旧的test2.txt呢?我知道,在提取存档后,我只会得到两个文件的最新更改,因此在本演示中,这个问题可能看起来微不足道,但实际上我存档了数千个5000行的文件,因此在重复运行时,存档大小会变得非常大。

我目前正在做的是将文件提取到一个临时目录中,然后在每次运行脚本时重新归档。这显然是非常低效的。我希望我遗漏了一个焦油选项。

共有1个答案

慎志国
2023-03-14

TAR 只是原始文件内容的串联,其中混合了一些元数据。正如您所注意到的,更新文件只是将该文件附加到 TAR 的末尾,按照惯例,TAR 中出现的最后一个文件“获胜”。TAR 不会简单地更新文件,因为这可能意味着更新文件之后的所有文件内容可能必须移开一些字节,以便为较大的较新版本腾出空间。

实际上,这里没有提到一个TAR选项,它适合您的用例:--occurrence=[NUMBER]。使用此选项,可以指定提取或删除具有相同名称/路径的文件的多个版本中的哪个版本。对于您的简单示例,它将很好地工作。我是这样设置的:

echo foo > test1.txt
echo foo > test2.txt
tar -cf updated.tar test1.txt test2.txt
sleep 1s
echo barbara > test2.txt
tar --update -f updated.tar test1.txt test2.txt
sleep 1s
echo foobar > test2.txt
tar --update -f updated.tar test1.txt test2.txt
tar tvlf updated.tar
    -rwx------ user/group   4 2022-03-29 19:00 test1.txt
    -rwx------ user/group   4 2022-03-29 19:00 test2.txt
    -rwx------ user/group   8 2022-03-29 19:01 test2.txt
    -rwx------ user/group   7 2022-03-29 19:01 test2.txt

请注意,tar--update将只检查时间戳而不是内容,时间戳的粒度只有1s!因此,我们需要等待1s,以确保时间戳至少在1秒之后,否则tar将不会将其添加到存档中。这在复制粘贴此代码时尤其重要。

只需html" target="_blank">调用 --delete 将删除所有版本:

tar --delete -f updated.tar test2.txt
tar tvlf updated.tar
    -rwx------ user/group   4 2022-03-29 19:00 test1.txt

当指定--event=1时,只有第一次出现,即最旧的版本将被删除:

tar --delete -f updated.tar test2.txt
tar tvlf updated.tar
    -rwx------ user/group   4 2022-03-29 19:00 test1.txt
    -rwx------ user/group   8 2022-03-29 19:01 test2.txt
    -rwx------ user/group   7 2022-03-29 19:01 test2.txt

不幸的是,对于 --delete,您只能只删除一个文件版本。因此,您必须重复删除最旧的版本,直到只剩下最新的版本。可以在bash中执行此操作,这至少比将其提取到临时文件夹更节省空间,但它可能会更慢,因为它必须多次检查存档,并且每次存档基本上完全重写到位。

我建议使用我编写的ratarmount。它将挂载存档(而无需实际提取它)并公开一个文件夹视图,显示每个文件的最新版本。使用它,您可以创建新的精简存档:

python3 -m pip install --user ratarmount
ratarmount updated.tar
ls -lA updated/
    -rwx------ 1 user group 4 Mar 29 19:14 test1.txt
    -rwx------ 1 user group 7 Mar 29 19:14 test2.txt
tar -c -f most-recent.tar -C updated/ .
tar tvlf updated.tar
    drwxrwxrwx user/group   0 2022-03-29 19:00 ./
    -rwx------ user/group   4 2022-03-29 19:00 ./test1.txt
    -rwx------ user/group   7 2022-03-29 19:01 ./test2.txt

就这样。tar tvlf的输出看起来与前面的点有点不同,因为我们使用了-C并指定存档.文件夹。通常,这不会造成伤害,但您可以使用以下任何问题稍大的替代方案来规避此问题:

tar -c -f most-recent.tar -C updated/ test1.txt test2.txt
tar -c -f most-recent.tar -C updated/ $( cd updated && find . -mindepth 1 -maxdepth 1 )
( cd updated/ && tar -c -f ../most-recent.tar {[^.],.[!.],..?}*; )

如果您遇到ratarmount问题,请在此处打开一个问题。请注意,ratarmount甚至会公开那些旧版本,但在隐藏良好的特殊文件夹中:

ratarmount updated.tar
ls -lA updated/test2.txt.versions/
    -rwx------ 1 user group 4 Mar 29 20:10 1
    -rwx------ 1 user group 8 Mar 29 20:10 2
    -rwx------ 1 user group 7 Mar 29 20:10 3

特殊<code>中的文件名。文件夹的版本与<code>--出现

上面提到的带有 --的 bash 中的版本将如下所示:

function deleteAllButMostRecentInTar()
{
    local archive=$1
    local filesToDelete=$( mktemp )

    while true; do
        tar --list --file "$archive" | sort | uniq -c |
            sed -n -E '/^[ \t]*1 /d; s|^[ \t]*[0-9]+ ||p' > "$filesToDelete"
        if [[ -s "$filesToDelete" ]]; then
            local fileCount=$( cat -- "$filesToDelete" | wc -l )
            echo -n "Found $fileCount files with more than version. Deleting ..."
            tar --delete --occurrence=1 --files-from="$filesToDelete" \
                --file "$archive"
            echo " OK"
        else
            break
        fi
    done
    rm -- "$filesToDelete"
    echo
}

deleteAllButMostRecentInTar updated.tar
tar tvlf updated.tar
    -rwx------ user/group   4 2022-03-29 19:00 test1.txt
    -rwx------ user/group   7 2022-03-29 19:01 test2.txt
 类似资料:
  • 我尝试只删除 1 个项目,或者当我使用打开的库存单击它时保留项目堆栈,但是,方法:,删除库存中的所有类似项目,如何只删除 1 个项目,或 1 ?

  • 问题内容: 我有一个自定义对象的ArrayList。我要删除重复的条目。 对象具有三个字段:和。如果字幕多次出现,那么我只需要带有thats字幕的第一项(忽略带有该字幕的其余对象)。 问题答案: 您可以使用自定义Comparator将ArrayList的内容放入TreeSet中,如果两个字幕相同,则应返回0。之后,您可以将Set转换为列表,并使列表中没有“重复项”。这是对象的示例,您当然应该使用正

  • 问题内容: 数据库类型为PostGres 8.3。 如果我写了: 我有一些计数超过1的行。如何取出重复的行(我仍然希望每个行都保留1行,而不是+1行…我不想删除所有行。) 例子: 应该变成: 我找到的唯一答案是 在那里,但是我想知道是否可以在没有哈希列的情况下做到这一点。 警告 我没有具有唯一编号的PK,因此无法使用min(…)技术。PK是3个字段。 问题答案: 这是所有表都应具有主键的众多原因之

  • 问题内容: 我有一个索引,其中很多纸在同一字段中具有相同的值。在这一领域,我有一个重复数据删除技术。 聚合器将作为计数器来找我。我想要一份文件清单。 我的索引: Doc 1 {domain:’domain1.fr’,name:’name1’,date:‘01 -01-2014’} Doc 2 {domain:’domain1.fr’,name:’name1’,date:‘01 -02-2014’}

  • 问题内容: 我使用下面的代码行遍历数据库中的一个表: 如果我打印出数组: 我会得到这个: 但是我想摆脱数组中的重复项,所以我使用 我得到下面的奇怪结果,这不是我想要的结果: 理想情况下,我认为它应该返回以下内容: 我该怎么做才能正确处理?我使用了错误的PHP语法/默认功能吗? 问题答案: 该功能将为您完成此操作。您只需要添加标志:

  • 问题内容: 我想从排序的链表{0 1 2 2 3 3 4 5}中删除重复项。 ` ` prev.setNext(tempHeader)在while循环内无法正常工作。理想情况下,当prev = 2且tempHeader = 3时,prev.next应该是data = 3的节点。 Printlist函数仅使用标题指针并打印列表。 节点定义如下。 问题答案: 循环已排序,因此您知道重复项将彼此相邻。如