5

I am trying to look for lines in a file that have one or more instances of 1234, but no other numbers (non-digit characters are allowed). Any other number should cause the line not to match.

Valid examples:

  • 1234
  • 1234 xxx
  • xxx 1234
  • 1234 1234
  • 1234 xxx 1234

Invalid examples:

  • 12341234
  • 12345
  • 1234xxx345
  • 1234 345
  • 1234xxx
  • xxx1234
  • 1234xxx1234

This is what I have used:

grep -E '^([^0-9]*1234)+[^0-9]*$' file.txt

But this command also outputs 12341234 as valid, how do I prevent that?

4
  • I edited to add more examples, as I understand it. Please re-edit if I have erred.
    – Sparhawk
    Sep 27, 2015 at 2:07
  • 1
    It seems to me that 12341234 has two instances of 1234 and no other numbers — so why is it invalid? Sep 27, 2015 at 2:07
  • 1
    @G-Man, I think because 12341234 constitutes a number that is different than 1234
    – iruvar
    Sep 27, 2015 at 3:00
  • @G-Man I guess because there needs to be non-digit characters between the two 1234? Presumably OP considers 12341234 as one number (in the first paragraph).
    – Sparhawk
    Sep 27, 2015 at 3:01

3 Answers 3

5

awk might be handier than grep at this. Set the field separator to runs of non-digit characters, then loop through fields and only print line if every non-empty field is exactly equal to 1234

awk -F'[^[:digit:]]+' '{
  for(i=1; i<=NF; ++i) 
  if (($i) && ($i != 1234)) next
  }; {print}' file.txt
1
  • Huh… clever! +1
    – Sparhawk
    Sep 27, 2015 at 3:03
4
grep -E '^[^0-9]*1234([^0-9]+1234)*[^0-9]*$' file.txt

Explanation

  • ^[^0-9]*1234: find the first match of 1234, which may be preceded by non-digit characters.
  • ([^0-9]+1234)*: there may be further iterations of 1234, but these must be separated from the first 1234 (and other 1234) by non-digit characters (hence use +).
  • [^0-9]*$: match the entire line (with $). There may be non-digit characters after the final 1234 (but not necessarily, hence *).

EDIT

If 1234 must be delimited by spaces (or be at the beginning or end of the line), then use

grep -E '^([^0-9]* )?1234(( [^0-9]*)? 1234)*( [^0-9]*)?$'

Explanation

  • ^([^0-9]* )?: there may be non-digit characters to start with, as long as they end with a space.
  • 1234: find the first (required) match of 1234.
  • (( [^0-9]*)? 1234)*: I'll work through the parentheses backwards. There may be (zero or more) further copies of 1234, but these must be preceded by a space, i.e. 1234. Before this space, there may be non-digit characters, which is fine as long as these are separated from the preceding copy of 1243 by another space, i.e.( [^0-9]*)?.
  • ( [^0-9]*)?$: there may be non-digit characters to end with, as long as they are preceded by a space.
17
  • ([^0-9]+1234)? is more correct in the case but grep -Ex '[^0-9]*(1234)[^0-9]*(|[^0-9]\1)' shorter.
    – Costas
    Sep 27, 2015 at 9:59
  • @Costas I don't quite understand where the first code goes. The second doesn't seem to work either. It would fail to match (e.g.) 1234 1234 1234.
    – Sparhawk
    Sep 27, 2015 at 10:15
  • As I understand 1234 1234 1234 shouldnot match just double like 1234 1234
    – Costas
    Sep 27, 2015 at 10:47
  • but if you'd like grep -Ex '[^0-9]*(1234)[^0-9]*([^0-9]\1)*'
    – Costas
    Sep 27, 2015 at 10:53
  • @Costas The question says "one or more instances". Also, your code now fails on '1234 1234x'
    – Sparhawk
    Sep 27, 2015 at 11:24
3

Search for the number either at the beginning of the (^) line or preceded by a non-digit ([^0-9]), and likewise at the end ($ or [^0-9]).

grep -E '(^|[^0-9])1234($|[^0-9])' file.txt

If you want to allow leading zeros, add 0* before the number.

grep -E '(^|[^0-9])0*1234($|[^0-9])' file.txt

If you also want to reject lines containing any sequence of one or more digits that isn't part of an occurrence of this particular number, then you're looking for lines consisting of sequences alternating one or more nondigits and that particular number, which is basically ([^0-9]+1234)*. The line may start and end with either the number or a nondigit, whereas internal sequences of nondigits may not be empty. Furthermore the line must contain the number at least once. Putting it all together:

grep -xE '[^0-9]*1234([^0-9]+1234)*[^0-9]*' file.txt
7
  • thanks, but i don't think the condition that no other numbers are allowed is being satisfied. for example: 20300 1234 is invalid but it matches with the command, how to handle this case?
    – KLMM
    Sep 27, 2015 at 0:33
  • @KLMM This finds the line since it does contain 1234. Do you want to exclude lines that contain any other number? That wasn't stated in your question. Sep 27, 2015 at 0:37
  • yes i mentioned in the question : "but no other numbers", all numbers except 1234 shouldn't be matched
    – KLMM
    Sep 27, 2015 at 0:40
  • nice, but this would fail the case when no numbers are present at all, for example: "hi how are you " would be matched with it but it should be exempted
    – KLMM
    Sep 27, 2015 at 0:57
  • 1
    @KLMM -x is a shortcut for putting ^ at the beginning of the regexp and $ at the end. If for some reason you don't like -x, use ^ and $ instead. There's no negation involved. Sep 27, 2015 at 1:06

You must log in to answer this question.

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