解析Linux top 命令输出并生成动态图表

news2024/11/17 13:58:37

文章目录

    • 0. 引言
    • 1. 功能
    • 2.使用步骤
    • 3. 程序架构
        • 流程图
        • 结构图
    • 4. 数据解析模块
    • 5. 图表绘制模块
    • 6. 主程序入口
    • 7. 总结
    • 8. 附录完整代码

0. 引言

在性能调优和系统监控中,top 命令是一种重要工具,提供了实时的系统状态信息,如 CPU 使用率、内存使用情况和进程状态。然而,仅凭命令行输出可能无法满足复杂的分析需求。
本文将介绍如何解析 top 命令的输出,并利用 Python 生成动态图表,以更直观地展示系统性能数据。

1. 功能

  • 解析top 输出

    • 解析top -1 -H -b -n 1800 -d 1获取的进程信息,文本数据通过 pandas 库处理,并使用 matplotlib 生成动态图表。
    • 提取每个进程的 PID、用户、CPU 使用率、内存使用率、运行时间和命令信息。
  • 生成动态图表

    • 绘制系统总体内存使用情况的动态曲线(总内存、空闲内存、使用内存、缓存/缓冲区内存)。
    • 绘制指定进程或线程的 CPU 和内存使用情况的动态曲线。

2.使用步骤

  • dump top文本信息
    • 需要系统统计的设备中,使用如下命令获取top信息top -1 -H -b -n 1800 -d 1 > topdump.txt
      参数 -n 1800 指定了 top 命令执行的次数,即执行1800次。而参数 -d 1 则指定了每次执行之间的时间间隔为1秒,意味着 top 命令每隔1秒就会输出一次进程信息。

    • 注意:dump top信息时,请避免做其他操作以降低干扰。

  • 使用以下命令解析 top 输出文件并生成动态图表:

    python top_parser.py --file topdump.txt --process <process_name> --show_threads --save_fig
    
    • 参数说明:
      • --file:指定 top 输出文件的路径。
      • --process:指定要绘制的进程名称。
      • --show_threads:可选参数,显示进程内所有线程的详细信息。
      • --save_fig:可选参数,保存生成的图表为 PNG 图像文件。
  • 如下图是使用python topparser.py --file topdump.txt --process terminator --save_fig生成的:

python topparser.py --file topdump.txt --process terminator --save_fig生成的

3. 程序架构

本程序主要分为以下几个模块:

  • 数据解析模块:负责解析 top 命令的输出文本,并将提取的数据存储到 pandas 的数据帧中。

  • 图表绘制模块:基于解析得到的数据帧,使用 matplotlib 生成动态图表。

  • 主程序入口:处理命令行参数,调用数据解析模块和图表绘制模块完成数据处理和图表生成的流程。

流程图

流程图将展示数据解析模块、图表绘制模块和主程序入口之间的交互过程。以下是流程图的示意:

主程序入口
解析 `top` 输出文件
生成 `pandas` 数据帧
调用图表绘制模块
生成动态图表
显示或保存图表
结束
结构图

结构图将展示程序的整体架构,包括数据解析模块、图表绘制模块和主程序入口的功能组成及其关系。以下是结构图的示意:

主程序入口
解析 `top` 输出文件
图表绘制模块
处理命令行参数
解析 `top` 输出文本
生成 `pandas` 数据帧
绘制内存使用动态曲线
绘制进程线程动态曲线
时间轴处理
内存使用情况处理
进程线程动态曲线处理
绘制动态图表
存储信息

4. 数据解析模块

数据解析模块的主要任务是读取 top 命令的输出文件,识别其格式并提取出需要的性能指标和进程信息。以下是核心代码片段的部分实现:

# 数据解析模块核心代码示例
import pandas as pd
import re

