正如我在其他線程中已經解釋過的While 語句:無法使多個條件複合作用我正在嘗試以某種方式解析輸入參數recursive descent
。
可悲的是,儘管該帖子的參與者比我更知識淵博並提供了很好的建議,但問題仍然存在。
我還在控制台中進一步探索了自己,但沒有成功。
我的腳本中有以下兩個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
是它們要么沒有進入,要么沒有停止。取決於我如何測試endOfInput
或如何分組。
如果我! endOfInput && ...
只替換為,! false && ...
例如將進入 while 循環,否則不會進入。
所以我猜肯定是函數有問題。它沒有被正確評估。問題可能是 1) 的定義endOfInput
,或 2) 如何測試它,即,
2.1 與什麼測試(例如
-eq
,字串比較=
,算術運算子等==
)2.2什麼經測試。 IE
- 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 一項或多項的組合
所以請看一下 的定義endOfInput
以及它在語句頭的使用方式while
。可能是什麼問題,為什麼會出現無限循環?我的意思是,其他兩個功能確實isWord?
有效。
我的函數定義應該如何endOfInput
,以及它在語句中的測試和串聯應該如何,while
以便正確評估?
編輯:ilkkachu 希望我發布一個“最小、完整且可驗證的示例”
我希望以下內容就足夠了。
首先,我呼叫get_installed_gems_with_versions
以取得關聯數組中的所有已安裝的 gem 及其版本。現在我有了全域關聯數組installedGems
。
那我想解析選項 --gems 透過調用parse_gems_with_versions
,依序調用parseGemVersions
以將已安裝的 gems 及其版本的選擇解析為$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
我相信你的問題主要是誤解 shell 腳本中的布林類型。
您基本上可以用以下函數取代第一個輔助函數:
#!/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 程式設計。shell 腳本中沒有$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