POSIX shで位置引数のリストをソートする方法はありますか?各位置引数には、すべての文字(スペース、改行、タブなど)を含めることができます。ソートアルゴリズムは、プログラマーが定義した比較に基づいてリストをソートするのに十分な一般的なものでなければなりません(例えば、数値/辞書ソートを使用するなど)。expr
比較、各位置引数の部分文字列のみを考慮したソートなど)。
POSIX shの位置パラメータのリストには、スタックとキューの両方の属性があるようです。 push(set -- "$x" "$@"
)、pop(x="$1"; shift
)、enqueue(set -- "$@" "$x"
)、およびdequeue()操作をサポートしますx="$1"; shift
。ただし、リストは1つしかない可能性があるため、整列は所定の位置で実行する必要があり、マージソートやクイックソートなどの一般的なアルゴリズムは使用できません。
サンプル:
#!/bin/sh
set -- "Hello" "world" 9 8 7 "$(printf "0\n1")" "$(printf "1\t2")"
# How do I sort the positional parameters above using comparison
# `expr "$x" \< "$y"` such that the result is the list below?
#
# "$(printf "0\n1")" "$(printf "1\t2")" 9 8 7 "Hello" "world"
注1:私は、ユーザーが提供したランダムな位置引数とプログラマーによって指定されたランダム比較で動作するPOSIXソリューションを探しています。
注2:私は実際の問題を解決しようとしていません。このソートの問題に挑戦しましたが、解決策がsh
見つからなかったため、Stack Exchangeに投稿しました。
ベストアンサー1
おそらく最も簡単な方法は、数値比較(浮動小数点数を含む)をawk
実行できるstrcoll()
方法を使用することです。strcmp()
ホイールの再発明を避けるために、awkの高速ソート実装を使用することができます。https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#AWK(GPLv2).
それから(編集する:より良い、より安全な方法は追加で確認してください):
code=$(
awk -v q="'" -- '
code-from-that-link-above
BEGIN {
delete ARGV[0]
n = qsortArbIndByValue(ARGV, sorted)
printf "set --"
for (i = 1; i <= n; i++) {
s = ARGV[sorted[i]]
gsub(q, q "\\" q q, s)
printf " %s", q s q
}
}' "$@"
) || exit
eval "$code"
位置引数にはユーザーロケールの有効なテキストが含まれており、リストはコマンドライン引数(およびawkの配列サイズの制限)に収まるほど小さいと仮定します。
オペランドが数値として認識された場合は、awk
演算子を使用して数値比較を実行し、それ以外の場合は比較を実行します。比較をから強制比較に変更し(比較のためにロケールをCに固定)、数値比較を強制に変更(POSIXですが実際に移植性はありません)して、これを行うか、いつでも実行するカスタムawk関数を作成できます。あなたが望むどんな比較。<
strcoll()
strcoll()
a < b
a"" < b""
strcmp()
a+0 < b+0
+a < +b
compare()
POSIXと互換性がある場合は、このコードを置き換える必要がありますが、gsub(q, q "\\\\" q q, s)
POSIXgsub(q, q "\\" q q, s)
が指定されていない動作を生成しても、後者は環境やビジボックスをgawk
除いて正常に動作しないため、移植性が優れています。$POSIXLY_CORRECT
awk
データがユーザーのロケールで有効なテキストであることが保証されていない場合は、ロケールを次のように設定できます。C
これにより、文字列は、strcmp()
ユーザーロケールソート順序ではなくバイト配列として処理されソートされます(ASCIIベースのシステムでは)。
不特定行為の結果かもしれないことを与えるのは多少不便ですが、もう一度考えてみるとそのようなものを出力せずに代わりに出力するならeval
信頼できるようにすることが可能でしょう。awk
set -- '3rd argument hoped to be quoted correctly' 'first' 'second'
set -- "${3}" "${1}" "${2}"
さらに、この作業はより簡単、短く、より効率的でなければなりません。
code=$(
awk -- '
code-from-that-link-above
BEGIN {
delete ARGV[0]
n = qsortArbIndByValue(ARGV, sorted)
printf "set --"
for (i = 1; i <= n; i++)
printf " \"${%s}\"", sorted[i]
}' "$@"
) || exit
eval "$code"