def parse_top_output(file_path):
    columns = ['timestamp', 'total_mem', 'free_mem', 'used_mem', 'buff_cache_mem', 'pid', 'user', 'cpu', 'mem', 'time', 'command']
    data = {col: [] for col in columns}

    with open(file_path, 'r') as file:
        lines = file.readlines()

    timestamp = None
    format_type = None
    for line in lines:
        if line.startswith('top -'):
            timestamp = re.search(r'top - (\d+:\d+:\d+)', line).group(1)
        elif 'KiB Mem :' in line or 'GiB Mem :' in line:
            format_type = 'format1' if 'KiB Mem :' in line else 'format2'
            mem_info = re.findall(r'[\d\.]+', line)
            if format_type == 'format1':
                data['total_mem'].append(int(mem_info[0]))
                data['free_mem'].append(int(mem_info[1]))
                data['used_mem'].append(int(mem_info[2]))
                data['buff_cache_mem'].append(int(mem_info[3]))
            else:
                total_mem_gb = float(mem_info[0])
                data['total_mem'].append(total_mem_gb * 1024 * 1024)
                data['free_mem'].append(None)
                data['used_mem'].append(None)
                data['buff_cache_mem'].append(None)
            data['timestamp'].append(timestamp)
            data['pid'].append(None)
            data['user'].append(None)
            data['cpu'].append(None)
            data['mem'].append(None)
            data['time'].append(None)
            data['command'].append(None)
        elif re.match(r'\s*\d+', line) or re.match(r'\s*\d+\s+\w+', line):
            if format_type == 'format1':
                proc_info = re.split(r'\s+', line.strip(), maxsplit=11)
                data['pid'].append(int(proc_info[0]))
                data['user'].append(proc_info[1])
                data['cpu'].append(float(proc_info[8]))
                data['mem'].append(float(proc_info[9]))
                data['time'].append(proc_info[10])
                data['command'].append(proc_info[11] if len(proc_info) > 11 else "")
            elif format_type == 'format2':
                proc_info = re.split(r'\s+', line.strip(), maxsplit=10)
                data['pid'].append(int(proc_info[0]))
                data['user'].append(proc_info[1])
                try:
                    cpu_value = float(proc_info[5].strip('%')) if '%' in proc_info[5] else float(proc_info[5])
                    mem_value = float(proc_info[6].strip('%')) if '%' in proc_info[6] else float(proc_info[6])
                except ValueError:
                    cpu_value = 0.0
                    mem_value = 0.0
                data['cpu'].append(cpu_value)
                data['mem'].append(mem_value)
                data['time'].append(proc_info[7])
                data['command'].append(proc_info[9] if len(proc_info) > 9 else "")
            data['timestamp'].append(timestamp)
            data['total_mem'].append(None)
            data['free_mem'].append(None)
            data['used_mem'].append(None)
            data['buff_cache_mem'].append(None)
        else:
            data['timestamp'].append(timestamp)
            for key in data:
                if key not in ['timestamp']:
                    data[key].append(None)

    df = pd.DataFrame(data)
    df['timestamp'] = pd.to_datetime(df['timestamp'], format='%H:%M:%S')
    df['relative_time'] = (df['timestamp'] - df['timestamp'].min()).dt.total_seconds()
    return df

5. 图表绘制模块

图表绘制模块利用 matplotlib 库生成动态图表,以下是绘制内存使用动态曲线和进程线程动态曲线的核心代码片段:

# 图表绘制模块核心代码示例
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator, AutoLocator

def plot_memory_usage(ax, df, process_name=None):
    if 'relative_time' not in df.columns:
        print("relative_time column is missing in the dataframe")
        return
    
    memory_cols = ['total_mem', 'free_mem', 'used_mem', 'buff_cache_mem']
    df_memory = df.dropna(subset=memory_cols).drop_duplicates(subset=['relative_time'])

    max_memory = df_memory[memory_cols].max().max()  # 获取内存使用的最大值

    for col in memory_cols:
        ax.plot(df_memory['relative_time'], df_memory[col], label=col.replace('_', ' ').title())

    ax.set_xlabel('Time (seconds)')
    ax.set_ylabel('Memory (KiB)')
    ax.set_ylim(0, max_memory * 1.1 if max_memory > 0 else 1)
    ax.set_title('Memory Usage Over Time')
    if process_name:
        ax.text(0.5, 0.5, process_name, transform=ax.transAxes, fontsize=20, ha='center', va='center', alpha=0.7, color='black')
    ax.legend()
    ax.grid(True)
    ax.xaxis.set_major_locator(AutoLocator())
    ax.yaxis.set_major_locator(MaxNLocator(integer=True))

    return ax

