"$@"
私は配列を使って反転することが可能であることを知っています。
arr=( "$@" )
そしてこの回答を使用してください、配列を反転します。
ただし、これには配列を含むシェルが必要です。
可能tac
:
set -- $( printf '%s\n' "$@" | tac )
ただし、引数に空白、タブ、または改行$IFS
(デフォルトは仮定)が含まれているか、ワイルドカードが含まれている(ワイルドカードが事前に無効になっていない場合)、空の要素が削除され、GNUtac
コマンド(tail -r
GNUシステムの外部で使用されます)が少しより移植性がある場合、これは中断されます。しかし、いくつかの実装は大きな入力のために失敗します)。
配列を使用せずにシェル位置パラメータを移植可能に戻し、パラメータに空白、改行、またはワイルドカードが含まれているか空になる可能性がある場合でも、そのメソッドを機能させる方法はありますか?
答え1
移植可能で、配列は必要なく(位置引数のみ)、スペースと改行を使用できます。
flag=''; for a in "$@"; do set -- "$a" ${flag-"$@"}; unset flag; done
例:
$ set -- one "two 22" "three
> 333" four
$ printf '<%s>' "$@"; echo
<one><two 22><three
333><four>
$ flag=''; for a in "$@"; do set -- "$a" ${flag-"$@"}; unset flag; done
$ printf '<%s>' "$@"; echo
<four><three
333><two 22><one>
値はflag
拡張を制御します${flag-"$@"}
。 setの場合(空の場合でも)flag
値に展開されます。flag
したがって、 flag
isのflag=''
場合は${flag....}
nullに展開され、引用符がないため、シェルから削除されます。設定されていない場合、flag
の値は の拡張である${flag-"$@"}
右の値に拡張されるので、すべての位置引数になります (引用符を含む、null 値は削除されません)。また、変数は最終的に削除(設定解除)され、後続のコードには影響しません。-
"$@"
flag
答え2
配列を一時記憶域として使用したくない場合は、for
ループを使用できます。いつも不変の静的要素のセットを繰り返します。ある意味、私たちはループを使うことができますそれ自体リストを逆順に書き換える間、位置引数の一時記憶域として機能します。
これを行うには、最初の反復でリストを消去する必要があります。以下のコードは単純なフラグを使用して、これが必要かどうかを検出します。リストがクリアされるとフラグが切り替わります。
flag=true
for value do
if "$flag"; then
set --
flag=false
fi
set -- "$value" "$@"
done
残念ながら、位置パラメータのリストは実際には非常に遅いです。繰り返しごとに再構築(set -- some-list
すべての位置パラメータを設定します)。シェルはbash
1から10000の整数を反転するのに約50秒かかりますが、zsh
15秒が少し長くかかります。
使用イサクのトリックwith(設定されていない場合にのみ拡張されます${flag-"$@"}
)は、実際に全体の実行速度を1分50秒(!)25秒遅くします。"$@"
flag
bash
zsh
$flag
これは、シェルがテストおよび/または拡張拡張を実行する方法"$@"
のいくつかの実装の詳細が原因であると仮定します${flag-"$@"}
(シェルが"$@"
内部で2回拡張される可能性がありますか?)。
配列を一時記憶域として使用することが許可されている場合(これはそうではありません)。基準しかし、まだかなり持ち運べる通常、スクリプトを作成するシェルを知っているので、$#
位置引数を繰り返すときにその値(位置引数の数)をインデックスとして使用して現在の値を格納できます。繰り返すたびにこの値を減らすと、shift
配列の終わりから先頭まで値を挿入する効果があります。
では、bash
配列はインデックス0から始まり、shift
割り当て後に発生するため、最後の位置引数はゼロではなくインデックス1に格納されます。これは、コードがどのように機能するかに影響を与えず、bash
まだ正しい結果を生成しますが、それは機能しますzsh
(デフォルトでは1ベースの配列インデックスを使用)。
パスワード:
tmp=()
for value do
tmp[$#]=$value
shift
done
set -- "${tmp[@]}"
bash
またはを使用すると、zsh
1から10000の整数を反転するのに約0.6秒かかります。
答え3
コピーした場所私の答え到着Bash - globを使用してリバースファイルリストを印刷する, POSIX 方式で位置引数のリストを反転します。
eval "set -- $(awk 'BEGIN {for (i = ARGV[1]; i; i--) printf " \"${"i"}\""}' "$#")"
あるいは、数行でもう少し明確に説明すると、次のようになります。
eval "set -- $(
awk '
BEGIN {
for (i = ARGV[1]; i; i--)
printf " \"${" i "}\""
}' "$#"
)"
たとえば、3つの要素があるときに解釈するシェルコードをawk
生成するのに役立つアイデアです。set -- "${3}" "${2}" "${1}"
eval
"$@"
大きなリストでは、シェルループ、特に各反復でリストを書き換えるループを使用するよりもはるかに高速です。このawk
コードは、同じ出力を提供するシェルループで置き換えることができます(@mosvyがコメントで提案したように)。ただし、bash5 + gawk4.1を使用したテストでは、非常に短いリストを除いて、まだ2倍遅いです。
配列を反転するために明示的に設計されたパラメータフラグをzsh
使用できます。Oa
set -- "${(Oa)@}"
私のシステム(@Kusalanandaより少し遅い)とbash5 + gawk4.2.1で取得した位置引数リストを使用すると、set $(seq 10000)
このeval
方法は0.4秒かかります。@クサラナンダ1分かかります@ISAACの2分かかります(zsh
方法Oa
は約2ミリ秒かかります)。
busybox 1.30.1sh
では、awk
この時間がそれぞれ 0.06 秒、11 秒、11 秒になります。