如何使用grep/awk/sed打印出特定模式之前(不包括模式本身)的所有字符?

15
例如。
echo "abc-1234a :" | grep <do-something>

只打印出abc-1234a


6
欢迎来到StackOverflow!这听起来像是一个XY问题。你真正想做什么?What are you really trying to do? - ghoti
1
什么是模式?冒号?空格冒号?你只处理单行输入(如echo),还是处理多行输入?如果是多行输入,你是在寻找第1行..N行,其中N是包含模式的第一行,忽略其余行,还是在寻找每行的初始段,直到该行上的模式? - Jonathan Leffler
谢谢大家!它是冒号前输入的第一行字符。输入可能有多行。 - user1224949
我在我的答案中添加了一个更新,以处理多行输入。 - ghoti
2个回答

29
我认为这些更接近你所想要的,但是如果不知道你真正想要实现什么,很难说。
echo "abc-1234a :" | egrep -o '^[^:]+'

......虽然这也将匹配没有冒号的行。如果你只想匹配包含冒号的行,并且你必须仅使用grep,那么这可能会起作用:

echo "abc-1234a :" | grep : | egrep -o '^[^:]+'
当然,只有在您的echo "abc-1234a :"是一个可能被替换为多行输入的示例时,这才有意义。
您可以使用的最小工具可能是cut
echo "abc-1234a :" | cut -d: -f1

而且 sed 总是可用的...

echo "abc-1234a :" | sed 's/ *:.*//'

对于最后一个问题,如果你只想打印包含冒号的行,则将其更改为:

echo "abc-1234a :" | sed -ne 's/ *:.*//p'

事实上,你甚至可以纯粹使用bash来做到这一点:

while read line; do
  field="${line%%:*}"
  # do stuff with $field
done <<<"abc-1234a :"

要获取有关%%位的信息,可以使用man bash并搜索"Parameter Expansion"。

更新:

你说:

它是冒号前输入的第一行字符。虽然输入可能有多行。

使用grep的解决方案可能不是最好的选择,因为它们还将打印可能包括冒号的后续行中的数据。当然,也有许多其他方式来满足这个要求。我们将从示例输入开始:

$ function sample { printf "abc-1234a:foo\nbar baz:\nNarf\n"; }
$ sample
abc-1234a:foo
bar baz:
Narf

你可以使用多个管道符,例如:

$ sample | head -1 | grep -Eo '^[^:]*'
abc-1234a
$ sample | head -1 | cut -d: -f1      
abc-1234a

或者你可以使用sed仅处理第一行:

$ sample | sed -ne '1s/:.*//p'
abc-1234a

或者告诉sed在打印第一行后退出(这比读取整个文件要快):

$ sample | sed 's/:.*//;q'
abc-1234a

或者做同样的事情,但只有在找到冒号时才显示输出(出于安全考虑):

$ sample | sed -ne 's/:.*//p;q'
abc-1234a
或者让awk做同样的事情(分别与最后3个示例相同):
$ sample | awk '{sub(/:.*/,"")} NR==1'
abc-1234a
$ sample | awk 'NR>1{nextfile} {sub(/:.*/,"")} 1'
abc-1234a
$ sample | awk 'NR>1{nextfile} sub(/:.*/,"")'
abc-1234a

在bash中,甚至不需要使用管道:

$ read line < <(sample)
$ printf '%s\n' "${line%%:*}"
abc-1234a

这非常完美!第二个选项对我很好。我只想要冒号前面的字符作为输入的第一行。谢谢! - user1224949
建议进行轻微的修改:使用 sed '/:/{ s/:.*//; q;}' 而不是假设只有输入的第一行是有意义的。 - William Pursell
@WilliamPursell - 说得好,不过OP在问题的评论中说字段名只会出现在输入的第一行。 - ghoti
egrep -o '^[^:]+' 等同于 grep -o '^[^:]\+' - kenorb
@kenorb - 不一定。虽然对于GNU grep是正确的,但并不适用于所有RE解析器。将您的grep与理论上(但不是)等效的sed -e 's/^\([^:]\+\).*/\1/'进行比较。GNU sed的行为与GNU grep相同,但在FreeBSD、OSX、Solaris等系统中则不同。有趣的是,这种行为甚至在Linux文档中得到了支持——来自regex(7)手册的倒数第二段描述部分:'|'、'+'和'?'是普通字符,没有它们功能的等价物。 - ghoti

0

只用sed就可以实现你想要的功能。

以下是一个例子:

#!/bin/sh
filename=$1
pattern=yourpattern

# flag -n disables print everyline (default behavior)
sed -n "
  1,/$pattern/ {
    /$pattern/n # skip line containing pattern
    p           # print lines ranging from line 1 untill pattern
  }
" $filename

exit 0

这至少适用于GNU的sed。对于其他sed也应该有效,除了注释(一些sed实现不支持注释)。

来源:https://www.grymoire.com/Unix/Sed.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接