shell grep -仅当第一个模式不产生匹配时检查第二个模式

flmtquvp  于 2023-05-18  发布在  Shell
关注(0)|答案(3)|浏览(172)

我正在写一个脚本,接受字符串作为参数。我想运行一个特定的命令,检查该命令的输出,以获取输入字符串,首先返回匹配<input string>$的行,并且只有在没有返回任何行的情况下,然后返回所有包含<input string>的行。我目前正在使用grep -E,但我对awk或sed开放。
考虑将此输出写入文件:

> cat command.out
A
A1
B
B1
B2
C1
C2
C3
XYZ
XYZ1
XYZ2

如果我的输入字符串是'B',那么我想返回

B

B
B1
B2

如果我的输入字符串是'C',那么我想返回

C1
C2
C3

如果我的输入字符串是'Z',那么我想返回

XYZ

如果我的输入字符串是'Y',那么我想返回

XYZ
XYZ1
XYZ2

使用|模式中的(or)不做我想要的,因为它会返回所有带有B的行。
我所拥有的工作,但似乎效率低下,我怀疑有更好的方法。

> command_output="$(cat command.out)"

> matches="$( (print "$command_output"|grep -E 'B$')||(print "$command_output"|grep -E 'B') )"
> print "$matches"
B

> matches="$( (print "$command_output"|grep -E 'C$')||(print "$command_output"|grep -E 'C') )"
> print "$matches"
C1
C2
C3

我必须持久化命令输出,并可能触发两个grep。我本希望能有一个管道式的俏皮话

matches="$(<run command>|grep <first pattern, if no match, second pattern>)"

但也许这是不可能的。

chhkpiq4

chhkpiq41#

matches=$(
    <run command> |
    awk '
        $0~r"$" && exact=1;
        !exact && $0~r { inexact[n++] = $0 }
        END {
            if(!exact)
                for(i=0;i<n;i++)
                    print inexact[i]
        }
    ' r='regex'
)
  • $连接到r的值以形成锚定到行尾的正则表达式。如果$0匹配:
  • 设置标志exact
  • 结果为非零/真,因此打印行
  • 如果exact尚未设置,且$0在任何地方匹配r
  • 将行追加到数组inexact
  • 最后,如果exact未设置(即未找到完全匹配项),打印所有存储行

请注意,传入的值用作正则表达式。这对应于问题中的grep用法。要匹配精确的字符串,而不是正则表达式,请记住转义任何正则表达式元字符。
另一种方法是使用精确的字符串比较而不是正则表达式(并且累加成字符串而不是数组):

matches=$(
    <run command> |
    awk -v s='input string' '
        BEGIN { len=length(s) }
        idx=index($0,s) {
            if ( idx+len > length ) {
                print
                exact=1
            } else approx = approx $0 ORS
        }
        END {
            ORS=""
            if (!exact) print approx
        }
    '
)

我们知道字符串和输入行的长度。当字符串出现在行中时,字符串的位置+长度只有出现在末尾时才会比行长:

s=SSS
                  idx+len     length
1234567SSS123 -->   8+3    <  13
1234567SSS1   -->   8+3    == 11
1234567SSS    -->   8+3    >  10
hts6caw3

hts6caw32#

在shell中使用||操作符运行grep两次,首先匹配作为整行的输入,然后作为前缀。

matches=$(printf "%s\n" "$command_output" |  grep -x B filename || printf "%s\n" "$command_output" | grep '^B' filename)
ecr0jaav

ecr0jaav3#

如果我没有理解你的问题,你希望匹配被分组在一起,这样组中的第一行总是单独的字符串或带有后缀的字符串,并且在文件中不会有其他匹配项。在这些情况下,如果前缀匹配,如果没有完全匹配,只需回退到打印。

<run command> |
awk -v value="$1" '$0 == value { print; matched=1 }
  ($0 ~ "^" value) && !matched'

相关问题