有没有办法只留下那些通过某些(可能任何)条件的字符串?根据字符串本身是否与某些模式匹配(使用 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
}