正则表达式 – 使用bash / coreutils而不是perl按函数排序

前端之家收集整理的这篇文章主要介绍了正则表达式 – 使用bash / coreutils而不是perl按函数排序前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我发现如果你按照文件扩展名而不是按字母顺序对文件列表进行排序,然后再将它们放入tar存档中,你可以大大提高压缩比(特别是对于你可能有很多.c,.o的大型源代码树,和.h文件).

我找不到一种简单的方法来使用shell来对文件进行排序,它在每种情况下都能按我期望的方式工作.一个简单的解决方案,如查找|转|排序| rev完成这项工作,但文件以奇数顺序出现,并且它不能很好地排列它们以获得最佳压缩比.其他工具(如ls -X)不适用于find和sort -t.当文件文件名中有多个句点时(例如版本1.5.tar),-k 2,2 -k 1,1会混乱.另一个快速n-dirty选项,使用sed替换最后一个句点用/(它永远不会出现在文件名中),然后排序,沿着/拆分:

sed 's/\(\.[^.]*\)$/\/\1/' | sort -t/ -k 2,1  |  sed 's/\/\([^/]*\)$/\1/'

但是,使用名称中包含/ s的find的输出再次无效,并且* nix中的文件名中允许使用所有其他字符(除0之外).

我发现使用Perl,您可以使用与cmp相同的输出编写自定义比较子例程(类似于C中的strcmp),然后运行perl sort函数,传递自己的自定义比较,这很容易用perl正则表达式编写.这正是我所做的:我现在有一个调用的perl脚本

@lines = <STDIN>;
print sort myComparisonFunction @lines;

但是,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

但是我仍然遇到这样的问题,要么我每次都要输入这个怪物,要么我有这个长行的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_'

它处理文件名中的特殊字符(例如*),并且首先放置没有扩展名的文件,因为它们通常是文本文件. (Makefile,COPYING,README,configure等).

附:如果有人想要我的原始比较功能或认为我可以改进它,这里是:

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)
}
如果您熟悉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

我正用我的发现阅读每个文件.我使用printf在我的文件名前加上我要排序的后缀.然后,我做我的排序.我的awk剥离了我的排序键,只留下我的文件名,仍然按后缀排序.

现在,您的files_to_tar.txt文件包含按后缀排序的文件名.您可以使用tar的-T参数从此文件中读取文件名称

$tar -czvf backup.tar.gz -T files_to_tar.txt

猜你在找的正则表达式相关文章