From 386b9d5fe2ce23228372d0ea5722469e4ff10585 Mon Sep 17 00:00:00 2001 From: krock21ru Date: Sun, 9 Feb 2020 07:38:56 +0300 Subject: [PATCH 1/5] added grep builtin command --- bash_builtins.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/bash_builtins.py b/bash_builtins.py index f785005..0424436 100644 --- a/bash_builtins.py +++ b/bash_builtins.py @@ -107,12 +107,59 @@ def exit_function(args, stdin, stdout): os.kill(os.getpid(), signal.SIGTERM) +def grep_function(args, stdin, stdout): + """ + Match lines in FILE or stdin with PATTERN + :param args: arguments + :param stdin: input file descriptor + :param stdout: output file descriptor + :return: nothing + """ + parser = argparse.ArgumentParser(prog="grep", description='print lines matching a pattern') + parser.add_argument("-i", action='store_true', + help='Ignore case distinctions, so that characters that differ only in case match each other.') + parser.add_argument("-w", action='store_true', + help='Select only those lines containing matches that form whole words.') + parser.add_argument("-A", nargs='?', action='store', default=0, + help='Print A lines after matched lines') + parser.add_argument("PATTERN", help='Regular expression to be matched in line') + parser.add_argument("FILE", nargs='?', help='Path to file to scan for') + parsed_args = vars(parser.parse_args(args)) + + pattern = parsed_args.get("PATTERN") + if parsed_args.get("-w"): + pattern = r'\b' + pattern + r'\b' + flags = 0 + if parsed_args.get('-i'): + flags |= re.IGNORECASE + + regexp = re.compile(pattern, flags) + + fin = None + if parsed_args.get('FILE'): + fin = open(parsed_args.get('FILE'), 'r', closefd=True) + else: + fin = open(stdin, 'r', closefd=False) + + fout = open(stdout, 'w', closefd=False) + + after_parameter = int(parsed_args.get('A')) + remaining_after = 0 + for line in fin: + if regexp.search(line): + remaining_after = after_parameter + 1 + if remaining_after > 0: + remaining_after -= 1 + fout.write(line) + + command_to_function = { "cat": cat_function, "echo": echo_function, "wc": wc_function, "pwd": pwd_function, - "exit": exit_function + "exit": exit_function, + "grep": grep_function } From 086cf9b3e87441ab9134d04c322b0175791b2885 Mon Sep 17 00:00:00 2001 From: krock21ru Date: Sun, 9 Feb 2020 08:07:15 +0300 Subject: [PATCH 2/5] added unit tests for grep, updated wc --- bash_builtins.py | 31 ++++++++-------- unittests.py | 97 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 18 deletions(-) diff --git a/bash_builtins.py b/bash_builtins.py index 0424436..df5b66b 100644 --- a/bash_builtins.py +++ b/bash_builtins.py @@ -65,20 +65,18 @@ def wc_function(args, stdin, stdout): parsed_args = parser.parse_args(args) filepath = vars(parsed_args).get("FILE") fout = open(stdout, "w", closefd=False) # we should not close stdout + fin = None if filepath: # READ FILEPATH fin = open(filepath, "r") - lines = fin.readlines() - words_count = sum([len(line.split()) for line in lines]) - file_size = os.path.getsize(filepath) - fout.write(str(len(lines)) + " " + str(words_count) + " " + str(file_size)) - fin.close() else: # READ STDIN fin = open(stdin, "r", closefd=False) + + with fin: lines = fin.readlines() words_count = sum([len(line.split()) for line in lines]) - file_size = sum([len(line) for line in lines]) + file_size = sum([len(line) for line in lines]) # TODO maybe replace with os.path.getsize() for files fout.write(str(len(lines)) + " " + str(words_count) + " " + str(file_size)) @@ -127,10 +125,10 @@ def grep_function(args, stdin, stdout): parsed_args = vars(parser.parse_args(args)) pattern = parsed_args.get("PATTERN") - if parsed_args.get("-w"): + if parsed_args.get("w"): pattern = r'\b' + pattern + r'\b' flags = 0 - if parsed_args.get('-i'): + if parsed_args.get('i'): flags |= re.IGNORECASE regexp = re.compile(pattern, flags) @@ -143,14 +141,15 @@ def grep_function(args, stdin, stdout): fout = open(stdout, 'w', closefd=False) - after_parameter = int(parsed_args.get('A')) - remaining_after = 0 - for line in fin: - if regexp.search(line): - remaining_after = after_parameter + 1 - if remaining_after > 0: - remaining_after -= 1 - fout.write(line) + with fin, fout: + after_parameter = int(parsed_args.get('A')) + remaining_after = 0 + for line in fin: + if regexp.search(line): + remaining_after = after_parameter + 1 + if remaining_after > 0: + remaining_after -= 1 + fout.write(line) command_to_function = { diff --git a/unittests.py b/unittests.py index 5817c57..607030d 100644 --- a/unittests.py +++ b/unittests.py @@ -29,8 +29,8 @@ def test_cat(self): file = tempfile.NamedTemporaryFile("w", delete=False) filename = file.name file.write(self.TEST_STRING) - read_pipe, write_pipe = os.pipe() file.close() + read_pipe, write_pipe = os.pipe() thread = bash_builtins.simple_interprete_single_builtin_command(["cat", file.name], stdin=sys.stdin.fileno(), @@ -55,8 +55,8 @@ def test_wc(self): file = tempfile.NamedTemporaryFile("w", delete=False) filename = file.name file.write(self.TEST_STRING) - read_pipe, write_pipe = os.pipe() file.close() + read_pipe, write_pipe = os.pipe() thread = bash_builtins.simple_interprete_single_builtin_command(["wc", file.name], stdin=sys.stdin.fileno(), @@ -93,6 +93,99 @@ def test_pwd(self): with open(read_pipe, "r") as fin: self.assertEqual(fin.read(), os.getcwd()) + def test_grep(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) + with open(write_stdin_pipe, "w") as finout: + finout.writelines( + ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), ['abc\n', 'abcd\n', 'aabcd\n', 'aabc\n', 'abc']) + + def test_grep_with_file(self): + file = tempfile.NamedTemporaryFile("w", delete=False) + filename = file.name + file.writelines(["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) + file.close() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "abc", filename], + stdin=sys.stdin.fileno(), + stdout=write_pipe) + thread.wait() + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), ['abc\n', 'abcd\n', 'aabcd\n', 'aabc\n', 'abc']) + os.remove(filename) + + def test_grep_i(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-i", "aBc"], + stdin=read_stdin_pipe, + stdout=write_pipe) + with open(write_stdin_pipe, "w") as finout: + finout.writelines( + ["a\n", "ab\n", "abc\n", "ABcd\n", "dCbA\n", "aaB\n", "aabCd\n", "aABC\n", "aBacBcA\n", "AbC"]) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), ['abc\n', 'ABcd\n', 'aabCd\n', 'aABC\n', 'AbC']) + + def test_grep_w(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-w", "abc def ghi"], + stdin=read_stdin_pipe, + stdout=write_pipe) + with open(write_stdin_pipe, "w") as finout: + finout.writelines( + ["a\n", "abc def ghi\n", "abc def ghij\n", "abc abc def ghi ghi\n", "aabc def ghi\n", + " abc def ghi \n"]) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), ['abc def ghi\n', 'abc abc def ghi ghi\n', ' abc def ghi \n']) + + def test_grep_A1(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-A", "1", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) + with open(write_stdin_pipe, "w") as finout: + finout.writelines( + ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), + ['abc\n', 'abcd\n', 'dcba\n', 'aabcd\n', 'aabc\n', 'abacbca\n', 'abc']) + + def test_grep_A2(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-A", "2", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) + with open(write_stdin_pipe, "w") as finout: + finout.writelines( + ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), + ['abc\n', 'abcd\n', 'dcba\n', 'aab\n', 'aabcd\n', 'aabc\n', 'abacbca\n', 'abc']) + class TestTokenize(unittest.TestCase): def test_shlex_tokenize(self): From fffb779bd70d1de63947e019bf207491d641daf0 Mon Sep 17 00:00:00 2001 From: krock21ru Date: Sun, 9 Feb 2020 08:13:39 +0300 Subject: [PATCH 3/5] fixed unittests for wc on windows --- unittests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests.py b/unittests.py index 607030d..31792cc 100644 --- a/unittests.py +++ b/unittests.py @@ -65,7 +65,7 @@ def test_wc(self): os.close(write_pipe) with open(read_pipe, "r") as fin: file_content = fin.read() - self.assertEqual(file_content, self.TEST_STRING_WC + " " + str(os.path.getsize(filename))) + self.assertEqual(file_content, self.TEST_STRING_WC + " " + str(len(self.TEST_STRING))) os.remove(filename) def test_interactive_wc(self): From b75052c8c6f1ae8bfb7fea60911d8d0c3a43f9c0 Mon Sep 17 00:00:00 2001 From: krock21ru Date: Mon, 30 Mar 2020 07:05:44 +0300 Subject: [PATCH 4/5] grep update, added check for -A parameter < 0 --- bash_builtins.py | 38 ++++++++++++++++++++----------------- unittests.py | 49 ++++++++++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/bash_builtins.py b/bash_builtins.py index e50a8d7..8c34874 100644 --- a/bash_builtins.py +++ b/bash_builtins.py @@ -2,7 +2,6 @@ import os import argparse import threading -import signal import globals @@ -128,15 +127,6 @@ def grep_function(args, stdin, stdout): parser.add_argument("FILE", nargs='?', help='Path to file to scan for') parsed_args = vars(parser.parse_args(args)) - pattern = parsed_args.get("PATTERN") - if parsed_args.get("w"): - pattern = r'\b' + pattern + r'\b' - flags = 0 - if parsed_args.get('i'): - flags |= re.IGNORECASE - - regexp = re.compile(pattern, flags) - fin = None if parsed_args.get('FILE'): fin = open(parsed_args.get('FILE'), 'r', closefd=True) @@ -146,14 +136,28 @@ def grep_function(args, stdin, stdout): fout = open(stdout, 'w', closefd=False) with fin, fout: + after_parameter = int(parsed_args.get('A')) - remaining_after = 0 - for line in fin: - if regexp.search(line): - remaining_after = after_parameter + 1 - if remaining_after > 0: - remaining_after -= 1 - fout.write(line) + + if after_parameter < 0: + fout.write("grep error: -A parameter shouldn't be negative") + else: + pattern = parsed_args.get("PATTERN") + if parsed_args.get("w"): + pattern = r'\b' + pattern + r'\b' + flags = 0 + if parsed_args.get('i'): + flags |= re.IGNORECASE + + regexp = re.compile(pattern, flags) + + remaining_after = 0 + for line in fin: + if regexp.search(line): + remaining_after = after_parameter + 1 + if remaining_after > 0: + remaining_after -= 1 + fout.write(line) command_to_function = { diff --git a/unittests.py b/unittests.py index 295666b..61fa33b 100644 --- a/unittests.py +++ b/unittests.py @@ -89,9 +89,9 @@ def test_pwd(self): def test_grep(self): read_stdin_pipe, write_stdin_pipe = os.pipe() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "abc"], - stdin=read_stdin_pipe, - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) with open(write_stdin_pipe, "w") as finout: finout.writelines( ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) @@ -107,9 +107,9 @@ def test_grep_with_file(self): file.writelines(["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) file.close() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "abc", filename], - stdin=sys.stdin.fileno(), - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "abc", filename], + stdin=sys.stdin.fileno(), + stdout=write_pipe) thread.wait() os.close(write_pipe) with open(read_pipe, "r") as fin: @@ -119,9 +119,9 @@ def test_grep_with_file(self): def test_grep_i(self): read_stdin_pipe, write_stdin_pipe = os.pipe() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-i", "aBc"], - stdin=read_stdin_pipe, - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "-i", "aBc"], + stdin=read_stdin_pipe, + stdout=write_pipe) with open(write_stdin_pipe, "w") as finout: finout.writelines( ["a\n", "ab\n", "abc\n", "ABcd\n", "dCbA\n", "aaB\n", "aabCd\n", "aABC\n", "aBacBcA\n", "AbC"]) @@ -134,9 +134,9 @@ def test_grep_i(self): def test_grep_w(self): read_stdin_pipe, write_stdin_pipe = os.pipe() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-w", "abc def ghi"], - stdin=read_stdin_pipe, - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "-w", "abc def ghi"], + stdin=read_stdin_pipe, + stdout=write_pipe) with open(write_stdin_pipe, "w") as finout: finout.writelines( ["a\n", "abc def ghi\n", "abc def ghij\n", "abc abc def ghi ghi\n", "aabc def ghi\n", @@ -150,9 +150,9 @@ def test_grep_w(self): def test_grep_A1(self): read_stdin_pipe, write_stdin_pipe = os.pipe() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-A", "1", "abc"], - stdin=read_stdin_pipe, - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "-A", "1", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) with open(write_stdin_pipe, "w") as finout: finout.writelines( ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) @@ -166,9 +166,9 @@ def test_grep_A1(self): def test_grep_A2(self): read_stdin_pipe, write_stdin_pipe = os.pipe() read_pipe, write_pipe = os.pipe() - thread = bash_builtins.simple_interprete_single_builtin_command(["grep", "-A", "2", "abc"], - stdin=read_stdin_pipe, - stdout=write_pipe) + thread = bash_builtins.simple_interpret_single_builtin_command(["grep", "-A", "2", "abc"], + stdin=read_stdin_pipe, + stdout=write_pipe) with open(write_stdin_pipe, "w") as finout: finout.writelines( ["a\n", "ab\n", "abc\n", "abcd\n", "dcba\n", "aab\n", "aabcd\n", "aabc\n", "abacbca\n", "abc"]) @@ -179,6 +179,19 @@ def test_grep_A2(self): self.assertListEqual(fin.readlines(), ['abc\n', 'abcd\n', 'dcba\n', 'aab\n', 'aabcd\n', 'aabc\n', 'abacbca\n', 'abc']) + def test_grep_incorrect_parameters(self): + read_stdin_pipe, write_stdin_pipe = os.pipe() + read_pipe, write_pipe = os.pipe() + thread = bash_builtins.simple_interpret_single_builtin_command( + ["grep", "-A", "-1", "param", "bash_builtins.py"], + stdin=read_stdin_pipe, + stdout=write_pipe) + thread.wait() + os.close(read_stdin_pipe) + os.close(write_pipe) + with open(read_pipe, "r") as fin: + self.assertListEqual(fin.readlines(), ["grep error: -A parameter shouldn't be negative"]) + class TestTokenize(unittest.TestCase): def test_shlex_tokenize(self): From 7a04f7f168721583ca4609616e1e69b11905fbca Mon Sep 17 00:00:00 2001 From: krock21ru Date: Mon, 4 May 2020 02:00:30 +0300 Subject: [PATCH 5/5] fix --- bash_builtins.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bash_builtins.py b/bash_builtins.py index 8c34874..4af8891 100644 --- a/bash_builtins.py +++ b/bash_builtins.py @@ -1,5 +1,6 @@ import re import os +import sys import argparse import threading import globals @@ -121,7 +122,7 @@ def grep_function(args, stdin, stdout): help='Ignore case distinctions, so that characters that differ only in case match each other.') parser.add_argument("-w", action='store_true', help='Select only those lines containing matches that form whole words.') - parser.add_argument("-A", nargs='?', action='store', default=0, + parser.add_argument("-A", nargs='?', action='store', default=0, type=int, help='Print A lines after matched lines') parser.add_argument("PATTERN", help='Regular expression to be matched in line') parser.add_argument("FILE", nargs='?', help='Path to file to scan for') @@ -129,7 +130,11 @@ def grep_function(args, stdin, stdout): fin = None if parsed_args.get('FILE'): - fin = open(parsed_args.get('FILE'), 'r', closefd=True) + try: + fin = open(parsed_args.get('FILE'), 'r', closefd=True) + except FileNotFoundError: + print("File is not accessible", file=sys.stderr) + return else: fin = open(stdin, 'r', closefd=False)