スキーマによって引数を解析してみますrecursive descent
。
ステートメントは、実行のたびに関数while
を呼び出して、次に処理するものを取得します。eat!
$current_token
問題は、この方法では無限ループに陥ってしまうことです。
正確に言うと、関数には 2 つの while ループがあります。
複合語が構文的に(または論理的に?)間違っているため、どちらか一方または両方が機能しません。
次のコードで何が間違っているのかを、可能な限り多くのレベルで確認していただけますか?
1)
while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
while isVersionNumber? $current_token && ! endOfInput?; do
また、何が正しいのでしょうか? チェック機能をコマンド置換に入れるかどうか?
短絡によってもendOfInput?
-テストが妨げられる可能性はありますか?
そうすればループは絶対に止まるはずだからです。
ilkkachuのリクエストにより
テスト関数のコード:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
if [[ $1 =~ $pattern ]]; then
echo true
else
echo false
fi
}
isVersionNumber? () {
local pattern="^[0-9]{1,2}(\.[0-9]{,2})*$"
if [[ $1 =~ $pattern ]]; then
echo true
else
echo false
fi
}
EO_ARGS=1
function endOfInput? {
if [ $current_token_index -ge $ARGC ]; then
EO_ARGS=0
fi
echo $EO_ARGS
}
コマンド置換で使用しているため、別のコマンドが出力されると思いますか?
いいえ、コマンド置換なしで条件内の関数を呼び出すことができるかどうかわからないので、試してみただけです。
完全性のために、eat!
関数も追加します。
eat! () {
if [[ ! $(($current_char_index + 1)) -gt $ARGC ]]; then
current_token=${ARGV[$current_token_index]}
((current_token_index += 1))
current_char=${current_token:0:1}
}
そして、条件はおそらくtest
/ [
( [[
) でより適切に定式化されるでしょうか?
感嘆符が失敗の原因なのでしょうか?
以下は文法的に間違っているようです:
while [ endOfInput -eq 1 ] && isWord? "$current_token" || ! isVersionNumber? "$current_token"; do
括弧から[
エラーメッセージが表示されます
[: endOfInput: integral expression expected (translation)
答え1
関数が true の場合は 0 を返し、false の場合はそれ以外の値を返した場合は、それを直接使用できます。
my-true () {
return 0
}
while my-true ; do
echo Infinite loop...
done
偽の場合は何も出力せず、真の場合は何かを出力する場合は、コマンド置換を使用します。
my-true () {
echo OK
}
while [ "$(my-true)" ] ; do
echo Infinite loop...
done
前者の場合、関数を連鎖するには、&&
and||
演算子を使用できます。
while func1 && func2 ; do
後者の場合、代わりに連結を使用できます||
。
while [ "$(func1 ; func2)" ] && [ "$(func3)" ]
後者の場合、関数はサブシェルで呼び出されるため、関数内のグローバル変数を変更できないことに注意してください。
ちなみに、?
関数名での使用は禁止されていませんが、ワイルドカードであるため問題が発生する可能性があります。
abc? () {
return $1
}
abc? 0 # Everything OK.
touch abcX
abc? 0 # line 9: ./abcX: Permission denied
abc\? 0 # This works, but where's the charm?
!
はデフォルトの履歴拡張文字ですが、これは主に対話型シェルにのみ関連します。
答え2
次のコードで何が間違っているのかを、可能な限り多くのレベルで確認していただけますか?
いいよ。
また、何が正しいのでしょうか? チェック機能をコマンド置換に入れるかどうか?
コマンド置換は、その中のコマンドをそのまま使用します。プリント、それをコマンドラインに置きます。(展開が引用符で囲まれていない場合は、分割とグロブの後)。したがって、
foo() {
echo true
}
if $(foo); then
echo hello
fi
すると、コマンド置換によって が生成されtrue
、それが -statement の条件部分で実行されif
、そこからの終了ステータスを使用して、 のメイン ブランチがif
実行されるかどうかが判断されます。この場合は、実行されます。
しかし、それは馬鹿げています。コマンド置換 (Bash ではフォークが関係します) を経由せずに、終了ステータスを直接返すこともできます。
foo() {
return 0
}
if foo; then
echo hello
fi
true
または、最後のコマンドの終了ステータスが関数の終了ステータスとして使用されるため、で関数を終了することもできます。
次のように書くこともできます:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
if [[ $1 =~ $pattern ]]; then
return 0
else
return 1
fi
}
while isWord? "$current_token"
あるいは:
isWord? () {
local pattern="^[a-zA-Z0-9_-]+$"
[[ $1 =~ $pattern ]]
}
while isWord? "$current_token"
通常の理由 (単語の分割やグロブなど) により、変数を引用符で囲む必要があることに注意してください。
while $(isWord? $current_token) || ! $(isVersionNumber? $current_token) && ! $(endOfInput?); do
ここで、a || b && c
構文は としてグループ化され(a || b) && c
、つまり左から右に実行されることに注意してください。実際のプログラミング言語とは異なり、そして演算子は、または(代わりになるという意味ですa || (b && c)
)。どちらが必要か確認してください。
また、Bash のデフォルト設定では、 のような名前はisWord?
それほど問題にならないかもしれませんが、それでも glob です。現在のディレクトリにそれに一致するファイルがある場合、それらのファイル名に展開されます。また、failglob
または を使用するように切り替えた場合nullglob
、それらも影響します。たとえば、 の場合failglob
:
$ foo? () { echo hi; }
$ foo?
bash: no match: foo?
$ "foo?"
hi
glob として動作しないようにするには、引用符で囲む必要があります。Zsh では、一致しない glob はデフォルトで失敗することに注意してください。
関数名の最後の文字としての感嘆符は、問題ではありません。感嘆符が関数名の先頭または途中にあり、シェルで履歴展開が有効になっている場合は、履歴展開が発生する可能性があります (対話型シェルではデフォルトで有効になっていますが、スクリプトでは有効になっていません)。
短絡によって endOfInput? テストも妨げられる可能性がありますか?
のような場合while bar && ! endOfInput?; do
、bar
左辺が偽の状態を返す場合、そしては既に分かっており、右辺はスキップされます。 および についても同様に||
、真偽の戻り値です。 これが および が行うこと&&
です||
。
しかし、この質問に対して、関数が呼び出されない場合になぜそれが問題になるのかといったコンテキストが示されていないため、より良い方法を考えるのは困難です。