| 1 | 1 | def grep(pattern: str, flags: str, files: list[str]) -> str: |
| 2 | | pass |
| 2 | """ |
| 3 | Search files for lines matching a search string and return all matching lines. |
| 4 | |
| 5 | Args: |
| 6 | pattern: The string to search for |
| 7 | flags: Space-separated string of flags (-n, -l, -i, -v, -x) |
| 8 | files: List of file paths to search in |
| 9 | |
| 10 | Returns: |
| 11 | String containing all matching lines, formatted according to flags |
| 12 | """ |
| 13 | # Parse flags |
| 14 | flag_list = flags.split() if flags else [] |
| 15 | line_numbers = '-n' in flag_list |
| 16 | files_only = '-l' in flag_list |
| 17 | case_insensitive = '-i' in flag_list |
| 18 | invert_match = '-v' in flag_list |
| 19 | exact_match = '-x' in flag_list |
| 20 | |
| 21 | # Edge Case: Empty pattern |
| 22 | if not pattern: |
| 23 | pattern = '' |
| 24 | |
| 25 | # Edge Case: Empty files list |
| 26 | if not files: |
| 27 | return '' |
| 28 | |
| 29 | # Edge Case: Case insensitive search |
| 30 | search_pattern = pattern.lower() if case_insensitive else pattern |
| 31 | |
| 32 | result_lines = [] |
| 33 | matching_files = set() |
| 34 | |
| 35 | # Edge Case: Multiple files |
| 36 | multiple_files = len(files) > 1 |
| 37 | |
| 38 | for file_path in files: |
| 39 | try: |
| 40 | # Edge Case: File not found |
| 41 | with open(file_path, 'r', encoding='utf-8') as file: |
| 42 | lines = file.readlines() |
| 43 | except FileNotFoundError: |
| 44 | # Edge Case: Handle file not found - skip the file |
| 45 | continue |
| 46 | |
| 47 | # Edge Case: Empty file |
| 48 | if not lines: |
| 49 | continue |
| 50 | |
| 51 | for line_num, line in enumerate(lines, 1): |
| 52 | # Determine if line matches |
| 53 | line_content = line.rstrip('\n') |
| 54 | |
| 55 | # Edge Case: Case insensitive comparison |
| 56 | compare_line = line_content.lower() if case_insensitive else line_content |
| 57 | |
| 58 | # Edge Case: Exact match vs substring match |
| 59 | if exact_match: |
| 60 | is_match = (compare_line == search_pattern) |
| 61 | else: |
| 62 | is_match = (search_pattern in compare_line) |
| 63 | |
| 64 | # Edge Case: Invert match |
| 65 | if invert_match: |
| 66 | is_match = not is_match |
| 67 | |
| 68 | if is_match: |
| 69 | # Edge Case: Files only output |
| 70 | if files_only: |
| 71 | matching_files.add(file_path) |
| 72 | # No need to process more lines for this file |
| 73 | break |
| 74 | else: |
| 75 | output_line = '' |
| 76 | # Edge Case: Multiple files - prepend filename |
| 77 | if multiple_files: |
| 78 | output_line += f'{file_path}:' |
| 79 | # Edge Case: Line numbers |
| 80 | if line_numbers: |
| 81 | output_line += f'{line_num}:' |
| 82 | output_line += line_content |
| 83 | result_lines.append(output_line) |
| 84 | |
| 85 | # Edge Case: Files only flag |
| 86 | if files_only: |
| 87 | return '\n'.join(sorted(matching_files)) |
| 88 | |
| 89 | # Edge Case: No matches |
| 90 | return '\n'.join(result_lines) |
| 91 | |
| 92 | # Handled Edge Cases: Empty pattern, empty files list, case insensitive search, multiple files, file not found, empty file, case insensitive comparison, exact match vs substring match, invert match, files only output, line numbers, no matches |