이미 다른 스레드에서 설명했듯이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에서는 대부분의 특수 문자를 식별자 기호로 선택할 수 있습니다. 문제 없습니다. 분명히 다른 프로그래밍 언어에서 온 경우 bash에 많은 경고가 있습니다. 따라서 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 하나 이상의 항목의 조합
따라서 의 정의 endOfInput
와 그것이 문장의 머리 부분에서 어떻게 사용되는지 살펴보십시오 while
. 무엇이 문제일 수 있으며 무한 루프가 발생하는 이유는 무엇입니까? 내 말은, 다른 두 기능은 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