답변:
실제로 그렇게 많은 코드가 필요하지는 않습니다.
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
지원 (한이 줄 바꿈이 아니라으로) 요소의 공백, 그리고 배쉬 3.x에서의 작업
예 :
$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]
참고 : @sorontar이있다 지적 요소와 같은 와일드 카드가 포함 된 경우 치료가 필요하다는 것을 *나 ?:
sorted = ($ (...)) 부분은 "split and glob"연산자를 사용하고 있습니다. 당신은 글로브을 끄고해야
set -f또는set -o noglob또는shopt -op noglob또는 같은 배열의 요소*파일 목록으로 확장됩니다.
결과는 다음 순서로 발생하는 6 가지 결과입니다.
IFS=$'\n'"${array[*]}"<<<sortsorted=($(...))unset IFSIFS=$'\n'이는 다음과 같은 방식으로 2와 5의 결과에 영향을 미치는 운영의 중요한 부분입니다.
주어진:
"${array[*]}" 첫 문자로 구분 된 모든 요소로 확장 IFSsorted=() 의 모든 문자를 분할하여 요소를 만듭니다 IFSIFS=$'\n' 새 줄 을 구분 기호로 사용하여 요소를 확장 한 다음 나중에 각 줄이 요소가되는 방식으로 만들어 지도록 설정합니다 . (즉, 새 줄로 나누기)
새로운 라인으로 구분하는 것이 중요합니다. 왜냐하면 그것이 sort운영 방식 (라인 당 정렬) 이기 때문입니다 . 새 줄 만으로 분할하는 것은 중요하지 않지만 공백이나 탭이 포함 된 요소를 유지해야합니다.
기본값 IFS은 space , tab , 그 뒤에 새 줄로 표시 되며 작업에 적합하지 않습니다.
sort <<<"${array[*]}"부분<<<여기에서 strings 이라고 "${array[*]}"하는는 위에서 설명한대로 의 확장을 가져 와서의 표준 입력에 공급합니다 sort.
이 예에서는 sort다음 문자열이 제공됩니다.
a c
b
f
3 5
때문에 sort 종류 , 그것은 생산 :
3 5
a c
b
f
sorted=($(...))부분$(...)라는 부분, 명령 치환은 , 그 내용 (원인 sort <<<"${array[*]}결과하면서, 일반 명령으로 실행)를 표준 출력 지금 어디가는 그 문자 등을 $(...)했다.
이 예에서는 단순히 쓰기와 비슷한 것을 생성합니다.
sorted=(3 5
a c
b
f
)
sorted 그런 다음 모든 리터럴에서이 리터럴을 분할하여 생성 된 배열이됩니다.
unset IFS이는의 값을 IFS기본값으로 재설정하며 좋은 방법입니다.
IFS나중에 스크립트 에 의존하는 어떤 문제도 발생하지 않도록하기위한 것입니다 . (그렇지 않으면 복잡한 스크립트에는 실용적이지 않은 것들을 바꿔 놓았다는 것을 기억해야합니다.)
IFS하나의 특정 종류의 공백이있는 경우 요소를 작은 조각으로 나눕니다. 좋은; not perfect :-)
unset IFS필요? IFS=명령 앞에 추가 하면 해당 명령의 변경 범위 만 지정하고 나중에 자동으로 이전 값으로 돌아갑니다.
sorted=()명령이 아니라 두 번째 변수 할당 이기 때문에 필요 합니다.
원래 답변 :
array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)
산출:
$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f
참고 (특수 문자 나 공백을 포함하는 값이 버전의 대처를 제외하고 줄 바꿈)
참고 readarray는 bash 4 이상에서 지원됩니다.
@Dimitre의 제안에 따라 편집 했습니다.
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
개행 문자가 올바르게 포함 된 정렬 요소를 이해하는 이점도 있습니다. 불행히도 @ruakh가 올바르게 신호를 보낸 것처럼 이것은 줄 바꿈 문자 로 일반 줄 바꿈 대신 사용할 옵션이 없기 때문에 결과 readarray가 정확 하다는 의미는 아닙니다 .readarrayNUL
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -z유용한 개선 사항입니다. -z옵션이 GNU 정렬 확장 이라고 가정합니다 .
sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z).. bash v3에서는 readarray를 사용할 수 없으므로 bash v4 대신 bash v3을 사용하는 경우에도 작동합니다.
< 와 결합 된 입력 리디렉션 ( ) 입니다. 또는 직관적으로 말하자면 파일이 아니기 때문 입니다. <(...)(printf "bla")
순수한 Bash 퀵 정렬 구현은 다음과 같습니다.
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
qsort() {
local pivot i smaller=() larger=()
qsort_ret=()
(($#==0)) && return 0
pivot=$1
shift
for i; do
if (( i < pivot )); then
smaller+=( "$i" )
else
larger+=( "$i" )
fi
done
qsort "${smaller[@]}"
smaller=( "${qsort_ret[@]}" )
qsort "${larger[@]}"
larger=( "${qsort_ret[@]}" )
qsort_ret=( "${smaller[@]}" "$pivot" "${larger[@]}" )
}
예를 들어,
$ array=(a c b f 3 5)
$ qsort "${array[@]}"
$ declare -p qsort_ret
declare -a qsort_ret='([0]="3" [1]="5" [2]="a" [3]="b" [4]="c" [5]="f")'
이 구현은 재귀 적입니다. 따라서 반복적 인 퀵 정렬이 있습니다 :
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
qsort() {
(($#==0)) && return 0
local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
qsort_ret=("$@")
while ((${#stack[@]})); do
beg=${stack[0]}
end=${stack[1]}
stack=( "${stack[@]:2}" )
smaller=() larger=()
pivot=${qsort_ret[beg]}
for ((i=beg+1;i<=end;++i)); do
if [[ "${qsort_ret[i]}" < "$pivot" ]]; then
smaller+=( "${qsort_ret[i]}" )
else
larger+=( "${qsort_ret[i]}" )
fi
done
qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
done
}
두 경우 모두 사용 순서를 변경할 수 있습니다. 문자열 비교를 사용했지만 산술 비교, wrt 파일 수정 시간 비교 등을 수행 할 수 있습니다. 적절한 테스트 만 사용하십시오. 좀 더 일반적인 것으로 만들 수도 있고 테스트 함수가 사용하는 첫 번째 인수를 사용할 수도 있습니다.
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
# First argument is a function name that takes two arguments and compares them
qsort() {
(($#<=1)) && return 0
local compare_fun=$1
shift
local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
qsort_ret=("$@")
while ((${#stack[@]})); do
beg=${stack[0]}
end=${stack[1]}
stack=( "${stack[@]:2}" )
smaller=() larger=()
pivot=${qsort_ret[beg]}
for ((i=beg+1;i<=end;++i)); do
if "$compare_fun" "${qsort_ret[i]}" "$pivot"; then
smaller+=( "${qsort_ret[i]}" )
else
larger+=( "${qsort_ret[i]}" )
fi
done
qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
done
}
그런 다음이 비교 기능을 가질 수 있습니다.
compare_mtime() { [[ $1 -nt $2 ]]; }
그리고 사용 :
$ qsort compare_mtime *
$ declare -p qsort_ret
현재 폴더의 파일을 수정 시간순으로 정렬합니다 (가장 먼저).
노트. 이 기능은 순수한 배쉬입니다! 외부 유틸리티도없고 서브 쉘도 없습니다! 그것들은 당신이 가질 수있는 재미있는 기호 (공백, 줄 바꿈 문자, 글로브 문자 등)로 안전합니다.
sort제공 하는 라인 기반 정렬 이 충분하면 sort+ read -a솔루션은 20 개 정도의 항목에서 시작하는 것이 더 빠르며 처리하는 요소가 많을수록 점점 더 빨라집니다. 예를 들어, Fusion Drive를 사용하여 OSX 10.11.1을 실행하는 2012 년 말 iMac에서 100 요소 배열 : ca. 0.03 초 ( qsort()) 대 ca. 0.005 초 ( sort+ read -a); 1000 요소 어레이 : ca. 0.375 초 ( qsort()) 대 ca. 0.014 초 ( sort+ read -a).
if [ "$i" -lt "$pivot" ]; then하여 확인한 경우 그렇지 않으면 해결 된 "2"< "10"이 true를 리턴했습니다. 나는 이것이 POSIX 대 Lexicographical이라고 믿는다. 또는 인라인 링크 일 수 있습니다.
배열 요소에서 특수 쉘 문자를 처리 할 필요가없는 경우 :
array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))
bash 를 사용 하면 외부 정렬 프로그램이 필요합니다.
zsh를 사용하면 외부 프로그램이 필요하지 않으며 특수 쉘 문자를 쉽게 처리 할 수 있습니다.
% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}"
3
5
a a
b
c
f
ksh 는 ASCIIset -s 로 정렬 해야 합니다.
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@" 물론 set 명령은 현재 위치 매개 변수가있는 경우 재설정합니다.
tl; dr :
배열을 정렬 a_in하고 결과를 저장하십시오 a_out(요소에는 개행 문자 가 포함되어 있지 않아야 함 [1]
).
배쉬 v4 + :
readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
배쉬 v3 :
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
antak 솔루션의 장점 :
실수로 글 로빙 (배열 요소를 파일 이름 패턴으로 우발적으로 해석)하는 것에 대해 걱정할 필요가 없으므로 글 로빙을 비활성화하기 위해 추가 명령이 필요하지 않습니다 ( set -f, set +f나중에 복원).
로 재설정 IFS에 대해 걱정할 필요가 없습니다 unset IFS. [2]
위의 내용은 Bash 코드와 외부 유틸리티 sort를 결합 하여 임의의 한 줄 요소 및 어휘 또는 숫자 정렬 (선택적 필드 별) 과 함께 작동 하는 솔루션입니다 .
성능 : 들어 약 20 원소 이상 ,이 될 것입니다 빠른 순수 배쉬 솔루션보다 - 상당히 점점 그래서 당신은 약 100 요소를 넘어 일단.
(정확한 임계 값은 특정 입력, 기계 및 플랫폼에 따라 다릅니다.)
printf '%s\n' "${a_in[@]}" | sort 정렬을 수행합니다 (기본적으로 기본적으로 sortPOSIX 사양 참조 ).
"${a_in[@]}"안전하게 배열의 요소로 확장 a_in등의 개별 인수 가 (공백 포함)를 포함 무엇이든.
printf '%s\n' 그런 다음 각 인수, 즉 각 배열 요소를 자체 줄에 그대로 인쇄합니다.
노트 (A)의 사용 방법 치환 ( <(...)) 에 대한 입력으로 정렬 된 출력을 제공 read/를 readarray(표준 입력으로 재 통해 <때문에) read/은 readarray실행해야 현재 쉘 (a 실행 안 하부 쉘 출력 변수의 순서) a_out가 볼 수 있도록 변수를 스크립트의 나머지 부분에 정의 된 상태로 유지하려면 현재 셸에 추가하십시오.
sort의 결과를 배열 변수 로 읽기 :
Bash v4 + : 각 요소 의 후행 을 포함하지 않고 readarray -t a_out개별 행 출력 sort을 array variable 요소로 읽습니다 ( ).a_out\n-t
배시 V3 : readarray그래서없는 read사용해야는 :
IFS=$'\n' read -d '' -r -a a_out지시 read(배열로 읽을 -a) 변수 a_out라인 (통해, 전체 입력을 읽는 -d ''), 그러나 바꿈에 의한 배열 요소로 분할하여 ( IFS=$'\n'. $'\n'리터럴 개행을 생성하는 (LF를 )는 소위 ANSI C 인용 문자열입니다 ).
( -r거의 항상와 함께 사용해야하는 옵션 read은 예기치 않은 \문자 처리를 비활성화합니다 .)
주석이 달린 샘플 코드 :
#!/usr/bin/env bash
# Define input array `a_in`:
# Note the element with embedded whitespace ('a c')and the element that looks like
# a glob ('*'), chosen to demonstrate that elements with line-internal whitespace
# and glob-like contents are correctly preserved.
a_in=( 'a c' b f 5 '*' 10 )
# Sort and store output in array `a_out`
# Saving back into `a_in` is also an option.
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
# Bash 4.x: use the simpler `readarray -t`:
# readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
# Print sorted output array, line by line:
printf '%s\n' "${a_out[@]}"
sort옵션없이 사용 하면 어휘 정렬 (문자 앞의 숫자 정렬 및 숫자 시퀀스가 숫자가 아닌 어휘 적으로 처리됨)이 생성됩니다.
*
10
5
a c
b
f
당신이 숫자 를 원한다면첫 번째 필드를 기준으로 정렬sort -k1,1n just 대신에를 사용 sort하면 숫자가 아닌 숫자가 숫자보다 먼저 정렬되고 숫자가 올바르게 정렬됩니다.
*
a c
b
f
5
10
[1] 개행 문자가 포함 된 요소를 처리하려면 다음 변형 ( GNU 와 함께 Bash v4 + sort)을 사용하십시오.
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z) .
Michał Górny의 유용한 답변 에는 Bash v3 솔루션이 있습니다.
하면서 2] IFS 된다 배시 V3 변이체 설정, 변경이되는 명령에 범위 .
대조적으로, IFS=$'\n' antak의 대답에서 따르는 것은 명령이 아닌 할당 이며,이 경우 IFS변경은 전역 적 입니다.
뮌헨에서 프랑크푸르트까지 3 시간의 기차 여행 (내일 옥토버 페스트가 시작되기 때문에 연락이 어려웠습니다)에서 첫 번째 게시물을 생각하고있었습니다. 전역 정렬을 사용하는 것이 일반적인 정렬 기능에 훨씬 좋습니다. 다음 함수는 임의의 문자열 (줄 바꿈, 공백 등)을 처리합니다.
declare BSORT=()
function bubble_sort()
{ #
# @param [ARGUMENTS]...
#
# Sort all positional arguments and store them in global array BSORT.
# Without arguments sort this array. Return the number of iterations made.
#
# Bubble sorting lets the heaviest element sink to the bottom.
#
(($# > 0)) && BSORT=("$@")
local j=0 ubound=$((${#BSORT[*]} - 1))
while ((ubound > 0))
do
local i=0
while ((i < ubound))
do
if [ "${BSORT[$i]}" \> "${BSORT[$((i + 1))]}" ]
then
local t="${BSORT[$i]}"
BSORT[$i]="${BSORT[$((i + 1))]}"
BSORT[$((i + 1))]="$t"
fi
((++i))
done
((++j))
((--ubound))
done
echo $j
}
bubble_sort a c b 'z y' 3 5
echo ${BSORT[@]}
인쇄합니다 :
3 5 a b c z y
동일한 출력이
BSORT=(a c b 'z y' 3 5)
bubble_sort
echo ${BSORT[@]}
아마도 Bash는 내부적으로 스마트 포인터를 사용하므로 스왑 작업 이 저렴 할 수 있습니다 (의심 할지라도). 그러나 bubble_sort보다 고급 기능 merge_sort도 쉘 언어에 도달 할 수 있음을 보여줍니다 .
local -n BSORT="$1"기능 시작시. 그런 다음 myarraybubble_sort myarray 를 정렬하기 위해 실행할 수 있습니다 .
외부를 사용하는 다른 솔루션 sort 과 및 대처 어떤 (:) NUL을 제외) 특수 문자를. bash-3.2 및 GNU 또는 BSD와 함께 작동해야합니다 sort(슬프게도 POSIX에는 포함되지 않음 -z).
local e new_array=()
while IFS= read -r -d '' e; do
new_array+=( "${e}" )
done < <(printf "%s\0" "${array[@]}" | LC_ALL=C sort -z)
먼저 입력 리디렉션을 살펴보십시오. printf내장 요소를 사용하여 0으로 끝나는 배열 요소를 작성합니다. 인용 부호는 배열 요소가있는 그대로 전달되도록하고 쉘의 특성으로 printf인해 나머지 각 매개 변수에 대해 형식 문자열의 마지막 부분을 재사용합니다. 즉, 다음과 같습니다.
for e in "${array[@]}"; do
printf "%s\0" "${e}"
done
그런 다음 널 종료 요소 목록이로 전달됩니다 sort. 그만큼-z 옵션을 사용하면 Null로 끝나는 요소를 읽고 정렬하고 Null로 끝나는 결과를 출력합니다. 고유 한 요소 만 -u가져와야하는 경우보다 이동성이 뛰어나 므로 전달할 수 있습니다 uniq -z. 는 LC_ALL=C로케일 독립적으로 안정적인 정렬 순서를 보장 - 스크립트를 때로는 유용하다. 당신이 원하는 경우 sort로케일 존중, 그것을 제거합니다.
이 <()생성자는 생성 된 파이프 라인에서 읽을 설명자를 가져 와서 <표준 입력을 리디렉션합니다.while 그것을 루프. 파이프 내부의 표준 입력에 액세스해야하는 경우 다른 설명자 (독자 연습)를 사용할 수 있습니다.
이제 처음으로 돌아갑니다. read내장은 리디렉션 된 표준 입력에서 출력을 읽습니다. 공란으로 설정 IFS하면 단어 분리가 비활성화되어 여기서 불필요합니다. 결과적 read으로 입력 된 전체 '행'을 단일 제공된 변수로 읽습니다.-r옵션은 여기서 바람직하지 않은 이스케이프 처리를 비활성화합니다. 마지막으로 -d ''줄 구분 기호를 NUL로 설정합니다.read 0으로 끝나는 문자열을 읽습니다.
결과적으로 루프는 연속적인 0으로 끝나는 모든 배열 요소에 대해 한 번씩 실행되며 값은 e . 이 예에서는 항목을 다른 배열에 넣지 만 직접 처리하는 것이 좋습니다. :).
물론 그것은 동일한 목표를 달성하는 많은 방법 중 하나 일뿐입니다. 보시다시피, bash에서 완전한 정렬 알고리즘을 구현하는 것보다 간단하며 경우에 따라 더 빠릅니다. 개행을 포함한 모든 특수 문자를 처리하며 대부분의 일반 시스템에서 작동합니다. 가장 중요한 것은 bash에 대해 새롭고 멋진 것을 가르쳐 줄 수 있습니다. :).
e하고 빈 IFS를 설정 하는 대신 REPLY 변수를 사용하십시오.
다음과 같이 배열의 각 요소에 대해 고유 한 정수를 계산할 수있는 경우 :
tab='0123456789abcdefghijklmnopqrstuvwxyz'
# build the reversed ordinal map
for ((i = 0; i < ${#tab}; i++)); do
declare -g ord_${tab:i:1}=$i
done
function sexy_int() {
local sum=0
local i ch ref
for ((i = 0; i < ${#1}; i++)); do
ch="${1:i:1}"
ref="ord_$ch"
(( sum += ${!ref} ))
done
return $sum
}
sexy_int hello
echo "hello -> $?"
sexy_int world
echo "world -> $?"
Bash는 항상 희소 배열을 사용하므로 사용하지 않는 인덱스에 대해 걱정할 필요가 없으므로 이러한 정수를 배열 인덱스로 사용할 수 있습니다.
array=(a c b f 3 5)
for el in "${array[@]}"; do
sexy_int "$el"
sorted[$?]="$el"
done
echo "${sorted[@]}"
최소 정렬 :
#!/bin/bash
array=(.....)
index_of_element1=0
while (( ${index_of_element1} < ${#array[@]} )); do
element_1="${array[${index_of_element1}]}"
index_of_element2=$((index_of_element1 + 1))
index_of_min=${index_of_element1}
min_element="${element_1}"
for element_2 in "${array[@]:$((index_of_element1 + 1))}"; do
min_element="`printf "%s\n%s" "${min_element}" "${element_2}" | sort | head -n+1`"
if [[ "${min_element}" == "${element_2}" ]]; then
index_of_min=${index_of_element2}
fi
let index_of_element2++
done
array[${index_of_element1}]="${min_element}"
array[${index_of_min}]="${element_1}"
let index_of_element1++
done
공백과 줄 바꾸기의 일반적인 문제에 대한 해결 방법이 있습니다.
원래의 배열 (같은 아닌 문자를 사용 $'\1'하거나 $'\4'또는 유사한).
이 함수는 작업을 수행합니다.
# Sort an Array may have spaces or newlines with a workaround (wa=$'\4')
sortarray(){ local wa=$'\4' IFS=''
if [[ $* =~ [$wa] ]]; then
echo "$0: error: array contains the workaround char" >&2
exit 1
fi
set -f; local IFS=$'\n' x nl=$'\n'
set -- $(printf '%s\n' "${@//$nl/$wa}" | sort -n)
for x
do sorted+=("${x//$wa/$nl}")
done
}
이것은 배열을 정렬합니다 :
$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>
소스 배열에 해결 방법 문자가 포함되어 있다고 불평합니다.
$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char
wa(해결 방법 char)와 null IFS를 설정했습니다.$*.[[ $* =~ [$wa] ]].exit 1set -fIFS=$'\n'루프 변수 x와 개행 변수 var ( nl=$'\n') 를 IFS ( ) 의 새 값으로 설정하십시오 .$@) 의 모든 값을 인쇄합니다 ."${@//$nl/$wa}".sort -n.set --.for xsorted+=(…)"${x//$wa/$nl}".이 질문 은 밀접한 관련이 있습니다. 그리고 BTW, 다음은 Bash의 병합 소트입니다 (외부 프로세스 제외).
mergesort() {
local -n -r input_reference="$1"
local -n output_reference="$2"
local -r -i size="${#input_reference[@]}"
local merge previous
local -a -i runs indices
local -i index previous_idx merged_idx \
run_a_idx run_a_stop \
run_b_idx run_b_stop
output_reference=("${input_reference[@]}")
if ((size == 0)); then return; fi
previous="${output_reference[0]}"
runs=(0)
for ((index = 0;;)) do
for ((++index;; ++index)); do
if ((index >= size)); then break 2; fi
if [[ "${output_reference[index]}" < "$previous" ]]; then break; fi
previous="${output_reference[index]}"
done
previous="${output_reference[index]}"
runs+=(index)
done
runs+=(size)
while (("${#runs[@]}" > 2)); do
indices=("${!runs[@]}")
merge=("${output_reference[@]}")
for ((index = 0; index < "${#indices[@]}" - 2; index += 2)); do
merged_idx=runs[indices[index]]
run_a_idx=merged_idx
previous_idx=indices[$((index + 1))]
run_a_stop=runs[previous_idx]
run_b_idx=runs[previous_idx]
run_b_stop=runs[indices[$((index + 2))]]
unset runs[previous_idx]
while ((run_a_idx < run_a_stop && run_b_idx < run_b_stop)); do
if [[ "${merge[run_a_idx]}" < "${merge[run_b_idx]}" ]]; then
output_reference[merged_idx++]="${merge[run_a_idx++]}"
else
output_reference[merged_idx++]="${merge[run_b_idx++]}"
fi
done
while ((run_a_idx < run_a_stop)); do
output_reference[merged_idx++]="${merge[run_a_idx++]}"
done
while ((run_b_idx < run_b_stop)); do
output_reference[merged_idx++]="${merge[run_b_idx++]}"
done
done
done
}
declare -ar input=({z..a}{z..a})
declare -a output
mergesort input output
echo "${input[@]}"
echo "${output[@]}"
Bash에서 외부 정렬 프로그램이 필요하다고 확신하지 않습니다.
다음은 간단한 버블 정렬 알고리즘에 대한 구현입니다.
function bubble_sort()
{ #
# Sorts all positional arguments and echoes them back.
#
# Bubble sorting lets the heaviest (longest) element sink to the bottom.
#
local array=($@) max=$(($# - 1))
while ((max > 0))
do
local i=0
while ((i < max))
do
if [ ${array[$i]} \> ${array[$((i + 1))]} ]
then
local t=${array[$i]}
array[$i]=${array[$((i + 1))]}
array[$((i + 1))]=$t
fi
((i += 1))
done
((max -= 1))
done
echo ${array[@]}
}
array=(a c b f 3 5)
echo " input: ${array[@]}"
echo "output: $(bubble_sort ${array[@]})"
이것은 다음을 인쇄합니다 :
input: a c b f 3 5
output: 3 5 a b c f
O(n^2)입니다. 나는 대부분의 정렬 알고리즘 O(n lg(n))이 마지막 12 개 정도의 요소까지 사용한다는 것을 기억 합니다. 최종 요소에는 선택 정렬이 사용됩니다.
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
bash / linux의 정신으로 각 단계마다 최상의 명령 줄 도구를 파이프합니다. sort주요 작업을 수행하지만 공간 대신 줄 바꿈으로 구분 된 입력이 필요하므로 위의 매우 간단한 파이프 라인은 간단합니다.
에코 배열 내용-> 줄 바꾸기로 공간 바꾸기-> 정렬
$() 결과를 에코하는 것입니다
($()) "반향 된 결과"를 배열에 넣는 것입니다
참고 : @sorontar 는 다른 질문 에 대한 의견 에서 언급했습니다 .
sorted = ($ (...)) 부분은 "split and glob"연산자를 사용하고 있습니다. glob를 해제해야합니다. set -f 또는 set -o noglob 또는 shopt -op noglob 또는 *와 같은 배열 요소는 파일 목록으로 확장됩니다.
mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort), 그렇지 않으면 sorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort).
echo ${array[@]} | tr " " "\n"과 같습니다 . : 배열 필드에 공백과 glob 문자가 포함되어 있으면 중단됩니다. 게다가, 그것은 서브 쉘을 생성하고 쓸모없는 외부 명령을 사용합니다. 그리고 echo바보이기 때문에 배열이 -e, -E또는로 시작하면 중단됩니다 -n. 대신 다음을 사용하십시오 printf '%s\n' "${array[@]}".. 다른 반 패턴은 : ($())"반향 된 결과"를 배열에 넣는 것 입니다. 확실히! 이것은 경로 이름 확장 (글 로빙) 및 단어 분리로 인해 끔찍한 반 패턴입니다. 이 공포를 사용하지 마십시오.
IFS공백이 있으면 요소를 작은 조각으로 나눕니다. 생략 된 예 를IFS=$'\n'보고보십시오!