2012-03-24 73 views
3

我正在努力做到這一點,實現這一目標的最有效途徑是什麼?Bash:從變量B中刪除變量A中的一系列字符串?

#!/bin/bash 

# Remove DOGS from CATSNDOGS to give CATS 

DOGS="fido rover oscar bowwow spike max" 

CATSNDOGS="bowwow figaro pussy oscar boots rover kitty max spike meowser fluffles fido" 

CATS="" #?? How do I do this? 

回答

1

您可以通過程序comm來完成。 -3選項擺脫了匹配行(不是單詞),並且輸入需要排序,所以還有一點點。事情是這樣的:

comm -3 <(echo $DOGS | tr ' ' '\n' | sort) <(echo $CATSNDOGS | tr ' ' '\n' | sort) 

爲了支持原來的輸入格式(包括空格),並避免創建臨時文件,我們把空格換行符,排序兩個輸入,並把它們作爲「虛擬」文件參數comm

編輯:我沒有捕獲輸出,它只是打印到標準輸出。你可以說CATS=$(...)來存儲它,雖然你可能需要稍微按摩它以回到空間,如果這是你想要的。

+0

它打破如果一隻狗不在'$ CATSNDOGS'中。爲了解決這個問題,可以添加'-1'選項。 – jfg956 2012-03-24 23:15:06

2

comm答案是有創意的,但當然不是唯一的方法。你也可以純粹用bash來做到這一點,而不需要額外的工具。

#!/bin/bash 

DOGS="fido rover oscar bowwow spike max" 
CATSNDOGS="bowwow figaro pussy oscar boots rover kitty max spike meowser fluffles fido" 

# make an associative array... 
declare -A dogs_a 
for dog in $DOGS; do 
    dogs_a[$dog]=1; 
done 

CATS="" 
# step through everything 
for beast in $CATSNDOGS; do 
    # if it's not a dog... 
    if [ -z "${dogs_a[$beast]}" ]; then 
    CATS="$CATS $beast" 
    fi 
done 

echo $CATS 

注意,這也依賴於空格作爲字段分隔符,你應該閱讀有關始終在bash編程時在引號包裹的變量。

1

另一種方法:

for i in $CATSNDOGS 
do 
     skip=0 
     for j in $DOGS 
     do 
       if [ "$j" == "$i" ]; then 
         skip=1 
       else 
         continue 
       fi 
     done 
     if [ "$skip" == "0" ]; then 
      CATS="$CATS $i" 
     else 
      continue 
     fi 
done 

echo -e "cats: $CATS" 

不過我喜歡ghoti的版本與關聯數組更多。

0

這是join的工作使用打印不可配對的行-a)的說法。然後,我們保留以空格結尾的行,並刪除該空格。爲避免使用臨時文件,我們使用bash進程替換。

join -a 1 -j 1 -o 1.1,2.1 \ 
    <(tr " " "\n" <<< "$CATSNDOGS" | sort) \ 
    <(tr " " "\n" <<< "$DOGS" | sort) | sed -e '/ $/!d;s/ //' 

它失去的$CATSNDOGS初始訂單,但我們可以很容易地添加cat -nsort找回初始排序。

把那個背在一個變量,這樣做:

CATS="$(join -a 1 -j 1 -o 1.1,2.1 \ 
    <(tr " " "\n" <<< "$CATSNDOGS" | sort) \ 
    <(tr " " "\n" <<< "$DOGS" | sort) | sed -e '/ $/!d;s/ //' | paste -s -d " ")" 
1

在一個單一的命令,保持貓的順序,但使用複雜的sed邏輯:

sed -e 'N;s/^/ /;s/$/ /;s/\n/ \n /;bbegin' \ 
    -e ':begin;s/ \(.*\) \(.*\)\n\(.*\) \1/\2\n\3 /;tbegin' \ 
    -e 's/^ //;s/ \n //' << EOF 
$CATSNDOGS 
$DOGS 
EOF 

這是什麼邏輯解釋如下:

  1. $CATSNDOGS$DOGS放在同一行上,用a新行(\n)。
  2. $CATSNDOGS$DOGS之前和之後添加空格以簡化以下邏輯。
  3. 如果在換行符前後找到一個單詞,請將其刪除。
  4. 只要刪除一個單詞,請重試。
  5. 打印之前,請刪除前導空格和尾隨空格以及新行。

編輯

我意識到,如果上面休息狗是不是$CATSNDOG或如果狗是兩次$CATSNDOG。改進後的版本是:

sed -e 'N;s/^/ /;s/$/ /;s/\n/ \n /;bbegin' \ 
    -e ':begin;s/ \(.*\) \(.*\)\n\(.*\) \1/\2\n\3 \1 /;tbegin' \ 
    -e 's/^ //;s/ \n.*//' << EOF 
$CATSNDOGS 
$DOGS 
EOF 
+0

Oooh,+1花式sed。 :) – ghoti 2012-03-25 15:03:24

0

另一個慶典,唯一的方法

cats=() 
for animal in $CATSNDOGS; do 
    if [[ " $DOGS " == *" $animal "* ]]; then 
    # animal is a dog 
    else 
    cats+=$animal 
    fi 
done 
echo "${cats[@]}" 
2

純巴什(注意空格):

CATS=" $CATSNDOGS " 

for dog in $DOGS ; do 
    CATS=${CATS/ $dog/} 
done 

echo -e "CATS : '$CATS'" 

結果:

CATS : ' figaro pussy boots kitty meowser fluffles '