按行长度(包括空格)sorting文本文件

我有一个看起来像这样的CSV文件

 AS2345,ASDF1232,Plain案例,110 Binary Ave.,Atlantis,RI,12345,(999)123-5555,1.56
 AS2345,ASDF1232,夫人平原例子,1121110三元st。  110 Binary ave ..,Atlantis,RI,12345,(999)123-5555,1.56
 AS2345,ASDF1232,Plain案例,110 Binary Ave.,Liberty City,RI,12345,(999)123-5555,1.56
 AS2345,ASDF1232,平原例,110三重大学,一些城市,RI,12345,(999)123-5555,1.56

我需要按照包括空格在内的行长来sorting。 下面的命令不包含空格,有没有办法修改它,所以它会为我工作?

cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}' 

回答

 cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- 

或者,对原始的(也许是无意的)任何等长的行进行子sorting:

 cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2- 

在这两种情况下,我们已经通过从awk中删除最终剪切来解决您所陈述的问题。

匹配长度的线 – 在平局的情况下该怎么做:

这个问题没有具体说明是否需要对匹配长度的行进一步分类。 我认为这是不必要的,并build议使用-s (– --stable )来防止这些行相互sorting,并保持它们在input中出现的相对顺序。

(那些想要对这些关系进行更多控制的人可能会考虑--key选项。)

为什么问题的尝试解决scheme失败(awk line-rebuilding):

有趣的是要注意区别:

 echo "hello awk world" | awk '{print}' echo "hello awk world" | awk '{$1="hello"; print}' 

他们分别产量

 hello awk world hello awk world 

(gawk's)手册的相关部分仅仅提到awk将在重新创build$ 0(基于分隔符等)时更改一个字段。 我猜这不是疯狂的行为。 它有这样的:

“最后,有时可以用awk来重build整个logging,使用字段和OFS的当前值,方法是使用看起来无害的任务:”

  $1 = $1 # force record to be reconstituted print $0 # or whatever else with $0 

“这迫使awk重buildlogging。”

testinginput包括一些等长的行:

 aa A line with MORE spaces bb The very longest line in the file ccb 9 dd equal len. Orig pos = 1 500 dd equal len. Orig pos = 2 ccz cca ee A line with some spaces 1 dd equal len. Orig pos = 3 ff 5 dd equal len. Orig pos = 4 g 

试试这个命令:

 awk '{print length, $0}' your-file | sort -n | cut -d " " -f2- 

如果你真的想使用awk ,那么neillb的AWK解决scheme是非常棒的,这也解释了为什么它很麻烦,但是如果你想要快速完成工作而不关心你在做什么,一种解决scheme是使用Perl的sort()函数和自定义的caparison例程遍历input行。 这里是一个class轮:

 perl -e 'print sort { length($a) <=> length($b) } <>' 

你可以把它放在你需要的地方,无论是接收STDIN(从cat还是shellredirect),或者把文件名作为另一个参数,让它打开文件。

在我的情况下,我需要最长的行首,所以我换了$a$b比较。

纯粹的Bash:

 declare -a sorted while read line; do if [ -z "${sorted[${#line}]}" ] ; then # does line length already exist? sorted[${#line}]="$line" # element for new length else sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length fi done < data.csv for key in ${!sorted[*]}; do # iterate over existing indices echo -e "${sorted[$key]}" # echo lines with equal length done 

length()函数包含空格。 我会对你的pipe道做一些小的调整(包括避免UUOC )。

 awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://' 

sed命令直接删除由awk命令添加的数字和冒号。 或者,保持您的格式从awk

 awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //' 

用POSIX Awk:

 { c = length m[c] = m[c] ? m[c] RS $0 : $0 } END { for (c in m) print m[c] } 

如果您的文件包含以数字开头的行,我发现这些解决scheme将不起作用,因为它们将与所有计数行一起按数字sorting。 解决的办法是sort -g (通用数字sorting)标志而不是-n (数字sorting):

 awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2- 

这是一个多字节兼容的方法,按行长sorting。 这个需要:

  1. wc -m可用(macOS有)。
  2. 您当前的语言环境支持多字节字符,例如,通过设置LC_ALL=UTF-8 。 你可以在.bash_profile中设置它,或者简单地在下面的命令之前加上它。
  3. testfile有一个符合你的语言环境的字符编码(例如,UTF-8)。

以下是完整的命令:

 cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2- 

部分解释:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l); ←使awkvariablesl的每一行的副本成为一行,并且双重转义每一行'因此行可以安全地作为shell命令回显( \047是以八进制表示法的单引号)。
  • cmd=sprintf("echo \047%s\047 | wc -m", l); ←这是我们要执行的命令,它将转义线回显到wc -m
  • cmd | getline c; ←执行命令并将返回的字符计数值复制到awkvariablesc
  • close(cmd); ←closurespipe道到shell命令,以避免在一个进程中打开文件的数量限制系统。
  • sub(/ */, "", c); ←由wc返回的字符计数值修整空白。
  • { print c, $0 } ←打印行的字符计数值,空格和原始行。
  • | sort -ns | sort -ns ←数字( -n )sorting行(通过字符计数值的前置),并保持稳定的sorting顺序( -s )。
  • | cut -d" " -f2- | cut -d" " -f2- ←删除前缀字符计数值。

速度很慢(在Macbook Pro上只有每秒160行),因为它必须为每一行执行一个子命令。

或者,只需要使用gawk (从版本3.1.5开始,gawk可以识别多字节),这样做会快得多。 执行所有转义和双引号以安全地通过来自awk的shell命令传递行,这是很麻烦的,但是这是我能find的唯一不需要安装额外软件的方法(gawk默认情况下不可用苹果系统)。

基准testing结果

另外,另一个Perl解决scheme:
perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

实验运行使用:

  • 在一台快速机器上连续运行10次,取平均值
  • Perl 5.24
  • awk 3.1.5(gawk 4.1.0倍〜2%快)
  • input文件是一个550MB,600万行的怪物(英国国家语料库txt)

结果:

  • fgm bash解决scheme要比awk解决scheme长400倍(使用截断的100000行testing用例)。 它工作正常,只需要很长时间。
  • 乔纳森awk解决了25秒
  • anubhava awk解决scheme耗时24秒
  • neillb awk解决scheme#2花了23秒钟
  • neillb awk解决scheme#1耗时20秒
  • 我的Perl解决scheme花了11.6秒
  • caleb的perl soution花了11.2秒