Bash 어레이에 값이 포함되어 있는지 확인합니다.
Bash에서 배열에 특정 값이 포함되어 있는지 테스트하는 가장 간단한 방법은 무엇입니까?
이 접근 방식은 (적어도 명시적이지 않은) 모든 요소를 반복할 필요가 없다는 장점이 있습니다.하지만 그 이후로array_to_string_internal()
array.c에서는 여전히 배열 요소를 루프하여 문자열로 연결합니다. 제안된 루프 솔루션보다 더 효율적이지는 않지만 더 읽기 쉽습니다.
if [[ " ${array[*]} " =~ " ${value} " ]]; then
# whatever you want to do when array contains value
fi
if [[ ! " ${array[*]} " =~ " ${value} " ]]; then
# whatever you want to do when array doesn't contain value
fi
검색하는 값이 공백이 있는 배열 요소의 단어 중 하나인 경우 잘못된 긍정을 제공합니다.예를들면
array=("Jack Brown")
value="Jack"
정규식은 "잭"이 배열에 있지 않더라도 배열에 있는 것으로 볼 것입니다.그래서 당신은 옷을 갈아입어야 합니다.IFS
에 있는 , 예를 이 을 사용할 때 사용합니다.
IFS="|"
array=("Jack Brown${IFS}Jack Smith")
value="Jack"
if [[ "${IFS}${array[*]}${IFS}" =~ "${IFS}${value}${IFS}" ]]; then
echo "true"
else
echo "false"
fi
unset IFS # or set back to original IFS if previously set
"false"로 인쇄됩니다.
분명히 이것은 테스트 문장으로도 사용될 수 있으며, 이를 한 줄로 표현할 수 있습니다.
[[ " ${array[*]} " =~ " ${value} " ]] && echo "true" || echo "false"
이를 위한 작은 기능은 다음과 같습니다.검색 문자열은 첫 번째 인수이고 나머지는 배열 요소입니다.
set +e #otherwise the script will exit on error
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
이 기능의 테스트 실행은 다음과 같습니다.
$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1
한 줄 솔루션
printf '%s\0' "${myarray[@]}" | grep -F -x -z -- 'myvalue'
설명.
그printf
문은 null 문자로 구분된 배열의 각 요소를 인쇄합니다.
그grep
문은 다음 플래그를 사용하여 다음과 같이 주어진 문자열을 정확히 포함하는 항목과 일치시킵니다.myvalue
(그 이상도 그 이하도 아님):
-z
/--null-data
줄은 새 줄 대신 0바이트로 끝납니다.-F
/--fixed-strings
패턴을 정규식이 아닌 고정 문자열로 해석합니다.-x
/--line-regexp
전체 줄과 정확히 일치하는 일치 항목만 선택합니다.--
옵션의 프로세스를 "Grep 프로세스로 .myvalue
합니다.
는 null 바이트 Null을 합니까?\0
행 \n
줄을 할 수 있습니다. ( 그렇지 , 하세요.)-z
그렙 선택권과 지위%s\n
첫 번째 인쇄물로.)
사용.
이것을 다음과 같이 표현하는 것.if ... then
문:
if printf '%s\0' "${myarray[@]}" | grep -Fxqz -- 'myvalue'; then
# ...
fi
추가했습니다.-q
에 기치를 달다.grep
일치 항목을 인쇄하지 않도록 표현합니다. 일치 항목의 존재를 "참"으로 처리합니다.
업데이트: 8에 앞서, 지적해 주셔서 감사합니다.--line-regexp
flag.항목.flag 새할 수 있는 티노, 배열 항목 내에 새로운 선이 존재할 수 있는 경우를 지적해 주셔서 감사합니다.
for i in "${array[@]}"
do
if [ "$i" -eq "$yourValue" ] ; then
echo "Found"
fi
done
문자열의 경우:
for i in "${array[@]}"
do
if [ "$i" == "$yourValue" ] ; then
echo "Found"
fi
done
$ myarray=(one two three)
$ case "${myarray[@]}" in *"two"*) echo "found" ;; esac
found
일반적으로 다음을 사용합니다.
inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)
0이 아닌 값은 일치하는 항목이 발견되었음을 나타냅니다.
바늘2가 하기 위해, 일치만을 그 , 사과, 바1에2서작지않는하해, 약당기정하위결해원을, 추만이이세아그요하가, 냥닌도하그도상실이일면다한확만치한신동을 하세요.w
는 깃을달다발에 뒤에 .-o
전체 단어 일치:
inarray=$(echo ${haystack[@]} | grep -ow "needle" | wc -w)
성능이 필요한 경우 검색할 때마다 전체 어레이를 루프 상태로 만들지 않습니다.
이 경우 해당 배열의 인덱스를 나타내는 연관 배열(해시 테이블 또는 사전)을 생성할 수 있습니다.즉, 각 배열 요소를 배열의 인덱스에 매핑합니다.
make_index () {
local index_name=$1
shift
local -a value_array=("$@")
local i
# -A means associative array, -g means create a global variable:
declare -g -A ${index_name}
for i in "${!value_array[@]}"; do
eval ${index_name}["${value_array[$i]}"]=$i
done
}
그런 다음 다음과 같이 사용할 수 있습니다.
myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"
테스트 멤버 자격은 다음과 같습니다.
member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND
또는 다음과 같습니다.
if [ "${myarray_index[$member]}" ]; then
echo FOUND
fi
이 솔루션은 테스트된 값 또는 배열 값에 공백이 있더라도 올바른 작업을 수행합니다.
또한 다음과 같은 기능을 통해 어레이 내 값의 인덱스를 얻을 수 있습니다.
echo "<< ${myarray_index[$member]} >> is the index of $member"
기능이 없는 또 다른 라이너:
(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"
@Qwerty 공간에 대해 알려주셔서 감사합니다!
해당 기능:
find_in_array() {
local word=$1
shift
for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
return 1
}
예:
some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"
Bash 어레이에 값이 포함되어 있는지 확인하는 방법
거짓 양성 일치
array=(a1 b1 c1 d1 ee)
[[ ${array[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
# output:
yes
[[ ${array[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
# output:
yes
[[ ${array[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
# output:
yes
[[ ${array[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
# output:
yes
정확한 일치
일치를 이 " 한일찾위정해패규식다턴같은음과은합값다니추전추공야가해"와 같은 값 전후에 추가 공간을 .(^|[[:space:]])"VALUE"($|[[:space:]])
# Exact match
array=(aa1 bc1 ac1 ed1 aee)
if [[ ${array[*]} =~ (^|[[:space:]])"a"($|[[:space:]]) ]]; then
echo "Yes";
else
echo "No";
fi
# output:
No
if [[ ${array[*]} =~ (^|[[:space:]])"ac1"($|[[:space:]]) ]]; then
echo "Yes";
else
echo "No";
fi
# output:
Yes
find="ac1"
if [[ ${array[*]} =~ (^|[[:space:]])"$find"($|[[:space:]]) ]]; then
echo "Yes";
else
echo "No";
fi
# output:
Yes
자세한 사용 예는 다음과 같습니다.
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }
이제 빈 배열을 올바르게 처리합니다.
전체 어레이에 대해 정확한 일치를 얻기 위해 반복할 가치가 있는지 확인하기 위해 빠르고 더러운 테스트를 수행하려는 경우 Bash는 어레이를 스칼라처럼 처리할 수 있습니다.스칼라에서 일치하는 항목을 테스트합니다. 일치하지 않는 경우 루프를 건너뛰면 시간이 절약됩니다.분명히 여러분은 잘못된 긍정을 얻을 수 있습니다.
array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
echo "Checking"
for element in "${array[@]}"
do
if [[ $element == "words" ]]
then
echo "Match"
fi
done
fi
그러면 "Checking" 및 "Match"가 출력됩니다.와 함께array=(word "two words" something)
"Checking"만 출력됩니다.와 함께array=(word "two widgets" something)
출력이 없습니다.
다음은 작은 기여입니다.
array=(word "two words" words)
search_string="two"
match=$(echo "${array[@]:0}" | grep -o $search_string)
[[ ! -z $match ]] && echo "found !"
참고: 이 방법은 "두 단어"의 대소문자를 구분하지 않지만 질문에 이 단어가 필요하지 않습니다.
다음은 통합 검증 및 간단한 벤치마킹(Bash > = 4.0 포함)을 통해 가능한 여러 구현의 모음입니다.
#!/usr/bin/env bash
# Check if array contains item [$1: item, $2: array name]
function in_array_1() {
local needle="$1" item
local -n arrref="$2"
for item in "${arrref[@]}"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2: array name]
function in_array_2() {
local needle="$1" arrref="$2[@]" item
for item in "${!arrref}"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2: array name]
function in_array_3() {
local needle="$1" i
local -n arrref="$2"
for ((i=0; i < ${#arrref[@]}; i++)); do
[[ "${arrref[i]}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2..$n: array items]
function in_array_4() {
local needle="$1" item
shift
for item; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2..$n: array items]
function in_array_5() {
local needle="$1" item
for item in "${@:2}"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2: array name]
function in_array_6() {
local needle="$1" arrref="$2[@]" array i
array=("${!arrref}")
for ((i=0; i < ${#array[@]}; i++)); do
[[ "${array[i]}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2..$n: array items]
function in_array_7() {
local needle="$1" array=("${@:2}") item
for item in "${array[@]}"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
# Check if array contains item [$1: item, $2..$n: array items]
function in_array_8() {
local needle="$1"
shift
while (( $# > 0 )); do
[[ "$1" == "${needle}" ]] && return 0
shift
done
return 1
}
#------------------------------------------------------------------------------
# Generate map for array [$1: name of source array, $2: name of target array]
# NOTE: target array must be pre-declared by caller using 'declare -A <name>'
function generate_array_map() {
local -n srcarr="$1" dstmap="$2"
local i key
dstmap=()
for i in "${!srcarr[@]}"; do
key="${srcarr[i]}"
[[ -z ${dstmap["${key}"]+set} ]] && dstmap["${key}"]=${i} || dstmap["${key}"]+=,${i}
done
}
# Check if array contains item [$1: item, $2: name of array map]
function in_array_9() {
local needle="$1"
local -n mapref="$2"
[[ -n "${mapref["${needle}"]+set}" ]] && return 0 || return 1
}
#------------------------------------------------------------------------------
# Test in_array function [$1: function name, $2: function description, $3: test array size]
function test() {
local tname="$1" tdesc="$2" tn=$3 ti=0 tj=0 ta=() tct=0 tepapre="" tepapost="" tepadiff=()
local -A tam=()
echo -e "\e[1m${tname} (${tdesc}):\e[0m"
# Generate list of currently defined variables
tepapre="$(compgen -v)"
# Fill array with random items
for ((ti=0; ti < ${tn}; ti++)); do
ta+=("${RANDOM} ${RANDOM} ${RANDOM} ${RANDOM}")
done
# Determine function call type (pass array items, pass array name, pass array map)
case "${tname}" in
"in_array_1"|"in_array_2"|"in_array_3"|"in_array_6") tct=0; ;;
"in_array_4"|"in_array_5"|"in_array_7"|"in_array_8") tct=1; ;;
"in_array_9") generate_array_map ta tam; tct=2; ;;
*) echo "Unknown in_array function '${tname}', aborting"; return 1; ;;
esac
# Verify in_array function is working as expected by picking a few random
# items and checking
echo -e "\e[1mVerification...\e[0m"
for ((ti=0; ti < 10; ti++)); do
tj=$(( ${RANDOM} % ${#ta[@]} ))
echo -n "Item ${tj} '${ta[tj]}': "
if (( ${tct} == 0 )); then
"${tname}" "${ta[tj]}" ta && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
echo -n " "
"${tname}" "${ta[tj]}.x" ta && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
elif (( ${tct} == 1 )); then
"${tname}" "${ta[tj]}" "${ta[@]}" && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
echo -n " "
"${tname}" "${ta[tj]}.x" "${ta[@]}" && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
elif (( ${tct} == 2 )); then
"${tname}" "${ta[tj]}" tam && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
echo -n " "
"${tname}" "${ta[tj]}.x" tam && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
fi
echo
done
# Benchmark in_array function
echo -en "\e[1mBenchmark...\e[0m"
time for ((ti=0; ti < ${#ta[@]}; ti++)); do
if (( ${tct} == 0 )); then
"${tname}" "${ta[ti]}" ta
elif (( ${tct} == 1 )); then
"${tname}" "${ta[ti]}" "${ta[@]}"
elif (( ${tct} == 2 )); then
"${tname}" "${ta[ti]}" tam
fi
done
# Generate list of currently defined variables, compare to previously
# generated list to determine possible environment pollution
echo -e "\e[1mEPA test...\e[0m"
tepapost="$(compgen -v)"
readarray -t tepadiff < <(echo -e "${tepapre}\n${tepapost}" | sort | uniq -u)
if (( ${#tepadiff[@]} == 0 )); then
echo -e "\e[1;32mclean\e[0m"
else
echo -e "\e[1;31mpolluted:\e[0m ${tepadiff[@]}"
fi
echo
}
#------------------------------------------------------------------------------
# Test in_array functions
n=5000
echo
( test in_array_1 "pass array name, nameref reference, for-each-loop over array items" ${n} )
( test in_array_2 "pass array name, indirect reference, for-each-loop over array items" ${n} )
( test in_array_3 "pass array name, nameref reference, c-style for-loop over array items by index" ${n} )
( test in_array_4 "pass array items, for-each-loop over arguments" ${n} )
( test in_array_5 "pass array items, for-each-loop over arguments as array" ${n} )
( test in_array_6 "pass array name, indirect reference + array copy, c-style for-loop over array items by index" ${n} )
( test in_array_7 "pass array items, copy array from arguments as array, for-each-loop over array items" ${n} )
( test in_array_8 "pass array items, while-loop, shift over arguments" ${n} )
( test in_array_9 "pre-generated array map, pass array map name, direct test without loop" ${n} )
결과:
in_array_1 (pass array name, nameref reference, for-each-loop over array items):
Verification...
Item 862 '19528 10140 12669 17820': ok ok
Item 2250 '27262 30442 9295 24867': ok ok
Item 4794 '3857 17404 31925 27993': ok ok
Item 2532 '14553 12282 26511 32657': ok ok
Item 1911 '21715 8066 15277 27126': ok ok
Item 4289 '3081 10265 16686 19121': ok ok
Item 4837 '32220 1758 304 7871': ok ok
Item 901 '20652 23880 20634 14286': ok ok
Item 2488 '14578 8625 30251 9343': ok ok
Item 4165 '4514 25064 29301 7400': ok ok
Benchmark...
real 1m11,796s
user 1m11,262s
sys 0m0,473s
EPA test...
clean
in_array_2 (pass array name, indirect reference, for-each-loop over array items):
Verification...
Item 2933 '17482 25789 27710 2096': ok ok
Item 3584 '876 14586 20885 8567': ok ok
Item 872 '176 19749 27265 18038': ok ok
Item 595 '6597 31710 13266 8813': ok ok
Item 748 '569 9200 28914 11297': ok ok
Item 3791 '26477 13218 30172 31532': ok ok
Item 2900 '3059 8457 4879 16634': ok ok
Item 676 '23511 686 589 7265': ok ok
Item 2248 '31351 7961 17946 24782': ok ok
Item 511 '8484 23162 11050 426': ok ok
Benchmark...
real 1m11,524s
user 1m11,086s
sys 0m0,437s
EPA test...
clean
in_array_3 (pass array name, nameref reference, c-style for-loop over array items by index):
Verification...
Item 1589 '747 10250 20133 29230': ok ok
Item 488 '12827 18892 31996 1977': ok ok
Item 801 '19439 25243 24485 24435': ok ok
Item 2588 '17193 18893 21610 9302': ok ok
Item 4436 '7100 655 8847 3068': ok ok
Item 2620 '19444 6457 28835 24717': ok ok
Item 4398 '4420 16336 612 4255': ok ok
Item 2430 '32397 2402 12631 29774': ok ok
Item 3419 '906 5361 32752 7698': ok ok
Item 356 '9776 16485 20838 13330': ok ok
Benchmark...
real 1m17,037s
user 1m17,019s
sys 0m0,005s
EPA test...
clean
in_array_4 (pass array items, for-each-loop over arguments):
Verification...
Item 1388 '7932 15114 4025 15625': ok ok
Item 3900 '23863 25328 5632 2752': ok ok
Item 2678 '31296 4216 17485 8874': ok ok
Item 1893 '16952 29047 29104 23384': ok ok
Item 1616 '19543 5999 4485 22929': ok ok
Item 93 '14456 2806 12829 19552': ok ok
Item 265 '30961 19733 11863 3101': ok ok
Item 4615 '10431 9566 25767 13518': ok ok
Item 576 '11726 15104 11116 74': ok ok
Item 3829 '19371 25026 6252 29478': ok ok
Benchmark...
real 1m30,912s
user 1m30,740s
sys 0m0,011s
EPA test...
clean
in_array_5 (pass array items, for-each-loop over arguments as array):
Verification...
Item 1012 '29213 31971 21483 30225': ok ok
Item 2802 '4079 5423 29240 29619': ok ok
Item 473 '6968 798 23936 6852': ok ok
Item 2183 '20734 4521 30800 2126': ok ok
Item 3059 '14952 9918 15695 19309': ok ok
Item 1424 '25784 28380 14555 21893': ok ok
Item 1087 '16345 19823 26210 20083': ok ok
Item 257 '28890 5198 7251 3866': ok ok
Item 3986 '29035 19288 12107 3857': ok ok
Item 2509 '9219 32484 12842 27472': ok ok
Benchmark...
real 1m53,485s
user 1m53,404s
sys 0m0,077s
EPA test...
clean
in_array_6 (pass array name, indirect reference + array copy, c-style for-loop over array items by index):
Verification...
Item 4691 '25498 10521 20673 14948': ok ok
Item 263 '25265 29824 3876 14088': ok ok
Item 2550 '2416 14274 12594 29740': ok ok
Item 2269 '2769 11436 3622 28273': ok ok
Item 3246 '23730 25956 3514 17626': ok ok
Item 1059 '10776 12514 27222 15640': ok ok
Item 53 '23813 13365 16022 4092': ok ok
Item 1503 '6593 23540 10256 17818': ok ok
Item 2452 '12600 27404 30960 26759': ok ok
Item 2526 '21190 32512 23651 7865': ok ok
Benchmark...
real 1m54,793s
user 1m54,326s
sys 0m0,457s
EPA test...
clean
in_array_7 (pass array items, copy array from arguments as array, for-each-loop over array items):
Verification...
Item 2212 '12127 12828 27570 7051': ok ok
Item 1393 '19552 26263 1067 23332': ok ok
Item 506 '18818 8253 14924 30710': ok ok
Item 789 '9803 1886 17584 32686': ok ok
Item 1795 '19788 27842 28044 3436': ok ok
Item 376 '4372 16953 17280 4031': ok ok
Item 4846 '19130 6261 21959 6869': ok ok
Item 2064 '2357 32221 22682 5814': ok ok
Item 4866 '10928 10632 19175 14984': ok ok
Item 1294 '8499 11885 5900 6765': ok ok
Benchmark...
real 2m35,012s
user 2m33,578s
sys 0m1,433s
EPA test...
clean
in_array_8 (pass array items, while-loop, shift over arguments):
Verification...
Item 134 '1418 24798 20169 9501': ok ok
Item 3986 '12160 12021 29794 29236': ok ok
Item 1607 '26633 14260 18227 898': ok ok
Item 2688 '18387 6285 2385 18432': ok ok
Item 603 '1421 306 6102 28735': ok ok
Item 625 '4530 19718 30900 1938': ok ok
Item 4033 '9968 24093 25080 8179': ok ok
Item 310 '6867 9884 31231 29173': ok ok
Item 661 '3794 4745 26066 22691': ok ok
Item 4129 '3039 31766 6714 4921': ok ok
Benchmark...
real 5m51,097s
user 5m50,566s
sys 0m0,495s
EPA test...
clean
in_array_9 (pre-generated array map, pass array map name, direct test without loop):
Verification...
Item 3696 '661 6048 13881 26901': ok ok
Item 815 '29729 13733 3935 20697': ok ok
Item 1076 '9220 3405 18448 7240': ok ok
Item 595 '8912 2886 13678 24066': ok ok
Item 2803 '13534 23891 5344 652': ok ok
Item 1810 '12528 32150 7050 1254': ok ok
Item 4055 '21840 7436 1350 15443': ok ok
Item 2416 '19550 28434 17110 31203': ok ok
Item 1630 '21054 2819 7527 953': ok ok
Item 1044 '30152 22211 22226 6950': ok ok
Benchmark...
real 0m0,128s
user 0m0,128s
sys 0m0,000s
EPA test...
clean
a=(b c d)
if printf '%s\0' "${a[@]}" | grep -Fqxz c
then
echo 'array “a” contains value “c”'
fi
원하는 경우 동등한 긴 옵션을 사용할 수 있습니다.
--fixed-strings --quiet --line-regexp --null-data
Dennis Williamson의 답변에서 차용한 다음 솔루션은 어레이, 셸 안전 인용 및 정규 표현식을 결합하여 루프 반복, 파이프 또는 기타 하위 프로세스 사용 또는 비배시 유틸리티 사용의 필요성을 방지합니다.
declare -a array=('hello, stack' one 'two words' words last)
printf -v array_str -- ',,%q' "${array[@]}"
if [[ "${array_str},," =~ ,,words,, ]]
then
echo 'Matches'
else
echo "Doesn't match"
fi
위의 코드는 배열 내용의 문자열화된 버전과 비교하기 위해 Bash 정규식을 사용하여 작동합니다.정규식 일치가 배열 내의 값의 교묘한 조합에 의해 속지 않도록 하기 위한 여섯 가지 중요한 단계가 있습니다.
- 제공 를 하여 비교
printf
조개껍질 벗기기, ,기기▁shell,%q
셸 인용은 특수 문자가 백슬래시로 이스케이프됨으로써 "쉘 세이프"가 되도록 보장합니다.\
. - 값 구분 기호로 사용할 특수 문자를 선택합니다.는 호기는분사용시이특는수문합니다중하자야나여를 할 때 중 .
%q
이것이 배열 내의 값이 정규식 일치를 속이는 교묘한 방법으로 구성되지 않도록 보장하는 유일한 방법입니다.는 쉼표를 합니다.,
왜냐하면 그 캐릭터는 예상치 못한 방식으로 평가되거나 오용될 때 가장 안전하기 때문입니다. - 특수 문자의 두 인스턴스를 구분 기호로 사용하여 모든 배열 요소를 단일 문자열로 결합합니다.쉼표를 예로 들어 사용했습니다.
,,%q
의printf
이것은 특수 문자의 두 인스턴스가 구분 기호로 나타날 때만 서로 옆에 나타날 수 있기 때문에 중요합니다. 특수 문자의 다른 인스턴스는 모두 이스케이프됩니다. - 배열의 마지막 요소와 일치하도록 구분 기호의 뒤에 있는 두 인스턴스를 문자열에 추가합니다.따라서, 비교하는 대신에
${array_str}
와교해보다비와 비교해 .${array_str},,
. - 검색하는 대상 문자열이 사용자 변수에 의해 제공되는 경우 특수 문자의 모든 인스턴스를 백슬래시로 이스케이프해야 합니다.그렇지 않으면 정규식 일치가 교묘하게 조작된 배열 요소에 속을 수 있습니다.
- 문자열에 대해 Bash 정규식 일치를 수행합니다.
이것은 저에게 효과가 있습니다.
# traditional system call return values-- used in an `if`, this will be true when returning 0. Very Odd.
contains () {
# odd syntax here for passing array parameters: http://stackoverflow.com/questions/8082947/how-to-pass-an-array-to-a-bash-function
local list=$1[@]
local elem=$2
# echo "list" ${!list}
# echo "elem" $elem
for i in "${!list}"
do
# echo "Checking to see if" "$i" "is the same as" "${elem}"
if [ "$i" == "${elem}" ] ; then
# echo "$i" "was the same as" "${elem}"
return 0
fi
done
# echo "Could not find element"
return 1
}
호출 예:
arr=("abc" "xyz" "123")
if contains arr "abcx"; then
echo "Yes"
else
echo "No"
fi
대부분의 표가 있는 답변은 매우 간결하고 깨끗하지만 공백이 배열 요소 중 하나일 때 잘못된 긍정을 가질 수 있습니다.이 문제는 IFS를 변경하고 사용할 때 극복할 수 있습니다."${array[*]}"
에 "${array[@]}"
방법은 동일하지만 덜 깨끗해 보입니다.을 사용하여"${array[*]}"
는 우는모든요인다니쇄합의 를 인쇄합니다.$array
구로분된자의 첫 됩니다.IFS
따서올바선택통해을른라▁a통▁choosing▁so를 선택함으로써.IFS
당신은 이 특정한 문제를 극복할 수 있습니다.이 특별한 경우, 우리는 다음과 같이 결정합니다.IFS
한 에게.$'\001'
이것은 SOH
헤딩의 시작()을 나타냅니다.
$ array=("foo bar" "baz" "qux")
$ IFS=$'\001'
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo${IFS}" ]] && echo yes || echo no
no
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo bar${IFS}" ]] && echo yes || echo no
yes
$ unset IFS
가 false positive로 되지만, 합니다.IFS
.
참고: 만약IFS
이므로, 하지 말고 저장하고 .unset IFS
관련:
'grep' 및 루프를 사용하지 않는 한 줄 바꿈
if ( dlm=$'\x1F' ; IFS="$dlm" ; [[ "$dlm${array[*]}$dlm" == *"$dlm${item}$dlm"* ]] ) ; then
echo "array contains '$item'"
else
echo "array does not contain '$item'"
fi
은 이접근방식다같음외은유않사다습니용지하를티틸리부과은▁utilities▁like▁uses▁external▁this▁neither다와 같은 외부 유틸리티를 사용하지 않습니다.grep
루프 없음.
여기서 일어나는 일은 다음과 같습니다.
- 와일드카드 하위 문자열 일치기를 사용하여 문자열로 연결된 항목을 배열에서 찾습니다.
- 한 쌍의 구분 기호 사이에 검색 항목을 포함하여 가능한 잘못된 긍정을 차단합니다.
- 인쇄 불가능한 문자를 구분 기호로 사용하여 안전한 쪽으로 이동합니다.
- 우리는 어레이 연결에도 사용되는 구분 기호를 임시로 교체함으로써 달성합니다.
IFS
변수 값; - 우리는 이것을 만듭니다.
IFS
하위 셸에서 조건식을 평가하여 값 대체 임시(괄호 쌍 포함)
미친 짓 그만해요!간편하고 깨끗하고 재사용 가능한 솔루션을 만드십시오.
이러한 함수는 인덱스 배열 및 연관 배열을 설명합니다.검색 알고리즘을 선형 검색에서 이진 검색(대규모 데이터 세트의 경우)으로 업그레이드하여 개선할 수 있습니다.
##
# Determines if a value exists in an array.
###
function hasArrayValue ()
{
local -r needle="{$1:?}"
local -nr haystack="{$2:?}" # Where you pass by reference to get the entire array in one argument.
# Linear search. Upgrade to binary search for large datasets.
for value in "${haystack[@]}"; do
if [[ "$value" == "$needle" ]]; then
return 0
fi
done
return 1
}
##
# Determines if a value exists in an associative array / map.
###
function hasMapValue ()
{
local -r needle="{$1:?}"
local -nr haystack="{$2:?}"
# Linear search. Upgrade to binary search for large datasets.
for value in "${haystack[@]}"; do
if [[ $value == $needle ]]; then
return 0
fi
done
return 1
}
예, 같은 논리지만 bash를 처리할 때 반복되는 것(또는 그렇지 않음)을 알 수 있는 이름을 가진 함수를 사용하는 것이 유용할 수 있습니다.
주어진:
array=("something to search for" "a string" "test2000")
elem="a string"
그런 다음 다음의 간단한 확인:
if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
echo "$elem exists in array"
fi
어디에
c is element separator
p is regex pattern
([ ] 안에서 직접 p를 사용하는 것이 아니라 p를 별도로 할당하는 이유는 bash 4에 대한 호환성을 유지하기 위해서입니다.)
용사를 합니다.grep
그리고.printf
한 다음 " " " " 를 사용합니다.grep
대사
if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
example:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true
이 방법은 거리와 공간에 문제가 없습니다.
@에 대한 . @ghostdog74의 사용에 대한 답변입니다.case
배열에 특정 값이 포함되어 있는지 확인하는 논리:
myarray=(one two three)
word=two
case "${myarray[@]}" in ("$word "*|*" $word "*|*" $word") echo "found" ;; esac
또는함과 함께.extglob
옵션을 켜면 다음과 같이 수행할 수 있습니다.
myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac
또한 우리는 그것을 할 수 있습니다.if
문:
myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi
여기에 제시된 몇 가지 아이디어를 결합하면 정확한 단어가 일치하는 루프가 없는 우아한 if 문을 만들 수 있습니다.
find="myword"
array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
echo "Array contains myword";
fi
은 이은트되않니다습지거에 .word
또는val
전체 단어만 일치합니다.각 배열 값에 여러 개의 단어가 포함된 경우 구분됩니다.
저는 그 문제에 접근할 수 있는 몇 가지 방법을 알고 있습니다.
" " " 를 사용합니다.grep
grep ${value} <<< ${array[*]} && true || false
의 경우 연배열키의경다사용다니합음을우를 사용합니다.grep
grep ${value} <<< "${!array[*]}" && true || false
애크가 필요할 수도 있지만, 아마도 과잉 살상일 겁니다.
awk --assign "v=${value}" '$v~$0 {print true}' <<<"${!array[*]}
사례 진술.
case "${array[*]}" in (*${value}*) true ;; (*) false ;; esac
ksh88 스타일 이중 사각 괄호 안의 Bash 조건식:
[[ ${array[@]} =~ ${value} ]] && true || false
합니다. 은 참고: 순는중니다합의 . 정규식은 오른쪽에 있습니다.=~
일치 연산자.
루프를 위한 Bash
for ((i=0;i<"${#array[*]}";i++)) ; [[ ${array[i]} = $value ]] && break 0 &> /dev/null || continue; done
참고로, 이 특수한 경우 진실 논리는 반대로 I.E. 1=true, 0=false입니다.그것은 우리가 사용하기 때문입니다.break 0
강제로 제로를 break
true , 이는 true가 경우 항상 는 true, 이항해당됩, 다 는 상 의 이 니 외 종 으 며 있 어 되 료 코 장 내 가 드 를 지 도 록 해 ▁besidesbreak n
매개 변수가 1보다 작습니다.우리는 반드시 루프를 깨기를 원하며, 기본 'true' 외에 부울 종료 코드를 원하기 때문에 이 경우에는 논리를 뒤집습니다.그러한 이유로, 아마도 함수를 사용하는 것이 더 말이 될 것입니다.return true
의미론
저는 일반적으로 이러한 유틸리티를 변수 값이 아닌 변수의 이름으로 작업하기 위해 작성합니다. 주로 bash가 참조로 변수를 전달할 수 없기 때문입니다.
다음은 어레이 이름과 함께 작동하는 버전입니다.
function array_contains # array value
{
[[ -n "$1" && -n "$2" ]] || {
echo "usage: array_contains <array> <value>"
echo "Returns 0 if array contains value, 1 otherwise"
return 2
}
eval 'local values=("${'$1'[@]}")'
local element
for element in "${values[@]}"; do
[[ "$element" == "$2" ]] && return 0
done
return 1
}
이를 통해 질문 예는 다음과 같습니다.
array_contains A "one" && echo "contains one"
기타.
매개 변수 확장 사용:
${parameter:+word} 매개 변수가 null이거나 설정되지 않은 경우에는 아무것도 대체되지 않으며, 그렇지 않으면 단어 확장이 대체됩니다.
declare -A myarray
myarray[hello]="world"
for i in hello goodbye 123
do
if [ ${myarray[$i]:+_} ]
then
echo ${!myarray[$i]} ${myarray[$i]}
else
printf "there is no %s\n" $i
fi
done
OP는 논평과 함께 다음과 같은 답변을 직접 추가했습니다.
답변과 의견의 도움을 받아 몇 가지 테스트를 거친 후 다음과 같은 결과를 얻었습니다.
function contains() {
local n=$#
local value=${!n}
for ((i=1;i < $#;i++)) {
if [ "${!i}" == "${value}" ]; then
echo "y"
return 0
fi
}
echo "n"
return 1
}
A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
echo "contains three"
fi
단순하게 유지:
Array1=( "item1" "item2" "item3" "item-4" )
var="item3"
count=$(echo ${Array1[@]} | tr ' ' '\n' | awk '$1 == "'"$var"'"{print $0}' | wc -l)
[ $count -eq 0 ] && echo "Not found" || echo "found"
: NeedleInArgs "$needle" "${haystack[@]}"
: NeedleInArgs "$needle" arg1 arg2 .. argN
NeedleInArgs()
{
local a b;
printf -va '\n%q\n' "$1";
printf -vb '%q\n' "${@:2}";
case $'\n'"$b" in (*"$a"*) return 0;; esac;
return 1;
}
다음과 같은 용도:
NeedleInArgs "$needle" "${haystack[@]}" && echo "$needle" found || echo "$needle" not found;
- 위해서
bash
v3.1 이상)printf -v
보조) - 포크 또는 외부 프로그램 없음
- 단, 루내없음제(확장외프부내제)외) 내의 내부 )
bash
) - 모든 가능한 값과 어레이에 대해 작동하며 예외 없이 걱정할 필요 없음
다음과 같이 직접 사용할 수도 있습니다.
if NeedleInArgs "$input" value1 value2 value3 value4;
then
: input from the list;
else
: input not from list;
fi;
위해서bash
v2.05b(v3.0)까지으로printf
족한이 -v
따라서 이것은 2개의 추가 포크가 필요합니다(그러나 임원은 없습니다).printf
입니다.bash
기본 제공):
NeedleInArgs()
{
case $'\n'"`printf '%q\n' "${@:2}"`" in
(*"`printf '\n%q\n' "$1"`"*) return 0;;
esac;
return 1;
}
타이밍을 테스트했습니다.
check call0: n: t4.43 u4.41 s0.00 f: t3.65 u3.64 s0.00 l: t4.91 u4.90 s0.00 N: t5.28 u5.27 s0.00 F: t2.38 u2.38 s0.00 L: t5.20 u5.20 s0.00
check call1: n: t3.41 u3.40 s0.00 f: t2.86 u2.84 s0.01 l: t3.72 u3.69 s0.02 N: t4.01 u4.00 s0.00 F: t1.15 u1.15 s0.00 L: t4.05 u4.05 s0.00
check call2: n: t3.52 u3.50 s0.01 f: t3.74 u3.73 s0.00 l: t3.82 u3.80 s0.01 N: t2.67 u2.67 s0.00 F: t2.64 u2.64 s0.00 L: t2.68 u2.68 s0.00
call0
그리고.call1
fast 다양한 변형 입니다.call2
이것이 여기에 있습니까?N
수 없음 = 찾을수 없음F
번째 매치 =첫경기경L
match = 마막경기지- 소문자는 짧은 배열이고 대문자는 긴 배열입니다.
보시다시피 이 변형은 런타임이 매우 안정적이므로 일치 위치에 크게 의존하지 않습니다.런타임은 대부분 배열 길이에 의해 지배됩니다.검색 변형의 런타임은 일치 위치에 따라 크게 다릅니다.따라서 가장자리의 경우 이 변형이 훨씬 더 빠를 수 있습니다.
그러나 매우 중요한 것은 검색 변형이 RAM 효율성이 훨씬 높다는 것입니다. 이 변형은 항상 전체 배열을 큰 문자열로 변환하기 때문입니다.
따라서 RAM이 부족하고 대부분 초기 일치가 예상되는 경우에는 여기에서 이를 사용하지 마십시오.그러나 예측 가능한 런타임을 원하는 경우, 예상과 일치하는 긴 어레이가 있거나 일치하지 않는 어레이가 있어야 하며, RAM을 이중으로 사용하는 것도 큰 문제가 되지 않습니다. 그렇다면 이점이 있습니다.
타이밍 테스트에 사용되는 스크립트:
in_array()
{
local needle="$1" arrref="$2[@]" item
for item in "${!arrref}"; do
[[ "${item}" == "${needle}" ]] && return 0
done
return 1
}
NeedleInArgs()
{
local a b;
printf -va '\n%q\n' "$1";
printf -vb '%q\n' "${@:2}";
case $'\n'"$b" in (*"$a"*) return 0;; esac;
return 1;
}
loop1() { for a in {1..100000}; do "$@"; done }
loop2() { for a in {1..1000}; do "$@"; done }
run()
{
needle="$5"
arr=("${@:6}")
out="$( ( time -p "loop$2" "$3" ) 2>&1 )"
ret="$?"
got="${out}"
syst="${got##*sys }"
got="${got%"sys $syst"}"
got="${got%$'\n'}"
user="${got##*user }"
got="${got%"user $user"}"
got="${got%$'\n'}"
real="${got##*real }"
got="${got%"real $real"}"
got="${got%$'\n'}"
printf ' %s: t%q u%q s%q' "$1" "$real" "$user" "$syst"
[ -z "$rest" ] && [ "$ret" = "$4" ] && return
printf 'FAIL! expected %q got %q\n' "$4" "$ret"
printf 'call: %q\n' "$3"
printf 'out: %q\n' "$out"
printf 'rest: %q\n' "$rest"
printf 'needle: %q\n' "$5"
printf 'arr: '; printf ' %q' "${@:6}"; printf '\n'
exit 1
}
check()
{
printf 'check %q: ' "$1"
run n 1 "$1" 1 needle a b c d
run f 1 "$1" 0 needle needle a b c d
run l 1 "$1" 0 needle a b c d needle
run N 2 "$1" 1 needle "${rnd[@]}"
run F 2 "$1" 0 needle needle "${rnd[@]}"
run L 2 "$1" 0 needle "${rnd[@]}" needle
printf '\n'
}
call0() { chk=("${arr[@]}"); in_array "$needle" chk; }
call1() { in_array "$needle" arr; }
call2() { NeedleInArgs "$needle" "${arr[@]}"; }
rnd=()
for a in {1..1000}; do rnd+=("$a"); done
check call0
check call1
check call2
답변을 마치고 특별히 마음에 드는 답변을 하나 더 읽었는데, 흠집이 나서 부결됐습니다.저는 영감을 얻었고 여기 제가 볼 수 있는 두 가지 새로운 접근법이 있습니다.
array=("word" "two words") # let's look for "two words"
용사를 grep
그리고.printf
:
(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>
용사를 for
:
(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>
의 경우 not_found를 합니다.|| <run_your_if_notfound_command_here>
언급URL : https://stackoverflow.com/questions/3685970/check-if-a-bash-array-contains-a-value
'programing' 카테고리의 다른 글
현지화를 위해 Android 문자열 리소스를 Excel로 가져오거나 내보내는 방법은 무엇입니까? (0) | 2023.05.05 |
---|---|
<%, <%@, <%=, <%#... 무슨 일입니까? (0) | 2023.05.05 |
mongoose를 사용하여 컬렉션에 ID가 있는지 확인합니다. (0) | 2023.05.05 |
복제할 수 없는 이유는 무엇입니까? (0) | 2023.05.05 |
WPF textBox에 새 줄을 추가하지 않음을 입력합니다. (0) | 2023.05.05 |