news 2026/8/16 22:54:11

LLM驱动CAD智能绘图:四种技术路线深度解析与工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM驱动CAD智能绘图:四种技术路线深度解析与工程实践

1. 从“对话”到“绘图”:当LLM试图理解CAD

最近几个月,我身边搞机械设计、建筑制图的朋友,还有几个做AI应用开发的老同事,都在讨论同一个话题:能不能让大语言模型(LLM)直接去操作CAD软件?比如,你对着电脑说“画一个直径50mm的圆,圆心在坐标原点”,或者“把左边那堵墙向右移动200mm”,模型就能理解并自动在AutoCAD、中望CAD(ZWCAD)或ZW3D里执行这些命令。

这个想法听起来很酷,但实操起来,你会发现这根本不是简单的“接口调用”问题。它背后是一道横跨自然语言理解、几何逻辑、软件工程和自动化控制的巨大鸿沟。我花了近两个月时间,把市面上能想到的技术路线都摸了一遍,从最直接的COM接口调用,到试图让LLM“理解”DXF文件结构,再到用Agent框架去模拟人类操作。每一条路都踩过坑,也都有其独特的适用场景和天花板。

今天,我就把这四条主流技术路线的核心逻辑、实现成本、潜在风险以及我个人的取舍建议,毫无保留地分享出来。无论你是想自己动手集成一个智能绘图助手,还是仅仅好奇这背后的技术实现,这篇文章都能给你一个清晰的路线图。我们会避开那些空洞的理论,直接深入到代码、配置和那些文档里不会写的“坑”里。

2. 路线一:COM接口直连——最直接,也最“脆弱”

这是大多数人首先想到的方案,也是理论上最“正统”的路径。以AutoCAD为例,它提供了完善的COM(Component Object Model)自动化接口。这意味着,你可以用Python、C#甚至VBScript等支持COM调用的语言,编写脚本,像遥控器一样远程控制CAD软件的一切操作:打开文件、创建图层、绘制图形、修改属性、执行保存。

2.1 核心原理与基础操作

COM接口的本质,是CAD软件(作为COM服务器)暴露出一系列对象(如AcadApplication,AcadDocument,AcadLine)和方法(如AddLine,Move),外部程序(作为COM客户端)通过创建这些对象的实例并进行方法调用,来实现控制。

一个最简单的Python示例,使用pyautocad库(它是对AutoCAD COM接口的Python封装)来画一条线:

import win32com.client # 连接到正在运行的AutoCAD实例,如果没运行则启动一个新实例 acad = win32com.client.Dispatch("AutoCAD.Application") doc = acad.ActiveDocument modelspace = doc.ModelSpace # 定义起点和终点坐标 start_point = (0, 0, 0) end_point = (100, 100, 0) # 在模型空间添加一条直线 line = modelspace.AddLine(start_point, end_point) doc.Regenerate(True) # 重生成图形,显示新画的线 print(f"已创建直线,句柄为:{line.Handle}")

这段代码的逻辑非常清晰:获取应用->获取活动文档->获取模型空间->调用AddLine方法。理论上,LLM只需要学会生成这样的代码片段,就能操作CAD。

2.2 为什么这条路“脆弱”?

然而,理想很丰满,现实很骨感。让LLM生成正确的COM调用代码,面临几个几乎无解的难题:

  1. 状态管理的复杂性:CAD操作是高度状态依赖的。比如,你想“移动那个圆”,LLM生成的代码必须能先精准选中“那个圆”。在COM接口中,这通常需要通过遍历图形数据库、按句柄(Handle)、图层、颜色或坐标范围来筛选对象。让LLM理解并生成这种带有复杂查询和上下文状态的代码,极其困难。它可能知道要调用object.Move方法,但根本构造不出正确的object引用。

  2. 错误处理的深渊:COM调用非常容易失败。CAD软件未启动、命令正在执行、对象不存在、坐标无效……任何一个小错误都会导致整个脚本崩溃。LLM生成的代码缺乏健壮的错误处理逻辑(如try...except块、对象存在性检查),一个点出错,全盘皆输。

  3. “黑盒”交互与反馈缺失:这是最致命的一点。LLM生成一段代码并执行后,它无法直接“看到”执行结果。圆真的移动了吗?移动的位置对吗?有没有和其他图形干涉?COM接口本身不提供丰富的、可供LLM理解的语义化反馈。LLM处在一个“盲操”的状态,无法根据结果进行验证和调整,这与LLM基于对话和上下文理解的核心能力背道而驰。

