回答:
実際にはそれほど多くのコードは必要ありません。
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は、要素にor などのワイルドカードが含まれている場合は注意が必要であることを指摘しました:*
?
Sorted =($(...))の部分は、「split and glob」演算子を使用しています。グロブをオフにする必要があります:
set -f
またはset -o noglob
またはshopt -op noglob
または配列の要素*
がファイルのリストに展開されます。
結果は、この順序で発生する6つの事柄の集大成です。
IFS=$'\n'
"${array[*]}"
<<<
sort
sorted=($(...))
unset IFS
IFS=$'\n'
これは、次のように2と5の結果に影響を与える操作の重要な部分です。
与えられた:
"${array[*]}"
の最初の文字で区切られたすべての要素に展開されます IFS
sorted=()
のすべての文字を分割して要素を作成します IFS
IFS=$'\n'
セット物事アップ要素を使用して展開されるように新しい行を区切りとして、そしてその後、各行が要素になるというようにして作成されました。(つまり、新しい行で分割します。)
新しい行で区切ることは重要です。これは、そのようにsort
動作するためです(行ごとの並べ替え)。改行だけで分割することはそれほど重要ではありませんが、スペースまたはタブを含む要素を保持するために必要です。
のデフォルト値IFS
は、スペース、タブ、改行の順であり、この操作には適していません。
sort <<<"${array[*]}"
パート<<<
ここでは文字列と呼ばれるは"${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
、特定の種類の空白のみが含まれている場合、要素が小さな断片に分割されます。良い; 完璧ではない:-)
unset IFS
必要?IFS=
コマンドの先頭に追加すると、そのコマンドへの変更のみがスコープされ、その後、自動的に前の値に戻ると思いました。
sorted=()
はコマンドではなく、2番目の変数割り当てなので必要です。
元の応答:
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は4+ bashでサポートされています。
編集 @Dimitreの提案に基づいて、私はそれを次のように更新しました。
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
これには、改行文字が正しく埋め込まれたソート要素を理解するという利点もあります。残念ながら、@ ruakhによって正しく通知されたので、これはの結果readarray
が正しいことを意味しませんでした。通常の改行の代わりに行区切りとしてreadarray
使用するオプションがないためです。NUL
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)
。readarrayはbash v3では使用できないため、これは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
}
どちらの場合でも、使用する順序を変更できます。文字列比較を使用しましたが、算術比較を使用したり、ファイルの変更時間を比較したりできます。適切なテストを使用するだけです。さらに汎用的にして、テスト関数の使用である最初の引数を使用することもできます。たとえば、
#!/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
現在のフォルダー内のファイルを変更時刻でソートする(最新が最初)。
注意。これらの関数は純粋なBashです!外部ユーティリティもサブシェルもありません!彼らはあなたが持っているかもしれない面白いシンボル(スペース、改行文字、グロブ文字など)に対して安全です。
sort
提供する並べ替えオプションを使用した行ベースの並べ替えで十分な場合、sort
+ read -a
ソリューションは約20アイテムから開始し、処理する要素が増えるほど大幅に高速になります。たとえば、Fusion Driveを備えたOSX 10.11.1を実行している2012年後半のiMacでは、100エレメントのアレイ:ca。0.03秒 (qsort()
)対約 0.005秒 (sort
+ read -a
); 1000要素配列:約 0.375秒 (qsort()
)対約 0.014秒(sort
+ read -a
)。
if [ "$i" -lt "$pivot" ]; then
、解決された "2" <"10"がtrueを返します。私はこれがPOSIX対辞書的であると信じています。またはおそらくインラインリンク。
配列要素で特別なシェル文字を処理する必要がない場合:
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はASCIIでset -s
ソートする必要があります。
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@"
です。もちろん、setコマンドは、現在の位置パラメータ(存在する場合)をリセットします。
tl; dr:
配列a_in
を並べ替えて結果を格納しますa_out
(要素に改行を埋め込むことはできません[1]
):
Bash 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)
あなたはそう余分なコマンドが無効グロブに必要とされている(、偶発グロブ(ファイル名パターンとして配列要素を誤って解釈)心配する必要はないset -f
、とset +f
後でそれを復元します)。
あなたは、再設定について心配する必要はありませんIFS
しunset IFS
。[2]
上記は、Bashコードと外部ユーティリティsort
を組み合わせて、任意の単一行要素と字句または数値の並べ替え(オプションでフィールドによる)で機能するソリューションを実現します。
パフォーマンス:約20要素以上の場合、これは純粋なBashソリューションよりも高速になります。約100要素を超えると、大幅かつますます高速になります。
(正確なしきい値は、特定の入力、マシン、およびプラットフォームによって異なります。)
printf '%s\n' "${a_in[@]}" | sort
ソートを実行します(字句的には、デフォルトで- sort
のPOSIX仕様を参照):
"${a_in[@]}"
含まれているものすべて(空白を含む)の個別の引数a_in
として配列の要素に安全に展開します。
printf '%s\n'
次に、各引数-つまり、各配列要素-をそのままの行に出力します。
(注)を使用するプロセス置換を(<(...)
)への入力としてソートされた出力を提供するために、read
/ readarray
(STDINへのリダイレクトを介して<
いるので、)read
/がreadarray
で実行する必要があり、現在のシェル(で実行されてはならないサブシェル順出力するための変数で)a_out
見えるように現在のシェルに(変数がスクリプトの残りの部分で定義されたままになるため)。
sort
の出力を配列変数に読み取る:
Bash v4 +:readarray -t a_out
によって出力さsort
れた個々の行を配列変数の要素に読み取ります。各要素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
、単にの代わりに使用しますsort
。これにより、(数値の前に非数値が並べ替えられ、数値が正しく並べ替えられます):
*
a c
b
f
5
10
[1]改行が埋め込まれた要素を処理するには、次のバリアント(Bash v4 +、GNUを使用 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"
、関数の開始時。次に、実行bubble_sort myarray
して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
ビルトインを使用して、ゼロで終了する配列要素を書き出しています。引用は配列要素がそのまま渡されることを確実にし、シェルの詳細により、printf
残りの各パラメーターに対してフォーマット文字列の最後の部分を再利用させます。つまり、次のようなものと同等です。
for e in "${array[@]}"; do
printf "%s\0" "${e}"
done
次に、nullで終了する要素のリストがに渡されsort
ます。この-z
オプションにより、nullで終了する要素が読み取られ、並べ替えられ、nullで終了する要素も出力されます。固有の要素のみを取得する必要がある場合-u
は、よりも移植性が高いため、渡すことができますuniq -z
。LC_ALL=C
スクリプトのための便利な場合-独立したロケールの安定ソート順序を保証します。sort
ロケールを尊重したい場合は、それを削除してください。
<()
構築物は、生成されたパイプラインから読み取るために、ディスクリプタを取得し、<
標準入力をリダイレクトwhile
それにループ。パイプ内の標準入力にアクセスする必要がある場合は、別の記述子を使用することができます—読者のために練習してください:)。
さて、最初に戻りましょう。read
ビルトインは、リダイレクト標準入力から出力を読み込みます。空IFS
に設定すると、ここでは不要な単語分割が無効になります。その結果、read
入力の「行」全体が単一の提供された変数に読み込まれます。-r
オプションは、ここでも望ましくないエスケープ処理を無効にします。最後に-d ''
、行区切り文字をNUL に設定します—つまり、read
ます。ゼロで終了する文字列を読み取るようします。
その結果、ゼロで終了する配列要素ごとにループが1回実行され、値がに格納されe
ます。例ではアイテムを別の配列に配置するだけですが、直接処理することもできます:)。
もちろん、これは同じ目標を達成するための多くの方法の1つにすぎません。私が見るように、bashで完全なソートアルゴリズムを実装するよりも簡単で、場合によってはより高速になります。改行を含むすべての特殊文字を処理し、ほとんどの一般的なシステムで動作するはずです。最も重要なのは、それはbashについて何か新しくて素晴らしいものを教えるかもしれません:)。
e
空のIFSを設定する代わりに、REPLY変数を使用します。
これを試して:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
出力は次のようになります。
3 5 a b c f
問題が解決しました。
次のように、配列の要素ごとに一意の整数を計算できる場合:
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
(回避文字)とnull IFS を設定します$*
ます。[[ $* =~ [$wa] ]]
。exit 1
set -f
IFS=$'\n'
)、ループ変数x
、および改行変数()を設定しますnl=$'\n'
。$@
)。"${@//$nl/$wa}"
。sort -n
。set --
。for x
sorted+=(…)
"${x//$wa/$nl}"
。この質問は密接に関連しているように見えます。ところで、ここに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))
、最後の数十の要素まで使用することを思い出しているようです。最後の要素には、選択ソートが使用されます。
a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f
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"
とおりです。:これは、配列のフィールドに空白文字とグロブ文字が含まれていると壊れます。また、サブシェルを生成し、役に立たない外部コマンドを使用します。echo
馬鹿げているため、配列が-e
、-E
またはで始まる場合は壊れます-n
。代わりに:を使用してくださいprintf '%s\n' "${array[@]}"
。もう1つのアンチパターンは($())
、「エコーされた結果」を配列に入れることです。確かにありません!これは、パス名の展開(グロビング)と単語の分割のために壊れる恐ろしいアンチパターンです。この恐怖を決して使用しないでください。
IFS
場合、空白が含まれていると、要素が細かく分割されます。省略してegを試してみてIFS=$'\n'
ください!