Sort
AWK 는 array 에 값이 입력될 때 기본적으로 정렬을 하지 않습니다.
다시 말해서 array 에 값이 입력된 순서대로 출력이 되지 않습니다.
index 나 value 별로 정렬하여 출력하려면 PROCINFO["sorted_in"]
변수를 설정하는 방법이나
asort()
, asorti()
함수를 이용해야 합니다.
다음은 테스트를 위한 코드인데요. index 값으로는 두 자리 정수 random 넘버를 사용하였고, value 값은 실제 array 에 입력된 순서가 됩니다. 출력 결과를 보면 index 도 정렬이 안 돼있고 array 에 입력된 순서에 따라 출력도 되지 않고 있습니다.
$ awk 'BEGIN{ srand()
for ( i=0; i<10; i++ )
arr[ int(rand() * 100 - 50) ] = i # index 는 random, value 는 입력 순서가 된다.
for ( i in arr )
printf "index %3d : value %d\n", i, arr[i]
}'
index 15 : value 9
index 17 : value 2
index 0 : value 6
index -25 : value 3
index -9 : value 8
index -16 : value 1
index 40 : value 7
index 48 : value 4
index -43 : value 0
index 27 : value 5
array 에서 index 가 자동으로 정렬되는 경우가 한가지 있는데요. 바로 index 값이 양의 정수일 때입니다. 다음을 보면 index 값으로 양의 정수 random 넘버를 사용하였는데 index 가 순서대로 정렬되어 출력되는 것을 볼 수 있습니다. 따라서 array 에 값을 입력할 때 입력된 순서대로 출력하고 싶으면 index 값을 양의 정수로 하면 됩니다.
$ awk 'BEGIN{ srand()
for ( i=1; i<=10; i++ )
arr[ int(rand() * 100) ] = i
for ( i in arr )
printf "index %3d : value %d\n", i, arr[i]
}'
index 1 : value 3
index 7 : value 7
index 13 : value 6
index 39 : value 1
index 45 : value 5
index 67 : value 4
index 77 : value 9
index 90 : value 10
index 97 : value 8
# index 값이 random 넘버이기 때문에 중복될 수가 있어서
# array 원소 개수가 10 개 미만으로 나올 수가 있습니다.
awk 는 PROCINFO["sorted_in"]
변수값 설정을 통해서 index 나 value 별로 정렬을 하여 출력할 수가 있습니다.
기본적으로 아래 표에서 보는 바와 같이 숫자 or 스트링 비교를 통한 오름, 내림차순 정렬을 지원하지만
사용자가 직접 비교 함수를 작성하여 적용할 수도 있습니다.
이 설정은 for ( idx in array )
문으로 array 값을 출력할 때 적용되는 것으로
필요할 때마다 for 문 이전에 위치하여 여러 번 사용할 수 있습니다.
PROCINFO ["sorted_in"] | Description |
---|---|
@unsorted | index 를 정렬하지 않습니다. ( default 값입니다. ) ( index 가 양의 정수일 경우 정렬됩니다. ) |
@ind_str_asc | index 값을 스트링 비교를 해서 ascending order 정렬합니다. |
@ind_str_desc | index 값을 스트링 비교를 해서 descending order 정렬합니다. |
@ind_num_asc | index 값을 숫자 비교를 해서 ascending order 정렬합니다. ( 숫자가 아닌 경우 0 으로 취급됩니다. ) |
@ind_num_desc | index 값을 숫자 비교를 해서 descending order 정렬합니다. |
@val_str_asc | value 값을 스트링 비교를 해서 ascending order 정렬합니다. |
@val_str_desc | value 값을 스트링 비교를 해서 descending order 정렬합니다. |
@val_num_asc | value 값을 숫자 비교를 해서 ascending order 정렬합니다. |
@val_num_desc | value 값을 숫자 비교를 해서 descending order 정렬합니다. |
@val_type_asc | value 값의 type 에 따라 ascending order 정렬합니다. 숫자로 된 값이 제일 먼저 나오고 다음으로 스트링, subarray 순서로 정렬합니다. 숫자->스트링->subarray |
@val_type_desc | value 값의 type 에 따라 descending order 정렬합니다. subarray->스트링->숫자 순서로 정렬합니다. |
다음은 @ind_num_asc
값 설정을 통하여 음수가 포함된 index 값을 정렬하여 출력하는 예입니다.
$ awk 'BEGIN{ srand()
for ( i=0; i<10; i++ )
arr[ int(rand() * 100 - 50) ] = i
PROCINFO["sorted_in"]="@ind_num_asc"
for ( i in arr )
printf "index %3d : value %d\n", i, arr[i]
}'
index -43 : value 9
index -35 : value 6
index -27 : value 3
index -11 : value 5
index -9 : value 0
index -7 : value 4
index 2 : value 7
index 19 : value 1
index 30 : value 2
index 47 : value 8
다음은 두 개의 for (idx in array)
문에 대해서 각각 PROCINFO["sorted_in"]
값을 적용시켰을 때
출력값이 어떻게 변하는지 비교해 보세요
$ awk 'BEGIN {
a["ccc"] = "aaa3"
a["aaa"] = "aaa1"
a["bbb"] = "aaa2"
b["bbb"] = "bbb2"
b["aaa"] = "bbb1"
b["ccc"] = "bbb3"
for ( i in a ) print a[i]
for ( j in b ) print b[j]
}'
aaa1 # 1,3,2 정렬 안됨
aaa3
aaa2
bbb1 # 1,3,2 정렬 안됨
bbb3
bbb2
----------------------------------
$ awk 'BEGIN {
a["ccc"] = "aaa3"
a["aaa"] = "aaa1"
a["bbb"] = "aaa2"
b["bbb"] = "bbb2"
b["aaa"] = "bbb1"
b["ccc"] = "bbb3"
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in a ) print a[i]
for ( j in b ) print b[j]
}'
aaa1 # 1,2,3 asc 정렬
aaa2
aaa3
bbb1 # 1,2,3 asc 정렬
bbb2
bbb3
--------------------------------------------
$ awk 'BEGIN {
a["ccc"] = "aaa3"
a["aaa"] = "aaa1"
a["bbb"] = "aaa2"
b["bbb"] = "bbb2"
b["aaa"] = "bbb1"
b["ccc"] = "bbb3"
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in a ) print a[i]
PROCINFO["sorted_in"] = "@ind_str_desc"
for ( j in b ) print b[j]
}'
aaa1 # 1,2,3 asc 정렬
aaa2
aaa3
bbb3 # 3,2,1 desc 정렬
bbb2
bbb1
##################### 두 번째 예제 #########################
$ awk 'BEGIN {
a["ccc"]["ccc"] = "ccc3"
a["ccc"]["aaa"] = "ccc1"
a["ccc"]["bbb"] = "ccc2"
a["aaa"]["bbb"] = "aaa2"
a["aaa"]["aaa"] = "aaa1"
a["aaa"]["ccc"] = "aaa3"
a["bbb"]["aaa"] = "bbb1"
a["bbb"]["ccc"] = "bbb3"
a["bbb"]["bbb"] = "bbb2"
for ( i in a )
for ( j in a[i] )
print a[i][j]
}'
aaa1 # aaa,ccc,bbb 정렬 안됨
aaa3 # 1, 3, 2 정렬 안됨
aaa2
ccc1
ccc3
ccc2
bbb1
bbb3
bbb2
----------------------------------
$ awk 'BEGIN {
a["ccc"]["ccc"] = "ccc3"
a["ccc"]["aaa"] = "ccc1"
a["ccc"]["bbb"] = "ccc2"
a["aaa"]["bbb"] = "aaa2"
a["aaa"]["aaa"] = "aaa1"
a["aaa"]["ccc"] = "aaa3"
a["bbb"]["aaa"] = "bbb1"
a["bbb"]["ccc"] = "bbb3"
a["bbb"]["bbb"] = "bbb2"
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in a )
for ( j in a[i] )
print a[i][j]
}'
aaa1 # aaa,bbb,ccc asc 정렬
aaa2 # 1, 2, 3 asc 정렬
aaa3
bbb1
bbb2
bbb3
ccc1
ccc2
ccc3
------------------------------------------
$ awk 'BEGIN {
a["ccc"]["ccc"] = "ccc3"
a["ccc"]["aaa"] = "ccc1"
a["ccc"]["bbb"] = "ccc2"
a["aaa"]["bbb"] = "aaa2"
a["aaa"]["aaa"] = "aaa1"
a["aaa"]["ccc"] = "aaa3"
a["bbb"]["aaa"] = "bbb1"
a["bbb"]["ccc"] = "bbb3"
a["bbb"]["bbb"] = "bbb2"
PROCINFO["sorted_in"] = "@ind_str_asc"
for ( i in a ) {
PROCINFO["sorted_in"] = "@ind_str_desc"
for ( j in a[i] )
print a[i][j]
}
}'
aaa3 # aaa,bbb,ccc asc 정렬
aaa2 # 3, 2, 1 desc 정렬
aaa1
bbb3
bbb2
bbb1
ccc3
ccc2
ccc1
asort, asorti 함수
asort()
, asorti()
함수를 이용하는 방법은 array 에서 index 나 value 값만 따로 분리하여
양의 정수를 index 로 가지는 정렬된 array 를 만들고자 할때 사용합니다.
asort()
함수는 value 를 type_asc 정렬하고 ( @val_type_asc
와 같이 )
asorti()
함수는 index 를 type_asc 정렬합니다.
# asort() 함수를 이용한 value 정렬
# 기존 array 는 삭제되고 양의 정수를 index 로 갖는 정렬된 array 가 생성된다.
$ awk '
BEGIN {
arr["ZZ"] = 30
arr["YY"] = 10
arr[70] = "BB"
arr[90] = 20
arr[80] = "AA"
asort(arr)
for ( i in arr )
printf "index %s : value %s\n", i, arr[i]
}'
index 1 : value 10
index 2 : value 20
index 3 : value 30
index 4 : value AA
index 5 : value BB
# asorti() 함수를 이용한 index 정렬
# 기존 array 는 삭제되고 양의 정수를 index 로 갖는 정렬된 array 가 생성된다.
$ awk '
BEGIN {
arr["ZZ"] = 30
arr["YY"] = 10
arr[70] = "BB"
arr[90] = 20
arr[80] = "AA"
asorti(arr)
for ( i in arr )
printf "index %s : value %s\n", i, arr[i]
}'
index 1 : value 70
index 2 : value 80
index 3 : value 90
index 4 : value YY
index 5 : value ZZ
기존 array 가 삭제되면 안 될 경우 두 번째 인수를 사용할 수 있습니다. 이때는 기존의 array 가 변경되지 않은 상태로 남아있게 되고 정렬된 결과는 dest array 에 설정됩니다.
asort(source, dest)
asorti(source, dest)
이 함수에서도 세 번째 인수를 사용하여 사용자 정의 비교 함수를 적용할 수 있습니다.
asort(source, dest, "my_compare_func")
asorti(source, dest, "my_compare_func")
사용자 정의 비교 함수
위에서 알아본 방법들은 기본적으로 array 의 index 와 value 값만을 가지고 정렬을 하기 때문에 가령 index 와 value 두 개의 값을 가지고 정렬을 한다거나 다차원 배열을 사용하는 환경에서 ORDER BY 절을 구현하기가 어려운 경우가 있습니다. 이때는 사용자 정의 비교 함수를 사용해야 합니다.
사용자 정의 비교 함수를 작성하는 방법은 먼저 함수의 매개변수로
첫 번째 원소의 index 와 value, 두 번째 원소의 index 와 value, 4 개를 기본적으로 사용합니다.
return 값으로 0
보다 작은 값이 사용될 경우 첫 번째 원소가 두 번째 원소보다 앞에 오게 되고
0
보다 큰 값이 사용되면 두 번째 원소가 첫 번째 원소보다 앞에 오게 됩니다.
return 값이 0
이면 함께 오게 됩니다.
# i1, v1 는 비교되는 첫 번째 원소의 index 와 value 에 해당하고
# i2, v2 는 비교되는 두 번째 원소의 index 와 value 에 해당합니다.
function comp_func(i1, v1, i2, v2 ...)
{
# comp_func(i1, v1, i2, v2) < 0
# Index i1 comes before index i2 during loop traversal.
#
# comp_func(i1, v1, i2, v2) == 0
# Indices i1 and i2 come together,
# but the relative order with respect to each other is undefined.
#
# comp_func(i1, v1, i2, v2) > 0
# Index i1 comes after index i2 during loop traversal.
return ?
}
작성 예제 )
$ awk -f - <<\EOF
BEGIN {
letters = "abcdefghijklmnopqrstuvwxyz" \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
split(letters, arr, "")
PROCINFO["sorted_in"] = "compare"
for( i in arr ) { printf "%s ", arr[i] }
print ""
}
function compare(i1, v1, i2, v2, l, r)
{
l = tolower(v1)
r = tolower(v2)
if (l < r) return -1
else if (l == r) return 0
else return 1
}
EOF
a A b B c C d D e E f F g G h H i I j J k K l L m M n N o O .....
Quiz
아래 데이터를 보면 첫번째 컬럼 값이 중복되어 나타나는데요.
처음 하나만 출력하되 순서를 유지해서 출력하려면 어떻게 할까요?
단 PROCINFO["sorted_in"]
는 사용하지 않습니다.
$ cat data.txt
66115f5 HEAD@{1}: reset: moving to 66115f5
49436cf HEAD@{3}: reset: moving to 49436cf
66115f5 HEAD@{5}: commit: update
49436cf HEAD@{6}: reset: moving to 49436cf
aab4238 HEAD@{7}: reset: moving to aab4238
49436cf HEAD@{8}: commit: update
aab4238 HEAD@{9}: reset: moving to HEAD
aab4238 HEAD@{10}: reset: moving to HEAD
a[i++]
와같이 index 로 양의 정수를 사용하면 입력 순서를 유지할 수 있습니다.!($1 in b)
와b[$1]
는 첫번째 컬럼 값의 중복 제거를 위한 것.
$ cat data.txt | awk '!($1 in b) {a[i++]=$0; b[$1]} END {for (i in a) print a[i]}'
66115f5 HEAD@{1}: reset: moving to 66115f5
49436cf HEAD@{3}: reset: moving to 49436cf
aab4238 HEAD@{7}: reset: moving to aab4238