3

Есть ли способ оставить только те строки, которые удовлетворяют некоторым (потенциально любым) условиям? Фильтровать строки тривиально, основываясь на том, что они сами соответствуют некоторому шаблону (с помощью grep). Но что, если у меня есть список имен файлов и я хочу оставить только те, которые являются каталогами? Что если у меня есть список URL и я хочу оставить только те, которые не возвращают 404, когда я их wget? И так далее. Эта логика обобщается с помощью 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

3 ответа3

2

Эта логика обобщается с помощью bash?

Нет.

Чтобы отфильтровать элементы в list , оставив только те, которые являются каталогами:

find  $list -maxdepth 0 -type d

Или же,

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

Обратите внимание, что хранение файлов в переменной оболочки, такой как list , в отличие от массива, приведет к проблемам, если любое из имен файлов будет содержать символ пробела.

Аналогично, чтобы отфильтровать список серверов, в соответствии с которыми запущены (отвечающие на запросы):

$ 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
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-адресов, вам придется изменить условия тестирования внутри цикла.

0

Вы можете реализовать свой собственный фильтр, например так:

# 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
}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .