web-dev-qa-db-fra.com

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

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

ce que je suis venu avec pour le 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 une list.contains(x) pour la plupart des langages de programmation.

Une addition:
Dites que la liste est:

list="11 22 33"

mon code fera écho au message uniquement pour ces valeurs car 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.

103
Ofir Farchy
[[ $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
128
chemila

Matvey a raison, mais vous devriez citer $ x et envisager tout type "d'espaces" (par exemple, une 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 ensuite

`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 "" dans le cas de variables:

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

Vous pouvez également utiliser (* caractères génériques) en dehors d'une instruction case si vous utilisez des crochets doubles:

string='My string';

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

IMHO solution la plus simple consiste à ajouter et ajouter la chaîne originale avec un espace et vérifier par rapport à une regex avec [[ ]]

haystack='foo bar'
needle='bar'

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

cela ne sera pas faux positif sur les valeurs avec des valeurs contenant l'aiguille en tant que sous-chaîne, par ex. avec une botte de foin foo barbaz.

(Le concept est volé sans vergogne de la méthode hasClass()- de la méthode JQuery)

11
blaimi

que diriez-vous

echo $list|grep $x

vous pouvez vérifier la sortie ou $? de la ligne ci-dessus pour prendre la décision.

10
Kent

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

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

Utilisez ensuite validate "$x" pour vérifier si $x est autorisé.

Si vous voulez une ligne, sans vous soucier des espaces dans les noms d'éléments, vous pouvez utiliser ceci (notez -w au lieu de -x):

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

Remarques:

  • Ceci est conforme à POSIX sh.
  • validate fait pas accepte des sous-chaînes (supprimez l’option -x de grep si vous le souhaitez).
  • validate interprète son argument comme une chaîne fixe, pas comme une expression régulière .__ (supprimez l'option -F de 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
5
Søren Løvborg

Je trouve qu'il 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 à tout 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 requis pour que le test fonctionne.

5
Sébastien Pierre

Envisagez d'exploiter les clés de tableaux associatifs . Je présume que cela surpasse à la fois la correspondance regex/pattern et les boucles, bien que je ne l’aie pas encore 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
RubyTuesdayDONO

Si votre liste de valeurs doit être codée en dur dans le script, il est relativement simple de tester avec case. Voici un court 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 au moment de l'exécution, l'une des autres réponses est probablement plus appropriée.

4
Toby Speight

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

dans la liste

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
Brad Parks

En supposant que la variable TARGET ne peut être que 'binomiale' ou 'régression', voici ce que vous ferez:

# 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 par un | caractère (pipe).

Avec egrep, vous pouvez facilement ajouter une insensibilité à la casse (-i) ou vérifier des scénarios plus complexes avec une expression régulière.

1
Tagar

C'est presque votre proposition initiale, mais presque une ligne. Pas si compliqué que d’autres réponses valables, et pas du tout dépendant des versions de bash (peut fonctionner avec d’anciens bashes).

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

Je suppose que ma proposition peut encore être améliorée, à la fois en longueur et en style.

0
carnicer

Je pensais ajouter ma solution à la liste.

# 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
0
SamWN