条件付きBash:私のテ​​ストは正しいですか?無限ループが必要です

条件付きBash:私のテ​​ストは正しいですか?無限ループが必要です

すでに他のスレッドで説明したように、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.30ターゲットtrue1ターゲットfalse
  • 3 この値を正しく返す方法は?

    • 3.1 合格return
    • 3.2 パスexit
    • 3.3 パスecho
  • 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 nounsetset -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 

関連情報