限定された定義済みリストの単語をパラメーターとして受け入れる必要があるスクリプトを作成しています。またぜひ行ってください。complete
との間の重複を避けるために、リストを変数に保存しますcase
。だから私はこれを書き、完成は機能しますが、case
ドアは機能しません。なぜ?ケースステートメントパラメータを作成するために変数を使用することはできませんか?
declare -ar choices=('foo' 'bar' 'baz')
function do_work {
case "$1" in
"${choices[*]}")
echo 'yes!'
;;
*)
echo 'no!'
esac
}
complete -W "${choices[*]}" do_work
答え1
のリストは区切りリストcomplete -W list
として解釈され、完成型が考慮されます。$IFS
$IFS
したがって、次のような場合:
complete -W 'a b,c d' do_work
do_work
a
スペースが含まれている場合とb,c
スペースが含まれている場合と含まれていない場合に完成機能が提供されます。d
$IFS
a b
c d
$IFS
,
だからほとんど設計上壊れました。また、任意の文字列を完成として提供することもできません。
これらの制限を適用するための最善の方法は変更されないと$IFS
仮定し(したがって、スペース、タブ、および改行が常に含まれるため、完成語に文字を使用できない)、次のことを実行することです。
choices='foo bar baz'
do_work() {
case "$1" in
(*' '*) echo 'no!';;
(*)
case " $choices " in
(*" $1 "*) echo 'yes!';;
(*) echo 'no!';;
esac;;
esac
}
complete -W "$choices" do_work
readonly IFS
変更しないように追加できますが、特に親範囲で読み取り専用として宣言されたローカル変数を宣言することを許可しないことを$IFS
考慮すると、問題が発生する可能性があります。したがって、これを実行する関数でも破壊されます。bash
local IFS=,
配列要素に文字列があるかどうかを確認する方法に関するより一般的な質問についてはbash
(zshとは異なり)演算子はありませんが、ループを使用して簡単に実装できます。
amongst() {
local string needle="$1"
shift
for string do
[[ $needle = "$string" ]] && return
done
false
}
それから:
do_work {
if amongst "$1" "${choices[@]}"; then
echo 'yes!'
else
echo 'no!'
fi
}
文字列を見つけるのに適した構造は、ハッシュテーブルまたは連想配列を使用することです。
typeset -A choices=( [foo]=1 [bar]=1 [baz]=1 )
do_work() {
if [[ -n ${choices[+$1]} ]]; then
echo 'yes!'
else
echo 'no!'
fi
}
complete -W "${!choices[*]}" do_work
これは"${!choices[*]}"
連想配列のキーを$IFS
その点の最初の文字に関連付けます(設定されていますが空の場合は区切り文字は使用されません)。
Bashアソシエーション配列は空のキーを持つことができないため、空の文字列はオプションの1つにすることはできませんが、とにかくサポートされていませcomplete -W
ん。許容できる値のうち
答え2
文の値は構文の一部であるため、明示的case
に区切る必要があります。ただし、Case文の値は必ずパターンである必要があるため、拡張パターンを使用することもできます。|
|
string="*@($(IFS="|";echo "${choices[*]}"))*"
次のように拡張スキーマで生成して使用します。
#!/bin/bash
declare -ar choices=('foo' 'bar' 'baz')
string="*@($(IFS="|";echo "${choices[*]}"))*" # value contains one option
#string="@($(IFS="|";echo "${choices[*]}"))" # value is exactly one option
shopt -s extglob
function do_work {
case "$1" in
$string) echo 'yes!' ;;
*) echo 'no!' ;;
esac
}
complete -W "${choices[*]}" do_work
しかし、これはextglobのオプションを変更しており、終了する前にスクリプトを起動したときの値として返すことで回避できます。$string
関数が実行されるのではなく、関数が定義されたときに値が拡張されることに注意してください。しかし、より簡単な解決策は、=
テストの右側が内部で動作するという[[...]]
事実を使用して、対応するシェルオプションを設定せずに設定を解除することです。extglobシェルオプションが有効になっているのと同様(いいえ、申し訳ありませんが動作しません。[...]
)したがって、ケース表現を[[ $1 = $string ]]
and(echoコマンドの場合)に単純化できます。これは次のように機能します。
[[ $1 = $string ]] && echo 'yes!' || echo 'no!'
ただし、一般的に関数を使用する方が良いので、関数が終了コードを返すことは保証されていないため、0
実際には次のものを使用する必要があります。
if [[ $1 = $string ]]; then yesaction; else noaction; fi
この回答の難しい部分はすでに解決されているので、スペースでオプションを使用するように補完機能を有効にすることもできます。 IFSがオプションを分割するときに必要なのは、${complete_var}
各オプションを引用符で囲み、スペースで区切るだけです。
#!/bin/bash
declare -a choices=('foo' 'foo bar' 'baz')
option_string="@($(printf -v var "%s|" "${choices[@]}"; printf '%s' "${var%?}" ))"
complete_var="$( printf -v var "'\"%s\"' " "${choices[@]}"; printf '%s' "${var%?}" )"
#printf '%s\n' "${option_string}" "${complete_var}"
yesaction() { echo 'yes!'; }
noaction () { echo 'no!' ; }
do_work () {
if [[ $1 == $option_string ]]
then yesaction
else noaction
fi
}
complete -W "${complete_var}" do_work
return 34;
exit
これにより、これらの値が関数内で固定値に設定されます。choices
読み取り専用に設定する必要はありません。そしてオプション内にスペースを入れてください。
.
これはシェルでスクリプトを get() すると仮定します。それは次のとおりです。
$ . ./script.sh
$ do_work <kbd>TAB</kbd>-<kbd>TAB</kbd> # will show "foo" "bar" and "foo bar" as options.
私が見ることができる唯一の問題は、完了した値が二重引用符内に表示されることです"foo"
。しかし、私の考えでは、これは解決すべき問題ではありません。
答え3
=~
演算子を使用して、値が配列にあることを確認することもできます。
if [[ " ${choices[*]} " =~ " ${1} " ]]; then
echo "yes!";
else
echo "no!";
fi