我希望有一些帮助函数进行数组排序,我可以 随时随地使用。所以我做了这样的事情。它有效。
#!/usr/bin/bash
# sorts an array given as param
function array_sort {
declare -a source_array=("${!1}")
sorted_array=($(for elmnt in "${source_array[@]}"; do echo $elmnt; done | sort))
echo "${sorted_array[@]}" # wont echo to stdout, because of assignment to a variable
}
# TEST CASE
# orginal array
arr=(c b a 3 2 1)
#assign function call to a variable
sorted=$(array_sort arr[@])
# echo-out the results
echo ${sorted[@]}
我的问题是,在从函数返回数组元素(没有实现更好的排序算法)方面,是否有一些更好(更干净)的方法呢?
答案 0 :(得分:1)
如果你想要一个强大的排序功能(即能完美处理空格和换行的那个),你可以考虑在Bash中实现一个排序算法:这里是一个快速排序。
quicksort() {
# sorts the positional elements wrt alphanumerical sort
# return is in array quicksort_ret
if (($#==0)); then
quicksort_ret=()
return
fi
local pivot=$1 greater=() lower=() i
shift
for i; do
if [[ "$i" < "$pivot" ]]; then
lower+=( "$i" )
else
greater+=( "$i" )
fi
done
quicksort "${greater[@]}"
greater=( "${quicksort_ret[@]}" )
quicksort "${lower[@]}"
quicksort_ret+=( "$pivot" "${greater[@]}" )
}
$ quicksort c b a 3 2 1
$ printf '%s\n' "${quicksort_ret[@]}"
1
2
3
a
b
c
您可以在行
中更改订购测试if [[ "$i" < "$pivot" ]]; then
无论你喜欢什么,都可以。例如,对于仅数字排序,您可以使用
if ((i<pivot)); then
您甚至可以使用将扩展为排序函数的变量(例如,quicksort_order)。在这种情况下,请用
替换前一行if $quicksort_order "$i" "$pivot"; then
并使用,例如,如果你想要字母数字排序:
order_alnum() { [[ $1 < $2 ]]; }
quicksort_order=order_alnum
quicksort
函数使用输入的位置参数和输出的变量quicksort_ret
。现在很容易围绕这个函数创建一个包装器来处理数组名称作为输入。
对于一种方法,与您的方法一样,使用sort
但修复了通配符和空格的问题(但没有解决换行问题)。使用内置mapfile
,所以这只是Bash≥4。对于Bash&lt; 4,还有其他解决方法(但是你不应该再使用Bash&lt; 4了)。
#!/usr/bin/bash
# sorts an array given as param
# return is in array sorted_array
array_sort() {
mapfile -t sorted_array < <( printf '%s\n' "${!1}" | sort )
}
# TEST CASE 1
echo "Test 1"
# original array
arr=(c b a 3 2 1)
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"
# TEST CASE 2
echo "Test 2"
# original array
arr=( '*' 'a space in this field' )
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"
# TEST CASE 3 (fails)
echo "Test 3"
# original array
arr=( $'there is\na newline\nin this array' )
# sort array
array_sort "arr[@]"
# display array
declare -p "sorted_array"
将输出:
Test 1
declare -a sorted_array='([0]="1" [1]="2" [2]="3" [3]="a" [4]="b" [5]="c")'
Test 2
declare -a sorted_array='([0]="*" [1]="a space in this field")'
Test 3
declare -a sorted_array='([0]="a newline" [1]="in this array" [2]="there is")'
在评论中回答您的问题:
这样我就必须知道
sorted_array
变量的名称,才能在我的脚本中使用它。这可以避免吗?
如果要提供已排序数组的名称,请将array_sort
修改为:
array_sort() {
# $1 is the name of array to sort (with the trailing [@])
# $2 is the name of the returned array (without [@])
# Note: name of output array can be name of input array
mapfile -t "$2" < <( printf '%s\n' "${!1}" | sort )
}
并用作:
$ a=( a g e z j r )
$ array_sort "a[@]" a_sorted
$ declare -p a_sorted
declare -a a_sorted='([0]="a" [1]="e" [2]="g" [3]="j" [4]="r" [5]="z")'
如果你想在我的第一个答案中使用quicksort
函数,你可以使用包装函数(抱歉名称)(*):
quicksort_gniourf() {
# $1 is the name of array to sort (with the trailing [@])
# $2 is the name of the returned array (without [@])
# Note: name of output array can be name of input array
# This is a wrapper function around the quicksort function
quicksort "${!1}"
local k=0 v
declare -g "$2=()"
for v in "${quicksort_ret[@]}"; do
printf -v "$2[$k]" '%s' "$v"
((++k))
done
}
并用作(这里我使用相同的数组名称输入和输出):
$ a=( a g e z j r )
$ quicksort_gniourf "a[@]" a
$ declare -p a
declare -a a='([0]="a" [1]="e" [2]="g" [3]="j" [4]="r" [5]="z")'
另外,你如何回应结果数组,阻止
*
的扩展,declare -p
可以,但printf
或echo会扩展文件名?
使用array
打印数组echo
而不扩展通配符(观察引号):
echo "${array[@]}"
并使用printf
,每行一个字段(观察引号):
printf '%s\n' "${array[@]}"
(*)正如@konsolebox在评论中提到的那样,declare -g
出现在bash 4.2中。如果您愿意,可以将此行替换为eval "$2=()"
(此时它相当安全,因为$2
应该是变量名称。)
答案 1 :(得分:0)
您可以按如下方式对数组进行排序:
arr=(c b a 3 2 1)
sarr=( $(sort < <(printf "%s\n" "${arr[@]}")) )
printf "%s\n" "${sarr[@]}"
1
2
3
a
b
c
编辑:将其变为函数:
array_sort() {
declare -a source_array=("${!1}");
sarr=( $(sort < <(printf "%s\n" "${arr[@]}")) );
echo "${sarr[@]}";
}
答案 2 :(得分:0)
从PlayShell&#39; array/sort.sh转发,这是一个使用Quicksort算法的纯Bash解决方案。
# ---- array.sh ----
# array_copy (avar <src>, avar <dest>) :: boolean
#
# Copies a whole array including index (key) structure.
#
# For a faster method that does not copy key structure, see
# array_get_all().
#
# This function will return true status code even if the source array
# is empty. It may only return false if other problem occurs like for
# example if source or destination array is not an indexed array
# variable or if the two array variables are not compatible.
# On the other hand, array_get_all() returns false if source array is
# empty.
#
function array_copy {
local -i __I
eval "$2=() && for __I in \${!$1[@]}; do $2[__I]=\${$1[__I]}; done"
# I hope AVAR=() does not reset variable's attributes. I've been
# wondering if I should use 'unset AVAR\[*\]' instead. The latter
# version probably is a bit slower though since it's a builtin call.
}
# array_reset (avar <array>, [mixed <element_value>, ...])
#
# Clears an array or resets it to optional elements.
#
function array_reset {
eval "$1=(\"\${@:2}\")"
}
# ---- array/sort.sh ----
# ----------------------------------------------------------------------
# array/sort.sh
#
# A portable utility that provides a function that sorts an array of a
# specific type. The sorted output can be in the form of values or
# indices.
#
# This methods were based from QuickSort (the one described in
# "A Book on C 4th Ed.").
#
# Credits have got to be given to the authors of the book
# "A Book on C 4th Ed." for this great algorithm. The algorithm was
# originally described by someone and was completely explained in the
# book with an implementation that's written in C.
#
# I knew C from many sources but most of what I learned came from this
# book and I therefore recommend it to starters for a good start and
# also to experienced programmers for a good reference and new methods
# that they may discover from it.
#
# I hope you enjoy using these functions and/or algorithms.
#
# Author: konsolebox
# Copyright free, 2008-2013
# ----------------------------------------------------------------------
# array_sort
# (
# ["from=<array>"],
# ["type=<string|integer>"],
# ["to=<array>"],
# ["as=<values|indices>"],
# ["--" [ SOURCEVALUES[@] ]]
# )
#
function array_sort {
[[ $# -eq 0 ]] && return
local __FROM __TYPE __TO __AS
local -a __ARRAY
local -a -i __INDICES
while [[ $# -gt 0 ]]; do
case "$1" in
from=*)
__FROM=${1#from=}
;;
type=*)
__TYPE=${1#type=}
;;
to=*)
__TO=${1#to=}
;;
as=*)
__AS=${1#as=}
;;
--)
shift
break
;;
#beginsyntaxcheckblock
*)
array_sort_error "unknown parameter: $1"
;;
#endsyntaxcheckblock
esac
shift
done
#beginsyntaxcheckblock
[[ -n $__FROM && $__FROM != [[:alpha:]_]*([[:alpha:][:digit:]_]) ]] && \
array_sort_error "variable name not valid for the source array: $__FROM"
[[ -n $__TYPE && $__TYPE != @(string|integer) ]] && \
array_sort_error "argument is not valid for type: $__TYPE"
[[ -n $__TO && $__TO != [[:alpha:]_]*([[:alpha:][:digit:]_]) ]] && \
array_sort_error "variable name not valid for the target array: $__TO"
[[ -n $__AS && $__AS != @(values|indices) ]] && \
array_sort_error "argument is not valid for as: $__AS"
[[ -z $__FROM && $# -eq 0 ]] && \
array_sort_error "a source should be specified either by 'from=<array>' or '-- CONTENTS[@]'"
#endsyntaxcheckblock
if [[ $# -gt 0 ]]; then
__ARRAY=("$@")
elif [[ -n $__FROM ]]; then
array_copy "$__FROM" __ARRAY || \
array_sort_error "failed to make a temporary working copy of $__FROM."
fi
[[ -z $__TYPE ]] && __TYPE=string
[[ -z $__TO ]] && __TO=__
[[ -z $__AS ]] && __AS=values
__INDICES=("${!__ARRAY[@]}")
if [[ ${#__INDICES[@]} -gt 1 ]]; then
case "$__TYPE" in
string)
array_sort_strings 0 "$(( ${#__INDICES[@]} - 1 ))"
;;
integer)
array_sort_integers 0 "$(( ${#__INDICES[@]} - 1 ))"
;;
esac
fi
case "$__AS" in
values)
local -i I J=0
array_reset "$__TO"
eval "for I in \"\${__INDICES[@]}\"; do ${__TO}[J++]=\${__ARRAY[I]}; done"
;;
indices)
eval "$__TO=(\"\${__INDICES[@]}\")"
;;
esac
}
# array_sort_strings (uint LEFT, uint RIGHT)
#
function array_sort_strings {
[[ $1 -lt $2 ]] || return
local -i LEFT=$1 RIGHT=$2 PIVOT PARTITION
if array_sort_strings_findpivot; then
array_sort_strings_partition
array_sort_strings "$LEFT" "$(( PARTITION - 1 ))"
array_sort_strings "$PARTITION" "$RIGHT"
fi
}
# array_sort_strings_findpivot () :: boolean
#
function array_sort_strings_findpivot {
local -i A B C P MIDDLE
(( MIDDLE = LEFT + (RIGHT - LEFT) / 2 ))
(( A = __INDICES[LEFT] ))
(( B = __INDICES[MIDDLE] ))
(( C = __INDICES[RIGHT] ))
[[ ${__ARRAY[A]} > "${__ARRAY[B]}" ]] && (( A = $B, B = $A ))
[[ ${__ARRAY[A]} > "${__ARRAY[C]}" ]] && (( A = $C, C = $A ))
[[ ${__ARRAY[B]} > "${__ARRAY[C]}" ]] && (( B = $C, C = $B ))
if [[ ${__ARRAY[A]} < "${__ARRAY[B]}" ]]; then
PIVOT=$B
return 0
fi
if [[ ${__ARRAY[B]} < "${__ARRAY[C]}" ]]; then
PIVOT=$C
return 0
fi
for (( P = LEFT + 1; P < MIDDLE; ++P )); do
if [[ ${__ARRAY[P]} > "${__ARRAY[A]}" ]]; then
PIVOT=$P
return 0
fi
if [[ ${__ARRAY[P]} < "${__ARRAY[A]}" ]]; then
PIVOT=$A
return 0
fi
done
for (( P = MIDDLE + 1; P < RIGHT; ++P )); do
if [[ ${__ARRAY[P]} > "${__ARRAY[A]}" ]]; then
PIVOT=$P
return 0
fi
if [[ ${__ARRAY[P]} < "${__ARRAY[A]}" ]]; then
PIVOT=$A
return 0
fi
done
return 1
}
# array_sort_strings_partition ()
#
function array_sort_strings_partition {
local -i L R T
local P=${__ARRAY[PIVOT]}
for (( L = LEFT, R = RIGHT; L <= R; )); do
while [[ ${__ARRAY[__INDICES[L]]} < "$P" ]]; do
(( ++L ))
done
until [[ ${__ARRAY[__INDICES[R]]} < "$P" ]]; do
(( --R ))
done
[[ L -lt R ]] && (( T = __INDICES[L], __INDICES[L] = __INDICES[R], __INDICES[R] = T, ++L, --R ))
done
(( PARTITION = L ))
}
# array_sort_integers (uint LEFT, uint RIGHT)
#
function array_sort_integers {
[[ $1 -lt $2 ]] || return
local -i LEFT=$1 RIGHT=$2 PIVOT PARTITION
if array_sort_integers_findpivot; then
array_sort_integers_partition
array_sort_integers "$LEFT" "$(( PARTITION - 1 ))"
array_sort_integers "$PARTITION" "$RIGHT"
fi
}
# array_sort_integers_findpivot () :: boolean
#
function array_sort_integers_findpivot {
local -i A B C P MIDDLE
(( MIDDLE = LEFT + (RIGHT - LEFT) / 2 ))
(( A = __INDICES[LEFT] ))
(( B = __INDICES[MIDDLE] ))
(( C = __INDICES[RIGHT] ))
[[ __ARRAY[A] -gt __ARRAY[B] ]] && (( A = $B, B = $A ))
[[ __ARRAY[A] -gt __ARRAY[C] ]] && (( A = $C, C = $A ))
[[ __ARRAY[B] -gt __ARRAY[C] ]] && (( B = $C, C = $B ))
if [[ __ARRAY[A] -lt __ARRAY[B] ]]; then
PIVOT=$B
return 0
fi
if [[ __ARRAY[B] -lt __ARRAY[C] ]]; then
PIVOT=$C
return 0
fi
for (( P = LEFT + 1; P < MIDDLE; ++P )); do
if [[ __ARRAY[P] -gt __ARRAY[A] ]]; then
PIVOT=$P
return 0
fi
if [[ __ARRAY[P] -lt __ARRAY[A] ]]; then
PIVOT=$A
return 0
fi
done
for (( P = MIDDLE + 1; P < RIGHT; ++P )); do
if [[ __ARRAY[P] -gt __ARRAY[A] ]]; then
PIVOT=$P
return 0
fi
if [[ __ARRAY[P] -lt __ARRAY[A] ]]; then
PIVOT=$A
return 0
fi
done
return 1
}
# array_sort_integers_partition ()
#
function array_sort_integers_partition {
local -i L R T P
for (( L = LEFT, R = RIGHT, P = __ARRAY[PIVOT]; L <= R; )); do
for (( ; __ARRAY[__INDICES[L]] < P; ++L )); do
continue
done
for (( ; __ARRAY[__INDICES[R]] >= P; --R )); do
continue
done
[[ L -lt R ]] && (( T = __INDICES[L], __INDICES[L] = __INDICES[R], __INDICES[R] = T, ++L, --R ))
done
(( PARTITION = L ))
}
# array_sort_error (string <message>)
#
function array_sort_error {
echo "array_sort: $1"
exit 1
}
# ----------------------------------------------------------------------
# Footnotes:
#
# * In some versions of bash, conditional statements does not properly
# parse the second string operand so sometimes this form doesn't work:
#
# [[ $STRINGVAR1 < $STRINGVAR2 ]]
#
# So to make it work, we have no choice but put it around quotes:
#
# [[ $STRINGVAR1 < "$STRINGVAR2" ]]
#
# * In some versions of bash, a segmentation fault occurs when
# assignment statements where sources are arrays are compounded.
#
# (( A = __A0[INDEX1], B = __A0[INDEX2] ))
# ----------------------------------------------------------------------