检查 Bash 中的列表中是否存在变量

2024-11-05 08:37:00
admin
原创
40
摘要:问题描述:我正在尝试用 bash 编写一个脚本来检查用户输入的有效性。 我想将输入(例如变量x)与有效值列表进行匹配。我现在想到的是:for item in $list do if [ "$x" == "$item" ]; th...

问题描述:

我正在尝试用 bash 编写一个脚本来检查用户输入的有效性。

我想将输入(例如变量x)与有效值列表进行匹配。

我现在想到的是:

for item in $list
do
    if [ "$x" == "$item" ]; then
        echo "In the list"
        exit
    fi
done

我的问题是,是否有更简单的方法来做到这一点,

类似于list.contains(x)大多数编程语言。

说清单是:

list="11 22 33"

我的代码将仅回显这些值的消息,因为list它被视为数组而不是字符串,所以所有字符串操作都将验证,1而我希望它失败。


解决方案 1:

[[ $list =~ (^|[[:space:]])$x($|[[:space:]]) ]] && echo 'yes' || echo 'no'

或者创建一个函数:

contains() {
    [[ $1 =~ (^|[[:space:]])$2($|[[:space:]]) ]] && exit(0) || exit(1)
}

使用它:

contains aList anItem
echo $? # 0: match, 1: failed

解决方案 2:

怎么样

echo $list | grep -w -q $x

您可以检查输出或 $?上面的行来做出决定。

grep -w检查整个单词模式。添加-q可防止回显列表。

解决方案 3:

我认为最简单的解决方案是在原始字符串前面和后面添加一个空格,然后使用正则表达式进行检查[[ ]]

haystack='foo bar'
needle='bar'

if [[ " $haystack " =~ .* $needle .* ]]; then
    ...
fi

对于包含针作为子字符串的值(例如 haystack ),这不会产生假阳性foo barbaz

(这个概念是从 JQuery 的hasClass()-Method 中偷来的)

解决方案 4:

Matvey 是对的,但你应该引用 $x 并考虑任何类型的“空格”(例如换行符)

[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no' 

因此,即

# list_include_item "10 11 12" "2"
function list_include_item {
  local list="$1"
  local item="$2"
  if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then
    # yes, list include item
    result=0
  else
    result=1
  fi
  return $result
}

然后结束

`list_include_item "10 11 12" "12"`  && echo "yes" || echo "no"

或者

if `list_include_item "10 11 12" "1"` ; then
  echo "yes"
else 
  echo "no"
fi

""请注意,在变量的情况下必须使用:

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"

解决方案 5:

有一种更简洁的方法来检查字符串是否在列表中:

if [[ $my_str = @(str1|str2|str3) ]]; then
    echo "string found"
fi

解决方案 6:

如果不是太长;您可以将它们沿着逻辑或比较串联在相等位置,就像这样。

if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then
    echo In the list
fi 

我遇到了同样的问题,虽然上面的内容很难看,但与其他通用解决方案相比,它更明显地表明了发生了什么。

解决方案 7:

如果使用双括号,那么也可以在 case 语句外使用 (* 通配符):

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi

解决方案 8:

如果您的值列表要在脚本中硬编码,则使用 进行测试相当简单case。这是一个简短的示例,您可以根据自己的要求进行调整:

for item in $list
do
    case "$x" in
      item1|item2)
        echo "In the list"
        ;;
      not_an_item)
        echo "Error" >&2
        exit 1
        ;;
    esac
done

如果列表在运行时是一个数组变量,那么其他答案之一可能更适合。

解决方案 9:

考虑利用关联数组的键。我认为这比正则表达式/模式匹配和循环都要好,尽管我还没有对它进行分析。

declare -A list=( [one]=1 [two]=two [three]='any non-empty value' )
for value in one two three four
do
    echo -n "$value is "
    # a missing key expands to the null string, 
    # and we've set each interesting key to a non-empty value
    [[ -z "${list[$value]}" ]] && echo -n '*not* '
    echo "a member of ( ${!list[*]} )"
done

输出:

one is a member of ( one two three )
two is a member of ( one two three )
three is a member of ( one two three )
four is *not* a member of ( one two three )

解决方案 10:

如果脚本中的列表是固定的,我最喜欢下面的内容:

validate() {
    grep -F -q -x "$1" <<EOF
item 1
item 2
item 3
EOF
}

然后用来validate "$x"测试是否$x允许。

如果您想要一行代码,并且不关心项目名称中的空格,那么可以使用这个(注意-w而不是-x):

validate() { echo "11 22 33" | grep -F -q -w "$1"; }

笔记:

  • 这是sh符合 POSIX 标准的。

  • validate不接受子字符串(如果需要,请删除 grep 选项)-x

  • validate将其参数解释为固定字符串,而不是正则表达式(-F如果需要,请删除 grep 选项)。

