import os import platform import re from ..subprocess_code_interpreter import SubprocessCodeInterpreter class Shell(SubprocessCodeInterpreter): file_extension = 'sh' proper_name = 'Shell' def __init__(self): super().__init__() # Determine the start command based on the platform if platform.system() == 'Windows': self.start_cmd = 'cmd.exe' else: self.start_cmd = os.environ.get('SHELL', 'bash') def preprocess_code(self, code): return preprocess_shell(code) def line_postprocessor(self, line): return line def detect_active_line(self, line): if '##active_line' in line: return int(line.split('##active_line')[1].split('##')[0]) return None def detect_end_of_execution(self, line): return '##end_of_execution##' in line def preprocess_shell(code): """ Add active line markers Wrap in a try except (trap in shell) Add end of execution marker """ # Add commands that tell us what the active line is # if it's multiline, just skip this. soon we should make it work with multiline if not has_multiline_commands(code): code = add_active_line_prints(code) # Add end command (we'll be listening for this so we know when it ends) code += '\necho "##end_of_execution##"' return code def add_active_line_prints(code): """ Add echo statements indicating line numbers to a shell string. """ lines = code.split('\n') for index, line in enumerate(lines): # Insert the echo command before the actual line lines[index] = f'echo "##active_line{index + 1}##"\n{line}' return '\n'.join(lines) def has_multiline_commands(script_text): # Patterns that indicate a line continues continuation_patterns = [ r'\\$', # Line continuation character at the end of the line r'\|$', # Pipe character at the end of the line indicating a pipeline continuation r'&&\s*$', # Logical AND at the end of the line r'\|\|\s*$', # Logical OR at the end of the line r'<\($', # Start of process substitution r'\($', # Start of subshell r'{\s*$', # Start of a block r'\bif\b', # Start of an if statement r'\bwhile\b', # Start of a while loop r'\bfor\b', # Start of a for loop r'do\s*$', # 'do' keyword for loops r'then\s*$', # 'then' keyword for if statements ] # Check each line for multiline patterns for line in script_text.splitlines(): if any( re.search(pattern, line.rstrip()) for pattern in continuation_patterns): return True return False