从一行中找到确切的单词并使用grep / sed删除该行

时间:2016-02-04 16:49:50

标签: bash shell sed grep command

我在Stack Overflow上搜索了一些问题和答案,但它们都不适用于我的情况,我也不知道为什么我的正则表达式不起作用。如果你能指出我错误的想法,我真的很感激。

测试用例:文本文件包含

AllenZhou:9175186661:111th 1111 NY, 11111
XiaoyuZhou:9175186662:2222 222th 22222 NY 22222
Allen:1231231234:abc rd, PA

这是我的功能:

checkEntry(){
    vaildName=true
    while read entry
    do
            if $( echo $entry | grep --quiet $name)//$name read from keyboard
            then
                    vaildName=false
            fi
    done < $fileName
}

如果我输入Zhou,我的函数将同时返回AllenZhouXiaoyuZhou。在我做了小型研究之后,我将grep命令参数更改为

if $( echo $entry | grep --quiet ^$name:$)

事实证明它永远找不到AllenZhouXiaoyuZhou的任何内容 - 我很困惑。

sed  -i -n /$name/d $fileName

这是我用来删除包含字符串模式的行的代码。问题与grep类似,如果我键入ZhouAllen,该命令将删除包含关键字的两行。但是当我改为

sed  -i -n /\<$name\>/d $fileName

它不会删除AllenZhouXiaoyuZhouZhou ...再次让我感到困惑。

1 个答案:

答案 0 :(得分:2)

if中使用命令替换不符合您的想法。您正在捕获grep的输出 - 其中-q选项始终为空字符串 - 并将 作为参数传递给if,它希望命令名称或管道作为其参数。它基本上试图执行空字符串,当然它没有做任何有用的事情(净效果是if条件将总是成功)。

你只想

if echo "$entry" | grep -q "$name"; then
    : stuff
fi

或更具惯用性和效率

if [[ "$entry" = *"$name"* ]]; then
    : stuff
fi

甚至

case $entry in *"$name"*)
    : stuff;;
esac

(双方括号[[ ... ]]仅限Bash,而case可移植到任何POSIX shell,甚至可移植到原始Bourne shell。单方括号将也是可移植的,他们可以做...... 这样的东西,但它比你想要的更丑,更脆,更复杂。)

还要注意引用。包含任意字符串needs to be quoted的变量。

另外,您希望使用read -r - 没有选项,read的行为会带来令人讨厌的遗留行为,以便在某些极端情况下实现历史向后兼容性。

然而,分别检查每一行只是麻烦。整个功能可能是

grep -q "$name" "$fileName"

也返回实际结果;你的函数无法做的事情(除非设置一个全局变量,如果它正在做什么 - 很难从上下文中分辨出来。即使在shell脚本中,在函数中使用全局变量也是一个坏主意)。

也许你想要一些正则表达式锚定来限制与第一个字段的匹配。 grep "^[^:]*$name"在第一个冒号之前的任何地方查找匹配项。

您的数据中没有单词边界(空格,标点符号等),只是大小写的变化,因此\<\>无法匹配这些名称。观察你的大写模式,也许你想在比赛后要求大写字母或冒号; "^[^:]*$name[[:upper:]:]"

如果最终目标是提取地址或电话号码,请直接执行此操作。您需要Awk而不是grep

awk -F : -v name="$name" 'BEGIN { pat = name "($|[[:upper:]])"; result = 1 }
    $1 ~ pat ( print $2; result = 0 }
    END { exit result }' "$fileName"

Awk脚本打印来自任何匹配行的第二个字段,设置结果代码,因此您可以在ifwhile条件下使用它。