注意:网络上很多关于“COM Surrogate”错误(如“文件已在 COM Surrogate 中打开”)的讨论,常常源于COM接口调用不当或CAD软件本身的不稳定。在自动化频繁交互的场景下,这类问题会被急剧放大。

我的实操心得:COM接口直连方案,只适用于任务极度简单、流程完全固定的场景。例如,每天定时批量将某种特定图块插入到几百个图纸的固定位置。这种场景下,你可以编写极其健壮的脚本,处理好所有异常。但如果你想实现“灵活的自然语言指挥”,这条路从设计上就走不通,它把最复杂的逻辑(状态管理、错误恢复)完全抛给了不擅长此道的LLM。

3. 路线二:解析与生成中间格式——绕过软件,直击数据

既然直接控制软件这么难,那不如换个思路:不让LLM直接操作CAD软件,而是让它理解和生成CAD软件能识别的文件。最常见的中间格式就是DXF(Drawing Exchange Format)和DWG(虽然DWG是二进制,但有其规范)。

3.1 DXF:可读的“图纸源代码”

DXF是一种文本文件(也有二进制格式,但ASCII格式更常用),它用特定的格式描述了图纸中的所有实体、图层、线型等信息。你可以把它想象成CAD图纸的“源代码”。一个描述一条直线的DXF片段大致如下:

0 SECTION 2 ENTITIES 0 LINE 8 // 图层名 0 // 图层0 10 // 起点X坐标 0.0 20 // 起点Y坐标 0.0 30 // 起点Z坐标 0.0 11 // 终点X坐标 100.0 21 // 终点Y坐标 100.0 31 // 终点Z坐标 0.0 0 ENDSEC 0 EOF

这条技术路线的设想是:训练或引导LLM,使其能够将自然语言指令(如“在图层‘Wall’上,从(0,0)到(5000,0)画一条红线”)转换为符合DXF标准的文本代码。

3.2 可行性分析与巨大挑战

这条路听起来比COM接口更“底层”,也更“干净”,因为它剥离了GUI和软件状态,只处理数据。但它的挑战同样巨大:

  1. 格式的严格性与复杂性:DXF格式极其繁琐和严格。它有特定的组码(如0表示实体类型或段开始,8表示图层),严格的段落结构(SECTION...ENDSEC)。LLM生成的文本必须百分百符合规范,一个换行符或组码错误就会导致整个文件无法被CAD软件打开。让LLM保证这种级别的格式正确性,需要极其精确的提示工程(Prompt Engineering)或针对性的微调(Fine-tuning),成本很高。

  2. 缺乏高级语义:DXF是低级的图形描述。像“阵列这个图形”、“对这个边界进行偏移”、“修剪这两条线的交叉部分”这类高级编辑操作,在DXF中对应着非常复杂的一系列基本实体创建、删除和修改操作。让LLM从“阵列”这个指令,推理并生成正确的、成百上千行的DXF代码,几乎是一个“AI完全体”级别的任务。

  3. 无法利用现有设计:如果你想让LLM修改一张已有的复杂图纸,它需要先解析出现有图纸的DXF文件,理解其中所有实体的关系和含义,然后再进行修改并输出新的完整DXF。这个过程对LLM的上下文长度、几何推理和符号推理能力提出了地狱级的挑战。

我的实操心得:解析与生成中间格式,目前更适合从零开始的简单图形创建,或者作为其他路线的补充输出。例如,一个专门生成简单二维示意图(如流程图、简单布局图)的工具,可以尝试让LLM输出SVG或简化版的DXF。但对于主流的、复杂的工程CAD修改任务,这条路目前还是一条“学术探索”之路,工程落地难度极大。它要求LLM同时是一个严格的格式校验器和一个顶级的几何推理引擎。

4. 路线三:模拟用户操作(UI Automation)——以“人”的方式交互

前两条路可以看作是“后台API”模式,而第三条路则回到了“前台GUI”模式:不关心CAD内部的数据结构或接口,而是用程序模拟真实用户的操作——移动鼠标、点击按钮、输入键盘命令。

