Dans Bash, quelle est la manière la plus simple de tester si un tableau contient une certaine valeur?
Edit : Avec l'aide des réponses et des commentaires, après quelques tests, j'ai trouvé ceci:
function contains() {
local n=$#
local value=${!n}
for ((i=1;i < $#;i++)) {
if [ "${!i}" == "${value}" ]; then
echo "y"
return 0
fi
}
echo "n"
return 1
}
A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
echo "contains three"
fi
Je ne sais pas si c'est la meilleure solution, mais cela semble fonctionner.
[[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
if
. Je ne pense pas qu'il existe un moyen d'éviter SC2199 avec cette approche. Vous devez explicitement boucler le tableau, comme indiqué dans certaines des autres solutions, ou ignorer l'avertissement.Voici une petite fonction pour y parvenir. La chaîne de recherche est le premier argument et les autres sont les éléments du tableau:
Un test de cette fonction pourrait ressembler à:
la source
"${array[@]}"
. Sinon, les éléments contenant des espaces briseront la fonctionnalité.if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi;
Merci pour votre aide!shift
décale la liste des arguments de 1 vers la gauche (en supprimant le premier argument) etfor
sansin
itère implicitement sur la liste des arguments.la source
case "${myarray[@]}" in *"t"*) echo "found" ;; esac
sorties:found
Pour les cordes:
la source
${#}
car Bash prend en charge les tableaux clairsemés.Solution en une ligne
Explication
L'
printf
instruction imprime chaque élément du tableau sur une ligne distincte.L'
grep
instruction utilise les caractères spéciaux^
et$
pour trouver une ligne qui contient exactement le modèle donné commemypattern
(ni plus, ni moins).Usage
Pour mettre cela dans une
if ... then
déclaration:J'ai ajouté un
-q
drapeau à l'grep
expression pour qu'elle n'imprime pas les correspondances; cela traitera simplement l'existence d'une correspondance comme "vraie".la source
Si vous avez besoin de performances, vous ne voulez pas parcourir votre tableau entier à chaque fois que vous effectuez une recherche.
Dans ce cas, vous pouvez créer un tableau associatif (table de hachage ou dictionnaire) qui représente un index de ce tableau. C'est-à-dire qu'il mappe chaque élément du tableau dans son index dans le tableau:
Ensuite, vous pouvez l'utiliser comme ceci:
Et testez l'adhésion comme ceci:
Ou aussi:
Notez que cette solution fait la bonne chose même s'il y a des espaces dans la valeur testée ou dans les valeurs du tableau.
En bonus, vous obtenez également l'index de la valeur dans le tableau avec:
la source
make_index
est un peu plus artificiel en raison de l'indirection; vous auriez pu utiliser un nom de tableau fixe avec un code beaucoup plus simple.J'utilise généralement juste:
une valeur non nulle indique qu'une correspondance a été trouvée.
la source
haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)
que -x amène grep à essayer de faire correspondre la chaîne d'entrée entièreUn autre liner sans fonction:
Merci @Qwerty pour les informations concernant les espaces!
fonction correspondante:
exemple:
la source
exit 0
fait (s'arrête dès que possible s'il est trouvé).|| echo not found
place de|| not found
ou le shell essaiera d'exécuter une commande par le nom de not avec un argument trouvé si la valeur demandée n'est pas dans le tableau.Gère désormais correctement les tableaux vides.
la source
"$e" = "$1"
(au lieu de"$e" == "$1"
) qui ressemble à un bug."e" == "$1"
est syntaxiquement plus clair.Voici une petite contribution:
Remarque: cette façon ne distingue pas le cas "deux mots" mais ce n'est pas obligatoire dans la question.
la source
Si vous voulez faire un test rapide et sale pour voir s'il vaut la peine d'itérer sur l'ensemble du tableau pour obtenir une correspondance précise, Bash peut traiter les tableaux comme des scalaires. Testez une correspondance dans le scalaire, si aucun, sauter la boucle permet de gagner du temps. De toute évidence, vous pouvez obtenir des faux positifs.
Cela produira "Vérification" et "Correspondance". Avec
array=(word "two words" something)
elle ne sortira que "Vérification". Avecarray=(word "two widgets" something)
il n'y aura pas de sortie.la source
words
par une expression régulière^words$
qui ne correspond qu'à la chaîne entière, ce qui élimine complètement la nécessité de vérifier chaque élément individuellement?pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]
que ne correspondra jamais car il vérifie tout le tableau à la fois comme s'il s'agissait d'un scalaire. Les vérifications individuelles dans ma réponse ne doivent être effectuées que s'il y a une raison de procéder en fonction de la correspondance approximative.Cela fonctionne pour moi:
Exemple d'appel:
la source
Si vous préférez, vous pouvez utiliser des options longues équivalentes:
la source
Empruntant à la réponse de Dennis Williamson , la solution suivante combine des tableaux, des citations shell-safe et des expressions régulières pour éviter d'avoir à: itérer sur les boucles; en utilisant des tuyaux ou d'autres sous-processus; ou en utilisant des utilitaires non bash.
Le code ci-dessus fonctionne en utilisant des expressions régulières Bash pour correspondre à une version chaîne du contenu du tableau. Il existe six étapes importantes pour garantir que la correspondance d'expression régulière ne peut pas être dupée par des combinaisons intelligentes de valeurs dans le tableau:
printf
coquille citant,%q
. Les guillemets shell garantissent que les caractères spéciaux deviennent "shell-safe" en étant échappés avec une barre oblique inverse\
.%q
; c'est la seule façon de garantir que les valeurs dans le tableau ne peuvent pas être construites de manière intelligente pour tromper la correspondance d'expression régulière. Je choisis une virgule,
parce que ce personnage est le plus sûr lorsqu'il est évalué ou utilisé de manière inattendue.,,%q
comme argument pourprintf
. Ceci est important car deux instances du caractère spécial ne peuvent apparaître l'une à côté de l'autre que lorsqu'elles apparaissent comme délimiteur; toutes les autres instances du caractère spécial seront échappées.${array_str}
, comparez contre${array_str},,
.la source
printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]
peut-être.case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
Un petit ajout à la réponse de @ ghostdog74 sur l'utilisation de la
case
logique pour vérifier que le tableau contient une valeur particulière:Ou avec l'
extglob
option activée, vous pouvez le faire comme ceci:Nous pouvons également le faire avec une
if
déclaration:la source
donné :
puis une simple vérification de:
où
(La raison d'affecter p séparément, plutôt que d'utiliser l'expression directement à l'intérieur de [[]] est de maintenir la compatibilité pour bash 4)
la source
En combinant quelques-unes des idées présentées ici, vous pouvez faire une déclaration élégante sans boucles qui correspond exactement aux mots .
Cela ne se déclenchera pas sur
word
ouval
, seuls les mots entiers correspondront. Il se cassera si chaque valeur de tableau contient plusieurs mots.la source
J'écris généralement ce genre d'utilitaires pour opérer sur le nom de la variable, plutôt que sur la valeur de la variable, principalement parce que bash ne peut pas autrement transmettre des variables par référence.
Voici une version qui fonctionne avec le nom du tableau:
Avec cela, l'exemple de question devient:
etc.
la source
Utilisation de
grep
etprintf
Formatez chaque membre du tableau sur une nouvelle ligne, puis
exemple:grep
les lignes.Notez que cela n'a aucun problème avec les délimètres et les espaces.
la source
Vérification sur une ligne sans «grep» et boucles
Cette approche n'utilise ni utilitaires externes comme
grep
ni boucles.Ce qui se passe ici, c'est:
IFS
valeur de la variable;IFS
remplacement de valeur temporaire en évaluant notre expression conditionnelle dans un sous-shell (à l'intérieur d'une paire de parenthèses)la source
Utilisation de l'expansion des paramètres:
la source
${myarray[hello]:+_}
fonctionne très bien pour les tableaux associés, mais pas pour les tableaux indexés habituels. La question est de trouver une valeur dans un tableau, pas de vérifier si la clé d'un tableau associatif existe.Après avoir répondu, j'ai lu une autre réponse qui m'a particulièrement plu, mais elle était imparfaite et dévalorisée. Je me suis inspiré et voici deux nouvelles approches que je vois viables.
en utilisant
grep
etprintf
:utilisant
for
:Pour les résultats non trouvés, ajoutez
|| <run_your_if_notfound_command_here>
la source
Voici mon point de vue à ce sujet.
Je préfère ne pas utiliser de boucle bash for si je peux l'éviter, car cela prend du temps à s'exécuter. Si quelque chose doit boucler, que ce soit quelque chose qui a été écrit dans un langage de niveau inférieur à un script shell.
Cela fonctionne en créant un tableau associatif temporaire
_arr
, dont les indices sont dérivés des valeurs du tableau en entrée. (Notez que les tableaux associatifs sont disponibles dans bash 4 et supérieur, donc cette fonction ne fonctionnera pas dans les versions antérieures de bash.) Nous définissons$IFS
pour éviter la division des mots sur les espaces.La fonction ne contient pas de boucles explicites, bien que bash en interne parcourt le tableau d'entrée afin de remplir
printf
. Le format printf utilise%q
pour garantir que les données d'entrée sont échappées de manière à pouvoir être utilisées en toute sécurité comme clés de tableau.Notez que tout ce que cette fonction utilise est intégré à bash, donc il n'y a pas de canaux externes vous entraînant, même dans l'extension de commande.
Et si vous n'aimez pas utiliser
eval
... eh bien, vous êtes libre d'utiliser une autre approche. :-)la source
eval
instruction à la fin de la réponse. :)eval
(je n'ai rien contre, contrairement à la plupart des gens qui pleurenteval
est mal, surtout sans comprendre ce qui est mal à ce sujet). Juste que votre commande est cassée. Peut - être au%q
lieu de%s
serait mieux.eval
, évidemment), mais vous avez absolument raison,%q
semble aider, sans casser quoi que ce soit d'autre que je puisse voir. (Je ne savais pas que% q échapperait également aux crochets.) Un autre problème que j'ai vu et corrigé concernait les espaces. Aveca=(one "two " three)
, similaire au problème de Keegan: non seulement aarray_contains a "two "
obtenu un faux négatif, mais aarray_contains a two
obtenu un faux positif. Assez facile à réparer en réglantIFS
.eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") )
et vous pouvez abandonner lelocal IFS=
. Il y a toujours un problème avec les champs vides dans le tableau, car Bash refusera de créer une clé vide dans un tableau associatif. Un moyen rapide et hacky de le corriger consiste à ajouter un caractère factice, par exemplex
:eval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )
etreturn $(( 1 - 0${_arr[x$2]} ))
.Ma version de la technique des expressions régulières qui a déjà été suggérée:
Ce qui se passe ici, c'est que vous développez la totalité du tableau des valeurs prises en charge en mots et ajoutez une chaîne spécifique, "X-" dans ce cas, à chacun d'eux, et faites de même à la valeur demandée. Si celui-ci est en effet contenu dans le tableau, alors la chaîne résultante correspondra tout au plus à l'un des jetons résultants, ou pas du tout au contraire. Dans ce dernier cas, le || déclencheurs d'opérateur et vous savez que vous avez affaire à une valeur non prise en charge. Avant tout cela, la valeur demandée est supprimée de tous les espaces blancs de début et de fin par une manipulation de chaîne shell standard.
C'est propre et élégant, je pense, bien que je ne sois pas trop sûr de la performance que cela peut être si votre tableau de valeurs prises en charge est particulièrement grand.
la source
Voici mon point de vue sur ce problème. Voici la version courte:
Et la version longue, qui je pense est beaucoup plus agréable pour les yeux.
Exemples:
la source
test_arr=("hello" "world" "two words")
?J'ai eu le cas où je devais vérifier si un ID était contenu dans une liste d'ID générés par un autre script / commande. Pour moi, a travaillé ce qui suit:
Vous pouvez également le raccourcir / le compacter comme ceci:
Dans mon cas, j'exécutais jq pour filtrer du JSON pour une liste d'ID et j'ai dû vérifier plus tard si mon ID était dans cette liste et cela a fonctionné le mieux pour moi. Cela ne fonctionnera pas pour les tableaux créés manuellement du type
LIST=("1" "2" "4")
mais pour les sorties de script séparées par des sauts de ligne.PS: je n'ai pas pu commenter une réponse car je suis relativement nouveau ...
la source
Le code suivant vérifie si une valeur donnée est dans le tableau et renvoie son décalage de base zéro:
La correspondance est effectuée sur les valeurs complètes, donc la définition de VALUE = "trois" ne correspondrait pas.
la source
Cela pourrait valoir la peine d'être étudié si vous ne voulez pas répéter:
Extrait adapté de: http://www.thegeekstuff.com/2010/06/bash-array-tutorial/ Je pense que c'est assez intelligent.
EDIT: Vous pourriez probablement simplement faire:
Mais ce dernier ne fonctionne que si le tableau contient des valeurs uniques. Rechercher 1 sur "143" donnera des faux positifs, je pense.
la source
Un peu tard, mais vous pouvez utiliser ceci:
la source
Je suis venu avec celui-ci, qui ne fonctionne que dans zsh, mais je pense que l'approche générale est agréable.
Vous ne retirez votre motif de chaque élément que s'il commence
${arr[@]/#pattern/}
ou se termine${arr[@]/%pattern/}
avec lui. Ces deux substitutions fonctionnent en bash, mais les deux en même temps${arr[@]/#%pattern/}
ne fonctionnent qu'en zsh.Si le tableau modifié est égal à l'original, il ne contient pas l'élément.
Éditer:
Celui-ci fonctionne en bash:
Après la substitution, il compare la longueur des deux tableaux. Obly si le tableau contient l'élément, la substitution le supprimera complètement et le nombre sera différent.
la source