Filtrar uma lista de strings com condição arbitrária

3

Existe uma maneira de deixar apenas as cadeias que passam por alguma condição (potencialmente alguma)? É trivial filtrar strings com base no fato de que elas mesmas correspondem a algum padrão (com grep). Mas e se eu tiver uma lista de nomes de arquivos e quiser deixar apenas aqueles que são diretórios? E se eu tiver uma lista de URLs e quiser deixar apenas as que não retornarem 404 quando eu as enviar? E assim por diante. Esse tipo de lógica é generalizável com o bash?

Exemplo:

$ 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
    
por jojman 12.05.2014 / 01:05

3 respostas

1

Você está falando de uma lista de potencialmente qualquer coisa. Se você tem uma lista de nomes de arquivos, você pode facilmente criar uma iteração sobre ela com o bash e selecionar aqueles que são diretórios. Se você tem uma lista de URL, você pode fazer o mesmo para verificar quais existem na rede. Mas, é claro, a única parte que você pode generalizar é a iteração:

#!/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

Se você tiver dois arquivos chamados 1 e 3 e um diretório chamado 2, a saída será:

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

Mas se você tiver uma lista de URLs, terá que alterar as condições de teste dentro do loop.

    
por 12.05.2014 / 01:21
2

Is this kind of logic generalizable with bash?

Não.

Para filtrar elementos em list , no entanto, deixando apenas aqueles que são diretórios:

find  $list -maxdepth 0 -type d

Ou

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

Observe que o armazenamento de arquivos em uma variável do shell, como list , em oposição a um array, causará problemas se algum dos nomes de arquivo contiver um caractere de espaço em branco.

Da mesma forma, para filtrar uma lista de servidores de acordo com o que está ativo (respondendo a 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
    
por 12.05.2014 / 01:15
0

Você poderia implementar seu próprio filtro, assim:

# 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[@]}
}

E então use assim:

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

Como alternativa, podemos defini-lo como:

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

Que nos permite usá-lo com tubos:

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

Finalmente, as duas versões podem ser combinadas em uma assim:

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
}
    
por 04.05.2018 / 23:07

Tags