如何在 bash 中从新到旧解压文件直到成功
How to untar files from youngest to oldest one until success in bash
我在一个目录中得到了 tar 个文件(备份)的列表,需要取消 tar 它们从最年轻到最旧的尝试直到成功。
问题是 tar 文件有时会损坏,需要取消 tar 旧文件。
这就是我找到最小的一个的方法,但不确定如何将它放入 while 中并找到下一个。
tar -xf `find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat | head -n 1`
提前致谢
您已经有一个可以工作的 find | xargs ls
可以为您提供排序的文件列表,最新的在前。 (正如@sjsam 指出的那样,假设 none 的文件名包含换行符。)您可以使用 process-substitution feature of bash to put that list in a file, one entry per line, so that it can be 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
请注意,您确实需要 < <(...)
,而不仅仅是 <(...)
。
(顺便说一句,你需要 ls -hat
,还是 ls -t
也可以?)
编辑! 又名 拍摄 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 $'[=26=]'
)
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 "[=12=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \
| while IFS= read -r -d $'[=12=]' name
do
printf "/%s/\n" "$name"
done
因此,将所有这些放在一起以获得 (1) 回答 OP 的问题并且 (2) 对具有奇怪内容的文件名具有鲁棒性的代码:
perl -e 'opendir(my $dirh, "."); print(join "[=13=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \
| while read -r -d $'[=13=]' tarname
do
tar -xf "$tarname" && break
done
或者,使用进程替换,
while IFS= read -r -d $'[=14=]' tarname
do
tar -xf "$tarname" && break
done < <(perl -e 'opendir(my $dirh, "."); print(join "[=14=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))')
简单吧? ;)
来源:
- 正在按排序顺序读取目录:this answer
- reference
-M
测试
- 读取 bash 中以 null 结尾的数据:this blog post(bash 的
read
可以与以 null 结尾的字符串一起使用)
- 在 perl 中过滤列表:this answer
下面的脚本可以做到
find /path/to/tarball/folder -type f -name "*.tar" -printf "%A@--%p[=10=]" |
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 循环解析空终止的排序文件
- 从文件名中删除
time--
并尝试解压它
- 如果尝试成功,则退出
我在一个目录中得到了 tar 个文件(备份)的列表,需要取消 tar 它们从最年轻到最旧的尝试直到成功。
问题是 tar 文件有时会损坏,需要取消 tar 旧文件。
这就是我找到最小的一个的方法,但不确定如何将它放入 while 中并找到下一个。
tar -xf `find /config/config_*.tar -maxdepth 1 -type f -size +1k -print0 | xargs -0 ls -hat | head -n 1`
提前致谢
您已经有一个可以工作的 find | xargs ls
可以为您提供排序的文件列表,最新的在前。 (正如@sjsam 指出的那样,假设 none 的文件名包含换行符。)您可以使用 process-substitution feature of bash to put that list in a file, one entry per line, so that it can be 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
请注意,您确实需要 < <(...)
,而不仅仅是 <(...)
。
(顺便说一句,你需要 ls -hat
,还是 ls -t
也可以?)
编辑! 又名 拍摄 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 $'[=26=]'
)
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 "[=12=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \
| while IFS= read -r -d $'[=12=]' name
do
printf "/%s/\n" "$name"
done
因此,将所有这些放在一起以获得 (1) 回答 OP 的问题并且 (2) 对具有奇怪内容的文件名具有鲁棒性的代码:
perl -e 'opendir(my $dirh, "."); print(join "[=13=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))' \
| while read -r -d $'[=13=]' tarname
do
tar -xf "$tarname" && break
done
或者,使用进程替换,
while IFS= read -r -d $'[=14=]' tarname
do
tar -xf "$tarname" && break
done < <(perl -e 'opendir(my $dirh, "."); print(join "[=14=]", sort { -M $a <=> -M $b } grep /.tar$/, readdir($dirh))')
简单吧? ;)
来源:
- 正在按排序顺序读取目录:this answer
- reference
-M
测试
- reference
- 读取 bash 中以 null 结尾的数据:this blog post(bash 的
read
可以与以 null 结尾的字符串一起使用) - 在 perl 中过滤列表:this answer
下面的脚本可以做到
find /path/to/tarball/folder -type f -name "*.tar" -printf "%A@--%p[=10=]" |
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 循环解析空终止的排序文件
- 从文件名中删除
time--
并尝试解压它 - 如果尝试成功,则退出