可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a list of names, which are out of order. How can I get them in the correct alphanumeric order, using a custom sort order for the alphabetical part?
My file numbers.txt
:
alpha-1
beta-3
alpha-10
beta-5
alpha-5
beta-1
gamma-7
gamma-1
delta-10
delta-2
The main point is that my script should recognize that it should print alpha
before beta
, and beta
before gamma
, and gamma
before delta
.
That is, the words should be sorted based on the order of the letters in the Greek alphabet they represent.
Expected order:
alpha-1
alpha-5
alpha-10
beta-1
beta-3
beta-5
gamma-1
gamma-7
delta-2
delta-10
PS: I tried with sort -n numbers.txt
, but it doesn't fit my need.
回答1:
You can use an auxiliary awk
command as follows:
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-
The awk
command is used to:
map the custom keys onto numbers that reflect the desired sort order; note that the full list of keys must be passed via variable keysInOrder
, in order.
prepend the numbers to the input as an auxiliary column, using separator -
too; e.g., beta-3
becomes 2-beta-3
, because beta
is in position 2 in the ordered list of sort keys.
sort
then sorts awk
's output by the mapped numbers as well as the original number in the 2nd column, yielding the desired custom sort order.
cut
then removes the aux. mapped numbers again.
回答2:
Here's a Python solution. Don't try to do hard things with Bash, sed, awk. You can usually accomplish what you want, but it'll be more confusing, more error prone, and harder to maintain.
#!/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))))
回答3:
I would reach for Perl here. This script will work:
#!/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} = 1..@greek_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;
Save the Perl code into a file (say, greeksort.pl
) and run perl greeksort.pl numbers.txt
to get your sorted output.
回答4:
Generic solution:
sort -t- -k 1,1 -k 2,2n numbers.txt
Below script will work for custom requirement. It is not the best solution.
Result will be again stored in 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
回答5:
If you have access to awk and sed then try this
Adding changes for Greek ordering..
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/'
回答6:
Don't try to do hard things with Bash, sed, awk
yeah, use an actuall shell and non-gnu userland commands. not much easier to code in the first place but at least won't be prone to random bugs introduced by idiotic maintainers who do not have a clue regarding backwards compatibility