def split_bash_commands(commands): # States NORMAL = 0 IN_SINGLE_QUOTE = 1 IN_DOUBLE_QUOTE = 2 IN_HEREDOC = 3 state = NORMAL heredoc_trigger = None result = [] current_command: list[str] = [] i = 0 while i < len(commands): char = commands[i] if state == NORMAL: if char == "'": state = IN_SINGLE_QUOTE elif char == '"': state = IN_DOUBLE_QUOTE elif char == '\\': # Check if this is escaping a newline if i + 1 < len(commands) and commands[i + 1] == '\n': i += 1 # Skip the newline # Continue with the next line as part of the same command i += 1 # Move to the first character of the next line continue elif char == '\n': if not heredoc_trigger and current_command: result.append(''.join(current_command).strip()) current_command = [] elif char == '<' and commands[i : i + 2] == '<<': # Detect heredoc state = IN_HEREDOC i += 2 # Skip '<<' while commands[i] == ' ': i += 1 start = i while commands[i] not in [' ', '\n']: i += 1 heredoc_trigger = commands[start:i] current_command.append(commands[start - 2 : i]) # Include '<<' continue # Skip incrementing i at the end of the loop current_command.append(char) elif state == IN_SINGLE_QUOTE: current_command.append(char) if char == "'" and commands[i - 1] != '\\': state = NORMAL elif state == IN_DOUBLE_QUOTE: current_command.append(char) if char == '"' and commands[i - 1] != '\\': state = NORMAL elif state == IN_HEREDOC: current_command.append(char) if ( char == '\n' and heredoc_trigger and commands[i + 1 : i + 1 + len(heredoc_trigger) + 1] == heredoc_trigger + '\n' ): # Check if the next line starts with the heredoc trigger followed by a newline i += ( len(heredoc_trigger) + 1 ) # Move past the heredoc trigger and newline current_command.append( heredoc_trigger + '\n' ) # Include the heredoc trigger and newline result.append(''.join(current_command).strip()) current_command = [] heredoc_trigger = None state = NORMAL continue i += 1 # Add the last command if any if current_command: result.append(''.join(current_command).strip()) # Remove any empty strings from the result result = [cmd for cmd in result if cmd] return result