Câu trả lời:
Bạn không thực sự cần nhiều mã như vậy:
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
Hỗ trợ khoảng trắng trong các phần tử (miễn là nó không phải là dòng mới) và hoạt động trong Bash 3.x.
ví dụ:
$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]
Lưu ý: @sorontar đã chỉ ra rằng cần phải cẩn thận nếu các phần tử có chứa ký tự đại diện như *hoặc ?:
Phần được sắp xếp = ($ (...)) đang sử dụng toán tử "split and global". Bạn nên tắt global:
set -fhoặcset -o noglobhoặcshopt -op noglobmột phần tử của mảng như*sẽ được mở rộng thành một danh sách các tệp.
Kết quả là một đỉnh cao sáu điều xảy ra theo thứ tự này:
IFS=$'\n'"${array[*]}"<<<sortsorted=($(...))unset IFSIFS=$'\n'Đây là một phần quan trọng trong hoạt động của chúng tôi có ảnh hưởng đến kết quả của 2 và 5 theo cách sau:
Được:
"${array[*]}" mở rộng đến mọi phần tử được giới hạn bởi ký tự đầu tiên của IFSsorted=() tạo ra các yếu tố bằng cách phân chia trên mỗi ký tự của IFSIFS=$'\n' thiết lập mọi thứ để các phần tử được mở rộng bằng cách sử dụng một dòng mới làm dấu phân cách và sau đó được tạo theo cách mà mỗi dòng trở thành một phần tử. (tức là Chia tách trên một dòng mới.)
Phân định theo một dòng mới rất quan trọng vì đó là cách sortvận hành (sắp xếp trên mỗi dòng). Việc phân tách chỉ bằng một dòng mới không quan trọng, nhưng cần bảo tồn các thành phần có chứa khoảng trắng hoặc tab.
Giá trị mặc định IFSlà một khoảng trắng , một tab , theo sau là một dòng mới và sẽ không phù hợp với hoạt động của chúng tôi.
sort <<<"${array[*]}"phần<<<, được gọi là các chuỗi ở đây , lấy sự mở rộng của "${array[*]}", như đã giải thích ở trên, và đưa nó vào đầu vào tiêu chuẩn của sort.
Với ví dụ của chúng tôi, sortđược cung cấp chuỗi sau đây:
a c
b
f
3 5
Vì sort các loại , nó tạo ra:
3 5
a c
b
f
sorted=($(...))phầnPhần $(...), được gọi là thay thế lệnh , làm cho nội dung của nó ( sort <<<"${array[*]}) chạy như một lệnh bình thường, trong khi lấy đầu ra tiêu chuẩn kết quả là nghĩa đen đi đến đâu $(...).
Trong ví dụ của chúng tôi, điều này tạo ra một cái gì đó tương tự như viết đơn giản:
sorted=(3 5
a c
b
f
)
sorted sau đó trở thành một mảng được tạo bằng cách tách chữ này trên mỗi dòng mới.
unset IFSĐiều này đặt lại giá trị của giá trị IFSmặc định, và chỉ là thực hành tốt.
Đó là để đảm bảo chúng tôi không gây rắc rối với bất cứ điều gì dựa vào IFSkịch bản sau này. (Nếu không, chúng ta cần nhớ rằng chúng ta đã thay đổi mọi thứ xung quanh - điều gì đó có thể không thực tế đối với các tập lệnh phức tạp.)
IFS, nó chia các phần tử của bạn thành các phần nhỏ nếu chúng chỉ có một loại khoảng trắng cụ thể trong đó. Tốt; không hoàn hảo :-)
unset IFScần thiết không? Tôi nghĩ rằng việc chuẩn bị trước IFS=một lệnh chỉ giới hạn thay đổi cho lệnh đó, tự động trở về giá trị trước đó.
sorted=()không phải là lệnh mà là gán biến thứ hai.
Phản hồi ban đầu:
array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)
đầu ra:
$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f
Lưu ý phiên bản này đối phó với các giá trị có chứa các ký tự hoặc khoảng trắng đặc biệt ( ngoại trừ dòng mới)
Lưu ý readarray được hỗ trợ trong bash 4+.
Chỉnh sửa Dựa trên đề xuất của @Dimitre, tôi đã cập nhật nó thành:
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
có lợi ích thậm chí hiểu các yếu tố sắp xếp với các ký tự dòng mới được nhúng chính xác. Thật không may, như được báo hiệu chính xác bởi @ruakh, điều này không có nghĩa là kết quả của readarraynó sẽ đúng , bởi vì readarraykhông có tùy chọn nào để sử dụng NULthay vì các dòng mới thông thường như các dấu tách dòng.
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -zlà một cải tiến hữu ích, tôi cho rằng -ztùy chọn này là một phần mở rộng sắp xếp GNU.
sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z). Điều này cũng hoạt động trong bạn đang sử dụng bash v3 thay vì bash v4, vì readarray không có sẵn trong bash v3.
<) kết hợp với thay thế quá trình <(...) . Hoặc để đặt nó bằng trực giác: bởi vì (printf "bla")không phải là một tập tin.
Đây là một triển khai quicksort Bash thuần túy:
#!/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[@]}" )
}
Sử dụng như, ví dụ,
$ 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")'
Việc triển khai này là đệ quy, vì vậy đây là một quicksort lặp:
#!/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
}
Trong cả hai trường hợp, bạn có thể thay đổi thứ tự bạn sử dụng: Tôi đã sử dụng so sánh chuỗi, nhưng bạn có thể sử dụng so sánh số học, so sánh thời gian sửa đổi tệp wrt, v.v. chỉ cần sử dụng thử nghiệm thích hợp; bạn thậm chí có thể làm cho nó chung chung hơn và để nó sử dụng một đối số đầu tiên là sử dụng hàm kiểm tra, ví dụ:
#!/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
}
Sau đó, bạn có thể có chức năng so sánh này:
compare_mtime() { [[ $1 -nt $2 ]]; }
Và sử dụng:
$ qsort compare_mtime *
$ declare -p qsort_ret
để có các tệp trong thư mục hiện tại được sắp xếp theo thời gian sửa đổi (mới nhất trước tiên).
GHI CHÚ. Các hàm này là Bash thuần túy! không có tiện ích bên ngoài, và không có subshells! chúng an toàn khi viết bất kỳ biểu tượng ngộ nghĩnh nào bạn có thể có (dấu cách, ký tự dòng mới, ký tự toàn cầu, v.v.).
sortcung cấp là đủ, giải pháp sort+ read -asẽ nhanh hơn bắt đầu từ xung quanh, giả sử, 20 mục và ngày càng nhanh hơn và nhanh hơn đáng kể các yếu tố bạn đang xử lý. Ví dụ, trên iMac cuối năm 2012 của tôi chạy OSX 10.11.1 với Fusion Drive: mảng 100 phần tử: ca. 0,03 giây. ( qsort()) so với ca. 0,005 giây. ( sort+ read -a); Mảng 1000 phần tử: ca. 0,375 giây. ( qsort()) so với ca. 0,011 giây ( sort+ read -a).
if [ "$i" -lt "$pivot" ]; thenđược yêu cầu nếu không thì "2" <"10" đã giải quyết trở lại đúng. Tôi tin rằng đây là POSIX so với Thuật ngữ; hoặc có lẽ Liên kết nội tuyến .
Nếu bạn không cần xử lý các ký tự shell đặc biệt trong các thành phần mảng:
array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))
Với bash bạn sẽ cần một chương trình sắp xếp bên ngoài nào.
Với zsh, không cần chương trình bên ngoài và các ký tự shell đặc biệt dễ dàng xử lý:
% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}"
3
5
a a
b
c
f
ksh có set -sđể sắp xếp ASCIIbetically .
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@" Và, tất nhiên, lệnh set sẽ đặt lại các tham số vị trí hiện tại, nếu có.
tl; dr :
Sắp xếp mảng a_invà lưu trữ kết quả trong a_out(các phần tử không được nhúng dòng mới [1]
):
Bash v4 +:
readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Bash v3:
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Ưu điểm so với giải pháp của antak :
Bạn không cần phải lo lắng về việc tạo hình ngẫu nhiên (giải thích ngẫu nhiên các thành phần mảng dưới dạng mẫu tên tệp), do đó không cần thêm lệnh để vô hiệu hóa tính năng tạo khối ( set -fvà set +fđể khôi phục lại sau này).
Bạn không cần phải lo lắng về việc đặt lại IFSvới unset IFS. [2]
Ở trên kết hợp mã Bash với tiện ích bên ngoài sortcho một giải pháp hoạt động với các phần tử dòng đơn tùy ý và sắp xếp từ vựng hoặc số (tùy chọn theo trường) :
Hiệu suất : Đối với khoảng 20 yếu tố trở lên , điều này sẽ nhanh hơn một giải pháp Bash thuần túy - đáng kể và ngày càng cao hơn khi bạn vượt qua khoảng 100 yếu tố.
(Ngưỡng chính xác sẽ phụ thuộc vào đầu vào, máy và nền tảng cụ thể của bạn.)
printf '%s\n' "${a_in[@]}" | sort thực hiện sắp xếp (theo từ vựng, theo mặc định - xem sortthông số POSIX ):
"${a_in[@]}"mở rộng một cách an toàn các phần tử của mảng a_indưới dạng các đối số riêng lẻ , bất cứ thứ gì chúng chứa (bao gồm cả khoảng trắng).
printf '%s\n' sau đó in từng đối số - tức là từng phần tử mảng - trên dòng riêng của nó, như hiện trạng.
Lưu ý việc sử dụng thay thế quy trình ( <(...)) để cung cấp đầu ra được sắp xếp làm đầu vào read/ readarray(thông qua chuyển hướng đến stdin, <), bởi vì read/ readarrayphải chạy trong trình bao hiện tại (không được chạy trong lớp con ) a_outđể hiển thị biến đầu ra đến trình bao hiện tại (để biến vẫn được xác định trong phần còn lại của tập lệnh).
Đọc sortkết quả đầu ra thành một biến mảng :
Bash v4 +: readarray -t a_outđọc đầu ra các dòng riêng lẻ bằng cách sortvào các phần tử của biến mảng a_out, mà không bao gồm dấu \nở mỗi phần tử ( -t).
Bash v3: readarraykhông tồn tại, do đó readphải được sử dụng:
IFS=$'\n' read -d '' -r -a a_outbảo readđọc vào biến mảng ( -a) a_out, đọc toàn bộ đầu vào, trên các dòng ( -d ''), nhưng tách nó thành các phần tử mảng bằng các dòng mới ( IFS=$'\n'. $'\n', Tạo ra một dòng mới theo nghĩa đen (LF ), là một chuỗi được gọi là ANSI C-trích dẫn ).
( -r, một tùy chọn hầu như luôn luôn được sử dụng read, vô hiệu hóa việc xử lý các \ký tự không mong muốn .)
Mã mẫu được chú thích:
#!/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[@]}"
Do sử dụng sortkhông có tùy chọn, điều này mang lại sự sắp xếp từ vựng (chữ số sắp xếp trước chữ cái và chuỗi chữ số được xử lý theo từ vựng , không phải là số):
*
10
5
a c
b
f
Nếu bạn muốn sắp xếp số theo trường thứ 1, bạn sẽ sử dụng sort -k1,1nthay vì chỉ sort, sẽ mang lại (sắp xếp không phải là số trước số và sắp xếp số chính xác):
*
a c
b
f
5
10
[1] Để xử lý các phần tử có dòng mới được nhúng, hãy sử dụng biến thể sau (Bash v4 +, với GNU sort ) :
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z).
Câu trả lời hữu ích của Michał Górny có giải pháp Bash v3.
[2] Trong khi IFS được đặt trong biến thể Bash v3, thay đổi nằm trong phạm vi lệnh .
Ngược lại, những gì tiếp theo IFS=$'\n' trong câu trả lời của antak là một bài tập chứ không phải là một lệnh, trong trường hợp đó sự IFSthay đổi là toàn cầu .
Trong chuyến đi kéo dài 3 giờ từ Munich đến Frankfurt (mà tôi gặp khó khăn để tiếp cận vì Lễ hội tháng mười bắt đầu vào ngày mai) tôi đã suy nghĩ về bài đăng đầu tiên của mình. Sử dụng một mảng toàn cầu là một ý tưởng tốt hơn nhiều cho một hàm sắp xếp chung. Hàm sau xử lý các chuỗi tùy ý (dòng mới, khoảng trắng, v.v.):
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[@]}
Bản in này:
3 5 a b c z y
Đầu ra tương tự được tạo ra từ
BSORT=(a c b 'z y' 3 5)
bubble_sort
echo ${BSORT[@]}
Lưu ý rằng có lẽ Bash trong nội bộ sử dụng các con trỏ thông minh, do đó, hoạt động hoán đổi có thể rẻ (mặc dù tôi nghi ngờ điều đó). Tuy nhiên, bubble_sortchứng minh rằng các chức năng nâng cao hơn như merge_sortcũng nằm trong phạm vi của ngôn ngữ shell.
local -n BSORT="$1"khi bắt đầu hàm. Sau đó, bạn có thể chạy bubble_sort myarrayđể sắp xếp myarray .
Một giải pháp khác sử dụng bên ngoài sortvà đối phó với bất kỳ ký tự đặc biệt nào (ngoại trừ NULs :)). Nên hoạt động với bash-3.2 và GNU hoặc BSD sort(đáng buồn thay, POSIX không bao gồm -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)
Đầu tiên nhìn vào chuyển hướng đầu vào ở cuối. Chúng tôi đang sử dụng tích printfhợp để viết ra các phần tử mảng, không kết thúc. Việc trích dẫn đảm bảo các phần tử mảng được truyền printfnguyên trạng và các chi tiết cụ thể của shell khiến nó sử dụng lại phần cuối của chuỗi định dạng cho mỗi tham số còn lại. Đó là, nó tương đương với một cái gì đó như:
for e in "${array[@]}"; do
printf "%s\0" "${e}"
done
Danh sách phần tử kết thúc null sau đó được chuyển đến sort. Các -ztùy chọn gây ra nó để đọc các yếu tố null-chấm dứt, sắp xếp chúng và đầu ra null-chấm dứt là tốt. Nếu bạn chỉ cần lấy các yếu tố duy nhất, bạn có thể vượt qua -uvì nó dễ mang theo hơn uniq -z. Thứ LC_ALL=Ctự sắp xếp ổn định độc lập với miền địa phương - đôi khi hữu ích cho các tập lệnh. Nếu bạn muốn sorttôn trọng miền địa phương, hãy loại bỏ điều đó.
Cấu <()trúc có được bộ mô tả để đọc từ đường ống được sinh ra và <chuyển hướng đầu vào tiêu chuẩn của whilevòng lặp đến nó. Nếu bạn cần truy cập vào đầu vào tiêu chuẩn bên trong đường ống, bạn có thể sử dụng một mô tả khác - bài tập cho người đọc :).
Bây giờ, trở lại từ đầu. Đầu ra tích readhợp đọc từ stdin chuyển hướng. Đặt trống sẽ IFSvô hiệu hóa việc tách từ không cần thiết ở đây - kết quả là, readđọc toàn bộ 'dòng' đầu vào cho biến được cung cấp duy nhất. -rtùy chọn cũng vô hiệu hóa xử lý thoát không mong muốn ở đây. Cuối cùng, -d ''đặt dấu phân cách dòng thành NUL - nghĩa là bảo readđọc các chuỗi kết thúc bằng không.
Kết quả là, vòng lặp được thực thi một lần cho mỗi phần tử mảng kết thúc bằng 0 liên tiếp, với giá trị được lưu trữ trong e. Ví dụ chỉ đặt các mục trong một mảng khác nhưng bạn có thể thích xử lý chúng trực tiếp :).
Tất nhiên, đó chỉ là một trong nhiều cách để đạt được cùng một mục tiêu. Như tôi thấy, nó đơn giản hơn việc thực hiện thuật toán sắp xếp hoàn chỉnh trong bash và trong một số trường hợp, nó sẽ nhanh hơn. Nó xử lý tất cả các ký tự đặc biệt bao gồm các dòng mới và sẽ hoạt động trên hầu hết các hệ thống phổ biến. Quan trọng nhất, nó có thể dạy cho bạn một cái gì đó mới và tuyệt vời về bash :).
evà đặt IFS trống, hãy sử dụng biến REPLY.
thử cái này:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
Đầu ra sẽ là:
3 5 một b c f
Vấn đề được giải quyết.
Nếu bạn có thể tính một số nguyên duy nhất cho mỗi phần tử trong mảng, như sau:
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 -> $?"
sau đó, bạn có thể sử dụng các số nguyên này làm chỉ mục mảng, vì Bash luôn sử dụng mảng thưa thớt, do đó không cần phải lo lắng về các chỉ mục không được sử dụng:
array=(a c b f 3 5)
for el in "${array[@]}"; do
sexy_int "$el"
sorted[$?]="$el"
done
echo "${sorted[@]}"
sắp xếp tối thiểu:
#!/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
Có một cách giải quyết cho vấn đề thông thường về không gian và dòng mới:
Sử dụng một nhân vật đó không phải là trong mảng ban đầu (như $'\1'hoặc $'\4'hoặc tương tự).
Hàm này hoàn thành công việc:
# 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
}
Điều này sẽ sắp xếp mảng:
$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>
Điều này sẽ phàn nàn rằng mảng nguồn chứa ký tự giải pháp:
$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char
wa(workaround char) và IFS null$*.[[ $* =~ [$wa] ]].exit 1set -fIFS=$'\n') một biến vòng lặp xvà một dòng mới var ( nl=$'\n').$@)."${@//$nl/$wa}".sort -n.set --.for xsorted+=(…)"${x//$wa/$nl}".Câu hỏi này có vẻ liên quan chặt chẽ. Và BTW, đây là một sự hợp nhất trong Bash (không có các quy trình bên ngoài):
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[@]}"
Tôi không tin rằng bạn sẽ cần một chương trình sắp xếp bên ngoài ở Bash.
Đây là triển khai của tôi cho thuật toán sắp xếp bong bóng đơn giản.
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[@]})"
Điều này sẽ in:
input: a c b f 3 5
output: 3 5 a b c f
O(n^2). Tôi dường như nhớ lại hầu hết các thuật toán sắp xếp sử dụng O(n lg(n))cho đến hàng tá phần tử cuối cùng hoặc lâu hơn. Đối với các yếu tố cuối cùng, sắp xếp lựa chọn được sử dụng.
a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
Theo tinh thần của bash / linux, tôi sẽ sử dụng công cụ dòng lệnh tốt nhất cho mỗi bước. sortthực hiện công việc chính nhưng cần đầu vào được phân tách bằng dòng mới thay vì không gian, do đó, đường ống rất đơn giản ở trên chỉ đơn giản là:
Echo nội dung mảng -> thay thế không gian bằng dòng mới -> sắp xếp
$() là để lặp lại kết quả
($()) là đặt "kết quả lặp lại" trong một mảng
Lưu ý : như @sorontar đã đề cập trong một nhận xét cho một câu hỏi khác:
Phần được sắp xếp = ($ (...)) đang sử dụng toán tử "split and global". Bạn nên tắt global: set -f hoặc set -o noglob hoặc shopt -op noglob hoặc một phần tử của mảng như * sẽ được mở rộng thành một danh sách các tệp.
mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort), nếu không sorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort).
echo ${array[@]} | tr " " "\n"điều này sẽ bị phá vỡ nếu các trường của mảng chứa khoảng trắng và ký tự toàn cầu. Bên cạnh đó, nó sinh ra một lớp con và sử dụng một lệnh bên ngoài vô dụng. Và do echobị câm, nó sẽ bị hỏng nếu mảng của bạn bắt đầu bằng -e, -Ehoặc -n. Thay vào đó sử dụng : printf '%s\n' "${array[@]}". Các antipotype khác là: ($())đặt "kết quả lặp lại" trong một mảng . Chắc chắn không! đây là một antipotype khủng khiếp bị phá vỡ vì sự mở rộng tên đường dẫn (globalbing) và chia tách từ. Không bao giờ sử dụng kinh dị này.
IFS, nó sẽ chia các phần tử của bạn thành các phần nhỏ nếu chúng có khoảng trắng trong đó. Hãy thử ví dụ vớiIFS=$'\n'bỏ qua và xem!