在Bash中,测试数组是否包含某个值的最简单方法是什么?
当前回答
$ myarray=(one two three)
$ case "${myarray[@]}" in *"two"*) echo "found" ;; esac
found
其他回答
别胡闹了!使您的解决方案简单、干净和可重用。
这些函数负责索引数组和关联数组。可以通过将搜索算法从线性搜索升级为二进制搜索(用于大型数据集)来改进它们。
##
# 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时,如果函数的名称可以让您知道迭代的对象(或不迭代的对象),则可能(可能)有用。
如果你不想重复,这是值得研究的:
#!/bin/bash
myarray=("one" "two" "three");
wanted="two"
if `echo ${myarray[@]/"$wanted"/"WAS_FOUND"} | grep -q "WAS_FOUND" ` ; then
echo "Value was found"
fi
exit
片段改编自:http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ 我认为这很聪明。
编辑: 你可以这样做:
if `echo ${myarray[@]} | grep -q "$wanted"` ; then
echo "Value was found"
fi
但后者仅在数组包含唯一值时有效。我认为,在143中寻找1只会给出假阳性。
我通常编写这类实用程序来操作变量的名称,而不是变量的值,这主要是因为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"
etc.
虽然这里有几个很好的和有用的答案,但我没有找到一个似乎是性能、跨平台和健壮性的正确组合;所以我想分享一下我为我的代码编写的解决方案:
#!/bin/bash
# array_contains "$needle" "${haystack[@]}"
#
# Returns 0 if an item ($1) is contained in an array ($@).
#
# Developer note:
# The use of a delimiter here leaves something to be desired. The ideal
# method seems to be to use `grep` with --line-regexp and --null-data, but
# Mac/BSD grep doesn't support --line-regexp.
function array_contains()
{
# Extract and remove the needle from $@.
local needle="$1"
shift
# Separates strings in the array for matching. Must be extremely-unlikely
# to appear in the input array or the needle.
local delimiter='#!-\8/-!#'
# Create a string with containing every (delimited) element in the array,
# and search it for the needle with grep in fixed-string mode.
if printf "${delimiter}%s${delimiter}" "$@" | \
grep --fixed-strings --quiet "${delimiter}${needle}${delimiter}"; then
return 0
fi
return 1
}
借鉴Dennis Williamson的答案,下面的解决方案结合了数组、shell-safe引号和正则表达式,以避免需要:遍历循环;使用管道或其他子过程;或者使用非bash实用程序。
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正则表达式来匹配数组内容的字符串化版本。有六个重要的步骤来确保正则表达式匹配不会被数组中的值的巧妙组合所欺骗:
Construct the comparison string by using Bash's built-in printf shell-quoting, %q. Shell-quoting will ensure that special characters become "shell-safe" by being escaped with backslash \. Choose a special character to serve as a value delimiter. The delimiter HAS to be one of the special characters that will become escaped when using %q; that's the only way to guarantee that values within the array can't be constructed in clever ways to fool the regular expression match. I choose comma , because that character is the safest when eval'd or misused in an otherwise unexpected way. Combine all array elements into a single string, using two instances of the special character to serve as delimiter. Using comma as an example, I used ,,%q as the argument to printf. This is important because two instances of the special character can only appear next to each other when they appear as the delimiter; all other instances of the special character will be escaped. Append two trailing instances of the delimiter to the string, to allow matches against the last element of the array. Thus, instead of comparing against ${array_str}, compare against ${array_str},,. If the target string you're searching for is supplied by a user variable, you must escape all instances of the special character with a backslash. Otherwise, the regular expression match becomes vulnerable to being fooled by cleverly-crafted array elements. Perform a Bash regular expression match against the string.