Comment vérifier si une variable existe dans une liste dans BASH

J'essaie d'écrire un script dans bash qui vérifie la validité d'une entrée utilisateur.
Je veux correspondre à l'entrée (disons variable x) à une liste de valeurs valides.

Ce que j'ai en ce moment est:

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

Ma question Est de savoir s'il existe un moyen plus simple de le faire,
quelque chose comme un list.contains(x) pour la plupart des langages de programmation.

Plus:
Dites liste est:

list="11 22 33"

Mon code fera écho au message uniquement pour ces valeurs puisque list est traité comme un tableau et non une chaîne, toutes les manipulations de chaîne valideront 1 alors que je voudrais qu'il échoue.

98
demandé sur Ofir Farchy 2011-11-09 13:57:19

11 réponses

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

Ou créer une fonction:

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

Pour l'utiliser:

contains aList anItem
echo $? # 0: match, 1: failed
122
répondu chemila 2016-11-08 11:03:42

Matvey a raison, mais vous devriez citer $x et considérer tout type d '"espaces" (par exemple nouvelle ligne) avec

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

Donc, c'est à dire

# 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
}

Fin puis

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

Ou

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

Notez que vous devez utiliser "" en cas de variables:

`list_include_item "$my_list" "$my_item"`  && echo "yes" || echo "no"
24
répondu Oriettaxx 2016-06-18 22:25:52

Vous pouvez également utiliser (*wildcards) en dehors d'une instruction case, si vous utilisez des crochets doubles:

string='My string';

if [[ $string == *My* ]]
then
echo "It's there!";
fi
11
répondu Mithun Sasidharan 2011-11-09 10:14:25

Que diriez-vous de

echo $list|grep $x

, Vous pouvez soit cocher la sortie ou $? de la ligne ci-dessus pour prendre la décision.

10
répondu Kent 2011-11-09 10:01:57

La solution la plus simple à mon humble avis est d'ajouter et d'ajouter la chaîne d'origine avec un espace et de vérifier une expression rationnelle avec [[ ]]

haystack='foo bar'
needle='bar'

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

Ce ne sera pas faux positif sur les valeurs avec des valeurs contenant l'aiguille comme sous-chaîne, par exemple avec une botte de foin foo barbaz.

(le concept est volé sans vergogne sous la forme hasClass()-Méthode de JQuery)

8
répondu blaimi 2017-10-04 11:48:50

Je trouve que c'est plus facile d'utiliser le formulaire echo $LIST | xargs -n1 echo | grep $VALUE comme illustré ci-dessous:

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

Cela fonctionne pour une liste séparée par des espaces, mais vous pouvez l'adapter à n'importe quel autre délimiteur (comme :) en procédant comme suit:

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

Notez que les " sont nécessaires pour que le test fonctionne.

5
répondu Sébastien Pierre 2017-02-20 19:35:22

Si votre liste de valeurs doit être codée en dur dans le script, il est assez simple de tester en utilisant case. Voici un petit exemple, que vous pouvez adapter à vos besoins:

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

Si la liste est une variable de tableau à l'exécution, l'une des autres réponses est probablement un meilleur ajustement.

4
répondu Toby Speight 2016-11-08 11:46:30

Envisagez d'exploiter les clés de tableaux associatifs. Je suppose que cela surpasse à la fois la correspondance regex/pattern et la boucle, bien que je ne l'ai pas profilé.

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

Sortie:

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 )
4
répondu RubyTuesdayDONO 2016-11-08 11:55:43

Si la liste est fixée dans le script, j'aime ce qui suit le mieux:

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

, Puis utilisez validate "$x" pour tester si $x est autorisé.

Si vous voulez un one-liner, et ne vous souciez pas des espaces dans les noms d'éléments, vous pouvez utiliser ceci (notice -w au lieu de -x):

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

Notes:

  • ceci est conforme à POSIX sh.
  • validate n'accepte pas les sous-chaînes (supprimez l'option -x pour grep si vous le souhaitez).
  • validate interprète son argument comme une chaîne fixe, pas un expression (supprimez l'option -F pour grep si vous le souhaitez).

Exemple de code pour exercer la fonction:

for x in "item 1" "item2" "item 3" "3" "*"; do
    echo -n "'$x' is "
    validate "$x" && echo "valid" || echo "invalid"
done
3
répondu Søren Løvborg 2014-06-30 17:34:40

Exemples

$ 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

In_list

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"
1
répondu Brad Parks 2016-04-11 12:27:33

En supposant que la variable cible ne peut être que "binomiale" ou "régression", alors suivre ferait:

# 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

Vous pouvez ajouter plus de chaînes dans la liste en les séparant avec un caractère / (pipe).

L'avantage d'utiliser egrep, c'est que vous pouvez facilement ajouter l'insensibilité à la casse (-i), ou vérifier des scénarios plus complexes avec une expression régulière.

1
répondu Tagar 2018-04-26 23:41:07