使用任意条件过滤字符串列表

使用任意条件过滤字符串列表

有没有办法只留下那些通过某些(可能任何)条件的字符串?根据字符串本身是否与某些模式匹配(使用 grep)这一事实来过滤字符串很简单。但是,如果我有一个文件名列表,并且只想留下那些目录,该怎么办?如果我有一个 URL 列表,并且只想留下那些在 wget 时不返回 404 的 URL,该怎么办?等等。这种逻辑可以用 bash 推广吗?

例子:

$ 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

答案1

你正在谈论一个可能包含任何内容的列表。如果你有一个文件名列表,你可以轻松地用 bash 对其进行迭代并选择目录。如果你有一个 URL 列表,你可以做同样的事情来检查网络上存在哪些内容。但是,当然,你唯一可以概括的部分是迭代:

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

如果您有两个名为 1 和 3 的文件以及一个名为 2 的目录,则输出将是:

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

但是如果您有一个 URL 列表,则必须更改循环内的测试条件。

答案2

这种逻辑可以用 bash 概括吗?

不。

但是,要过滤中的元素list,只留下目录元素:

find  $list -maxdepth 0 -type d

或者,

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

请注意,如果将文件存储在 shell 变量(例如list,而不是数组)中,则如果任何文件名包含空格字符,就会导致问题。

类似地,要根据哪些服务器处于启动状态(响应 ping),请过滤服务器列表:

$ 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

答案3

您可以实现自己的过滤器,如下所示:

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

然后像这样使用它:

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

或者,我们可以将其定义为:

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

这使我们能够将其与管道一起使用:

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

最后,两个版本可以合并为一个,如下所示:

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
}

相关内容