Iterate over the lines with _
as the IFS
, get the desired first portion containing digits, and the copy the files starting with those digits:
shopt -s nullglob
while IFS=_ read -r i _; do [[ $i =~ ^[0-9]{5}$ ]] && echo cp -it dest/ "${i}"*; done <file.txt
Expanded:
#!/bin/bash
shopt -s nullglob ##Expands to null string if no match while doing glob expansion,
##rather than the literal
while IFS=_ read -r i _; do ##Iterate over the lines of file.txt,
##with `_` as the `IFS` i.e. word splitting
##happens on each `_` only, variable `i`
##will contain the digits at start; `_` is a
##throwaway variable containing the rest
[[ $i =~ ^[0-9]{5}$ ]] \ ##Check if the variable contains only 5 digits
&& echo cp -it /destination/ "${i}"* ##if so, copy the relevant files starting with those digits
done <file.txt
Replace file.txt
with the actual source file, and /destination/
with your actual destination directory. Here echo
is included to do the dry-run; if satisfied with the commands to be run, just get rid of echo
:
shopt -s nullglob
while IFS=_ read -r i _; do [[ $i =~ ^[0-9]{5}$ ]] && cp -it dest/ "${i}"*; done <file.txt
Given
$ cat file
Good-Black-Cat
Bad-Red-Cat
Bad-Gray-Dog
Good-Golden-Dog
Bad-White-Dog
Good-Tabby-Cat
Bad-Siamese-Cat
then
$ grep -c 'Good-.*-Cat' file
2
Note that this is a count of matching lines - so for example it won't work for multiple occurrences per line, or for occurrences that span lines.
Alternatively, with awk
awk '/Good-.*-Cat/ {n++} END {print n}' file
If you need to match multiple possible occurrences per line, then I'd suggest perl
:
perl -lne '$c += () = /Good-.*?-Cat/g }{ print $c' file
where /Good-.*?-Cat/g
matches multiple times (g
) and non-greedily* (.*?
) and the () =
assignment forces the matches to be evaluated in a scalar context so we can add them to the count.
Alternatively, you could use grep
in perl-comparible regular expression (PCRE) mode (so as to enable the non-greedy modifier), with -o
to output only the matching portions - then count those with wc
:
grep -Po 'Good-.*?-Cat' file | wc -l
If you also need to match occurrences that may span a line boundary, then you can do so in perl
by unsetting the record separator (note: this means that that the whole file is slurped into memory) and adding the s
regex modifier e.g.
perl -0777 -nE '$c += () = /Good-.*?-Cat/gs }{ say $c' file
Best Answer