Bash:从变量B中删除变量A中的一系列字符串?

时间:2012-03-24 19:38:01

标签: string bash

我正在尝试这样做,实现这一目标的最有效方法是什么?

#!/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?

7 个答案:

答案 0 :(得分: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 :(得分:2)

Pure Bash(注意空白):

CATS=" $CATSNDOGS "

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

echo -e "CATS : '$CATS'"

结果:

CATS : ' figaro pussy boots kitty meowser fluffles '

答案 2 :(得分:1)

您可以使用程序comm执行此操作。 -3选项摆脱了匹配的行(不是单词),输入需要进行排序,因此还有更多内容。像这样:

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

为了支持原始输入格式(带空格)并避免创建临时文件,我们将空格转换为换行符,对两个输入进行排序,并将它们用作comm的“虚拟”文件参数。

编辑:我没有捕获输出,它只是打印到stdout。您可以说CATS=$(...)来存储它,但如果这是您想要的,您可能需要稍微按摩它以回到空间。

答案 3 :(得分: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版本。

答案 4 :(得分: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放在同一行,由新行(\n)分隔。
  2. $CATSNDOGS$DOGS之前和之后添加空格,以简化以下逻辑。
  3. 如果在换行符之前和之后找到某个字词,请将其删除。
  4. 只要删除一个单词,请在上面再试一次。
  5. 在打印之前,请删除前导空格和尾随空格以及新行。
  6. 修改

    我意识到,如果一只狗不在$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
    

答案 5 :(得分: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 " ")"

答案 6 :(得分:0)

另一种仅限bash的方法

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