【问题标题】:How to untar files from youngest to oldest one until success in bash如何从最年轻到最老的文件解压文件,直到在 bash 中成功
【发布时间】:2017-05-01 05:46:27
【问题描述】:

我在一个目录中有一个 tar 文件(备份)的列表,需要解压它们,从最年轻的到最老的,直到成功。

问题是有时 tar 文件已损坏,需要解压较旧的文件。

这是我找到最小的,但不知道如何将它放入 while 并找到下一个。

tar -xf `find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat | head -n 1`

提前致谢

【问题讨论】:

    标签: bash while-loop tar


    【解决方案1】:

    您已经有一个有效的find | xargs ls,它会为您提供排序后的文件列表,最新的优先。 (正如@sjsam 指出的那样,假设您的文件名都不包含换行符。)您可以使用 bash 的process-substitution 功能将该列表放入文件中,每行一个条目,以便它可以是 read by a while ... read loop。然后,您可以在备份成功后立即退出循环。

        # vvvvvvvvvvvvvvvvvvvvvv get the next filename into $tarname
    while IFS= read -r tarname
    do  
        tar -xf "$tarname" && break
                        #  ^^^^^^^^ done with the loop if tar succeeds
    done <   <(find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat)  
       # ^   ^^^^---- the process substitution
       # |
       # +--------- the redirection from the process subsitution
    

    请注意,您确实需要&lt; &lt;(...),而不仅仅是&lt;(...)

    (顺便问一下,你需要ls -hat,还是ls -t也需要?)


    编辑!又名Take 3

    我受到@sjsam 评论的启发,写了ls0,这是一个ls 替代品,可以提供以NULL 结尾的输出。如果发现任何错误,请检查并提交问题!它在 Cygwin 上使用 Perl 5.22 进行了测试,但应该适用于大多数 Perl 5。它是一个单独的文件——安装时,将ls0 放在你的路径中,chmod ugo+x ls0

    使用ls0,上面的技术可以从容处理奇怪命名的文件:

    while IFS= read -r -d '' tarname
    do  
        tar -xf "$tarname" && break
    done <   <(find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | ls0 -t)
    

    (正如@sjsam 所说,-d '' 在功能上等同于-d $'\0'


    Take 2 page sjsam links 建议使用 perl 而不是 ls 对文件名进行排序。这样您就可以处理包含换行符或其他奇怪字符的文件名。

    这是该技术的一个示例(不是 OP 问题的答案)。我已经使用包含换行符的文件名对其进行了测试。以下代码打印当前目录中的 *.tar 文件,最新的在前。此示例在每个文件名之前和之后打印一个斜杠,以便您可以看到带有换行符的文件名被正确处理。在 cygwin、bash 4.3.46、perl 5.22.2 上测试。

    perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \ 
    | while IFS= read -r -d $'\0' name
    do 
        printf "/%s/\n" "$name"
    done
    

    因此,将所有这些放在一起以获得 (1) 回答 OP 的问题并且 (2) 对具有奇怪内容的文件名具有鲁棒性的代码:

    perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \ 
    | while read -r -d $'\0' tarname
    do
        tar -xf "$tarname" && break
    done
    

    或者,使用进程替换,

    while IFS= read -r -d $'\0' tarname
    do
        tar -xf "$tarname" && break
    done < <(perl -e 'opendir(my $dirh, "."); print(join "\0", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))')
    

    很简单,对吧? ;)

    来源:

    • 按排序顺序读取目录:this answer
    • 在 bash 中读取以 null 结尾的数据:this blog post(bash 的 read 可以与以 null 结尾的字符串一起使用)
    • 在 perl 中过滤列表:this answer

    【讨论】:

    • 解析ls输出的是[ not suggested ]
    • 我使用了第一个版本,因为名称中没有空格。
    • @sjsam 查看ls0 - 现在您不必再解析ls 的输出了!你怎么看?感谢wooledge link,它给了我灵感。 :)
    【解决方案2】:

    下面的脚本会做

    find /path/to/tarball/folder -type f -name "*.tar" -printf "%A@--%p\0" |
    sort -rnz | while read -r -d '' stampedfile 
    do
      tar -xzf "${stampedfile#*--}" >/dev/null 2>&1
      [ $? -eq 0 ] && echo "Newest Working Tar: ${stampedfile#*--}" && exit
    done
    

    我们做了什么

    • 使用 find 列出空终止的 tarball 文件,这些文件由自纪元以来的最后一次访问时间(以秒为单位)和我们稍后用作分隔符的 -- 前缀
    • 使用修改时间按降序对文件名进行排序。这就是 -- 分隔符派上用场的地方
    • 使用 while 循环解析以 null 结尾的排序文件
    • 从文件名中去除time-- 并尝试解压
    • 如果尝试成功,则退出

    【讨论】:

    • 好东西,但为什么$? 的测试很笨拙? (我假设 $0 是一个错字)你可以简单地 &amp;&amp; exit 直接跟随 tar (甚至,while read &amp;&amp; !tar; do echo failure &gt;&amp;2; done
    • 可能还值得一提的是,这将累积来自失败的 tar 调用的部分结果,这可能不是我们想要的 - 在这种情况下,您可能需要在使用之前对每个文件执行一次试运行第一个成功的。不过,从这个问题中并不清楚想要什么。
    • @TobySpeight:感谢$? 的指点。是的!缩短这个的多种方法。为了解释,我放了较长的那一个。 :) 干杯
    • @sjsam 不应该 "${stampedfile##*--}""${stampedfile#*--}"(最短匹配),以防 tar 的名称包含 --
    • @cxw 好点,并入。理想情况下,分隔符应该是独一无二的,不应该出现在文件名中
    猜你喜欢
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-15
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多