这通常通过UI自动化框架实现,例如Windows上的pyautoguiuiautomation,或者专门针对AutoCAD的AutoHotkey脚本。

4.3 实现一个简单的点击示例

假设我们要点击AutoCAD的“画圆”按钮,用pyautogui可以这样写:

import pyautogui import time # 假设CAD窗口已经激活 # 首先,找到“画圆”按钮的图标在屏幕上的位置(需要事先获取或通过图像识别) # 这里使用图像匹配(需要事先截取‘画圆’按钮的截图circle_button.png) try: button_location = pyautogui.locateOnScreen('circle_button.png', confidence=0.9) if button_location: button_center = pyautogui.center(button_location) pyautogui.click(button_center) print("已点击画圆按钮。") # 随后可以模拟键盘输入坐标,例如输入圆心和半径 time.sleep(0.5) pyautogui.write('0,0') # 输入圆心坐标 pyautogui.press('enter') pyautogui.write('50') # 输入半径 pyautogui.press('enter') else: print("未找到按钮图像。") except Exception as e: print(f"操作失败: {e}")

4.4 此路为何“不可控”?

模拟操作看似直观,但它引入了所有GUI自动化固有的、且更严重的缺陷:

  1. 环境极度脆弱:窗口位置变了、分辨率改了、主题换了、工具栏布局调整了、弹出了一个意外对话框(比如许可证提醒)……任何一个微小的界面变化都会导致图像识别失败或点击错位,脚本立刻崩溃。维护成本随着CAD软件版本更新和用户环境差异呈指数级增长。

  2. 缺乏状态感知:和COM接口类似,脚本无法可靠地“知道”操作是否成功。它只是执行了“点击”和“输入”的动作。画出来的圆对不对?有没有报错?脚本无从得知,除非再结合OCR去读命令行提示区,但那又引入了新的复杂性和不确定性。

  3. 效率极其低下:每一个操作都需要等待界面响应(time.sleep),速度比COM接口慢几个数量级。对于复杂的绘图任务,耗时是无法接受的。

我的实操心得:UI自动化路线,在我看来是最不推荐用于LLM驱动CAD的方案。它唯一的价值可能在于录制和回放极其固定的、包含大量GUI点击的宏操作。但对于需要智能理解和灵活响应的LLM来说,这条路的不可靠性和高维护成本是致命的。它把问题从“如何让AI理解几何”降级成了“如何让AI在不断变化的屏幕上找到那个小图标”,这完全偏离了方向。

5. 路线四:LLM Agent + 专用工具函数——当前的最优解

在尝试并排除了前三条路线的诸多问题后,第四条路线逐渐浮出水面,并且被证明是目前最可行、最灵活的策略:不追求LLM直接生成最终的操作代码,而是让它作为一个“大脑”(Agent),去调用你为它精心编写好的“工具手”(Tool Functions)

这就是LLM Agent框架(如LangChain、LlamaIndex、AutoGPT等)的核心思想。你将复杂的CAD操作封装成一个个原子化的、高可靠性的函数,然后告诉LLM这些函数的功能、输入和输出。LLM负责理解用户的自然语言指令,规划步骤,并决定在何时调用哪个函数、传入什么参数。

5.1 构建CAD操作的工具集

首先,你需要基于前面提到的COM接口(或其他稳定SDK,如中望CAD的ZRX API),构建一个健壮的工具函数库。每个函数都要有清晰的输入输出和完整的错误处理。

# cad_tools.py import win32com.client from typing import List, Tuple, Optional class CADOperator: def __init__(self): self.acad = None self.doc = None self._connect() def _connect(self): """连接到CAD,包含重试和错误处理""" try: self.acad = win32com.client.Dispatch("AutoCAD.Application") self.doc = self.acad.ActiveDocument print("已连接到CAD。") except Exception as e: print(f"连接CAD失败: {e}") # 这里可以加入启动CAD程序的逻辑 raise def draw_circle(self, center: Tuple[float, float, float], radius: float, layer: str = "0"): """在指定图层画一个圆。返回创建对象的句柄或None。""" if not self.doc: return {"status": "error", "message": "未连接到文档"} try: modelspace = self.doc.ModelSpace circle = modelspace.AddCircle(center, radius) circle.Layer = layer self.doc.Regenerate(True) return {"status": "success", "handle": circle.Handle, "message": f"圆已创建于图层{layer}"} except Exception as e: return {"status": "error", "message": f"画圆失败: {e}"} def move_object(self, handle: str, displacement: Tuple[float, float, float]): """通过句柄移动对象。""" # ... 实现根据句柄查找对象并移动的逻辑,包含详细的错误处理 pass def get_object_info(self, handle: str): """获取指定句柄对象的类型、图层、坐标等信息。""" # ... 实现对象信息查询 pass def run_command(self, cmd_string: str): """发送一个CAD命令字符串到命令行。""" # ... 实现命令发送,注意处理命令执行中的等待和提示 pass

