문자열 변수의 내용 간에 가능한 모든 비교를 수행해야 하는 스크립트가 있습니다. 각 조합에는 변수 내용에 대한 서로 다른 접근 방식이 필요하므로 다음과 같습니다.
if $a contains "a" AND "b" AND "c"; then do x
elif $a contains "a" AND "b" but NOT "c"; then do y
elif $a contains "b" AND "c" but NOT "a"; then do z
...
내가 아는 한, 이를 수행하는 방법은 다음과 같은 if 조건을 구성하는 것입니다.
if [[ $A == *"b"* ]] && [[ $A == *"c"* ]] && [[ $A == *"d"* ]]
elif [[ $A == *"b"* ]] && [[ $A == *"c"* ]]
elif [[ $A == *"c"* ]] && [[ $A == *"d"* ]]
elif [[ $A == *"b"* ]] && [[ $A == *"d"* ]]
elif [[ $A == *"b"* ]]
elif [[ $A == *"c"* ]]
elif [[ $A == *"d"* ]]
fi
그러나 물론 변수 이름($A)과 하위 문자열(b, c, d)이 이보다 훨씬 길기 때문에 오류 없이 읽고 이해하고 쓰기에는 너무 복잡합니다. 그래서 조건식의 내용을 변수에 저장하는 방법이 있는지 알아보고 싶었습니다.
contains_b= *a condition for [[ $A == *"b"* ]]*; echo $contains_b
-> 참|거짓
답글만 찾았어요여기[ contains_b=$(! [ "$A" = *"b"* ]; echo $?)
]; 그러나 다음과 같은 $contains_b -> 0
이유로 후속 조건에서는 작동하지 않습니다.
if $contains_b; then do x; fi
->bash: [0]: command not found
그래서 제가 생각할 수 있는 유일한 해결책은 수동으로 수행하는 것입니다.
if [[ $A == *"b"* ]]; then
contains_b=true
else
contains_b=false
fi
그러나 결국 세 개의 변수를 얻기 위해 세 개의 if 문을 실행하고 각각의 다른 조합에 대해 7개의 다른 비교를 수행하게 됩니다.
이 작업을 수행하는 다른/더 효율적인 방법이 있는지 궁금합니다. 그렇지 않은 경우 다중 비교를 수행하는 다른 방법에 대한 제안 사항이 있습니까? 일을 너무 복잡하게 만드는 것 같은 느낌이 드네요...
도움을 주셔서 감사합니다.
답변1
바이너리 마스크를 생성한 다음 이에 대해 작업을 수행합니다. 이것의 이점은 각 테스트가 한 번만 실행되고 테스트 결과에 대한 조치와 테스트를 분리한다는 것입니다.
코드에서는 패턴을 확장 정규식으로 사용합니다. 문자열로 비교하려면 다음을 사용하십시오.
[[ $string == *"$pattern"* ]]
바꾸다
[[ $string =~ $pattern ]]
아래 코드에서.
patterns=( a b c )
string='abba'
mask=0; i=0
for pattern in "${patterns[@]}"; do
if [[ $string =~ $pattern ]]; then
# setting the i:th bit from the right to one
mask=$(( mask | (1 << i) ))
fi
i=$(( i + 1 ))
done
case $mask in
0) echo no match ;;
1) echo first pattern matched ;;
2) echo second pattern matched ;;
3) echo first and second pattern matched ;;
4) echo third pattern matched ;;
5) echo first and third pattern matched ;;
6) echo second and third pattern matched ;;
7) echo all patterns matched ;;
*) echo error
esac
또는 1과 0(일치 없음의 경우 0, 일치의 경우 1)이 포함된 문자열 마스크를 사용합니다. 아래 문자열은 mask
위 코드에 사용된 숫자의 실제 이진 표현과 반대라는 점에 유의하세요.
patterns=( a b c )
string='abba'
unset -v mask
for pattern in "${patterns[@]}"; do
! [[ $string =~ $pattern ]]
# string concatenation of the exit status of the previous command
mask+=$?
done
case $mask in
000) echo no match ;;
100) echo first pattern matched ;;
010) echo second pattern matched ;;
110) echo first and second pattern matched ;;
001) echo third pattern matched ;;
101) echo first and third pattern matched ;;
011) echo second and third pattern matched ;;
111) echo all patterns matched ;;
*) echo error
esac
각 스크립트의 출력은 다음과 같습니다.
first and second pattern matched
...문자열이 abba
처음 두 패턴과 일치 a
하고 b
.
답변2
이것과 함께bash
4.0또는 위 또는mksh
R40case
이상에서는 두 가지 명령문으로 모든 작업을 수행할 수 있습니다.
a=n b=n c=n
case $string in
(*a*) a=y ;;&
(*b*) b=y ;;&
(*c*) c=y
esac
case $a$b$c in
(nnn) echo none ;;
(ynn) echo a only ;;
(yyn) echo ab ;;
(yyy) echo all ;;
(*) echo other
esac
이것zsh
에bash
해당;;&
;|
, 반품서포터mksh
.
답변3
나는 [여기][1]에서만 답장을 찾았지만
contains_b=$(! [ "$A" = *"b"* ]; echo $?)]
다음과$contains_b -> 0
같은 이유로 후속 조건에서는 작동하지 않습니다.
if $contains_b; then do x; fi
->bash: [0]: command not found
숫자 결과를 얻은 후에는 명령으로 실행하는 대신 산술 연산을 수행해야 합니다.
contains_a=$(! [[ $A == *$pattern_a* ]]; echo $?)
contains_b=$(! [[ $A == *$pattern_b* ]]; echo $?)
contains_c=$(! [[ $A == *$pattern_c* ]]; echo $?)
if (( contains_a && contains_b && contains_c )); then
echo all
elif (( contains_a && contains_b )); then
echo ab
...
fi
답변4
=~
Bash에서 정규식 일치를 사용하여 가능한 각 일치 패턴을 한 번만 테스트함으로써 바이너리를 생성할 수 있습니다. 올바른 순서(MSB가 보존되고 마지막으로 실행된 테스트에 해당)로 바이너리를 빌드하려면 (음수) 종료 코드를 변수에 추가해야 합니다.
#!/bin/bash --
string=${1:-"abba"}
patterns=( a b c )
unset bin
for re in "${patterns[@]}"; do
! [[ $string =~ $re ]]; bin="$?$bin"
done
case $((2#$bin)) in
0) echo no match ;;
1) echo first pattern matched ;;
2) echo second pattern matched ;;
3) echo first and second pattern matched ;;
4) echo third pattern matched ;;
5) echo first and third pattern matched ;;
6) echo second and third pattern matched ;;
7) echo all patterns matched ;;
*) echo error
esac
이전 답변에 대한 대체 솔루션입니다.