从一行中找到确切的单词并用 grep/sed 删除该行
Find exact word from a line and delete that line with grep/sed
我在 Stack Overflow 上搜索了几个问题和答案,但其中 none 个适用于我的情况,我不知道为什么我的正则表达式不起作用。如果您能指出我的错误想法,我将不胜感激。
测试用例:文本文件包含
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
,我的函数将 return 同时 AllenZhou
和 XiaoyuZhou
。小学习后,我将grep命令参数改为
if $( echo $entry | grep --quiet ^$name:$)
事实证明它从来没有找到 AllenZhou
或 XiaoyuZhou
的任何东西 – 我很困惑。
sed -i -n /$name/d $fileName
这是我用来删除包含字符串模式的行的代码。问题与 grep 类似,如果我键入 Zhou
或 Allen
,该命令将删除包含关键字的两行。但是当我改为
sed -i -n /\<$name\>/d $fileName
它不会为 AllenZhou
或 XiaoyuZhou
或 Zhou
删除...我再次感到困惑。
在 if
中使用命令替换并不符合您的想法。您正在捕获 grep
的输出——带有 -q
选项的输出将始终为空字符串——并将 that 作为参数传递给 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。单方括号也可以移植,它们可以做... something 像这样,但它丑陋、易碎等等比你想象的要复杂。)
还要注意引用。包含任意字符串的变量 needs to be quoted.
另一方面,您想使用 read -r
——没有选项,read
的行为在某些特殊情况下会背负着令人讨厌的遗留行为以实现历史向后兼容性。
但是,分别检查每一行非常麻烦。整个功能可以是
grep -q "$name" "$fileName"
这也是 returns 一个实际结果;你的函数没有做的事情(除了可能通过设置一个全局变量,如果这是它正在做的事情——很难从上下文中分辨出来。即使在 shell 脚本中,在函数中使用全局变量也是一个坏主意)。
也许您需要一些正则表达式锚定来限制与第一个字段的匹配。 grep "^[^:]*$name"
在第一个冒号之前的任何位置查找匹配项。
您的数据中没有单词边界(空格、标点符号等),只有大小写的变化,因此 \<
或 \>
无法匹配这些名称。观察你的大小写模式,也许你想在匹配后要求一个大写字符或一个冒号; "^[^:]*$name[[:upper:]:]"
?
如果最终目标是提取地址或 phone 号码,请直接这样做。为此,您需要 Awk 而不是 grep
。
awk -F : -v name="$name" 'BEGIN { pat = name "($|[[:upper:]])"; result = 1 }
~ pat ( print ; result = 0 }
END { exit result }' "$fileName"
Awk 脚本打印任何匹配行的第二个字段 并且 设置结果代码,因此您可以在 if
或 while
中使用它条件。
我在 Stack Overflow 上搜索了几个问题和答案,但其中 none 个适用于我的情况,我不知道为什么我的正则表达式不起作用。如果您能指出我的错误想法,我将不胜感激。
测试用例:文本文件包含
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
,我的函数将 return 同时 AllenZhou
和 XiaoyuZhou
。小学习后,我将grep命令参数改为
if $( echo $entry | grep --quiet ^$name:$)
事实证明它从来没有找到 AllenZhou
或 XiaoyuZhou
的任何东西 – 我很困惑。
sed -i -n /$name/d $fileName
这是我用来删除包含字符串模式的行的代码。问题与 grep 类似,如果我键入 Zhou
或 Allen
,该命令将删除包含关键字的两行。但是当我改为
sed -i -n /\<$name\>/d $fileName
它不会为 AllenZhou
或 XiaoyuZhou
或 Zhou
删除...我再次感到困惑。
在 if
中使用命令替换并不符合您的想法。您正在捕获 grep
的输出——带有 -q
选项的输出将始终为空字符串——并将 that 作为参数传递给 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。单方括号也可以移植,它们可以做... something 像这样,但它丑陋、易碎等等比你想象的要复杂。)
还要注意引用。包含任意字符串的变量 needs to be quoted.
另一方面,您想使用 read -r
——没有选项,read
的行为在某些特殊情况下会背负着令人讨厌的遗留行为以实现历史向后兼容性。
但是,分别检查每一行非常麻烦。整个功能可以是
grep -q "$name" "$fileName"
这也是 returns 一个实际结果;你的函数没有做的事情(除了可能通过设置一个全局变量,如果这是它正在做的事情——很难从上下文中分辨出来。即使在 shell 脚本中,在函数中使用全局变量也是一个坏主意)。
也许您需要一些正则表达式锚定来限制与第一个字段的匹配。 grep "^[^:]*$name"
在第一个冒号之前的任何位置查找匹配项。
您的数据中没有单词边界(空格、标点符号等),只有大小写的变化,因此 \<
或 \>
无法匹配这些名称。观察你的大小写模式,也许你想在匹配后要求一个大写字符或一个冒号; "^[^:]*$name[[:upper:]:]"
?
如果最终目标是提取地址或 phone 号码,请直接这样做。为此,您需要 Awk 而不是 grep
。
awk -F : -v name="$name" 'BEGIN { pat = name "($|[[:upper:]])"; result = 1 }
~ pat ( print ; result = 0 }
END { exit result }' "$fileName"
Awk 脚本打印任何匹配行的第二个字段 并且 设置结果代码,因此您可以在 if
或 while
中使用它条件。