You can't do this with grep
alone. You might be able to do it in sed
but it'll be a PITA and require far more knowledge about pattern and hold spaces than you probably have (and such knowledge generally isn't worth learning because it's much easier to do in other languages).
In short, you'll need to use awk or perl. For example:
$ perl -lne 'BEGIN { $, = "," };
if (m=(\b\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}\b)=) {
$dt = $1
} elsif (m/(INC-\d+-\d+)/) {
print $1, $dt;
#$dt = ""; # uncomment to clear $dt before next input line
}' input.txt
INC-220721-00007628,8/1/2022 6:15
This perl one-liner script uses perl's -l
option to enable automatic handling of line-endings while reading the input and printing output (e.g. \n
on unix or \r\n
on windows), the -n
option to make perl work like sed -n
. The -e
option indicates that the next argument is the script to be run by perl.
First, this script sets the output field separator ($,
) to a comma. This variable is documented in man perlvar. It does this in a BEGIN {...}
block so that it runs only once, when the script is started, rather than once for each input line read.
BTW, you can optionally use perl's English module if you don't like or can't remember the cryptic single-character variable names (with use English;
inside the script or -MEnglish
with a one-liner), this gives you access to both long descriptive English aliases and awk-like equivalents where appropriate for the short variables. e.g. with use English
you can use either $,
, $OUTPUT_FIELD_SEPARATOR
, or the awk-like $OFS
- they all mean the same thing and refer to the same variable.
The script uses perl's regex match operator m
to match (and capture, due to the parentheses in the regex) the required patterns. See man perlop and search for "m/PATTERN". The first time I use m
, I'm using =
as the regex delimiter so I don't have to escape the /
s in the date pattern. The second-time, I'm using the more familiar /
.
See also the man pages for perlre, as well as perlrequick and perlretut.
For each input line read, it tries to match the required date and time pattern and, if successful, stores the captured date and time (from perl's $1
sub-pattern match variable, which is similar to \1
in sed
- see man perlvar
and search for the section headed "Variables related to regular expressions") into a variable called $dt
.
If the previous match was not successful, it tries to match the INC-\d+-\d+
pattern. If successful, it prints the captured pattern and the $dt
variable.
All other input is ignored.
or with awk:
$ awk -v OFS=, '
match($0,/\<[[:digit:]]{1,2}\/[[:digit:]]{1,2}\/[[:digit:]]{4} [[:digit:]]{1,2}:[[:digit:]]{1,2}\>/,a) {
dt = a[0]; next
};
match($0,/INC-[[:digit:]]+-[[:digit:]]+/,a) {
print a[0], dt
}' input.txt
INC-220721-00007628,8/1/2022 6:15
awk has a handy -v
option to set awk variables, so we don't need a BEGIN
block to set OFS.
This awk one-liner is pretty much a direct translation of the perl version, but uses awk's match()
function to do the match-and-capture test. It capture any matches into array a
.
It also uses [[:digit:]]
which is the equivalent to perl's \d
. In many locales, you can get away with using [0-9]
instead, but both [[:digit:]]
and perl's \d
will work in any locale.
8/1/2022 6:15
? Do you need to find any date and time? How far into the future or past? And how do we recognize the next pattern? Will it always beINC-
followed by two sets of numbers separated by a-
? Will it always be that exact number? Please edit and tell us what can change and what will be always the same so we can use it to find what you need. Finally, please also tell us what operating system you are using.grep
is simply the wrong tool to use.