Leo*_*zen 8 regex sorting bash shell perl
我发现如果你按照文件扩展名而不是按字母顺序对文件列表进行排序,然后再将它们放入tar存档中,你可以大大提高压缩率(特别是对于你可能有很多.c,.o的大型源代码树,和.h文件).
我找不到一种简单的方法来使用shell来对文件进行排序,它在每种情况下都能按照我的预期运行.一个简单的解决方案,例如find | rev | sort | rev
工作,但文件以奇数顺序出现,并且它不能很好地排列它们以获得最佳压缩比.其他工具如ls -X
不使用find
,并且sort -t. -k 2,2 -k 1,1
当文件在文件名中有多个句点时会混乱(例如version-1.5.tar).另一个快速n-dirty选项,使用sed
替换最后一个句点/
(它永远不会出现在文件名中),然后排序,拆分/
:
sed 's/\(\.[^.]*\)$/\/\1/' | sort -t/ -k 2,2 -k 1,1 | sed 's/\/\([^/]*\)$/\1/'
Run Code Online (Sandbox Code Playgroud)
但是,再次使用名称中find
包含/
s 的输出不起作用,并且*nix中的文件名中允许使用所有其他字符(除0之外).
我发现使用Perl,您可以使用相同的输出cmp
(类似于strcmp
C)编写自定义比较子例程,然后运行perl sort函数,传递自己的自定义比较,这很容易用perl正则表达式编写.这正是我所做的:我现在有一个调用的perl脚本
@lines = <STDIN>;
print sort myComparisonFunction @lines;
Run Code Online (Sandbox Code Playgroud)
但是,perl不像bash那样可移植,所以我希望能够使用shell脚本.此外,find
不会在目录名称上放置尾随/目录,因此脚本认为目录与没有扩展名的文件相同.理想情况下,我想tar
首先阅读所有目录,然后是常规文件(并对它们进行排序),然后是符号链接,我可以通过
cat <(find -type d) <(find -type f | perl exsort.pl) <(find -not -type d -and -not -type f) | tar --no-recursion -T - -cvf myfile.tar
Run Code Online (Sandbox Code Playgroud)
但是我仍然遇到这样的问题:我每次都要输入这个怪物,或者我有这个长行的shell脚本和用于排序的perl脚本,并且perl在任何地方都不可用,因此将所有内容都填充到一个perl脚本中也不是一个好的解决方案.(我主要关注的是老式计算机,因为现在所有现代Linux和OSX都带有最新版本的perl).
我希望能够将所有内容放在一个shell脚本中,但我不知道如何将自定义函数传递给GNU排序工具.我运气不好,必须使用一个perl脚本吗?或者我可以使用一个shell脚本吗?
编辑:感谢Schwartizan变换的想法.我用了一个稍微不同的方法sed
.我的最终排序程序如下:
sed 's_^\(\([^/]*/\)*\)\(.*\)\(\.[^\./]*\)$_\4/\3/\1_' | sed 's_^\(\([^/]*/\)*\)\([^\./]\+\)$_/\3/\1_' | sort -t/ -k1,1 -k2,2 -k3,3 | sed 's_^\([^/]*\)/\([^/]*\)/\(.*\)$_\3\2\1_'
Run Code Online (Sandbox Code Playgroud)
这会处理文件名中的特殊字符(例如*),并且首先放置没有扩展名的文件,因为它们通常是文本文件.(Makefile,COPYING,README,configure等).
PS如果有人想要我的原始比较功能或认为我可以改进它,这里是:
sub comparison {
my $first = $a;
my $second = $b;
my $fdir = $first =~ s/^(([^\/]*\/)*)([^\/]*)$/$1/r;
my $sdir = $second =~ s/^(([^\/]*\/)*)([^\/]*)$/$1/r;
my $fname = $first =~ s/^([^\/]*\/)*([^\/]*)$/$2/r;
my $sname = $second =~ s/^([^\/]*\/)*([^\/]*)$/$2/r;
my $fbase = $fname =~ s/^(([^\.]*\.)*)([^\.]*)$/$1/r;
my $sbase = $sname =~ s/^(([^\.]*\.)*)([^\.]*)$/$1/r;
my $fext = $fname =~ s/^([^\.]*\.)*([^\.]*)$/$2/r;
my $sext = $sname =~ s/^([^\.]*\.)*([^\.]*)$/$2/r;
if ($fbase eq "" && $sbase ne ""){
return -1;
}
if ($sbase eq "" && $fbase ne ""){
return 1;
}
(($fext cmp $sext) or ($fbase cmp $sbase)) or ($fdir cmp $sdir)
}
Run Code Online (Sandbox Code Playgroud)
如果您熟悉Perl,也可以在BASH中使用Schwartzian Tranform.
Schwartian转换只是向您的排序信息添加您想要的排序键,进行排序,然后删除排序键.它由Randal Schwartz创建,在Perl中大量使用.但是,使用其他语言也很好:
您想按扩展名对文件进行排序:
find . -type f 2> /dev/null | while read file #Assuming no strange characters or white space
do
suffix=${file##*.}
printf "%-10.10s %s\n" "$suffix" "$file"
done | sort | awk '{print substr( $0, 8 ) }' > files_to_tar.txt
Run Code Online (Sandbox Code Playgroud)
我正在阅读每个文件find
.我用printf
我的文件名前缀我想要排序的后缀.然后,我做我的排序.我awk
剥离了我的排序键,只留下我的文件名,它仍然按后缀排序.
现在,您的files_to_tar.txt
文件包含按后缀排序的文件名.您可以使用-T
参数tar
来从此文件中读取文件的名称:
$ tar -czvf backup.tar.gz -T files_to_tar.txt
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
426 次 |
最近记录: |