5.2 让LLM学会使用工具

接下来,在一个Agent框架中(这里以简化的伪代码示意),你将工具的描述提供给LLM。

# 工具描述,用于提供给LLM tools = [ { "name": "draw_circle", "description": "在指定的三维坐标中心点,用指定的半径画一个圆。可以指定图层。", "parameters": { "center": {"type": "list[float]", "description": "圆心坐标,如 [0.0, 0.0, 0.0]"}, "radius": {"type": "float", "description": "圆的半径"}, "layer": {"type": "string", "description": "图层名称,默认为'0'"} } }, { "name": "get_object_info", "description": "根据对象的唯一句柄,获取其详细信息,如类型、图层、坐标等。", "parameters": { "handle": {"type": "string", "description": "CAD对象的句柄"} } }, # ... 其他工具 ]

当用户说“在原点画一个半径为25的圆,放在‘标注’层”时,LLM(如GPT-4)会进行如下推理:

  1. 理解指令:动作是“画圆”,中心是(0,0,0),半径是25,图层是“标注”。
  2. 匹配工具:找到draw_circle工具。
  3. 构造参数:生成{"center": [0.0, 0.0, 0.0], "radius": 25.0, "layer": "标注"}
  4. 系统执行该函数,并将结果(成功或失败,包含句柄)返回给LLM。
  5. LLM根据结果组织自然语言回复给用户:“已在图层‘标注’上创建了一个圆,句柄为xxxx。”

5.3 为什么Agent路线是“最优解”?

  1. 责任分离,扬长避短:LLM擅长的是理解和规划(将模糊指令解析为明确步骤和参数),而不擅长生成绝对正确的低级代码。Agent路线把LLM不擅长的部分(精确的API调用、复杂的错误处理、状态管理)封装在可靠的工具函数里,让LLM只做它最擅长的事。这完美规避了COM路线和DXF路线的核心缺陷。

  2. 具备反馈与修正能力:工具函数可以返回结构化的结果(成功/失败、对象句柄、错误信息)。LLM可以接收到这些反馈。如果draw_circle返回“图层‘标注’不存在”,LLM可以接着调用一个create_layer的工具,然后再重试画圆。这使得系统具备了初步的自我修正和规划能力,这是前三条路线都无法实现的。

  3. 可扩展性强:新的功能只需要封装成新的工具函数,并更新工具描述给LLM即可。你可以从简单的几何创建开始,逐步加入尺寸标注、块操作、图纸空间布局等复杂工具,逐步构建一个功能强大的智能CAD助手。

  4. 降低对LLM的要求:你不再需要寻找或训练一个精通DXF语法或AutoCAD COM对象模型的“专家级”LLM。一个通用的、能力较强的对话LLM(如GPT-4、Claude 3),配合清晰工具描述,就能完成大部分任务。

我的实操心得与关键技巧:走Agent路线,成功的关键在于工具函数的设计提示工程(Prompting)

  • 工具要足够原子化:一个工具只做一件事,并且做好错误处理。比如,create_layer,draw_line,select_object_by_window是好的工具;draw_a_floor_plan就不是一个好工具,它太复杂。
  • 工具描述要极度清晰:LLM完全依靠你的描述来理解工具。参数的类型、格式、单位(是毫米还是米?)、取值范围,都必须描述清楚。模糊的描述会导致LLM传错参数。
  • 提供丰富的上下文:在系统提示词(System Prompt)中,要明确告诉LLM它的角色(“你是一个CAD操作助手”)、可用的工具、以及一些基本规则(如“坐标单位是毫米”,“默认在模型空间操作”)。
  • 处理模糊指令:用户会说“把那个圆弄大点”。你需要LLM能通过对话澄清:“您想放大哪个圆?请点击它,或者告诉我它的编号。”这可能需要结合一个list_nearby_objects的工具,让用户从列表中选择。

