Valid Phone Numbers
EasyPrompt
Given a text file file.txt that contains a list of phone numbers (one per line), write a one-liner bash script to print all valid phone numbers.
You may assume that a valid phone number must appear in one of the following two formats: (xxx) xxx-xxxx or xxx-xxx-xxxx. (x means a digit)
You may also assume each line in the text file must not contain leading or trailing white spaces.
Example:
Assume that file.txt has the following content:
987-123-4567
123 456 7890
(123) 456-7890Your script should output the following valid phone numbers:
987-123-4567
(123) 456-7890Approaches
4 approaches with complexity analysis and trade-offs.
This approach uses a while loop in bash to read the file line by line. For each line, it uses the [[ ... =~ ... ]] conditional expression to test if the line matches the required regular expression for a valid phone number. If it matches, the line is printed to standard output.
Algorithm
-
- Start a
whileloop to readfile.txtline by line into a variableline.
- Start a
-
- For each
line, use the[[ ... =~ ... ]]operator to perform a regex match.
- For each
-
- The regex checks for two patterns separated by
|(OR):\([0-9]{3}\) [0-9]{3}-[0-9]{4}or[0-9]{3}-[0-9]{3}-[0-9]{4}.
- The regex checks for two patterns separated by
-
- The entire pattern is anchored with
^and$to ensure the whole line matches.
- The entire pattern is anchored with
-
- If the line matches the regex, print the line.
-
- Continue until all lines are processed.
Walkthrough
The script reads file.txt line by line using while read -r line. The -r option prevents backslash interpretation. Inside the loop, an if statement checks the line against a regular expression. The regex ^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$ is used to validate the two possible phone number formats. The ^ and $ anchors ensure that the entire line must match one of the formats. If the condition is true, echo "$line" prints the valid phone number. This can be written as a one-liner using the && operator.
while read -r line; do [[ "$line" =~ ^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$ ]] && echo "$line"; done < file.txtComplexity
Time
O(N * L)
Space
O(L)
Trade-offs
Pros
No external utilities are needed; it's a pure bash solution.
Cons
Significantly slower than using dedicated text-processing tools like
grep,sed, orawk, especially for large files.The one-liner syntax can be less readable.
This approach uses awk, a powerful pattern-scanning and processing language. awk can filter lines based on a regular expression. By default, awk prints any line that matches the provided pattern.
Algorithm
-
- Invoke
awkwith a script that consists of a single pattern-action rule.
- Invoke
-
- The pattern is the regular expression
/^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$/.
- The pattern is the regular expression
-
- The action is omitted, which defaults to
{ print $0 }(print the current line).
- The action is omitted, which defaults to
-
awkreadsfile.txtline by line.
-
- For each line, it tests if it matches the pattern.
-
- If a match is found, the default action is executed, printing the line.
Walkthrough
awk processes the input file line by line. The core of the command is awk '/pattern/' file.txt. The pattern is a regular expression enclosed in slashes: /^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$/. awk evaluates this pattern for each line. If the pattern matches the line, awk's default action is to print the entire line (print $0), which is exactly what is needed. The regex is anchored with ^ and $ to match the whole line.
awk '/^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$/' file.txt
Complexity
Time
O(N * L)
Space
O(L)
Trade-offs
Pros
Concise and readable one-liner.
awkis a standard and powerful tool available on most Unix-like systems.
Cons
Generally slightly slower than
sedorgrepfor simple line filtering becauseawkis a more feature-rich tool.
This approach utilizes sed (Stream Editor), a classic Unix utility for parsing and transforming text. We can use sed to filter and print only the lines that match the specified pattern.
Algorithm
-
- Invoke
sedwith the-n(no-print) and-r(extended-regex) flags.
- Invoke
-
- Provide the script
'/pattern/p'.
- Provide the script
-
sedreadsfile.txtline by line into its pattern space.
-
- For each line, it checks if the content of the pattern space matches the regex
^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$.
- For each line, it checks if the content of the pattern space matches the regex
-
- If there is a match, the
pcommand is executed, which prints the pattern space (the current line).
- If there is a match, the
-
- If there is no match, nothing is printed for that line (due to the
-nflag).
- If there is no match, nothing is printed for that line (due to the
Walkthrough
The sed command is used with the -n option to suppress the default behavior of printing every line. The -r (or -E on BSD/macOS) option is used to enable extended regular expressions, which makes the pattern more readable. The script provided to sed is '/pattern/p'. This tells sed to print (p) only the lines that match the pattern. The pattern is the same regex used in the other approaches: ^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$. The command effectively filters the input file, printing only the valid phone numbers.
sed -n -r '/^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$/p' file.txt
Complexity
Time
O(N * L)
Space
O(L)
Trade-offs
Pros
Very efficient and concise.
sedis a standard, powerful tool.
Cons
The syntax (
-nand/p) can be slightly less intuitive thangrepfor beginners.The flag for extended regex can differ between systems (
-rfor GNU sed,-Efor BSD sed).
The most direct and typically most efficient approach is to use grep, the standard Unix utility for finding lines that match a regular expression.
Algorithm
-
- Invoke
grepwith the-Eflag for extended regular expressions.
- Invoke
-
- Provide the regex pattern to match. The pattern
^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$combines the two valid formats with an OR (|) and anchors the match to the beginning (^) and end ($) of the line.
- Provide the regex pattern to match. The pattern
-
- Specify the input file,
file.txt.
- Specify the input file,
-
grepreads the file line by line, applies the regex, and prints any matching lines to standard output.
Walkthrough
grep is designed specifically for this task: searching for patterns in text. We use the -E flag (or egrep) to enable extended regular expressions, which allows for features like | (OR) and () (grouping) without backslashes. The regular expression ^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$ is passed to grep. The ^ and $ anchors are crucial to ensure that the entire line matches one of the valid phone number formats, preventing partial matches within a longer, invalid line. grep will then read file.txt and print only the lines that fully match this pattern. An alternative, slightly more compact regex can also be used: ^(\([0-9]{3}\) |[0-9]{3}-)[0-9]{3}-[0-9]{4}$.
grep -E '^(\([0-9]{3}\) [0-9]{3}-[0-9]{4}|[0-9]{3}-[0-9]{3}-[0-9]{4})$' file.txt
Complexity
Time
O(N * L)
Space
O(L)
Trade-offs
Pros
Most idiomatic and efficient solution for this task.
The command's purpose is immediately clear.
grepis universally available on Unix-like systems.
Cons
None for this particular problem.
Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.