Issue
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 strings 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")
Solution
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 Answer Checked By - Marie Seifert (WPSolving Admin)