Jef*_*ler 13 sort numeric-data
我有一行(或多行)由任意字符分隔的数字。我可以使用哪些 UNIX 工具按数字对每一行的项目进行排序,同时保留分隔符?
例子包括:
10 50 23 42
; 排序:10 23 42 50
10.1.200.42
; 排序:1.10.42.200
1,100,330,42
; 排序:1,42,100,330
400|500|404
; 排序:400|404|500
由于分隔符是任意的,请随意使用您选择的单字符分隔符提供(或扩展)答案。
αғs*_*нιη 14
使用gawk
( GNU awk
) 作为asort()
函数:
gawk -v SEP='*' '{ i=0; split($0, arr, SEP); len=asort(arr);
while ( ++i<=len ){ printf("%s%s", i>1?SEP:"", arr[i]) };
print ""
}' infile
Run Code Online (Sandbox Code Playgroud)
用您的分隔符替换*
为字段分隔符。SEP='*'
您也可以在单行的情况下使用以下命令(因为最好不要使用 shell-loops 进行文本处理)
tr '.' '\n' <<<"$aline" | sort -n | paste -sd'.' -
Run Code Online (Sandbox Code Playgroud)
用您的分隔符替换点 .
。
添加-u
到sort
上面的命令以删除重复项。
注意:
您可能需要使用-g, --general-numeric-sort
选项sort
而不是-n, --numeric-sort
来处理任何类别的数字(整数、浮点数、科学数、十六进制数等)。
$ aline='2e-18,6.01e-17,1.4,-4,0xB000,0xB001,23,-3.e+11'
$ tr ',' '\n' <<<"$aline" |sort -g | paste -sd',' -
-3.e+11,-4,2e-18,6.01e-17,1.4,23,0xB000,0xB001
Run Code Online (Sandbox Code Playgroud)
在awk
没有必要的改变,它仍然会处理这些。
Ste*_*ris 12
使用perl
有一个明显的版本;拆分数据,对其进行排序,然后将其重新连接起来。
分隔符需要列出两次(一次在 中split
一次在 中join
)
例如对于一个 ,
perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
Run Code Online (Sandbox Code Playgroud)
所以
echo 1,100,330,42 | perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
1,42,100,330
Run Code Online (Sandbox Code Playgroud)
由于split
是正则表达式,该字符可能需要引用:
echo 10.1.200.42 | perl -lpi -e '$_=join(".",sort {$a <=> $b} split(/\./))'
1.10.42.200
Run Code Online (Sandbox Code Playgroud)
通过使用-a
和-F
选项,可以删除拆分。使用-p
循环,和以前一样,将结果设置为$_
,这将自动打印:
perl -F'/\./' -aple '$_=join(".", sort {$a <=> $b} @F)'
Run Code Online (Sandbox Code Playgroud)
使用 Python 和与Stephen Harris 的回答类似的想法:
python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' <delmiter>
Run Code Online (Sandbox Code Playgroud)
所以像:
$ cat foo
10.129.3.4
1.1.1.1
4.3.2.1
$ python3 -c 'import sys; c = sys.argv[1]; sys.stdout.writelines(map(lambda x: c.join(sorted(x.strip().split(c), key=int)) + "\n", sys.stdin))' . < foo
3.4.10.129
1.1.1.1
1.2.3.4
Run Code Online (Sandbox Code Playgroud)
遗憾的是,必须手动执行 I/O 使得这远不如 Perl 版本优雅。
sed
对 IP 地址的八位字节进行排序sed
没有内置sort
函数,但如果您的数据在范围内受到足够的限制(例如 IP 地址),您可以生成一个 sed 脚本来手动实现简单的冒泡排序。基本机制是寻找无序的相邻数字。如果数字顺序不正确,则交换它们。
这sed
脚本本身包含针对每对无序数字的两个搜索和交换命令:一个用于前两对八位位组(强制出现尾随定界符以标记第三个八位位组的结尾),另一个用于搜索和交换命令。第二个用于第三对八位位组(以 EOL 结尾)。如果发生交换,程序会分支到脚本顶部,查找无序的数字。否则,它退出。
生成的脚本部分是:
$ head -n 3 generated.sed
:top
s/255\.254\./254.255./g; s/255\.254$/254.255/
s/255\.253\./253.255./g; s/255\.253$/253.255/
# ... middle of the script omitted ...
$ tail -n 4 generated.sed
s/2\.1\./1.2./g; s/2\.1$/1.2/
s/2\.0\./0.2./g; s/2\.0$/0.2/
s/1\.0\./0.1./g; s/1\.0$/0.1/
ttop
Run Code Online (Sandbox Code Playgroud)
这种方法将句点硬编码为分隔符,必须对其进行转义,否则对于正则表达式语法来说它将是“特殊的”(允许任何字符)。
要生成这样的 sed 脚本,此循环将执行以下操作:
#!/bin/bash
echo ':top'
for (( n = 255; n >= 0; n-- )); do
for (( m = n - 1; m >= 0; m-- )); do
printf '%s; %s\n' "s/$n\\.$m\\./$m.$n./g" "s/$n\\.$m\$/$m.$n/"
done
done
echo 'ttop'
Run Code Online (Sandbox Code Playgroud)
将该脚本的输出重定向到另一个文件,例如sort-ips.sed
.
示例运行可能如下所示:
ip=$((RANDOM % 256)).$((RANDOM % 256)).$((RANDOM % 256)).$((RANDOM % 256))
printf '%s\n' "$ip" | sed -f sort-ips.sed
Run Code Online (Sandbox Code Playgroud)
生成脚本的以下变体使用单词边界标记\<
并\>
消除第二次替换的需要。这还将生成的脚本的大小从 1.3 MB 减少到略低于 900 KB,同时大大减少了脚本本身的运行时间sed
(大约为原始脚本的 50%-75%,具体取决于所sed
使用的实现):
#!/bin/bash
echo ':top'
for (( n = 255; n >= 0; --n )); do
for (( m = n - 1; m >= 0; --m )); do
printf '%s\n' "s/\\<$n\\>\\.\\<$m\\>/$m.$n/g"
done
done
echo 'ttop'
Run Code Online (Sandbox Code Playgroud)
小智 5
加载高级语言需要时间。
对于几行,shell 本身可能就是一个解决方案。
我们可以使用外部命令sort
、和命令tr
。一种对于行排序非常有效,另一种对于将一个分隔符转换为换行符非常有效:
#!/bin/bash
shsort(){
while IFS='' read -r line; do
echo "$line" | tr "$1" '\n' |
sort -n | paste -sd "$1" -
done <<<"$2"
}
shsort ' ' '10 50 23 42'
shsort '.' '10.1.200.42'
shsort ',' '1,100,330,42'
shsort '|' '400|500|404'
shsort ',' '3 b,2 x,45 f,*,8jk'
shsort '.' '10.128.33.6
128.17.71.3
44.32.63.1'
Run Code Online (Sandbox Code Playgroud)
因为使用<<<
only所以需要bash。如果将其替换为here-doc,则该解决方案对 posix 有效。
这能够使用制表符、空格或 shell 全局字符 ( *
, ?
, [
) 对字段进行排序。不是换行符,因为每一行都在排序。
更改<<<"$2"
为<"$2"
处理文件名并调用它,如下所示:
shsort '.' infile
Run Code Online (Sandbox Code Playgroud)
整个文件的分隔符是相同的。如果这是一个限制,那么可以改进。
然而,处理只有 6000 行的文件需要 15 秒。确实,shell 并不是处理文件的最佳工具。
对于超过几行(超过几十行),最好使用真正的编程语言。awk 解决方案可能是:
#!/bin/bash
awksort(){
gawk -v del="$1" '{
split($0, fields, del)
l=asort(fields)
for(i=1;i<=l;i++){
printf( "%s%s" , (i==0)?"":del , fields[i] )
}
printf "\n"
}' <"$2"
}
awksort '.' infile
Run Code Online (Sandbox Code Playgroud)
对于上述相同的 6000 行文件,只需要 0.2 秒。
了解<"$2"
for 文件可以更改回<<<"$2"
for shell 变量内的行。
最快的解决方案是perl。
#!/bin/bash
perlsort(){ perl -lp -e '$_=join("'"$1"'",sort {$a <=> $b} split(/['"$1"']/))' <<<"$2"; }
perlsort ' ' '10 50 23 42'
perlsort '.' '10.1.200.42'
perlsort ',' '1,100,330,42'
perlsort '|' '400|500|404'
perlsort ',' '3 b,2 x,45 f,*,8jk'
perlsort '.' '10.128.33.6
128.17.71.3
44.32.63.1'
Run Code Online (Sandbox Code Playgroud)
如果您想对文件更改<<<"$a"
进行简单排序"$a"
并添加-i
到 perl 选项以使文件版本“就位”:
#!/bin/bash
perlsort(){ perl -lpi -e '$_=join("'"$1"'",sort {$a <=> $b} split(/['"$1"']/))' "$2"; }
perlsort '.' infile; exit
Run Code Online (Sandbox Code Playgroud)
重击脚本:
#!/usr/bin/env bash
join_by(){ local IFS="$1"; shift; echo "$*"; }
IFS="$1" read -r -a tokens_array <<< "$2"
IFS=$'\n' sorted=($(sort -n <<<"${tokens_array[*]}"))
join_by "$1" "${sorted[@]}"
Run Code Online (Sandbox Code Playgroud)
例子:
$ ./sort_delimited_string.sh "." "192.168.0.1"
0.1.168.192
Run Code Online (Sandbox Code Playgroud)
基于
归档时间: |
|
查看次数: |
3676 次 |
最近记录: |