-i
フラグ(→)を取るスクリプトがあります。設定されて$interactive
いる場合は、各ターゲットとは異なるターゲットについてはい/いいえ - デフォルト - いいえ質問をしますrm -i
。
Zshread -q
はこのような状況のために設計されています。単一のキーを許可し、y
キーがまたは場合はy変数をに設定しY、そうでない場合はに設定しますn
。
私の問題は、プロンプトを繰り返していることです。だからそれ自体同じ行に私のプロンプトを繰り返し印刷します。つまり、このコードは次のようになります。
# moshPids is an array of pids
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -q "answer?Kill $p? (N/y)"
[[ "${answer}" == 'n' ]] && continue
fi
# do_kill set to print “killing $1” in debug
do_kill $p
done
結果:
$ myCmd
Kill 123? (N/y)nKill 456? (N/y)nKill 789? (N/y)yKilling 789
$
次のようにプロンプトに改行を含めるなど、これには多くのソリューションがあります(その一部はこのサイトの回答にあります)。
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -q "answer?Kill $p? (N/y)
"
[[ "${answer}" == 'n' ]] && continue
fi
do_kill $p
done
結果:
$ myCmd
Kill 123? (N/y)
nKill 456? (N/y)
nKill 789? (N/y)
yKilling 789
$
これは私にとって醜いようです。別の解決策は、コマンドecho >&2
の後に追加することですread
。これは少なくとも一見すると完璧に見えます。
$ myCmd
Kill 123? (N/y)n
Kill 456? (N/y)y
Killing 456
Kill 789? (N/y)n
$
ただし、デフォルト値を受け入れると空白行⏎が表示されます(⏎
実際の出力には表示されませんが、入力を表示するために追加されます)。
$ myCmd
Kill 123? (N/y)⏎
Kill 456? (N/y)y
Killing 456
Kill 789? (N/y)⏎
$
基準
したがって、ワンクリック応答が必要です。
- 複数のヒント/回答は1行に印刷されません。
- ⏎「デフォルト値を受け入れる」をクリックすると空白行は生成されません。
- キーがまたはの場合に
read -q
設定する動作を維持し続けます。それ以外の場合に設定します。answer
y
yYn
ソリューション1
これが私の最初の解決策です。
for p in $moshPids; do
if [[ -n $interactive ]]; then
read -k1 "answer?Kill $p? (N/y)"
[[ $answer != $'\n' ]] && echo >&2
[[ "${answer}" =~ '[yY]' ]] || continue
fi
do_kill $p
done
ここでは、-k1
代わりに「1文字、すべての文字を読む」アクションを取得するように-q
渡されます。read
これにより、文字が改行文字と一致するかどうかをテストできます。 ($answer != $'\n'
)がない場合は、行方不明の改行文字を印刷できます。
しかし、今では、または$answer
キーだけでなく、押したキーに設定されます。だから我々は()を確認する必要があります。y
n
y
Y
"${answer}" =~ '[yY]'
この問題を処理するより良い方法はありますか?
ソリューション2
stty
また、キーボードエコーを一時的に無効にする呼び出しを使用してこれを検討しました。
for p in $moshPids; do
if [[ -n $interactive ]]; then
stty -echo
read -q "answer?Kill $p? (N/y)"
stty echo
echo >&2
[[ "${answer}" == 'n' ]] && continue
fi
do_kill $p
done
きれいな出力の優先順位を決めるのか、ロールバック時に入力を見ることができるかに応じて、キーレスポンスの可視性を無効にすると、より良いかもしれません。 (この場合はい/いいえ質問では、yesオプションは常に何かを印刷し、noechoは大丈夫に見えます。必要に応じて常にifのecho -n "${answer}"
後に追加し、read
いくつかの拡張注文を使用して改行をスペースに置き換えることができます。)
最初の解決策よりも1行長いですが、正規表現をread -q
テストする必要はなく、無条件に改行をエコーするので、理解しやすくなります。
しかし、一方では、する猿対ターミナル。両方のコマンドを十分に近づけましたが、そうしないでください。またコマンドの中断の時間ウィンドウは、端末が狂ってしまうのに十分な大きさですが、まだ問題になります。 (また、完全性のために-i
非対話型で呼び出されないチェックを追加する必要があります。そうしないと、stty
ANDread
操作は失敗します。)
私が提案した2つ以上のより良い、より慣用的な解決策はありますか?これは-i
、多くの「インタラクティブ」コマンド(もちろん、ほとんどはシェル以外のもので書かれています)が続く明らかな動作のようです。
答え1
修正する:
マニュアルから:
read
...
-s Don't echo back characters if reading from the terminal.
効果があります:
for p in $pids; do
read -qs "?Kill $p? (N/y)"
>&2 echo $REPLY
case $REPLY in
(y) do_kill $p ;;
esac
done
read -q
返品状況も提供されます。
for p in $pids; do
if read -qs "?Kill $p? (N/y)"; then
>&2 echo $REPLY; do_kill $p # answer was y or Y
else
>&2 echo $REPLY # answer was not y or Y
fi
done
あなたが望むなら。
-i
設定を処理するオプションについては、forをループの外に$interactive
置きます。たとえば、次のようになります。test
$interactive
for
interactive_kill() {
for p in $@; do
read -qs "?Kill $p? (N/y)"
>&2 echo $REPLY
case $REPLY in
(y) do_kill $p ;;
esac
done
}
if [[ -n $interactive ]]; then
interactive_kill $pids
else
do_kill $pids # if do_kill() needs exactly one arg wrap in a for loop
fi
返品:
- シェルモードをテストする必要
y
はY
ないので、=~
演算子を使用するかステートメントで使用できます。[yY]
[[
=
[yY]
case
- 最初の例では、ロジックが少し歪んでいるようです。
[[ "${answer}" == 'n' ]] && continue
do_kill $p
いつ
if [[ $answer = y ]]; then
do_kill $p
fi
読みやすくなります。
答え2
私もあなたが説明する動作をしたいが、単純ではありません。私はヘルパー関数を書いた。
ask_yes_no() {
read -qs "?$1 (y/N) "
out=$?
print $REPLY >&2
return $out
}
# example usage
for thing in one two three; do
if ask_yes_no "Do thing $thing?"; then
echo "Do thing $thing"
else
echo "Nope"
fi
done
出力例:
Do thing one? (y/N) n
Nope
Do thing two? (y/N) y
Do thing two
Do thing three? (y/N) y
Do thing three