他のスレッドでもすでに説明したようにwhile 文: 複数の条件の組み合わせを機能させることができません入力引数をある方法で解析しようとしていますrecursive descent
。
残念なことに、このスレッドの参加者は私よりもはるかに知識が豊富で、良いアドバイスをくれたにもかかわらず、問題は依然として残っています。
また、コンソールでさらにいろいろ調べてみましたが、何も見つかりません。
私のスクリプトには次の 2 つのステートメントがありますが、明らかに関数は while ループの終了に影響を与えないwhile
ため、問題があります。endOfInput
いろいろ調べてみると、ループに入らないことさえありました。
外側は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 では、ほとんどの特殊文字を問題なく識別子として選択できます。他のプログラミング言語から来た場合、bash には多くの警告があります) ...endOfInput
関数の真理値は、条件が満たされているかどうかを評価するための独自の必要性を持つ while ステートメント内の異なるテストまたは比較構文とともに、関数の複数の定義によって正しく評価されません。したがって、方程式には条件付き星座の形成に関与する 3 つの要素があります。
上に投稿したループ ヘッドの問題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 を呼び出すとparse_gems_with_versions
、parseGemVersions
インストールされている gem とそのバージョンを選択して解析するためにが呼び出されます。$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
ご存知のとおり、true/false などを echo しようとしないでください。テスト コマンド ([..]
または[[..]]
) 自体は適切なブール値 (実際には整数) を返します。独自の true/false 構造は使用しないでください。
さらに、常に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