When parsing strings, code is adding string contents and last part of string?

Question:

I’m making my own shell in Python and trying to make it as user-friendly and customizable as possible. I’m having an issue when parsing command line strngs though..

I have a list of args to be passed to the command.

When I try:

echo "Hello World!"

My args look like this:

['Hello World!', 'World!"']

And not this:

['Hello World!']

Here is my command parser:

import os
import imp

from rich.console import Console


def handle_command(console: Console, command: str):
    split_string = command.split(" ")
    command_name = split_string[0]
    command_name = command_name.lower()

    if command_name == "":
        return True

    args = []

    for i in range(1, len(split_string)):
        if split_string[i].startswith('"'):
            res = ""

            pos = i

            while pos <= len(split_string):
                try:
                    res += f"{split_string[pos]} "
                except IndexError:
                    console.print(f"[bold bright_red]ERR: Unterminated string in command arguments[/bold bright_red]")
                    return False
                if split_string[pos].endswith('"'):
                    break
                pos += 1

            if pos == len(split_string):
                if not split_string[pos].endswith('"'):
                    console.print(f"[bold bright_red]ERR: Unterminated string in command arguments[/bold bright_red]")
                    return False

            res = res.replace('"', "")

            args.append(res)
            continue

        args.append(split_string[i])

    commands_dir = os.path.join(os.getcwd(), "shells/Fresh/commands")
    
    if os.path.exists(commands_dir) and os.path.isdir(commands_dir):
        for f in os.listdir(commands_dir):
            if os.path.isfile(os.path.join(commands_dir, f)):
                #try:
                    cmd = imp.load_source(command_name, os.path.join(commands_dir, f))

                    if cmd.name == command_name:
                        if cmd.min_args <= len(args):
                            if cmd.max_args >= len(args):
                                cmd.run(console, args)

                                return True
                            else:
                                console.print(f"[bold bright_red]ERR: {command_name} takes a maximum of {cmd.max_args} arguments[/bold bright_red]")
                                return False
                        else:
                            console.print(f"[bold bright_red]ERR: {command_name} requires atleast {cmd.min_args} argument[/bold bright_red]")
                            return False
                #except Exception as e:
                    #console.print(f"[bold red]ERR: An error occured while running this command[/bold red]")
                    #return False

        console.print(f"[bold bright_red]ERR: Invalid or unkown command '{command_name}'[/bold bright_red]")
        return False
    else:
        raise FileNotFoundError("Commands directory is corrupted or does not exist")

Any help would be appreciated!

Asked By: Spooky

||

Answers:

In your example,

split_string = ["echo", '"Hello', 'World!"']

So:

for i in range(1, len(split_string)):

will execute it 2 times (one for Hello and one for World)

It will first add Hello world! to args than continue and this time will add ‘World!"’ because it doesnt fit the

if split_string[i].startswith('"')

I think the problem here is mainly the way the tokens are interpreted. Generally, just splitting a string by spaces is not enough to parse a command correctly when it contains a string

I think you should take a look at what a Lexer is

Answered By: TKirishima

You are splitting the command with split_string = command.split(" "), so you will get three items ['echo', '"Hello', 'World!"'].

You iterate starting at index 1 for i in range(1, len(split_string)):.

Your code goes through and correctly parses `Hello World!’, but the issue is you add this line args.append(split_string[i]) after your while loop.

Because of this it will append 'World!"' to the result, because if split_string[i].startswith('"'): will return False.

Answered By: Sterling
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.