Filtrer une liste de chaînes avec une condition arbitraire


4

Existe-t-il un moyen de ne laisser que les chaînes qui satisfont à une condition (potentiellement aucune)? Il est trivial de filtrer les chaînes en se basant sur le fait qu'elles correspondent elles-mêmes à un motif (avec grep). Mais que se passe-t-il si j'ai une liste de noms de fichiers et que je souhaite ne laisser que ceux qui sont des répertoires? Que se passe-t-il si j'ai une liste d'URL et que je souhaite ne laisser que celles qui ne renvoient pas 404 lorsque je les wget? Etc. Est-ce que ce genre de logique est généralisable avec bash?

Exemple:

$ echo $LIST
/home/me/a
/home/me/b
/home/me/b/some.jpg
$ echo $LIST | ${//%(!$SOME_FANCY_BASH_FILTERING_LOGIC_TO_CHECK_IF_THIS_IS_A_DIRECTORY&%^#}
/home/me/a
/home/me/b

Réponses:


1

Vous parlez d'une liste de potentiellement n'importe quoi. Si vous avez une liste de noms de fichiers, vous pouvez facilement créer une itération dessus avec bash et sélectionner ceux qui sont des répertoires. Si vous avez une liste d'URL, vous pouvez faire de même pour vérifier celles qui existent sur le net. Mais, bien entendu, la seule partie que vous pouvez généraliser est l'itération:

#!/bin/bash
IFS='
'
LIST='1
2
3
'
for I in $LIST
do
  if [ -d $I ]; then
    echo $I is a directory
  elif [ -f $I ]; then
    echo $I is a file
  fi
done

Si vous avez deux fichiers nommés 1 et 3 et un répertoire nommé 2, le résultat sera:

1 is a file
2 is a directory
3 is a file

Mais si vous avez une liste d'URL, vous devrez changer les conditions de test dans la boucle.


3

Est-ce que ce genre de logique est généralisable avec bash?

Non.

Pour filtrer les éléments dans list, cependant, ne laissant que ceux qui sont des répertoires:

find  $list -maxdepth 0 -type d

Ou,

for d in  $list; do [ -d "$d" ] && echo "$d"; done

Notez que le stockage de fichiers dans une variable shell list, par exemple, par opposition à un tableau, entraînera des problèmes si l'un des noms de fichier contient un caractère d'espacement.

De même, pour filtrer une liste de serveurs en fonction desquels sont actifs (répondant aux pings):

$ server="yahoo.com google.com nonexistent.com"
$ for s in $server; do ping -qc1 "$s" >/dev/null 2>&1 && echo "$s" ; done
yahoo.com
google.com

0

Vous pouvez implémenter votre propre filtre, comme ceci:

# Filter a list of anything, based on predicate.
# $1: predicate, which take one argument (one item)
# $2: a collections of strings (IFS separated).
filter() {
    items=()
    for x in $2; do
        if $1 "$x"; then
            items+=("$x")
        fi
    done
    echo ${items[@]}
}

Et puis utilisez-le comme ceci:

x() { [ $1 -gt 3 ]; }  # you can make any predicate
filter x "1 2 3 4 5"
-> 4 5

Alternativement, nous pourrions le définir comme suit:

filter() {
    while read line; do
        for x in $line; do
        if $1 "$x"; then
            echo "$x"
        fi
        done
    done
}

Ce qui nous permet de l'utiliser avec des tuyaux:

echo 1 2 3 4 5 | filter x
-> 4
   5

Enfin, les deux versions peuvent être combinées en une seule comme ceci:

filter() {
    inner() (
        for x in $2; do
            $1 "$x" && echo "$x"
        done
    )
    if [ -t 0 ] && [ $# -gt 1 ]; then
        inner $1 "$2"
    else
        while read line; do
            inner $1 "$line"
        done
    fi
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.