【问题标题】:Check if a string does not exist in multiple arrays检查多个数组中是否不存在字符串
【发布时间】:2019-01-17 16:04:21
【问题描述】:

试图检查一个字符串值是否不是两个不同数组的成员。如果其中任何一个都不存在字符串值,那么我需要执行一个语句。我可以用 if 语句来做到这一点......

if [[ $REQ_FIELDS_LIST != *"$XFR_FIELD"* && $NON_INPUT_FIELDS_LIST != *"$XFR_FIELD"* ]];then

但是星号导致子字符串返回误报。不幸的是,删除“*”和 if 语句根本不起作用。看来,检查这个站点,在 bash 中唯一安全的方法是执行 for 循环。但是对于两个不同的数组,最有效的方法是什么。此外,字符串值是数组本身的成员。所以我们已经在遍历一个数组了。遍历字符串值数组,并为每个字符串值检查该字符串是否不是其他两个数组的成员。如果是,则执行语句。

所以我需要...

for XFR_FIELD in $INPUT_FIELDS_LIST
do
    if XFR field is not a member of REQ_FIELDS_LIST AND is not a member of NON_INPUT_FIELDS_LIST then
        "return 0" 

【问题讨论】:

  • $REQ_FIELDS_LIST && $NON_INPUT_FIELDS_LIST 是数组还是变量?它们被写为变量,但您将它们称为数组。另外 - $INPUT_FIELDS_LIST 中有多少空格或其他分隔值,这是一个数组吗?
  • @BrianGurka 如果REQ_FIELDS_LIST 是一个数组,那么$REQ_FIELDS_LIST 只获取它的第一个元素;您需要使用 "$REQ_FIELDS_LIST[@]" 之类的东西来获取所有元素。但这在测试中也行不通;你真的需要遍历元素。
  • 您是否考虑过使用关联数组?这样字符串值就可以成为一个键并且成员资格是一个简单的测试。
  • (检查关联数组键成员资格也是一个摊销常数时间操作,因此它不会随着数组变长而变慢;因此它不仅代码更少,而且速度更快)。
  • 与您的问题无关,但通常您应该避免使用大写的变量名。这是因为bash 使用了许多大写名称,您可能会与其中一个发生名称冲突。

标签: bash shell ksh


【解决方案1】:

有效的方法是使用 bash 4.0 的关联数组:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Requires bash 4.0+" >&2; exit 1;; esac

declare -A input_fields_list=( [one]=1 [two]=1 [three]=1 [four]=1 [five]=1 )
declare -A req_fields_list=( [one]=1 [six]=1 [seven]=1 [eight]=1 )
declare -A non_input_fields_list=( [two]=1 [three]=1 [seven]=1 [eight]=1 [nine]=1 )

for xfr in "${!input_fields_list[@]}"; do
  [[ ${req_fields_list[$xfr]} ]] && continue
  [[ ${non_input_fields_list[$xfr]} ]] && continue
  echo "$xfr not found" >&2
  exit 1
done

echo "All input fields are valid" >&2
exit 0

正如您在https://ideone.com/IhmVKy 看到的那样,这会以five not found 正确退出。

【讨论】:

  • 噢!你打败了我!
  • 非常简洁的解决方案,很好地使用了键!
  • 为什么在错误输出中输出信息性消息“所有输入字段都有效”?
  • @Bsquare,因为它是一条信息/日志消息,这就是 stderr 的用途(根据 POSIX 定义)。将日志放在标准输出上会使它们污染输出。 shell(没有单独的 TTY fd)向标准错误写入提示是有原因的,即使这些也不是错误。
【解决方案2】:

作为一个例子,你可以这样迭代:

#!/bin/bash

INPUT_FIELDS_LIST=( one two three four five)
REQ_FIELDS_LIST=( one six seven eight )
NON_INPUT_FIELDS_LIST=( two three seven eight nine )

for ifl in "${INPUT_FIELDS_LIST[@]}"
do

        for rfl in "${REQ_FIELDS_LIST[@]}"
        do
                myvar=0
                if [[ $ifl == "$rfl" ]]
                then myvar=1; break
                else continue
                fi
        done

        for nifl in "${NON_INPUT_FIELDS_LIST[@]}"
        do
                myvar2=0
                if [[ $ifl == "$nifl" ]]
                then myvar2=1; break
                else continue
                fi
        done

        if [[ $myvar == 1 ]] || [[ $myvar2 == 1 ]]
        then continue
        else echo "$ifl"
        fi

done

手动遍历辅助循环,然后将它们“标记”为无效。

最后的 if 语句检查是否有任何一个值匹配,如果匹配则继续进行下一次主要迭代。

输出:

four
five

【讨论】:

  • 我选择使用这种方法。万分感谢!!正是我想要的。你摇滚
  • @BrianGurka 我很高兴它为你解决了 :) 如果这符合你的需要,请考虑将其标记为已回答 :)
【解决方案3】:

你可以检查一个元素是否在一个类似这样的数组中,没有循环并且不受元素中空格的影响

#! /bin/bash
function elem_in_array() {
    local e="$1"
    shift
    local a=("$@")
    [[ $(printf '\x01%s\x01' "${a[@]}") =~ $(printf '\x01%s\x01' "$e") ]]
}

a1=(A B "C D" E F)
elem_in_array "A" "${a1[@]}" && echo Y || echo N
elem_in_array "B" "${a1[@]}" && echo Y || echo N
elem_in_array "C D" "${a1[@]}" && echo Y || echo N
elem_in_array "AB" "${a1[@]}" && echo Y || echo N

【讨论】:

  • [[ ]] 将其值解析为 C 字符串。它不能可能尊重 NUL 文字(而不是,比如说,默默地丢弃它们)。实际上,请参阅ideone.com/USLNi9 的 stderr 日志中的“忽略 NUL 字节文字”警告
  • (并且命令替换不是免费的——真正的 David Korn ksh93 优化了 $(printf ...) 以不分叉,但 bash 并不那么聪明)。
  • 尝试使用此代码运行elem_in_array "AB" "${a1[@]}" && echo BUG || echo OK;您会看到它发出 BUG,认为 AB 是您的内容中存在的字符串。
猜你喜欢
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2021-10-18
相关资源
最近更新 更多