2017-01-23 115 views
3

我有一个名称的列表,这些名称是乱序的。我怎样才能得到他们在正确的字母数字顺序,使用定制排序为字母部分?使用自定义排序顺序在Unix中对字母数字字符串进行排序

我的文件numbers.txt

alpha-1 
beta-3 
alpha-10 
beta-5 
alpha-5 
beta-1 
gamma-7 
gamma-1 
delta-10 
delta-2 

主要的一点是,我的脚本应该认识到,应该beta之前打印alpha,并betagamma面前,gammadelta之前。

也就是说,单词应该根据它们代表的希腊字母中的字母顺序排序。

预期的顺序:

alpha-1 
alpha-5 
alpha-10 
beta-1 
beta-3 
beta-5 
gamma-1 
gamma-7 
delta-2 
delta-10 

PS:我试图与sort -n numbers.txt,但它不适合我的需要。

+1

[排序多个密钥对Unix排序(http://stackoverflow.com/questions/357560/sorting-multiple-keys-with-unix-sort) –

+1

正如前面所说的可能的复制,主要点是有一个特定的顺序来尊重(alpha,beta,gamma和delta)。我认为'sort -k'不会解决这个问题。没有? – Daniel

+1

我现在看到的更清楚你想做什么,不幸的是,用简单的'sort'并不是真的可以做到。有可能使用'awk',但最好的解决方案可能是创建一个程序(例如Python)来进行排序。 –

回答

2

您可以使用指令如下:

awk -F- -v keysInOrder="alpha,beta,gamma,delta" ' 
    BEGIN { 
     split(keysInOrder, a, ",") 
     for (i = 1; i <= length(a); ++i) keysToOrdinal[a[i]] = i 
    } 
    { print keysToOrdinal[$1] "-" $0 } 
' numbers.txt | sort -t- -k1,1n -k3,3n | cut -d- -f2- 
  • awk命令用于:

    • 映射用户键上反映所期望的排序顺序号码;请注意,必须按顺序通过变量keysInOrder传递完整的密钥列表。

    • 同样使用分隔符-将数字作为辅助列添加到输入中;例如beta-3变成2-beta-3,因为beta位于排序键的有序列表中的位置。

  • sort然后排序awk的输出由映射号码,以及原来的号码在第2列,从而产生所需的自定义排序顺序。

  • cut然后删除aux。再次映射数字。

+0

请参阅我的新问题:http://stackoverflow.com/questions/41835799/sort-a-find-command-to-respect-a-custom- order-in-unix – Daniel

0

通用的解决方案: 排序-T- -k 1,1 -k 2,2n numbers.txt

下面的脚本将自定义的要求工作。这不是最好的解决方案。 结果将被再次存储在numbers.txt

#!/bin/bash 

sort -t- -k 1,1 -k 2,2n numbers.txt > new_test.txt 
while IFS= read -r i 
do 
    if [[ $i == *"delta"* ]] 
    then 
     echo $i >> temp_file 
    else 
     echo $i >> new_numbers.txt 
    fi 
done < new_test.txt 
cat temp_file >> new_numbers.txt 
cat new_numbers.txt > numbers.txt 

rm -rf new_test.txt 
rm -rf temp_file 
rm -rf new_numbers.txt 
+0

谢谢,但它仍然在' gamma':/ – Daniel

+1

因为'delta'字母顺序位于'gamma'之前。如果您正在寻找能够识别以拉丁字母*写成的希腊*字母名称并按希腊字母顺序排列的东西,我认为您可能需要自己写一些东西。 –

+0

更新后的答案现在只处理一个特例 - 这会导致非常麻烦的推广,而且效率也很低。 – mklement0

0

如果你有机会获得AWK,然后sed的尝试这个

希腊订货增加的变化..

 
cat test.txt | awk -F "-" '{ printf "%s-%0100i\n" , $1, $2 }' | \ 
sed 's/^alpha-\(.*\)$/01-\1/' | \ 
sed 's/^beta-\(.*\)$/02-\1/' | \ 
sed 's/^gamma-\(.*\)$/03-\1/' | \ 
sed 's/^delta-\(.*\)$/04-\1/' | \ 
sort | \ 
sed 's/\(.*\)-\([0]*\)\(.*\)/\1-\3/' | \ 
sed 's/^01-\(.*\)$/alpha-\1/' | \ 
sed 's/^02-\(.*\)$/beta-\1/' | \ 
sed 's/^03-\(.*\)$/gamma-\1/' | \ 
sed 's/^04-\(.*\)$/delta-\1/' 
+1

这有效,但由于多个'sed'命令,效率非常低;请注意,您可以将它们全部组合到一个_single_'sed'脚本中,'''调用由''分隔;' 您不需要在输入中填充数字以实现数字排序;相反,使用'sort'和'-n'选项(字段选择性):'sort -t- -k 1,1n -k 2,2n'。 如果您在排序之前没有使用数字映射替换单词,但将_映射的数字作为(临时)第一个字段添加到_prepended_,那么您只需要使用 即可在排序之后使用'cut - d- -f2-' - 不再需要'sed' – mklement0

1

我会在这里找到Perl。该脚本将工作:

#!/usr/bin/env perl 
use v5.14;   # turn on modern features 

# Greek alphabet 
my @greek_letters =qw(alpha beta  gamma delta epsilon zeta 
         eta theta iota kappa lambda mu 
         nu xi  omicron pi rho  sigma 
         tau upsilon phi  chi psi  omega); 

# An inverted map from letter name to position number; 
# $number{alpha} = 1, $number{beta} = 2, etc: 
my %number; 
@number{@greek_letters} = [email protected]_letters; 

# Read the lines to sort 
chomp(my @lines = <>); 

# split on hyphen into arrays of individual fields 
my @rows = map { [ split /-/ ] } @lines; 

# prepend the numeric position of each item's Greek letter 
my @keyed = map { [ $number{$_->[0]}, @$_ ] } @rows; 

# sort by Greek letter position (first field, index 0) and then 
# by final number (third field, index 2) 
my @sorted = sort { $a->[0] <=> $b->[0] 
        || $a->[2] <=> $b->[2] } @keyed; 

# remove the extra field we added 
splice(@$_, 0, 1) for @sorted; 

# combine the fields back into strings and print them out 
say join('-', @$_) for @sorted; 

保存Perl代码到一个文件中(比如,greeksort.pl),然后运行perl greeksort.pl numbers.txt,让您的有序输出。

+2

有人抱怨说Perl不容易破译? ;) –

+1

我不觉得比其他答案中的Python代码更难阅读,但我添加了内联评论以帮助易读。 –

1

这是一个Python解决方案。不要试图用Bash,sed,awk做很多事情。通常你可以完成你想要的任务,但它会更混乱,更容易出错,而且难以维护。

#!/usr/bin/env python3 

# Read input lines 
use_stdin = True 
if use_stdin: 
    import sys 
    lines = sys.stdin.read().strip().split() 
else: 
    # for testing 
    with open('numbers.txt') as input: 
     lines = input.read().strip().split() 

# Create a map from greek letters to integers for sorting 
greek_letters = """alpha beta  gamma delta epsilon zeta 
        eta theta iota kappa lambda mu 
        nu xi  omicron pi rho  sigma 
        tau upsilon phi  chi psi  omega""" 
gl = greek_letters.strip().split() 
gl_map = {letter:rank for rank, letter in enumerate(gl)} 

# Split each line into (letter, number) 
a = (x.split('-') for x in lines) 
b = ((s, int(n)) for s,n in a) 

# Using an order-preserving sort, sort by number, then letter 
by_number = lambda x: x[1] 
by_greek_letter = lambda x: gl_map.get(x[0]) 
c = sorted(sorted(b, key=by_number), key=by_greek_letter) 

# Re-assemble and print 
for s,n in c: 
    print('-'.join((s, str(n))))