You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python自定义Shell命令解析Bug:带引号参数重复添加问题

自定义Shell命令解析Bug:带引号参数拆分错误

我正在用Python开发一款易用且高度可定制的自定义Shell,但命令行字符串解析出了问题。执行命令:

echo "Hello World!"

得到的参数列表是:

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

期望的正确输出应该是:

['Hello World!']

以下是我的命令解析代码(可独立运行):

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")

问题原因及修复方案

核心问题

  1. 重复添加参数:用for循环遍历拆分后的元素时,处理完引号包裹的多段内容后,没有跳过已经处理过的元素,导致后续被拼接的元素被再次单独添加到参数列表中。
  2. 循环边界越界:while pos <= len(split_string)会触发IndexError,后续判断split_string[pos]也会越界访问。
  3. 多余空格残留:拼接引号内元素时每个部分后加空格,会导致参数末尾多出不必要的空格。

修复后的代码

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 = []
    i = 1
    while i < len(split_string):
        part = split_string[i]
        if part.startswith('"'):
            res = part[1:]  # 移除开头引号
            pos = i + 1
            # 遍历直到找到结尾引号或元素耗尽
            while pos < len(split_string):
                current_part = split_string[pos]
                if current_part.endswith('"'):
                    res += " " + current_part[:-1]  # 移除结尾引号并拼接
                    pos += 1
                    break
                res += " " + current_part
                pos += 1
            else:
                # 未找到结尾引号,抛出错误
                console.print(f"[bold bright_red]ERR: 命令参数中存在未闭合的字符串[/bold bright_red]")
                return False
            args.append(res)
            i = pos  # 跳过已处理的元素
        else:
            args.append(part)
            i += 1

    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):
            file_path = os.path.join(commands_dir, f)
            if os.path.isfile(file_path):
                try:
                    cmd = imp.load_source(command_name, file_path)

                    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} 最多接受 {cmd.max_args} 个参数[/bold bright_red]")
                                return False
                        else:
                            console.print(f"[bold bright_red]ERR: {command_name} 至少需要 {cmd.min_args} 个参数[/bold bright_red]")
                            return False
                except Exception as e:
                    console.print(f"[bold red]ERR: 运行命令时发生错误[/bold red]")
                    return False

        console.print(f"[bold bright_red]ERR: 无效或未知命令 '{command_name}'[/bold bright_red]")
        return False
    else:
        raise FileNotFoundError("命令目录损坏或不存在")

关键修复点

  • 将for循环改为while循环,手动控制索引步进,避免重复处理已拼接的元素。
  • 直接移除首尾引号,避免全局替换引号导致的潜在问题(后续可扩展支持转义引号)。
  • 修正循环边界,消除越界访问风险。
  • 避免拼接后残留多余空格,保证参数内容准确。

内容的提问来源于stack exchange,提问作者Spooky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 18:30:44