我有一行(或多行)由任意字符分隔的数字。我可以使用哪些 UNIX 工具对每行的项目进行数字排序,并保留分隔符?
示例包括:
- 号码列表;输入:
10 50 23 42
;排序:10 23 42 50
- IP地址;输入:
10.1.200.42
;排序:1.10.42.200
- CSV;输入:
1,100,330,42
;排序:1,42,100,330
- 以竖线分隔;输入:
400|500|404
;排序:400|404|500
由于分隔符是任意的,因此请随意使用您选择的单字符分隔符来提供(或扩展)答案。
答案1
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
*
将字段分隔符替换SEP='*'
为您的分隔符。
如果是单行,您还可以使用以下命令(因为最好不要使用 shell 循环进行文本处理)
tr '.' '\n' <<<"$aline" | sort -n | paste -sd'.' -
代替点 .
与你的分隔符。
添加-u
到sort
上面的命令以删除重复项。
笔记:
您可能需要使用-g, --general-numeric-sort
选项 ofsort
而不是-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
不需要awk
改变,它仍然会处理这些。
答案2
使用perl
有一个明显的版本;分割数据,排序,然后再次连接起来。
分隔符需要列出两次(一次在 the 中split
,一次在 the 中join
)
例如对于一个,
perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
所以
echo 1,100,330,42 | perl -lpi -e '$_=join(",",sort {$a <=> $b} split(/,/))'
1,42,100,330
由于 是split
正则表达式,因此该字符可能需要引用:
echo 10.1.200.42 | perl -lpi -e '$_=join(".",sort {$a <=> $b} split(/\./))'
1.10.42.200
通过使用-a
和-F
选项,可以消除分割。使用-p
循环,像以前一样并将结果设置为$_
,这将自动打印:
perl -F'/\./' -aple '$_=join(".", sort {$a <=> $b} @F)'
答案3
使用Python和类似的想法斯蒂芬·哈里斯的回答:
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>
所以像这样:
$ 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
遗憾的是,必须手动执行 I/O 使得它远不如 Perl 版本优雅。
答案4
重击脚本:
#!/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[@]}"
例子:
$ ./sort_delimited_string.sh "." "192.168.0.1"
0.1.168.192
基于