6. 实战中的抉择:四条路线如何选?

分析了四条路线的原理和优劣,那么在实际项目中究竟该如何选择?我的建议是基于你的项目目标、资源投入和风险承受能力来做一个权衡。

技术路线核心思想优点缺点适用场景
COM接口直连LLM生成控制CAD的API调用代码功能强大、直接、执行效率高代码生成难、状态管理难、错误处理难、无反馈固定流程的批量后台处理任务
解析/生成中间格式(DXF等)LLM直接生成或解析CAD数据文件脱离软件环境、纯数据处理格式极其复杂、缺乏高级语义、修改现有图难从零生成简单示意图、学术研究
模拟用户操作(UI Automation)LLM驱动程序模拟鼠标键盘操作无需理解内部API,理论上可操作任何软件极度脆弱、效率低下、无状态感知、维护成本极高录制回放固定GUI操作宏
LLM Agent + 工具函数LLM作为大脑,调用封装好的工具责任分离、具备反馈能力、扩展性强、对LLM要求相对低需要前期投入开发工具库、设计提示词绝大多数智能交互式CAD助手场景

对于绝大多数旨在打造“智能CAD对话助手”、“基于自然语言的绘图工具”的项目,路线四(LLM Agent + 工具函数)是当前唯一具备可行性和扩展性的选择。它虽然需要前期投入来搭建工具库,但这条路是“越走越宽”的。你可以从一个画圆、画线的简单原型开始,快速验证想法,然后像搭积木一样不断增加工具,逐步覆盖更复杂的操作。

路线一和路线二可以作为Agent工具函数内部的实现方式。例如,你的draw_complex_geometry工具函数,内部可能就是通过精细调用一系列COM接口,或者生成一段复杂的DXF代码片段再导入来实现的。这样,你既利用了LLM的规划能力,又保证了底层操作的精确性。

至于路线三,除非有极其特殊的、必须模拟GUI且无法用API实现的场景,否则应坚决避免。

7. 避坑指南:从想法到原型的关键几步

如果你决定采用Agent路线开始实践,以下是我从零搭建一个可用的LLM-CAD智能助手原型过程中,总结出的几个关键步骤和必坑点:

7.1 第一步:最小可行工具集定义

不要一开始就想做一个“全能CAD”。定义你的第一个核心场景。比如:“用户描述一个简单的二维机械零件轮廓,助手能把它画出来”。 围绕这个场景,列出最必须的工具:

  1. create_new_drawing: 创建新图。
  2. set_current_layer: 设置当前图层。
  3. draw_line: 给定两点画线。
  4. draw_circle: 给定圆心半径画圆。
  5. draw_arc: 给定三点画弧。
  6. save_drawing: 保存图纸。

先实现这6个工具,并确保每个工具都鲁棒。用它们已经可以组合出很多简单图形了。

7.2 第二步:选择并封装稳定的底层接口

对于AutoCAD,就用Pythonwin32com;对于中望CAD(ZWCAD/ZW3D),查阅其提供的ZRX(.NET)或LISP API,用Python的pythonnetclr)库进行调用。关键点:在你的工具函数内部,一定要做彻底的异常捕获和状态检查。CAD软件可能无响应、命令可能被用户中断、对象可能不存在。你的工具函数必须能优雅地失败,并返回结构化的错误信息给LLM,而不是让整个Python脚本崩溃。

7.3 第三步:设计清晰的工具描述与系统提示

这是LLM能否正确使用工具的关键。工具描述要像给一个新员工写的API文档一样清晰。

差的描述“移动一个对象。”好的描述“通过对象的唯一句柄(Handle),将其在三维空间中平移指定的距离。例如,向量[100, 50, 0]表示向X轴正方向移动100单位,向Y轴正方向移动50单位,Z轴不变。如果句柄无效或对象不能被移动,将返回错误。”

