我找不到一种简单的方法来使用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) }
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