Bash 脚本:比较内联数组中的字符串与使用 mapfile 导入的数组

Bash script: Comparing strings in inline array vs. array imported with mapfile

我有两个数组,一个包含域列表,另一个包含黑名单域列表,我想比较它们。这个想法是,如果域未列入黑名单,脚本将执行 X。

如果我创建这样的数组,脚本可以正常工作:

    DOMAINS=(
    'domain1.no 443'
    'domain2.no 443'
    'domain3.no 443'
    'domain4.no 443'
    'domain5.no 443'
    )
    BLACKLIST=(
    'domain1.no'
    'domain3.no'
    'domain5.no'
    )
Output:
domain1.no is blacklisted
domain2.no is NOT blacklisted
domain3.no is blacklisted
domain4.no is NOT blacklisted
domain5.no is blacklisted

但是如果我通过使用 mapfile -t 从 domains.txt/blacklist.txt 导入域来创建数组,那么脚本将不起作用。像这样:

mapfile -t DOMAINS < domains.txt
mapfile -t BLACKLIST < blacklist.txt
domains.txt contents:
domain1.no 443
domain2.no 443
domain3.no 443
domain4.no 443
domain5.no 443

blacklist.txt contents:
domain1.no
domain3.no
domain5.no

Output:
domain1.no is NOT blacklisted
domain2.no is NOT blacklisted
domain3.no is NOT blacklisted
domain4.no is NOT blacklisted
domain5.no is blacklisted

这是脚本的其余部分:

function test_function ()
{
    host=
    is_blacklisted=0
    
    for domain in "${BLACKLIST[@]}"; do
        if [[ " $host " == *" $domain "* ]]; then
            is_blacklisted=1
        fi
    done
    
    if [ $is_blacklisted == 1 ]; then
        printf "%s\n" "$host is blacklisted"
        
    elif [ $is_blacklisted == 0 ]; then
        printf "%s\n" "$host is NOT blacklisted"
    fi
}

for domain in "${DOMAINS[@]}"; do
    test_function $domain
done

我的问题是,使用mapfile数组时比较不正常是什么原因?

我非常,非常 bash 脚本编写(以及本网站),我的代码可能不太好,明显的答案可能不会对我来说太明显了!

'443' 被添加到另一个检查 SSL 的脚本的 DOMAINS 数组中,这就是它存在但未在此脚本中使用的原因。我想使用这些 .txt 文件,这样我就不必手动更新每个脚本数组,而是可以更新 .txt 文件。

如果重要的话,我正在使用来自 Microsoft 应用程序商店的 Ubuntu/WSL。

正如评论中所讨论的,最有可能的问题是在每行末尾留下一个 \r 字符。这是删除此类 \r 字符的可能解决方案。此外,它将黑名单预处理为关联数组,以提高查找效率。

#!/bin/bash
set -euo pipefail

if (($# != 2)); then
  echo "Usage: [=10=] <blacklist file> <domains file>"
  exit 1
fi

is_blacklisted() {
  (($# == 2))  # crash on wrong number of arguments
  local -nr linenum_map=""  # array passed by name reference
  local -ir linenum='linenum_map[""]'  # integer evaluation
  if ((linenum)); then
    printf '%s is blacklisted on line %d\n' "" "$((linenum))"
    return 1  # easier for callers than output parsing
  else
    printf '%s is NOT blacklisted\n' ""
  fi
}

readarray -t input < ""
declare -Ai blacklist
for i in "${!input[@]}"; do
  ((blacklist["${input[i]%$'\r'}"] = i + 1))  # domain without \r -> line
done

readarray -t input < ""
domains=("${input[@]% *}")  # remove everything after space (maybe also \r)
for domain in "${domains[@]}"; do
  is_blacklisted 'blacklist' "$domain" || :  # don't crash on error
done

它似乎与提供的输入示例合理地工作:

$ /tmp/blacklist.sh /tmp/blacklist.txt /tmp/domains.txt
domain1.no is blacklisted on line 1
domain2.no is NOT blacklisted
domain3.no is blacklisted on line 2
domain4.no is NOT blacklisted
domain5.no is blacklisted on line 3