Issue
I am writing a script that accepts as an argument a string. I want to run a particular command, check the output of that command for that input string, first returning lines that match on <input string>$
and only if that does not return any lines, then return all lines that contain <input string>
anywhere in the line. I am currently using grep -E
but am open to awk or sed
.
Consider this output written to file:
> cat command.out
A
A1
B
B1
B2
C1
C2
C3
XYZ
XYZ1
XYZ2
If my input string is 'B' then I want to return
B
not
B
B1
B2
If my input string is 'C' then I want to return
C1
C2
C3
If my input string is 'Z' then I want to return
XYZ
If my input string is 'Y' then I want to return
XYZ
XYZ1
XYZ2
Using a | (or) in the pattern doesn't do what I am after as it would return all lines with B.
What I have works but seems inefficient and I suspect there is a better way.
> 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
I have to persist the command output and fire off potentially two greps. I was hoping for a piped one-liner
matches="$(<run command>|grep <first pattern, if no match, second pattern>)"
but maybe that is not possible.
Solution
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'
)
$
is concatenated to the value ofr
to form a regex anchored to end of line. If$0
matches this:- set flag
exact
- result is non-zero / true, so print line
- set flag
- if
exact
has not been set and$0
matchesr
anywhere:- append the line to array
inexact
- append the line to array
- at end, if
exact
is unset (ie. no exact match was found), print any stored lines
Note that the value passed in is used as a regex. This corresponds to the grep
usage in the question. To match an exact string, rather than a regex, remember to escape any regex metacharacters.
An alternative approach using exact string comparison rather than regex (and accumulating to a string rather than an array):
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
}
'
)
We know the length of the string and the input line. When the string appears in the line, the position + length of the string will be longer than the line length only if it appears at the end:
s=SSS
idx+len length
1234567SSS123 --> 8+3 < 13
1234567SSS1 --> 8+3 == 11
1234567SSS --> 8+3 > 10
Answered By - jhnc Answer Checked By - Candace Johnson (WPSolving Volunteer)