2012-03-24 74 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 '