Print only valid phone numbers
Problem statement
A support export contains one phone number per line, in mixed formats. Print only the lines that are a valid number in one of two formats: xxx-xxx-xxxx or (xxx) xxx-xxxx, where x is a digit. Anything else, including extra spaces or missing separators, must be dropped.
file.txt
987-123-4567123 456 7890(123) 456-7890(555)123-4567555-1234-567(212) 555-0199415-555-0123 ext 9Examples
Example 1
Input: Run the command on `file.txt`.
Output: 987-123-4567
(123) 456-7890
(212) 555-0199
Explanation: (555)123-4567 has no space after the area code, 555-1234-567 has the wrong grouping, and the last line has trailing text.
Hints
Approach
Optimal
grep -E enables extended regular expressions. The pattern splits into two parts. The prefix ([0-9]{3}-|\([0-9]{3}\) ) is an alternation: either three digits and a dash, or three digits wrapped in literal parentheses followed by one space. The literal parentheses are escaped as \( and \) because in ERE a bare ( starts a group. The suffix [0-9]{3}-[0-9]{4} is common to both formats. ^ and $ anchor the match to the whole line, which is what rejects 415-555-0123 ext 9.
grep -E '^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}$' file.txtFollow-up questions
- How would you do the same with sed or awk instead of grep?
- How would you print the invalid lines with their line numbers? (
grep -vnE ...)
Frequently asked questions
Plain grep uses basic regular expressions, where {3}, | and grouping ( ) must be written as \{3\}, \| and \( \), and a bare ( is a literal. Mixing the two styles is the most common cause of a regex that 'matches nothing'. Use -E (or egrep) and escape only the characters you want literally.