系统提示词要定好基调: “你是一个专业的CAD绘图助手。用户会用自然语言描述绘图或修改需求。你可以使用一系列工具来操作CAD软件。操作时请注意:1. 所有坐标和距离单位均为毫米(mm)。2. 默认操作空间为模型空间(Model Space)。3. 如果你不确定用户的指令,请主动提问澄清,例如询问具体的尺寸、位置或选择哪个对象。”

7.4 第四步:实现Agent执行循环

你需要一个主循环,它负责:

  1. 将用户输入和对话历史传给LLM。
  2. LLM返回一个“思考过程”,其中可能包含工具调用请求。
  3. 解析出LLM想要调用的工具和参数。
  4. 在你的Python环境中执行对应的工具函数。
  5. 将工具执行的结果(成功信息或错误信息)附加到对话历史中。
  6. 将新的历史再次传给LLM,让它生成面向用户的回复或决定下一步动作。 这个过程可以使用LangChain的AgentExecutor,也可以自己用OpenAI的Function Calling API简单实现一个循环。

7.5 第五步:处理复杂性与模糊性

这是从“能用”到“好用”的飞跃。

  • 对象选择问题:用户说“删除那个小圆”。你需要工具来列出当前空间中的所有圆(list_circles),并返回它们的句柄、位置和大致尺寸给LLM,由LLM在回复中列出选项让用户选择,或者LLM根据“小”这个描述自行判断(有风险)。
  • 单位与比例:用户可能说“画一个5厘米的线”,但你的CAD模板单位是毫米。需要在系统提示或工具调用时进行转换。
  • 多步复杂操作:“画一个螺栓的俯视图”。这需要LLM进行多步规划:画中心线、画螺纹小径圆、画螺栓头六角形……这考验LLM的复杂任务分解能力,你可能需要提供一些高级的复合工具(如draw_hex_bolt_top_view)来降低规划难度。

这条路走下来,你会发现,真正的挑战逐渐从“如何让程序操作CAD”变成了“如何设计一套让LLM能高效、准确理解的工具和交互协议”。这更像是一个高级的人机交互设计问题,而不仅仅是编程问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/16 22:52:52

基于状态机的异步任务管理:解决图片生成任务失踪问题

1. 项目概述:当图片生成任务“神秘失踪” 最近在搞一个图片生成的后台服务,相信不少做AI应用或者大文件处理的朋友都遇到过类似的问题:用户提交了一个生成动漫头像或者将文字描述转成图片的请求,前端显示“处理中”,然…

作者头像 李华
网站建设 2026/8/16 22:52:35

某里RAG三面追问:知识库检索不到怎么办?四层兜底架构与工程边界

文章目录 前言 一、面试现场还原:为什么这道题能筛掉八成候选人 1. 大多数候选人踩的第一个坑 2. "检索不到"不等于"没有答案" 3. 这道题真正在筛什么 二、检索层抢救:查询改写与混合召回的三个方向 1. 查询改写:让模型先翻译用户的真实意图 2. 混合检索…

作者头像 李华
网站建设 2026/8/16 22:50:33

OpenCode深度体验:一站式AI开发工具,解决模型选择与集成难题

1. 从“模型焦虑”到“工具自由”:我的OpenCode深度体验最近几个月,AI圈的朋友们估计都跟我一样,陷入了某种“甜蜜的烦恼”。DeepSeek V4 Flash、GLM-5.2、Qwen3.8 Max、GPT 5.6 Luna……这些名字像走马灯一样在眼前晃,每个都宣称…

作者头像 李华
网站建设 2026/8/16 22:47:30

KKCE: 基于TCPing的平台,全球300+节点-快快测

一、引言:为什么 TCPing 显示 RTT 30ms,API 首字节却要 300ms? 在排查网络延迟时,我们习惯用 TCPing 测一个端口,看到往返时间(RTT)只有 30ms,便认为这条链路“很快”。 但真实用户…

作者头像 李华
网站建设 2026/8/16 22:43:57

Mac终端自动化:使用osascript实现任务完成后自动关闭窗口

1. 一个看似简单却暗藏玄机的自动化需求 在Mac上进行开发或者日常脚本操作时,我们经常会遇到这样一个场景:你写了一个脚本或者编译了一个可执行程序,通过终端(Terminal)窗口来运行它。程序运行结束后,终端窗…

作者头像 李华