0
votes

Le tableau 1 contient-il l'une des chaînes du tableau 2

J'essaie de faire une instruction if où si array1 contient l'une des chaînes de array2, il devrait afficher "match" sinon imprimer "no match"

Jusqu'à présent, j'ai ce qui suit. Je ne sais pas comment le compléter. Les deux boucles devraient se rompre dès que la première correspondance est trouvée.

if [ array1 contains strings in array2 ]
then
  echo "match"
else
  echo "no match"
fi

Ce n'est peut-être même pas la meilleure façon de le faire?

Cela illustre le résultat souhaité

#!/bin/bash

array1=(a b c 1 2 3)
array2=(b 1)

for a in "${array1[@]}"
do
    for b in "${array2[@]}"
    do
        if [ "$a" == "$b" ]; then
            echo "Match!"
            break
        fi
    done
done


0 commentaires

4 Réponses :


3
votes

Pour vérifier si array1 contient une entrée de array2 , vous pouvez utiliser grep . Ce sera beaucoup plus rapide et plus court que les boucles dans bash.

Les commandes suivantes se terminent avec le code d'état 0 si et seulement s'il y a une correspondance. Utilisez-les comme ...

printf %q\\n "${array1[@]}" | grep -qFxf <(printf %q\\n "${array2[@]}")

Entrées de tableau sur une seule ligne

La version simple pour les chaînes sans saut de ligne est

printf %s\\n "${array1[@]}" | grep -qFxf <(printf %s\\n "${array2[@]}")


8 commentaires

Merci pour la réponse. Cela semble faire exactement ce que je voulais. Je vais faire quelques recherches sur Google pour voir comment cela fonctionne.


Malheureusement, si les éléments du tableau contiennent une nouvelle ligne, cela ne fonctionnera pas


Heureux d'avoir pu aider. Quelques conseils: <(cmd) est processus de substitution et émule un fichier contenant la sortie de cmd . grep -f fichier fait correspondre stdin à une liste de modèles du fichier .


@KamilCuk Ouais. Je l'ai déjà signalé. J'ai essayé de le faire fonctionner, mais cela semble impossible. Si vous avez des conseils, je serais ravi de les essayer.


Qu'en est-il de si je voulais faire "si Array1 contient des cordes pas dans Array2"? Je pense que ce serait très différent?


@Chris Ajoutez simplement l'option -v à grep .


@Kamilcuk a trouvé un hack pour résoudre le problème de la nouvelle ligne.


@socowi Merci. Je pense que je le comprends. Nous imprimons array1 sous forme de liste, puis le grep en utilisant l'entrée de fichier, mais au lieu d'un fichier, nous imprimons array2 et utilisons le "<".



0
votes

Pour les éléments de tableau qui ne contiennent pas de nouvelles lignes, le grep -qf avec printf "% s \ n" serait une bonne option. Pour comparer des tableaux avec n'importe quel élément, j'ai terminé par ceci:

printf "%s\0" "${array1[@]}" | eval grep -qzFx "$(printf " -e %q" "${array2[@]}")"

Le printf "% s \ 0" "$ {array [@]}" | sort -z affiche une liste triée de zéro élément de tableau terminé. Le comm -z12 extrait ensuite les éléments communs dans les deux listes. Le cmp -s / dev / null vérifie si la sortie de comm est vide, qui ne sera pas vide si un élément est dans les deux listes. Vous pouvez utiliser [-z "$ (comm -z ...)"] pour vérifier si la sortie de comm serait vide, mais bash se plaindra que la sortie d'une commande capturée avec $ (..) contient un octet nul, il est donc préférable de cmp -s /dev/null.

Je pense | est plus rapide que , donc votre if pourrait être:

if ! printf "%s\0" "${array1[@]}" | sort -z |
     comm -z12 - <(printf "%s\0" "${array2[@]}" | sort -z) |
     cmp -s /dev/null -; then
        echo "Some elements are in both array1 and array2"
fi

Ce qui suit pourrait fonctionner:

cmp -s /dev/null <(comm -z12 <(printf "%s\0" "${array1[@]}" | sort -z) <(printf "%s\0" "${array2[@]}" | sort -z))

Mais je crois avoir trouvé un bogue dans grep v3.1 lors de la correspondance d'un caractère de nouvelle ligne avec -x drapeau. Si vous n'utilisez pas le caractère de nouvelle ligne, la ligne ci-dessus fonctionne.


0 commentaires

1
votes

Pour une solution rapide, il vaut mieux utiliser un outil externe capable de traiter l'ensemble du tableau dans son ensemble (comme les réponses basées sur grep ). Faire des boucles imbriquées en bash pur est susceptible d'être plus lent pour toute quantité substantielle de données (où le traitement élément par élément dans bash est susceptible d'être plus coûteux que le

Cependant, si vous avez besoin d’une solution bash pure, je vois que votre solution actuelle n’a aucun moyen d’imprimer le " aucun match "scénario. De plus, il peut afficher "match" plusieurs fois.

Pour résoudre ce problème, vous pouvez simplement stocker le fait qu'une correspondance a été trouvée et l'utiliser pour les deux:

  • quitter la boucle externe plus tôt ainsi que la boucle interne; et
  • affiche la chaîne correcte à la fin.

Pour ce faire, vous pouvez utiliser quelque chose comme:

#!/bin/bash

# Test data.

array1=(a b c 1 2 3)
array2=(b 1)

# Default to not-found state.

foundMatch=false
for a in "${array1[@]}" ; do
    for b in "${array2[@]}" ; do
        # Any match switches to found state and exits inner loop.

        [[ "$a" == "$b" ]] && foundMatch=true && break
    done

    # If found, exit outer loop as well.

    ${foundMatch} && break
done

# Output appropriate message for found/not-found state.

$foundMatch && echo "Match" || echo "No match"


1 commentaires

Très bien merci. J'ai commencé de cette façon parce que je ne savais pas mieux. Je pense que ce serait bien car les tableaux ne seront pas gros. 10 éléments max. Je vais passer à ceci si je rencontre des problèmes.



0
votes

Voulez-vous essayer ce qui suit:

array1=(a b c 1 2 3)
array2=(b 1)

declare -A seen    # set marks of the elements of array1
for b in "${array2[@]}"; do
    (( seen[$b]++ ))
done

for a in "${array1[@]}"; do
    (( ${seen[$a]} )) && echo "match" && exit
done
echo "no match"


0 commentaires