执行该功能的示例代码:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done

解决方案 11:

我发现使用echo $LIST | xargs -n1 echo | grep $VALUE如下所示的表格更简单:

LIST="ITEM1 ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then
    ...
fi

这适用于空格分隔的列表,但您可以:通过执行以下操作使其适应任何其他分隔符(如):

LIST="ITEM1:ITEM2"
VALUE="ITEM1"
if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then
   ...
fi

请注意,"测试必须能够进行。

解决方案 12:

shell 内置的 compgen 可以帮上忙。它可以使用 -W 标志获取列表并返回它找到的任何潜在匹配项。

# My list can contain spaces so I want to set the internal
# file separator to newline to preserve the original strings.
IFS=$'
'

# Create a list of acceptable strings.
accept=( 'foo' 'bar' 'foo bar' )

# The string we will check
word='foo'

# compgen will return a list of possible matches of the 
# variable 'word' with the best match being first.
compgen -W "${accept[*]}" "$word"

# Returns:
# foo
# foo bar

我们可以编写一个函数来测试一个字符串是否等于可接受字符串的最佳匹配。这允许您分别返回 0 或 1 来表示通过或失败。

function validate {
  local IFS=$'
'
  local accept=( 'foo' 'bar' 'foo bar' )
  if [ "$1" == "$(compgen -W "${accept[*]}" "$1" | head -1)" ] ; then
    return 0
  else
    return 1
  fi
}

现在您可以编写非常干净的测试来验证字符串是否可接受。

validate "blah" || echo unacceptable

if validate "foo" ; then
  echo acceptable
else 
  echo unacceptable
fi

解决方案 13:

我想将我的解决方案添加到列表中。

# Checks if element "$1" is in array "$2"
# @NOTE:
#   Be sure that array is passed in the form:
#       "${ARR[@]}"
elementIn () {
    # shopt -s nocasematch # Can be useful to disable case-matching
    local e
    for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
    return 1
}

# Usage:
list=(11 22 33)
item=22

if elementIn "$item" "${list[@]}"; then
    echo TRUE;
else
    echo FALSE
fi
# TRUE

item=44
elementIn $item "${list[@]}" && echo TRUE || echo FALSE
# FALSE

解决方案 14:

先前的答案不使用tr ,我发现它对grep很有用。假设列表中的项目以空格分隔,则检查是否完全匹配:

echo $mylist | tr ' ' '
' | grep -F -x -q "$myitem"

如果该项目在列表中,则返回退出代码 0,如果不在列表中,则返回退出代码 1。

最好将其用作函数:

_contains () {  # Check if space-separated list $1 contains line $2
  echo "$1" | tr ' ' '
' | grep -F -x -q "$2"
}

mylist="aa bb cc"

# Positive check
if _contains "${mylist}" "${myitem}"; then
  echo "in list"
fi

# Negative check
if ! _contains "${mylist}" "${myitem}"; then
  echo "not in list"
fi

解决方案 15:

迟到了?以下非常简单的变体尚未明确提及。我用它case来检查简单列表,这是 Bourne Shell 的通用习惯用法,不依赖于任何外部或扩展:

haystack='a b c'
needle='b'

case " $haystack " in (*" $needle "*) :;; (*) false;; esac
  • 请注意使用分隔符(此处SPC:)来正确界定模式:在的开始和结束处" $haystack ",以及在的测试中同样如此" $needle "

  • 如果处于 中,该语句返回true( ) ,否则返回 false。$?=0`$needle`$haystack

  • 您还可以非常轻松地测试多个$needle。当有多个类似情况时,

if (haystack.contains(needle1)) { run1() } elif (haystack.contains(needle2)) { run2() } else { run3() }

您也可以将其包装到 中case

case " $haystack " in (*" $needle1 "*) run1;; (*" $needle2 "*) run2;; (*) run3;; esac

等等

这也适用于所有不包含分隔符本身的列表,例如逗号:

haystack=' a , b , c '
needle=' b '

case ",$haystack," in (*",$needle,"*) :;; (*) false;; esac

请注意,如果值可以包含任何内容,包括分隔符序列(除了NUL,因为 shell 不支持NUL变量,因为您无法将包含的参数传递NUL给命令),那么您需要使用数组。数组是 ksh/bashisms,不受“普通”POSIX/Bourne shell 支持。(您可以在 POSIX-Shells 中解决此限制$@,但这与此处所问的完全不同。)

(*) false部分可以省略吗?

  • 不,因为这是关键的返回值。默认情况下case返回true

  • 是的,如果你不需要返回值并将处理放在:

