0

So I have a text file containing strings of numbers with a word at the end:

123456 126 2 12456 1256 4 46 12346 123456 4 56 word
24 245 1234 356 12346 6 3 346 245 5 12346 12356 word

I want to find all lines that have at least 8 strings containing either 1 or 6 or both. So the first line would pass because it has 8 strings (seperated by spaces) that contain either 1 or 6 or both. The second one only has 7 strings that contain either 1 or 6 or both.

I tried the follow regex, but it gives a backtracking limit error: ([0-9]*(1|6)[0-9]* .*){8,}

5
  • do you have a string that 1 or 6 repeated more than once? either 1 or 6 or both repeated like 1112, 116565, 6000066. please edit to respond Commented Nov 8, 2022 at 16:57
  • Try similar like this sed -n "s/\([^ ]*[1|6][^ ]*\).*/\1/p" (It's just for one occurence...)
    – K-attila-
    Commented Nov 8, 2022 at 17:01
  • 1
    @K-att- Note that [1|6] matches one of 1, |, or 6.
    – Kusalananda
    Commented Nov 8, 2022 at 17:04
  • May I ask what you tried your regular expression with? It seems to work with grep -E.
    – Kusalananda
    Commented Nov 8, 2022 at 17:05
  • If I understand well, 1 or 6 or both => 1 or 6.
    – K-attila-
    Commented Nov 9, 2022 at 8:33

3 Answers 3

1

Instead of trying to craft a regular expression, the following iterates over all whitespace-delimited fields, apart from the last field, incremeting a counter if it contains either 1 or 6. If the counter's value is 8 or more, the current record is outputted:

awk '{ count = 0; for (i = 1; i < NF; ++i) count += ($i ~ "[16]") }; count >= 8' file

The following does the same thing, but stops counting when we know we would want to output the record:

awk '{ count = 0; for (i = 1; i < NF && count < 8; ++i) count += ($i ~ "[16]") }; count == 8' file

As a shorter (unreadable) one-liner (with the counter running down from 8):

awk '{c=8;for(i=1;i<NF&&c;++i)c-=$i~"[16]"}!c' file
1

With perl:

$ perl -ane 'print if (grep /[16]/, @F) >= 8' file.txt 
123456 126 2 12456 1256 4 46 12346 123456 4 56 word
  • -a auto-splits each input line into array @F.

  • -n makes perl run similarly to sed -n (i.e. loop over each input line, but don't print anything unless explicitly told to).

  • -e the next arg is the script to execute.

  • The perl grep() function returns a count of matches, when used in scalar context (while in list context, it returns the list of matches). i.e. it counts the number of matching elements in array @F.

    Note that this function is similar to, but not the same as the grep command-line program. See perldoc -f grep for details.


BTW, if you wanted to print the number of matches too, you could do something like this:

$ perl -ane '$x = grep /[16]/, @F; if ($x >= 8) {printf "%2i: %s", $x, $_}' /tmp/junk.txt 
 8: 123456 126 2 12456 1256 4 46 12346 123456 4 56 word
0

Using sed

$ sed -En 's/ ?[0-9]*[16][0-9]* /&/p8' input_file
123456 126 2 12456 1256 4 46 12346 123456 4 56 word

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .