Как отфильтровать набор строк A из набора строк B, используя Bash - PullRequest
4 голосов
/ 24 октября 2009

У меня есть список строк, которые я хочу удалить из супернабора других строк, а не в каком-либо определенном порядке и, таким образом, создать новый набор. Это выполнимо в Bash?

Ответы [ 8 ]

5 голосов
/ 24 октября 2009

Похоже, вы ищете что-то с временем работы лучше, чем O (нм), так что вот ответ на это. Fgrep или grep -F использует алгоритм Aho-Corasick для создания одного FSM из списка фиксированных строк, поэтому проверка каждого слова в SET2 занимает O (длину слова) времени. Это означает, что все время выполнения этого скрипта составляет O (n + m).

(очевидно, время выполнения также зависит от длины слов)

[meatmanek@yggdrasil ~]$ cat subtract.sh 
#!/bin/bash
subtract()
{
  SET1=( $1 )
  SET2=( $2 )
  OLDIFS="$IFS"
  IFS=$'\n'
  SET3=( $(grep -Fxv "${SET1[*]}" <<< "${SET2[*]}") )
  IFS="$OLDIFS"
  echo "${SET3[*]}"
  # SET3 = SET2-SET1
}
subtract "$@"
[meatmanek@yggdrasil ~]$ . subtract.sh 

[meatmanek@yggdrasil ~]$ subtract "package-x86 test0 hello world" "computer hello sizeof compiler world package-x86 rocks"
computer sizeof compiler rocks
[meatmanek@yggdrasil ~]$ 
2 голосов
/ 06 марта 2017
> echo "aa b1 c b2 d" |xargs -d' ' -n 1
aa
b1 
c
b2
d

> echo "aa b1 c b2 d" |xargs -d' ' -n 1| grep "^b"
b1
b2
1 голос
/ 24 октября 2009

Как насчет уродливого злоупотребления встроенной командой hash?

#!/bin/bash
set -eu

filter_out() {
    local words="$2" words_to_remove="$1"
    ( # do this in a subshell to avoid contaminating the main script
        set +e
        hash -r
        hash -p bogus-placeholder $words
        hash -d $words_to_remove > /dev/null 2>&1
        left=''
        for word in $words; do
            hash -t "$word" > /dev/null 2>&1 && left="${left}${left:+ }$word"
        done
        printf '%s\n' "$left"
    )
}

filter_out "package-x86 test0 hello world" "computer hello sizeof compiler world package-x86 rocks test0"
w='foo bar baz quux toto'
d='baz toto quux'
filter_out "$d" "$w"
1 голос
/ 24 октября 2009

Я думаю, вам нужно будет хотя бы охарактеризовать параметры подмножества строк, которые вы хотите извлечь. Если это данные, подобные текстовым полям, посмотрите awk.

0 голосов
/ 24 октября 2009

Без использования каких-либо специфичных для bash или внешних команд:

SET1="package-x86 test0 hello world"
SET2="computer hello sizeof compiler world package-x86 rocks test0"
SET3=

for arg in $SET2; do
  case $SET1 in
    $arg\ * | *\ $arg | *\ $arg\ *) ;;
    *) SET3="$SET3 $arg" ;;
  esac
done
0 голосов
/ 24 октября 2009

Это что, O (n) или O (n + m)?

#!/bin/bash
SET1="package-x86 test0 hello world"
SET2="computer hello sizeof compiler world package-x86 rocks test0"
for i in $SET2
do
    [[ ! $SET1 =~ $i  ]] && SET3="${SET3:+${SET3} }$i"
done
echo "..${SET3}.."

Запуск:

$ ./script
..computer sizeof compiler rocks..
0 голосов
/ 24 октября 2009
#!/bin/bash
SET1="package-x86 test0 hello world"
SET2="computer hello sizeof compiler world package-x86 rocks test0"
awk -v s1="$SET1" -v s2="$SET2" 'BEGIN{
    m=split(s1,set1)
    n=split(s2,set2)
    for(i=1;i<=n;i++){
        for (j=1;j<=m;j++){
            if ( set1[j] == set2[i]){
                 delete set2[i]
            }   
        }
    }
    for(i in set2) if (set2[i]!="") {print set2[i]}
}' 

выход

# ./shell.sh
compiler
rocks
computer
sizeof
0 голосов
/ 24 октября 2009

Здесь используется grep, чтобы увидеть, нужно ли удалить слово, но это не чисто BASH и, вероятно, быстрее, чем другой вариант (см. Ниже).

#!/bin/bash
REMOVE="package-x86 test0 hello world"
WORDBAG="computer hello sizeof compiler world package-x86 rocks test0"
OFS=$IFS
IFS=" "
WORDBAG_ARRAY=($WORDBAG)
IFS=$OFS
RESULT=""

for str2 in ${WORDBAG_ARRAY[@]}
do
        echo $REMOVE | grep $str2 >/dev/null
        if [[ $? == 1 ]] #Not Found
        then
                RESULT="$RESULT $str2"
        fi
done

echo $RESULT

Это немного многословно, использует массивы BASH, O (N * M), но работает.

#!/bin/bash
REMOVE="package-x86 test0 hello world"
WORDBAG="computer hello sizeof compiler world package-x86 rocks test0"
OFS=$IFS
IFS=" "
REMOVE_ARRAY=($REMOVE)
WORDBAG_ARRAY=($WORDBAG)
IFS=$OFS
RESULT=""

for str2 in ${WORDBAG_ARRAY[@]}
do
        found=0
        for str1 in ${REMOVE_ARRAY[@]}
        do
                if [[ "$str1" == "$str2" ]]
                then
                        found=1
                fi
        done
        if [[ $found == 0 ]]
        then
                RESULT="$RESULT $str2"
        fi
done

echo $RESULT
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...