すでに他のスレッドで説明したように、Whileステートメント:複数の条件を複合的に使用することはできません。どういうわけか入力パラメータを解析しようとしていますrecursive descent
。
残念ながら、このスレッドの参加者は私よりも知識が豊富で素晴らしいアドバイスを提供しますが、問題は持続します。
私はまた、成功していないままコンソールで自分自身を探りました。
私のスクリプトには2つの文がありますwhile
。これは明らかに、endOfInput
関数がwhileループを終了することに影響を与えないために問題があります。
周りを見回すとループに入らない場合もあります。
したがって、外層はwhile-statement
次のようになります。
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
さらに、ここも内部です。while-statement
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
ヘルパーメソッドもあります。
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=false
function endOfInput? {
if [ $current_token_index -ge $ARGC ]; then
EO_ARGS=true
fi
echo $EO_ARGS
}
また、このeat!
関数は場所のコピーから次のトークンを取得できます。
eat! () {
current_token=${ARGV[$current_token_index]}
((current_token_index += 1))
current_char=${current_token:0:1}
}
しかも宣言します。
# as set by eat!()
current_token=""
current_char=""
current_token_index=0
curent_char_index=0
私の質問はこの可能性についてです。endOfInput
(もともとendOfInput?
私?
は知っている限り削除しましたが、bashではワイルドカードの意味を持ち、問題を引き起こす可能性があります。Rubyではほとんどの特殊文字を識別子記号として選択できます。したがって、endOfInput
関数の複数の定義とwhileステートメントの異なるテストまたは比較構文を使用して、関数の真実性を正しく評価することはできません。責任があります。
上記で公開されたループヘッダーの問題while
は、進入しないか停止しないことです。テスト方法endOfInput
やグループ化方法によって異なります。
! endOfInput && ...
たとえば、ちょうど交換すると! false && ...
whileループに入り、それ以外の場合は入力されません。
それで、機能に何か問題があるようです。正しく評価されません。問題は、1)の定義、endOfInput
または2)これをテストする方法、つまり
2.1どんなテスト(例:
-eq
文字列比較=
、算術演算子など==
)2.2何テスト済みです。つまり
- 2.2.1 文字列 "true"/"false"
- 2.2.2 リテラルで
true
合計するfalse
- 2.2.3
0
ターゲットtrue
と1
ターゲットfalse
3 この値を正しく返す方法は?
- 3.1 合格
return
- 3.2 パス
exit
- 3.3 パス
echo
- 3.1 合格
4 どのテスト構造によって、戻り値が他の値と比較されますか?
- 4.1 なし、機能のみ実行
- 4.2角括弧(
[
または[[
コマンド) - 4.3 算術括弧
((...))
- 4.4 算術拡張
$((...))
- 4.5 1つ以上の項目の組み合わせ
したがって、その定義endOfInput
とそれが文の頭の部分でどのように使用されるかを見てくださいwhile
。何が問題である可能性があり、無限ループが発生するのですか?私は、他の2つの機能がisWord?
動作することを意味します。
私の関数定義はどうすればいいですか、正しく評価するためにendOfInput
ステートメントでどのようにテストしてリンクする必要がありますか?while
編集:ilkkachuは、「最小限で完全で検証可能な例」を投稿したいと思いました。
次の内容で十分です。
まずget_installed_gems_with_versions
、インストールされているすべてのgemとそのバージョンを連想配列にインポートするために呼び出します。これでグローバル連想配列が作成されましたinstalledGems
。
だから私の考えでは分析するオプション--gemsは、次の呼び出しによってインストールされたgemとそのバージョンの選択を確認するためにparse_gems_with_versions
解析されます。parseGemVersions
$chosenGemVersions
解析部分に関連するいくつかのテストコードを捨てましたが、whileループが機能しない場合、現在の質問には関係ありません。
コードは次のとおりです。
get_installed_gems_with_versions () {
unset installedGems
declare -Ag installedGems
local last_key=""
local values=""
while read -r line; do
line=${line##*/}
KEY="${line%-*}"
VALUE="${line##*-}"
if [ -z ${installedGems[$KEY]+x} ]; then
echo "key $KEY doesn't yet exist."
echo "append value $VALUE to key $KEY"
installedGems[$KEY]="$VALUE"
continue
else
echo "key already exists"
echo "append value $VALUE to $KEY if not already exists"
installedGems[$KEY]="${installedGems[$KEY]} $VALUE"
echo "result: ${installedGems[$KEY]}"
fi
done < <(find $directory -maxdepth 1 -type d -regextype posix-extended -regex "^${directory}\/[a-zA-Z0-9]+([-_]?[a-zA-Z0-9]+)*-[0-9]{1,3}(.[0-9]{1,3}){,3}\$")
}
parseGemVersions () {
local version_list
declare -Ag chosenGemVersions
while [[ "$current_token_index" -le "$ARGC" ]] && [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
if versionOfGemInstalled? $gem $current_token; then
if [ -z ${chosenGemVersions[$gem]+x} ]; then
chosenGemVersions[$gem]=$current_token
# continue
else
chosenGemVersions[$gem]="${chosenGemVersions[$gem]} $current_token"
fi
else
parsing_error! "While parsing function $FUNCNAME, current version number $current_token is not installed!"
fi
echo "result: ${chosenGemVersions[$gem]}"
eat!
done
}
parse_gems_with_versions () {
# option --gems
# --gems gem-name-1 1.1.1 1.2.1 1.2.5 gem-name-2 latest gem-name-3 ...
unset gem
unset chosenGemVersions
gem=""
declare -Ag chosenGemVersions
while [ $current_token_index -le $ARGC ] && [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; do
if isWord? $current_token && [ ! "$current_token" = "latest" ]; then
if isWord? $current_token; then
if gemInstalled? $current_token; then
# We can conjecture that the word token is in fact a gems' name
gem=$current_token
local version_list
if isWord? $current_token && [ "$current_token" = "latest" ]; then
version_list=(${installedGems[$gem]})
chosenGemVersions[$gem]="${version_list[${#version_list} -1]}"
else
# gets list chosenGemVersions
parseGemVersions
fi
else
parsing_error! "Gem $token_name not installed!"
fi
fi
else
parsing_error! "While parsing function $FUNCNAME, "latest" is not a gemname!"
fi
eat!
done
}
答え1
あなたの問題は主にシェルスクリプトのブール型の誤解だと思います。
デフォルトでは、最初のヘルパー関数を次の関数に置き換えることができます。
#!/bin/bash
set -o nounset
set -o errexit
is_word ()
{
local pattern='^[a-zA-Z0-9_-]+$'
[[ "$1" =~ $pattern ]]
}
is_version_number ()
{
local pattern='^[0-9]{1,2}(\.[0-9]{,2})*$'
[[ "$1" =~ $pattern ]]
}
# testing is_word ()
if is_word 'bla01'; then
echo IS word
else
echo NOT word
fi
# testing is_version_number ()
if is_version_number '1.1'; then
echo IS version number
else
echo NOT version number
fi
真/偽などをエコーしようとせず、コマンド([..]
または[[..]]
)自体をテストして正しいブール値(実際には整数)を返し、独自の真/偽構成を使用しないでください。
また、常にShellCheck(https://www.shellcheck.net/)スクリプトを保護またはデバッグします。
set -o nounset
(set -u
)とset -o errexit
()を活用するのも良い習慣ですset -e
。
あなたの質問が更新されたバージョンでは、あなたがCプログラミングに慣れていることがわかります。$ARGC
、またははシェルスクリプトにありません。$ARGV
代わりに、以下を使用してください。
ARGC
(パラメータの数):"$#"
ARGV
(パラメータ配列):"$@"
たとえば、次のようにできます。
eat_args ()
{
for arg in "$@"; do
current_char=${arg:0:1}
echo "$current_char"
done
}
eat_args "$@"
これは与えられた各引数の最初の文字を印刷するので、私にとってはあまり意味がありません。乾杯と幸運を祈ります。
答え2
解決策は、演算子で連結された条件をグループ化することです||
。
while [[ $current_token_index -le $ARGC ]] && { [[ "$current_token" =~ ^[a-zA-Z0-9_-]+$ ]] || ! [[ "$current_token" =~ ^[0-9]{1,2}(\.[0-9]{,2})*$ ]]; }; do