按行长度(包括空格)对文本文件进行排序

时间:2011-05-06 22:15:28

标签: bash sorting text awk

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

AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st.                                        110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56

我需要按行长度(包括空格)对其进行排序。以下命令不会 包含空格,有没有办法修改它以便它对我有用?

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

12 个答案:

答案 0 :(得分:183)

答案

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

或者,对任何等长线进行原始(可能是无意的)子排序:

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

在这两种情况下,我们都通过远离awk来解决你所说的问题。

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

问题没有说明是否需要对匹配长度的行进行进一步排序。我假设这是不需要的,并建议使用-s--stable)来防止这些行相互排序,并保持它们在输入中出现的相对顺序。

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

为什么问题的尝试解决方案失败(awk行重建):

值得注意的是:

之间的区别
echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

他们分别屈服

hello   awk   world
hello awk world

relevant section of (gawk's) manual只提到了当你改变一个字段时,awk将重建整个$ 0(基于分隔符等)。我想这不是疯狂的行为。有这个:

“最后,有时候使用字段和OFS的当前值强制awk重建整个记录很方便。为此,请使用看似无害的任务:”

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

“这迫使awk重建记录。”

测试输入包括一些相等长度的行:

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

答案 1 :(得分:18)

AWK solution from neillb非常棒,如果你真的想使用awk并且它解释了为什么那里有麻烦,但如果你想要的是快速完成工作并且不在乎你是什么做到这一点,一个解决方案是使用Perl的sort()函数和自定义caparison例程迭代输入行。这是一个班轮:

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

您可以将它放在您需要它的管道中,或者接收STDIN(来自cat或shell重定向),或者只是将文件名作为另一个参数提供给perl并让它打开文件。

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

答案 2 :(得分:13)

请尝试使用此命令:

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

答案 3 :(得分:6)

基准测试结果

以下是基准测试的结果。

另外,我添加了另一个Perl解决方案:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <link rel="icon" href="../../../../favicon.ico"> <title>Sticky Footer Navbar Template for Bootstrap</title> <!-- Bootstrap core CSS --> <link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- Custom styles for this template --> <link href="sticky-footer-navbar.css" rel="stylesheet"> <style> html { position: relative; height: 100%; } body { /* Margin bottom by footer height */ } .footer { position: absolute; bottom: 0; width: 100%; /* Set the fixed height of the footer here */ background-color:#000; text-align:center; color:#fff; } .footer-bottom{ padding:3px 0; position:absolute; bottom:0; background-color:green; text-align:center; width:100%; } </style> </head> <body class="page-body"> <!-- Fixed navbar --> <!-- Fixed navbar --> <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark"> <a class="navbar-brand" href="#">Fixed navbar</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarCollapse"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> </li> <li class="nav-item"> <a class="nav-link" href="#">Link</a> </li> <li class="nav-item"> <a class="nav-link disabled" href="#">Disabled</a> </li> </ul> <form class="form-inline mt-2 mt-md-0"> <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search"> <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button> </form> </div> </nav> <!--[ container ] --> <div class="container"> content here </div> <!--[ footer ] --> <div class="footer"> <p>Place sticky footer content here.</p> </div> <div class="footer-bottom"> <p>footer Bottom</p> </div> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> <script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script> <script> function sizing(){ var completeFooter = $(".footer").height() + $(".footer-bottom").height(); $(".footer").css("height", completeFooter); $(".page-body").css("margin-bottom", completeFooter); } $(document).ready(sizing); $(window).resize(sizing); </script> </body> </html>

实验使用:

进行
  • 在快速机器上连续运行10次,平均
  • Perl 5.24
  • awk 3.1.5(gawk 4.1.0倍快〜2%)
  • 输入文件为550MB,600万行怪物(英国国家语料库txt)

结果:

  • fgm bash解决方案比awk解决方案长400倍(使用100000行的截断测试用例)。它工作正常,需要很长时间。
  • jonathan awk解决方案需要25秒
  • anubhava awk解决方案需要24秒
  • neillb awk解决方案#2耗时23秒
  • neillb awk解决方案#1耗时20秒
  • 我的perl解决方案耗时11.6秒
  • caleb的perl soution花了11.2秒

答案 4 :(得分:5)

Pure 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

答案 5 :(得分:3)

length()函数包含空格。我会对您的管道进行微调(包括避免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]* //'

答案 6 :(得分:2)

如果您的文件包含以数字开头的行,我发现这些解决方案无效,因为它们将与所有计算的行一起按数字排序。解决方案是给sort -g(通用数字排序)标志而不是-n(数字排序):

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

答案 7 :(得分:2)

使用POSIX Awk:

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

Example

答案 8 :(得分:2)

1)纯awk溶液。假设行长不能大于1024 然后

cat文件名| awk'BEGIN {min = 1024; s =“”;} {l = length($ 0);如果(l

2)假设所有行只有1个单词的一种内衬bash解决方案,但是对于所有行都具有相同单词数的任何情况,都可以重做:

LINES = $(猫文件名); $ LINES中的k;做printf“ $ k”;回声$ k | wc -L;完成|排序-k2 |头-n 1 |切-d“” -f1

答案 9 :(得分:1)

这是一种按字节长度排序的多字节兼容方法。它要求:

  1. wc -m可供您使用(macOS有)。
  2. 您当前的区域设置支持多字节字符,例如,通过设置LC_ALL=UTF-8。您可以在.bash_profile中设置它,或者只需在以下命令之前添加它。
  3. testfile的字符编码与您的语言环境匹配(例如,UTF-8)。
  4. 这是完整的命令:

    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); ←awk变量l中每行的副本,并且每'双重转义,因此该行可以安全地回显为shell命令(\047是八进制表示法中的单引号)。
    • cmd=sprintf("echo \047%s\047 | wc -m", l); ←这是我们要执行的命令,它将转义的行回传到wc -m
    • cmd | getline c; ←执行命令并复制返回到awk变量c的字符计数值。
    • close(cmd); ←关闭管道命令,以避免在一个进程中对打开文件的数量达到系统限制。
    • sub(/ */, "", c); ←从wc返回的字符数值中修剪空格。
    • { print c, $0 } ←打印行的字符计数值,空格和原始行。
    • | sort -ns ←按行(-n)对行(按前置字符计数值)进行排序,并维护稳定的排序顺序(-s)。
    • | cut -d" " -f2- ←删除前置字符计数值。

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

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

答案 10 :(得分:1)

使用Raku(以前称为Perl6)

~$ cat "BinaryAve.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};'

AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st.                                        110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56

要反转排序,请在方法调用链的中间添加.reverse,紧接在.sort()之后。以下代码显示.chars包含空格:

~$ cat "number_triangle.txt" | raku -e 'given lines() {.map(*.chars).say};'
(1 3 5 7 9 11 13 15 17 19 0)
~$ cat "number_triangle.txt"
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 0

以下是使用Genbank的9.1MB txt文件对awk和Raku进行的时间比较:

~$ time cat "rat_whole_genome.txt" | raku -e 'given lines() {.sort(*.chars).join("\n").say};' > /dev/null
    
    real    0m1.308s
    user    0m1.213s
    sys 0m0.173s
    
~$ #awk code from neillb
~$ time cat "rat_whole_genome.txt" | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-  > /dev/null
    
    real    0m1.189s
    user    0m1.170s
    sys 0m0.050s

HTH。

答案 11 :(得分:0)

重温这个。这就是我的处理方式(计算 LINE 的长度并将其存储为 LEN,按 LEN 排序,仅保留 LINE):

cat test.csv | while read LINE; do LEN=$(echo ${LINE} | wc -c); echo ${LINE} ${LEN}; done | sort -k 2n | cut -d ' ' -f 1