def plot_process_threads(ax, df, processes, show_threads, metric='cpu'):
    for process in processes:
        df_process = df[df['command'].str.contains(process, na=False)]
        if show_threads:
            unique_pids = df_process['pid'].unique()
            for pid in unique_pids:
                df_pid = df_process[df_process['pid'] == pid]
                ax.plot(df_pid['relative_time'], df_pid[metric], label=f'{process} {metric.upper()} (PID {pid})')
        else:
            df_process_grouped = df_process.groupby('relative_time').agg({metric: 'sum'}).reset_index()
            ax.plot(df_process_grouped['relative_time'], df_process_grouped[metric], label=f'{process} (Total {metric.upper()})')
    ax.set_xlabel('

Time (seconds)')
    ax.set_ylabel(f'{metric.upper()} Usage')
    ax.set_title(f'{metric.upper()} Usage of Processes and Threads Over Time')
    ax.legend()
    ax.grid(True)
    ax.xaxis.set_major_locator(AutoLocator())
    ax.yaxis.set_major_locator(MaxNLocator(integer=True))

    return ax

6. 主程序入口

主程序入口负责处理命令行参数,并调用数据解析和图表绘制模块完成数据处理和图表生成的流程。以下是主程序入口的核心代码片段:

# 主程序入口核心代码示例
import argparse

def main(file_path, processes, show_threads, save_fig=False):
    df = parse_top_output(file_path)
    plot_all(df, processes, show_threads, save_fig)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Parse and plot top command output.')
    parser.add_argument('--file', type=str, required=True, help='Path to the top output file')
    parser.add_argument('--process', type=str, nargs='+', required=True, help='List of processes to plot')
    parser.add_argument('--show_threads', action='store_true', help='Show CPU and memory for all threads within the process')
    parser.add_argument('--save_fig', action='store_true', help='Save the generated plots as PNG images')

    args = parser.parse_args()
    main(args.file, args.process, args.show_threads, args.save_fig)

7. 总结

通过本文介绍的方法,可以有效解析 top 命令输出并生成动态图表,帮助用户更直观地分析系统性能数据。该方法不仅支持不同格式的 top 输出,还能够灵活配置,满足各种监控需求。

8. 附录完整代码

import pandas as pd
import matplotlib.pyplot as plt
import re
import argparse
from matplotlib.ticker import MaxNLocator, AutoLocator

# 解析top命令输出
def parse_top_output(file_path):
    columns = ['timestamp', 'total_mem', 'free_mem', 'used_mem', 'buff_cache_mem', 'pid', 'user', 'cpu', 'mem', 'time', 'command']
    data = {col: [] for col in columns}

    with open(file_path, 'r') as file:
        lines = file.readlines()

    timestamp = None
    format_type = None
    for line in lines:
        if line.startswith('top -'):
            timestamp = re.search(r'top - (\d+:\d+:\d+)', line).group(1)
        elif 'KiB Mem :' in line or 'GiB Mem :' in line:
            format_type = 'format1' if 'KiB Mem :' in line else 'format2'
            mem_info = re.findall(r'[\d\.]+', line)
            if format_type == 'format1':
                data['total_mem'].append(int(mem_info[0]))
                data['free_mem'].append(int(mem_info[1]))
                data['used_mem'].append(int(mem_info[2]))
                data['buff_cache_mem'].append(int(mem_info[3]))
            else:
                total_mem_gb = float(mem_info[0])
                data['total_mem'].append(total_mem_gb * 1024 * 1024)
                data['free_mem'].append(None)
                data['used_mem'].append(None)
                data['buff_cache_mem'].append(None)
            data['timestamp'].append(timestamp)
            data['pid'].append(None)
            data['user'].append(None)
            data['cpu'].append(None)
            data['mem'].append(None)
            data['time'].append(None)
            data['command'].append(None)
        elif re.match(r'\s*\d+', line) or re.match(r'\s*\d+\s+\w+', line):
            if format_type == 'format1':
                proc_info = re.split(r'\s+', line.strip(), maxsplit=11)
                data['pid'].append(int(proc_info[0]))
                data['user'].append(proc_info[1])
                data['cpu'].append(float(proc_info[8]))
                data['mem'].append(float(proc_info[9]))
                data['time'].append(proc_info[10])
                data['command'].append(proc_info[11] if len(proc_info) > 11 else "")
            elif format_type == 'format2':
                proc_info = re.split(r'\s+', line.strip(), maxsplit=10)
                data['pid'].append(int(proc_info[0]))
                data['user'].append(proc_info[1])
                try:
                    cpu_value = float(proc_info[5].strip('%')) if '%' in proc_info[5] else float(proc_info[5])
                    mem_value = float(proc_info[6].strip('%')) if '%' in proc_info[6] else float(proc_info[6])
                except ValueError:
                    cpu_value = 0.0
                    mem_value = 0.0
                data['cpu'].append(cpu_value)
                data['mem'].append(mem_value)
                data['time'].append(proc_info[7])
                data['command'].append(proc_info[9] if len(proc_info) > 9 else "")
            data['timestamp'].append(timestamp)
            data['total_mem'].append(None)
            data['free_mem'].append(None)
            data['used_mem'].append(None)
            data['buff_cache_mem'].append(None)
        else:
            data['timestamp'].append(timestamp)
            for key in data:
                if key not in ['timestamp']:
                    data[key].append(None)

    df = pd.DataFrame(data)
    df['timestamp'] = pd.to_datetime(df['timestamp'], format='%H:%M:%S')
    df['relative_time'] = (df['timestamp'] - df['timestamp'].min()).dt.total_seconds()
    return df

# 将时间戳转换为秒数
def convert_timestamp_to_seconds(timestamp):
    h, m, s = map(int, timestamp.split(':'))
    return h * 3600 + m * 60 + s

# 绘制内存动态曲线
def plot_memory_usage(ax, df, process_name=None):
    if 'relative_time' not in df.columns:
        print("relative_time column is missing in the dataframe")
        return

    memory_cols = ['total_mem', 'free_mem', 'used_mem', 'buff_cache_mem']
    df_memory = df.dropna(subset=memory_cols).drop_duplicates(subset=['relative_time'])

    max_memory = df_memory[memory_cols].max().max()  # 获取内存使用的最大值

    for col in memory_cols:
        ax.plot(df_memory['relative_time'], df_memory[col], label=col.replace('_', ' ').title())

    ax.set_xlabel('Time (seconds)')
    ax.set_ylabel('Memory (KiB)')
    ax.set_ylim(0, max_memory * 1.1 if max_memory > 0 else 1)
    ax.set_title('Memory Usage Over Time')
    if process_name:
        ax.text(0.5, 0.5, process_name, transform=ax.transAxes, fontsize=20, ha='center', va='center', alpha=0.7, color='black')
    ax.legend()
    ax.grid(True)
    ax.xaxis.set_major_locator(AutoLocator())
    ax.yaxis.set_major_locator(MaxNLocator(integer=True))

    return ax

# 绘制进程和线程动态曲线
def plot_process_threads(ax, df, processes, show_threads, metric='cpu'):
    for process in processes:
        df_process = df[df['command'].str.contains(process, na=False)]
        if show_threads:
            unique_pids = df_process['pid'].unique()
            for pid in unique_pids:
                df_pid = df_process[df_process['pid'] == pid]
                ax.plot(df_pid['relative_time'], df_pid[metric], label=f'{process} {metric.upper()} (PID {pid})')
        else:
            df_process_grouped = df_process.groupby('relative_time').agg({metric: 'sum'}).reset_index()
            ax.plot(df_process_grouped['relative_time'], df_process_grouped[metric], label=f'{process} (Total {metric.upper()})')
    ax.set_xlabel('Time (seconds)')
    ax.set_ylabel(f'{metric.upper()} Usage')
    ax.set_title(f'{metric.upper()} Usage of Processes and Threads Over Time')
    ax.legend()
    ax.grid(True)
    ax.xaxis.set_major_locator(AutoLocator())
    ax.yaxis.set_major_locator(MaxNLocator(integer=True))

    return ax

# 绘制图表
def plot_all(df, processes, show_threads, save_fig=False):
    for process in processes:
        fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(12, 12))
        df_process = df[df['command'].str.contains(process, na=False)]
        if show_threads:
            plot_process_threads(axes[0], df_process, [process], show_threads, metric='mem')
            plot_process_threads(axes[1], df_process, [process], show_threads, metric='cpu')
        else:
            plot_memory_usage(axes[0], df, process_name=process)
            plot_process_threads(axes[1], df, [process], show_threads)

        plt.tight_layout(pad=3.0)

        if save_fig:
            fig.savefig(f'{process}_analysis.png')  # 保存图像

        plt.show()