为什么:;;

  • 我们也可以写true;;,但我习惯用:而不是 ,true因为它更短,打字更快

  • 此外,我认为不写任何东西都是不好的做法,因为并不是每个人都清楚的知道默认返回值case是真的。

  • 此外,“省略”命令通常表示“这里忘记了一些东西”。因此,在这里放置一个多余的“:”清楚地表明“它除了在这里返回 true 之外不打算做其他事情”。

bash也可以使用 ksh/bashisms 如;&(fallthroug) 或;;&(test other patterns) 来表达if (haystack.contains(needle1)) { run1(); }; if (haystack.contains(needle2)) { run2(); }

因此通常case比其他正则表达式构造更易于维护。此外,它不使用正则表达式,而只使用 shell 模式,这甚至可能更快。


可重复使用的功能:

: Needle "list" Seperator_opt
NeedleListSep()
{
  if [ 3 -gt $# ]; 
  then NeedleListSep "$1" "$2" " ";
  else case "$3$2$3" in (*"$3$1$3"*) return 0;; esac; return 1;
  fi;
}

bash可以将其简化为

: Needle "list" Seperator_opt
NeedleListSep()
{
  local s="${3-" "}";
  case "$s$2$s" in (*"$s$1$s"*) return 0;; esac; return 1;
}

像这样使用

Test() {
NeedleListSep "$1" "a b c"           && echo found $1 || echo no $1;
NeedleListSep "$1" "a,b,c"     ','   && echo found $1 || echo no $1;
NeedleListSep "$1" "a # b # c" ' # ' && echo found $1 || echo no $1;
NeedleListSep "$1" "abc"       ''    && echo found $1 || echo no $1;
}
Test a
Test z

如上所示,这也适用于分隔符为空字符串的退化情况(因此列表的每个字符都是一根针)。例如:

Test

返回

no
no
no
found

因为空字符串显然abc是分隔符的一部分,对吗?

请注意,此功能属于公共领域,因为其中绝对没有任何内容可以真正受到版权保护。

解决方案 16:

简单的一行...

if [[ " 11 22 33 " == *" ${x} "* ]]; then echo "${x} is in the list"; fi;

在 fi 之前添加:else echo "${x} is NOT in the list";

解决方案 17:

受已接受的响应启发的替代解决方案,但使用了反向逻辑:

MODE="${1}"

echo "<${MODE}>"
[[ "${MODE}" =~ ^(preview|live|both)$ ]] && echo "OK" || echo "Uh?"

这里,输入 ($MODE) 必须是正则表达式中的选项之一(“preview”、“live”或“both”),而不是将整个选项列表与用户输入匹配。当然,您不希望正则表达式发生变化。

解决方案 18:

示例

$ in_list super test me out
NO

$ in_list "super dude" test me out
NO

$ in_list "super dude" test me "super dude"
YES

# How to use in another script
if [ $(in_list $1 OPTION1 OPTION2) == "NO" ]
then
  echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2"
  exit;
fi

列表

function show_help()
{
  IT=$(CAT <<EOF

  usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ...

  e.g. 

  a b c d                    -> NO
  a b a d                    -> YES
  "test me" how "test me"    -> YES

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

if [ "$#" -eq 0 ]; then
  show_help
fi

SEARCH_FOR=$1
shift;

for ITEM in "$@"
do
  if [ "$SEARCH_FOR" == "$ITEM" ]
  then
    echo "YES"
    exit;
  fi
done

echo "NO"

解决方案 19:

假设目标变量只能是“二项式”或“回归”,则以下内容可行:

# Check for modeling types known to this script
if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then
    echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2
    usage
fi

您可以通过用 | (管道)字符分隔来将更多字符串添加到列表中。

使用 egrep 的优点在于,您可以轻松添加不区分大小写(-i),或使用正则表达式检查更复杂的场景。

解决方案 20:

这几乎就是您的原始建议,但几乎是一行代码。不像其他有效答案那样复杂,也不那么依赖于 bash 版本(可以与旧 bash 一起使用)。

OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella"
for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done
[ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }

我想我的提案还有待改进,无论是长度还是风格。

解决方案 21:

肯特答案的更短版本,没有echo

grep -wq term <<< "item1 item2 item3"

解决方案 22:

下面的脚本实现了包含列表的功能。

    function contains {
      local target=$1
      shift
    
      printf '%s
' "$@" | grep -x -q "$target"
      out=$?
      (( out = 1 - out ))
      return $out
    }

如果将基于空格的字符串转换为列表并使用它,似乎可以按如下方式解决。


    list="11 22 33"
    IFS=" " read -ra parsed_list <<< "$list"
    
    # parsed_list would be ("11" "22" "33")
    
    contains "11" "${parsed_list[@]}"
    echo $?  # 1
    
    contains "22" "${parsed_list[@]}"
    echo $?  # 1
    
    contains "1" "${parsed_list[@]}"
    echo $? # 0
    
    contains "11 22" "${parsed_list[@]}"
    echo $? # 0
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用