我正在写一个Korn shell脚本。我有两个数组(比如,arr1
和arr2
),两个都包含字符串,我需要检查arr1
中哪些元素(作为整个字符串或子字符串){{1 }}。最直观的解决方案是嵌套for循环,并检查arr2
中的每个元素是否都可以在arr1
中找到(通过arr2
),如下所示:
grep
问题是for arr1Element in ${arr1[*]}; do
for arr2Element in ${arr2[*]}; do
# using grep to check if arr1Element is present in arr2Element
echo $arr2Element | grep $arr1Element
done
done
有大约3000个元素,因此运行嵌套循环需要很长时间。我想知道在Bash中是否有更好的方法可以做到这一点。
如果我在Java中执行此操作,我可以计算其中一个数组中元素的哈希值,然后在另一个数组中查找这些哈希值,但我不认为Bash有任何执行某些功能的功能像这样(除非我愿意在Bash中编写哈希计算函数)。
有什么建议吗?
答案 0 :(得分:3)
从版本4.0开始,Bash具有关联数组:
$ declare -A elements
$ elements[hello]=world
$ echo ${elements[hello]}
world
您可以像使用Java Map一样使用它。
declare -A map
for el in "${arr1[@]}"; do
map[$el]="x"
done
for el in "${arr2[@]}"; do
if [ -n "${map[$el]}" ] ; then
echo "${el}"
fi
done
处理子串是一个更加重要的问题,并且在任何语言中都是一个挑战,缺少你已经使用的蛮力算法。您可以构建字符序列的二叉树索引,但我不会在Bash中尝试那!
答案 1 :(得分:2)
BashFAQ #36描述了使用comm
在bash中进行集算术(联合,不相交集等)。
假设您的值不能包含文字换行符,则以下内容将在arr1和arr2中为每个项目发出一行:
comm -12 <(printf '%s\n' "${arr1[@]}" | sort -u) \
<(printf '%s\n' "${arr2[@]}" | sort -u)
如果您的数组已预先排序,您可以删除sort
(这将使大型数组的内存和时间效率极高,超过基于grep
的方法)。
答案 2 :(得分:0)
这是一个bash/awk
想法:
# some sample arrays
$ arr1=( my first string "hello wolrd")
$ arr2=( my last stringbean strings "well, hello world!)
# break array elements into separate lines
$ printf '%s\n' "${arr1[@]}"
my
first
string
hello world
$ printf '%s\n' "${arr2[@]}"
my
last
stringbean
strings
well, hello world!
# use the 'printf' command output as input to our awk command
$ awk '
NR==FNR { a[NR]=$0 ; next }
{ for (i in a)
if ($0 ~ a[i]) print "array1 string {"a[i]"} is a substring of array2 string {"$0"}" }
' <( printf '%s\n' "${arr1[@]}" ) \
<( printf '%s\n' "${arr2[@]}" )
array1 string {my} is a substring of array2 string {my}
array1 string {string} is a substring of array2 string {stringbean}
array1 string {string} is a substring of array2 string {strings}
array1 string {hello world} is a substring of array2 string {well, hello world!}
NR==FNR
:仅适用于文件#1:将元素存储到名为“a”next
:处理文件#1中的下一行;此时,文件#1会忽略其余的awk脚本;对于文件#2中的每一行...... for (i in a)
:对于数组'a'中的每个索引'i'... if ($0 ~ a[i] )
:查看[i]是否是文件#2中当前行($ 0)的子字符串,如果是这样的话...... print "array1...
:输出有关匹配的信息使用以下数据进行测试:
arr1 == 3300 elements
arr2 == 500 elements
当所有arr2
个元素在arr1
中都有子串/模式匹配(即500个匹配)时,总运行时间约为27秒......所以重复循环遍历数组需要通行费。
显然(?)需要减少重复动作的量......
comm
解决方案是有意义的(它在大约0.5秒内针对相同的3300/500测试集运行)egrep
解决方案(请参阅我的其他答案/帖子)答案 3 :(得分:0)
用于子字符串/模式匹配的egrep
解决方案......
egrep -f <(printf '.*%s.*\n' "${arr1[@]}") \
<(printf '%s\n' "${arr2[@]}")
egrep -f
:从-f
指定的文件中搜索模式,在本例中为... <(printf '.*%s.*\n' "${arr1[@]}")
:将arr1
元素转换为每行1个模式,为前缀和后缀附加正则表达式通配符(。*)<(printf '%s\n' "${arr2[@]}")
:将arr2
元素转换为每行1个字符串针对样本数据集运行时:
arr1 == 3300 elements
arr2 == 500 elements
...有500场比赛,总运行时间约为5秒; egrep
仍然有一些重复的处理,但没有我的其他答案(bash/awk
)那么糟糕......当然没有那么快comm
解决方案消除了重复处理。