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

gna*_*ian 124 sorting bash awk text

我有一个看起来像这样的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}'
Run Code Online (Sandbox Code Playgroud)

nei*_*llb 200

回答

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
Run Code Online (Sandbox Code Playgroud)

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

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-
Run Code Online (Sandbox Code Playgroud)

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

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

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

(那些想要更多控制排序这些关系的人可能会考虑排序的--key选择.)

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

值得注意的是:

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'
Run Code Online (Sandbox Code Playgroud)

他们分别屈服

hello   awk   world
hello awk world
Run Code Online (Sandbox Code Playgroud)

(gawk's)手册相关部分仅提到当你改变一个字段时,awk将重建整个$ 0(基于分隔符等).我想这不是疯狂的行为.它有这个:

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

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0
Run Code Online (Sandbox Code Playgroud)

"这迫使重建记录."

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

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
Run Code Online (Sandbox Code Playgroud)

  • heemayl,是的,谢谢。我试图尽可能匹配 OP 尝试解决方案的形状,使他能够只关注他和我之间的重要差异。 (2认同)
  • 值得指出的是,`cat $@` 也坏了。你绝对想引用它,比如`cat "$@"` (2认同)

Cal*_*leb 22

来自neillbAWK解决方案非常棒,如果您真的想要使用awk它,它解释了为什么它在那里很麻烦,但如果您想要的是快速完成工作并且不关心您在做什么,一个解决方案是使用Perl的sort()函数使用自定义caparison例程迭代输入行.这是一个班轮:

perl -e 'print sort { length($a) <=> length($b) } <>'
Run Code Online (Sandbox Code Playgroud)

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

就我而言,我需要最长行第一,所以我换出$a,并$b在比较.

  • 这是更好的解决方案,因为当输入文件包含数字和字母数字行时,awk 会导致意外排序这里是单行命令: $ cat testfile | perl -e '打印排序 { length($a) &lt;=&gt; length($b) } &lt;&gt;' (2认同)
  • 快速地!当输出重定向到另一个文件时,在 &lt;1 秒内完成 465,000 行文件(每行一个单词) - 因此:`cat testfile.txt | perl -e '打印排序 { 长度($a) &lt;=&gt; 长度($b) } &lt;&gt;' &gt; out.txt` (2认同)
  • 使用 StrawberryPerl 的 Windows 可以工作:`type testfile.txt | perl -e "打印排序 { 长度($a) &lt;=&gt; 长度($b) } &lt;&gt;" &gt; out.txt` (2认同)

anu*_*ava 14

请尝试此命令:

awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
Run Code Online (Sandbox Code Playgroud)


Tho*_*asH 8

Python解决方案

这是一个具有相同功能的 Python 单行代码,已使用 Python 3.9.10 和 2.7.18 进行了测试。它比 Caleb 的 perl 解决方案快约 60%,并且输出相同(使用包含 1480 万行的 300MiB 单词列表文件进行测试)。

python -c 'import sys; sys.stdout.writelines(sorted(sys.stdin.readlines(), key=len))'
Run Code Online (Sandbox Code Playgroud)

基准:

python -c 'import sys; sys.stdout.writelines(sorted(sys.stdin.readlines(), key=len))'
real    0m5.308s
user    0m3.733s
sys     0m1.490s

perl -e 'print sort { length($a) <=> length($b) } <>'
real    0m8.840s
user    0m7.117s
sys     0m2.279s
Run Code Online (Sandbox Code Playgroud)


Chr*_*nat 7

基准测试结果

以下是基准测试的结果.

另外,我添加了另一个Perl解决方案:
perl

实验使用:

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

结果:

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

  • 超级优雅,你把额外的工作放在时间.谢谢! (2认同)
  • 我不知道,但如果你尝试一下请告诉我们 (2认同)

Jon*_*ler 5

length()函数确实包含空格。我只会对您的管道进行细微调整(包括避免UUOC)。

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

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

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


Fri*_*ner 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
Run Code Online (Sandbox Code Playgroud)