# 主函数
def main(file_path, processes, show_threads, save_fig=False):
    df = parse_top_output(file_path)
    plot_all(df, processes, show_threads, save_fig)

# 处理命令行参数
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Parse and plot top command output.')
    parser.add_argument('--file', type=str, required=True, help='Path to the top output file')
    parser.add_argument('--process', type=str, nargs='+', required=True, help='List of processes to plot')
    parser.add_argument('--show_threads', action='store_true', help='Show CPU and memory for all threads within the process')
    parser.add_argument('--save_fig', action='store_true', help='Save the generated plots as PNG images')

    args = parser.parse_args()
    main(args.file, args.process, args.show_threads, args.save_fig)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1884520.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL篇】Percona XtraBackup物理备份工具的基础理论概述(第一篇,总共五篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

Python海量数据处理脚本大集合:pyWhat

pyWhat&#xff1a;精简海联数据&#xff0c;直达数据弱点要害- 精选真开源&#xff0c;释放新价值。 概览 pyWhat是Github社区上一款比较实用的开源Python脚本工具。它能够快速提取信息中的 IP 地址、邮箱、信用卡、数字货币钱包地址、YouTube 视频等内容。当你遇到了一串莫名…

【Python实战因果推断】13_线性回归的不合理效果3

目录 Regression Theory Single Variable Linear Regression Multivariate Linear Regression Frisch-Waugh-Lovell Theorem and Orthogonalization Regression Theory 我不打算太深入地探讨线性回归是如何构建和估计的。不过&#xff0c;一点点理论知识将有助于解释线性回归…

申请一张含100个域名的证书-免费SSL证书

挑战一下&#xff0c;申请一张包含100个域名的证书 首先&#xff0c;我们访问来此加密网站&#xff0c;进入登录页面&#xff0c;输入我的账号密码。 登录后&#xff0c;咱们就可以开始申请证书&#xff0c;首先说一下&#xff0c;咱账号是SVIP哦&#xff0c;只有SVIP才可以申…

拍摄的vlog视频画质模糊怎么办?视频画质高清修复

在短视频逐渐成为主流的今天&#xff0c;许多朋友都会通过vlog的形式记录下自己的生活。但我们会发现&#xff0c;自己拍摄的视频与专业博主拍摄的视频&#xff0c;在画质上就会有所差别&#xff0c;拍摄的vlog视频画质模糊不清晰怎么办&#xff1f; 拍摄的vlog视频画质模糊怎么…

Linux-网络安全私房菜

文章目录 前言入门基本指令篇章字符集设置cdlsdatemkdirtouch-d-m 修改主机名rmshredrename重命名mv移动tar打包与压缩打包但是不压缩打包且压缩更新包文件解压对应的包 zip压缩文件命令cat查看显示行号交互写入&#xff08;追加&#xff09;显示空行 more和lesshead和tailhead…

虚拟环境管理

虚拟环境 在使用 Python 时我们一般使用“pip install 第三方包名”来安装第三方包&#xff0c;但是由于pip的特性&#xff0c;系统只能安装每个包的一个版本。而在实际开发中&#xff0c;可能同时开发多个项目&#xff0c;如&#xff1a;上图有三个项目&#xff1b;每个项目需…

【笔记】通过shell脚本自动部署项目(未完成)

然后将gitee仓库上的代码克隆至linux上 如果不知道gitee仓库怎么上传代码移步【笔记】如何在gitee仓库上传idea代码-CSDN博客 写到一半不想写了自己去复习p138-139吧

【QT】输入类控件

目录 Line Edit 核心属性 核心信号 正则表达式 示例&#xff1a;使用正则表达式验证输入框内容 示例&#xff1a;切换输入框密码模式下的显示状态 Text Edit 核心属性 核心信号 示例&#xff1a;获取多行输入框的内容同步显示到label 示例&#xff1a;获取文本的选…

微信小程序template模板引入

如图&#xff1a;temp.wxml是template引入的模板 在two.wxml中&#xff1a; import&#xff1a;是引入temp的页面让template中的内容显示出来在two页面中&#xff1b; include:是显示temp页面内容不在template包裹&#xff0c;template以外的view标签文字和不在view的文字让…

独家首发 | Matlab实现SVM-Transformer多变量回归预测

独家首发 | Matlab实现SVM-Transformer多变量回归预测 目录 独家首发 | Matlab实现SVM-Transformer多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现SVM-Transformer多变量回归预测&#xff0c;SVM递归特征消除Transformer多输入单输出回归预测…

GOM引擎源码 完整可编译 带微端 附带基础附件

GOM引擎源码 完整可编译 带微端 附带基础附件 时间紧迫&#xff0c;无暇顾及&#xff0c;无意中得到即公布GameOfMir源码未测试&#xff0c;专业人事自行编译测试&#xff01;非诚勿扰&#xff01;源码下载&#xff1a;极速云

已解决org.omg.CORBA.portable.RemarshalException:在CORBA中需要重新编组的正确解决方法,亲测有效!!!

已解决org.omg.CORBA.portable.RemarshalException&#xff1a;在CORBA中需要重新编组的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 服务器端代码 客户端代码 报错原因 解决思路 解决方法 1. 检查网络连接 …

递归----计算P函数

注意运算中的符号不能少&#xff01;&#xff01;&#xff01;&#xff01; * 必须体现出&#xff01;&#xff01;&#xff01;&#xff01; #include <stdio.h>double P( int n, double x );int main() {int n;double x;scanf("%d %lf", &n, &x);pri…

MMM高可用性部署

MMM高可用性部署 MMM概述MMMMMM架构 MMM部署实验环境实验拓扑图 数据库安装时间同步搭建 MySQL 多主多从模式修改MySQL配置文件配置主主复制Master1Master2配置复制 配置主从复制 安装配置 MySQL-MMM安装 MySQL-MMM对 MySQL-MMM 进行配置修改代理配置文件修改监控配置文件授权代…

软件著作权申请:开发者的重要保障与助力

一、引言 随着信息技术的飞速发展&#xff0c;软件产业已成为推动经济增长的重要动力。然而&#xff0c;在软件开发过程中&#xff0c;保护创作者的权益、防止抄袭和侵权行为显得尤为重要。软件著作权作为保护软件开发者权益的重要法律工具&#xff0c;其申请和登记流程对于软…

typescript学习回顾(五)

今天来分享一下ts的泛型&#xff0c;最后来做一个练习 泛型 有时候&#xff0c;我们在书写某些函数的时候&#xff0c;会丢失一些类型信息&#xff0c;比如我下面有一个例子&#xff0c;我想提取一个数组的某个索引之前的所有数据 function getArraySomeData(newArr, n:numb…

AI系统:未来科技的驱动力

引言 人工智能&#xff08;Artificial Intelligence, AI&#xff09;是一门研究如何使计算机模拟、延伸和扩展人类智能的学科。自20世纪50年代起&#xff0c;人工智能作为一项科学研究领域开始兴起。早期的AI系统主要集中在简单的任务&#xff0c;如棋类游戏和数学证明。随着计…

NLP篇1

场景&#xff1a;假设给你一篇文章。 目标&#xff1a;说白了&#xff0c;就是数学的分类。但是如何实现分类呢。下面将逐步一 一 分析与拆解。先把目标定好了和整体框架定好了。而不是只见树木而不见森林。 情感分类&#xff08;好评、差评&#xff0c;中性&#xff09; 整体…

Windows 安装 Terraform

Windows 安装 Terraform 下载 Terraform 压缩文件安装 Terraform测试 Terraform 下载 Terraform 压缩文件 访问 https://developer.hashicorp.com/terraform/install&#xff0c;下载 Windows 版安装文件&#xff0c; 安装 Terraform 解压下载 zip 包&#xff0c;然后将解压的…