是否可以重命名 tar 存档中的目录?我的用例是,我有一个外部提供的 RPM 规范文件,该文件假定具有特定目录结构的 tarball,并且我有一个外部提供的 tarball,其顶级目录名称与规范文件所期望的不匹配。我无法控制生成 tarball 的脚本或 RPM 规范文件,因此我无法对其中任何一个进行更改以匹配另一个。
我一直在做的是解压,更改目录名称,然后创建一个新的 tarball,但我想知道是否有其他方法可以做到这一点。
答案1
这应该不是很困难,至少对于与旧式格式兼容的档案来说,其中文件名存储在固定大小(100 字节)字段中,但我不知道有任何工具可以重命名文件位于 tar 存档中。此外,对于压缩存档,您无论如何都需要创建一个新文件。
它应该更容易,但我不知道有任何现有工具可以过滤存档,并在文件运行时重命名文件。您可以在脚本语言的 tar 库之上构建一个;例如,这是一个概念验证脚本,用于使用以下命令重命名 tar 存档中的目录Perl 与Archive::Tar
。存档完全加载到内存中;这是 的固有限制Archive::Tar
。
#!/usr/bin/env perl
## Usage: tar-rename OLDPREFIX NEWPREFIX
use strict;
use warnings;
use Archive::Tar;
my ($from, $to) = @ARGV;
my $tar = Archive::Tar->new(\*STDIN);
foreach my $file ($tar->get_files()) {
my $name = $file->name;
$name =~ s~\A\Q$from\E($|/)~$to$1~;
$file->rename($name) unless $name eq $file->name;
}
$tar->write(\*STDOUT);
GNU tar 没有动态重命名成员的能力,但是pax
(POSIX 的cpio
和替代品tar
)确实如此。但是,您无法pax
同时从存档中进行读取和写入。您可以做的是将存档作为常规树公开AVFS,并创建一个新的存档pax
。这会保留文件名(转换后的除外)、内容、时间和模式,但会将文件所有权重置给您(除非以 root 身份执行)。
mountavfs
cd "~/.avfs$PWD/old.tgz#"
pax -w -s '!bar!baz!' -s '!bar/!baz/' . | gzip >new.tgz
答案2
sr_ 的 hack 和 Gilles 的答案看起来都很好,但如果您的问题只是目标 tarball 的根目录名称,则在运行 rpmbuild 时,不同的解决方案可能是重新定义宏%setup
来执行所需的目录重命名。
类似的东西(您必须根据您的实际配置进行调整和完善,特别是替换old-dir
并desired-dir
使用正确的解压缩工具)~/.rpmmacros
:
%setup cd ../BUILD \
rm -rf cd-player \
bunzip2 -dc ../SOURCES/%{name}-%{version}.tar.bz2 | tar -xvvf - \
if [ $? -ne 0 ]; then \
exit $? \
fi \
mv <old-dir> <desired-dir> \
cd <desired-dir> \
cd ../BUILD/cd-player \
chmod -R a+rX,g-w,o-w .
老实说,如果不是在最奇特的情况下,我不会这样做,但你的情况可能就是这样:)
答案3
只需查看此页面即可在其他地方找到正确的答案:
http://www.rpm.org/max-rpm/s1-rpm-inside-macros.html
它表示您可以将 -n 传递给 %setup 宏来告诉 rpmbuild tarball 中顶级文件夹的名称
答案4
这篇文章非常接近我所需要的,但没有雪茄。 Archive::Tar 内部的重命名功能弄乱了我的文件夹。它对于文件来说效果很好,但是文件夹出来时没有尾部斜杠(/)。例子:
前
$VAR1 = [
'old_root_folder/',
'old_root_folder/.dockerignore',
'old_root_folder/.github/',
.....
后
$VAR1 = [
'newrootfolder',
'newrootfolder/.dockerignore',
'newrootfolder/.github/',
......
请注意,根文件夹不再有斜杠指定!事实证明这并不重要。新的存档可以正常提取,文件夹不以斜杠结尾
我最终得到了以下 Perl 片段:
sub renameRootFolderInTar
{
my $file = shift;
my $new_root_folder_name = shift;
my $tar = Archive::Tar->new($file);
my @files_in_archive = $tar->list_files;
my $root_folder = @files_in_archive[0]; # whatever they named the root folder in the archive
$root_folder =~ s/\/$//g;
foreach(@files_in_archive)
{
my $this_archive_file = $_;
my $dest = $this_archive_file;
$dest =~ s/^$root_folder\/(.*)/$new_root_folder_name\/$1/g;
$tar->rename($this_archive_file,$dest);
}
my $success = $tar->write( "renamed.tar.gz", COMPRESS_GZIP );
if($success)
{
unlink $file;
return "renamed.tar.gz";
}
else
{
print "Sorry, there was a problem when dealing with the raw archive $file:\n";
print "Could not save renamed.tar.gz\n";
exit;
}
}
诚然,这假设存档中的第一个条目是一个文件夹。但你明白了。