鸿蒙应用框架开发【N-Body模拟程序】

news2024/11/15 2:01:05

N-Body模拟程序

介绍

在本示例中,使用ArkTS编程语言开发了业界编程语言基准测试项目[Benchmarks Game]中的[N体问题模拟程序],实现类木星体轨道计算。

本示例用到了@ohos.taskpool和@ohos.worker 接口。示例中的程序可以用于AOT(Ahead Of Time)等性能测试。

效果预览

1

使用说明

1.点击按钮Calculate By TaskPool,会从任务池创建一个任务,进行N-Body计算。

2.点击按钮Calculate By Worker,会创建一个Worker,进行N-Body计算。

工程目录

├──entry/src/main/ets/
│  ├──entryability
│  │  └──EntryAbility.ets                 // 封装整个模块启用
│  ├──model
│  │  ├──CalculateUtil.ets                // worker和taskpool都在这里调用            
│  │  └──NBody_ETS_6.ts                   // 天体轨道计算
│  ├──pages
│  │  └──Index.ets                        // 首页
│  ├──utils
│  │  ├──Constants.ts                     // 封装只读常量
│  │  └──Logger.ts                        // 封装整个日志
│  └──worker
│     └──CalculateWorker.ts               // worker线程
└──entry/src/main/resources               // 应用静态资源目录        
                                            

相关概念

AOT(Ahead Of Time),即预先编译,在应用程序运行前,将代码预先编译成高性能机器代码,避免在运行时的编译性能消耗和内存消耗,让程序在首次运行就能通过执行高性能机器码获得性能收益。

具体实现

  • 使用TaskPool开启子线程运行,计算500万次时间推移天体运行轨道,源码参考[CalculateUtil.ets]
/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { taskpool, worker, MessageEvents, util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { offsetMomentum, energy, advance } from './NBody_ETS_6';
import Logger from '../utils/Logger';

const TAG: string = 'CalculateUtil';
let calculateResult: string = "Total time costed = %s ms."

class WorkerMessage {
  timeSteps: number;

  constructor(timeSteps: number) {
    this.timeSteps = timeSteps;
  }
}

/**
 * 运行天体轨道计算程序
 * @param totalTimeSteps 时间推移量
 * @returns 计算时间
 */
@Concurrent
export function computeTask(totalTimeSteps: number): number {
  const tagInTask: string = 'computeTask';
  const timeStep: number = 0.01; // 单位:hour
  const fractionDigits: number = 9; // 机械能数值小数位
  let start: number = new Date().getTime();

  // 建立孤立系统的动量守恒
  offsetMomentum();
  Logger.info(tagInTask, energy().toFixed(fractionDigits));

  // 更新天体在按指定的时间变化后的位置信息
  for (let i: number = 0; i < totalTimeSteps; i++) {
    advance(timeStep);
  }

  // 判断系统计算前后机械能守恒
  Logger.info(tagInTask, energy().toFixed(fractionDigits));
  let end: number = new Date().getTime();
  return end - start;
}

/**
 * 使用TaskPool开启子线程,执行轨道计算任务
 * @param totalTimeSteps 时间推移量
 */
export function computeNBodyByTaskPool(totalTimeSteps: number): void {
  Logger.info(TAG, "computeNBodyByTaskPool: start executing");
  let task: taskpool.Task = new taskpool.Task(computeTask, totalTimeSteps);
  try {
    Logger.info(TAG, 'computeNBodyByTaskPool: start calculating...');

    // 向taskpool线程池派发子线程任务
    taskpool.execute(task, taskpool.Priority.HIGH).then((res) => {
      Logger.info(TAG, 'computeNBodyByTaskPool: executed successfully, total time costed = ' + res + ' ms.');
      AppStorage.set<String>('timeCost', util.format(calculateResult, res.toString()))
    })
  } catch (err) {
    Logger.error(TAG, "computeNBodyByTaskPool: execute failed, " + (err as BusinessError).toString());
  }
  Logger.info(TAG, "computeNBodyByTaskPool: finish executing");
}

/**
 * 使用Worker开启子线程,执行轨道计算任务
 * @param totalTimeSteps 时间推移量
 */
export function computeNBodyByWorker(totalTimeSteps: number): void {
  Logger.info(TAG, "computeNBodyByWorker: start executing");
  let workerInstance = new worker.ThreadWorker("entry/ets/workers/CalculateWorker.ts");

  // 设置如何处理,来自worker线程的消息
  workerInstance.onmessage = (e: MessageEvents): void => {
    let data: Record<string, Object> = e.data;
    Logger.info(TAG, 'computeNBodyByWorker: executed successfully, total time costed = ' + data.result + ' ms.');
    AppStorage.set<String>('timeCost', util.format(calculateResult, data.result));
  }

  // 设置由主线程向worker线程发送什么消息
  workerInstance.postMessage(new WorkerMessage(totalTimeSteps));
}
  • 通过调用computeNBodyByTaskPool()创建一个task任务,并向taskpool线程池派发子线程任务。

  • 等待子线程执行任务computeTask(),计算完成后再把结果返回主线程。

  • 使用Worker开启子线程运行,计算5000万次时间推计算移天体运行轨道,源码参考[CalculateUtil.ets]

/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { taskpool, worker, MessageEvents, util } from '@kit.ArkTS';
import { BusinessError } from '@kit.BasicServicesKit';
import { offsetMomentum, energy, advance } from './NBody_ETS_6';
import Logger from '../utils/Logger';

const TAG: string = 'CalculateUtil';
let calculateResult: string = "Total time costed = %s ms."

class WorkerMessage {
  timeSteps: number;

  constructor(timeSteps: number) {
    this.timeSteps = timeSteps;
  }
}

/**
 * 运行天体轨道计算程序
 * @param totalTimeSteps 时间推移量
 * @returns 计算时间
 */
@Concurrent
export function computeTask(totalTimeSteps: number): number {
  const tagInTask: string = 'computeTask';
  const timeStep: number = 0.01; // 单位:hour
  const fractionDigits: number = 9; // 机械能数值小数位
  let start: number = new Date().getTime();

  // 建立孤立系统的动量守恒
  offsetMomentum();
  Logger.info(tagInTask, energy().toFixed(fractionDigits));

  // 更新天体在按指定的时间变化后的位置信息
  for (let i: number = 0; i < totalTimeSteps; i++) {
    advance(timeStep);
  }

  // 判断系统计算前后机械能守恒
  Logger.info(tagInTask, energy().toFixed(fractionDigits));
  let end: number = new Date().getTime();
  return end - start;
}

/**
 * 使用TaskPool开启子线程,执行轨道计算任务
 * @param totalTimeSteps 时间推移量
 */
export function computeNBodyByTaskPool(totalTimeSteps: number): void {
  Logger.info(TAG, "computeNBodyByTaskPool: start executing");
  let task: taskpool.Task = new taskpool.Task(computeTask, totalTimeSteps);
  try {
    Logger.info(TAG, 'computeNBodyByTaskPool: start calculating...');

    // 向taskpool线程池派发子线程任务
    taskpool.execute(task, taskpool.Priority.HIGH).then((res) => {
      Logger.info(TAG, 'computeNBodyByTaskPool: executed successfully, total time costed = ' + res + ' ms.');
      AppStorage.set<String>('timeCost', util.format(calculateResult, res.toString()))
    })
  } catch (err) {
    Logger.error(TAG, "computeNBodyByTaskPool: execute failed, " + (err as BusinessError).toString());
  }
  Logger.info(TAG, "computeNBodyByTaskPool: finish executing");
}

/**
 * 使用Worker开启子线程,执行轨道计算任务
 * @param totalTimeSteps 时间推移量
 */
export function computeNBodyByWorker(totalTimeSteps: number): void {
  Logger.info(TAG, "computeNBodyByWorker: start executing");
  let workerInstance = new worker.ThreadWorker("entry/ets/workers/CalculateWorker.ts");

  // 设置如何处理,来自worker线程的消息
  workerInstance.onmessage = (e: MessageEvents): void => {
    let data: Record<string, Object> = e.data;
    Logger.info(TAG, 'computeNBodyByWorker: executed successfully, total time costed = ' + data.result + ' ms.');
    AppStorage.set<String>('timeCost', util.format(calculateResult, data.result));
  }

  // 设置由主线程向worker线程发送什么消息
  workerInstance.postMessage(new WorkerMessage(totalTimeSteps));
}
  • 通过调用computeNBodyByWorker()创建一个worker线程,把要计算的时间推移量发送给worker线程。

  • 等待worker线程调用computeTask()计算完成后再把结果返回主线程。

  • computeTask()完成具体计算任务,源码参考[NBody_ETS_6.ts]

/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import Constants from '../utils/Constants';

/**
 * 天体类
 */
class Body {
  x: number;
  y: number;
  z: number;
  vx: number;
  vy: number;
  vz: number;
  mass: number;

  constructor(x: number, y: number, z: number, vx: number, vy: number, vz: number, mass: number) {
    this.x = x;
    this.y = y;
    this.z = z;
    this.vx = vx;
    this.vy = vy;
    this.vz = vz;
    this.mass = mass;
  }
}

const jupiter: Body = new Body(Constants.JUPITER_X, Constants.JUPITER_Y, Constants.JUPITER_Z, Constants.JUPITER_VX,
                               Constants.JUPITER_VY, Constants.JUPITER_VZ, Constants.JUPITER_MASS);
const saturn: Body = new Body(Constants.SATURN_X, Constants.SATURN_Y, Constants.SATURN_Z, Constants.SATURN_VX,
                              Constants.SATURN_VY, Constants.SATURN_VZ, Constants.SATURN_MASS);
const uranus: Body = new Body(Constants.URANUS_X, Constants.URANUS_Y, Constants.URANUS_Z, Constants.URANUS_VX,
                              Constants.URANUS_VY, Constants.URANUS_VZ, Constants.URANUS_MASS);
const neptune: Body = new Body(Constants.NEPTUNE_X, Constants.NEPTUNE_Y, Constants.NEPTUNE_Z, Constants.NEPTUNE_VX,
                               Constants.NEPTUNE_VY, Constants.NEPTUNE_VZ, Constants.NEPTUNE_MASS);
const sun: Body = new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Constants.SOLAR_MASS);

const bodies: Body[] = Array(sun, jupiter, saturn, uranus, neptune);

/**
 * 调整太阳的速度,保证该孤立系统动量守恒
 */
export function offsetMomentum(): void {
  // p为momentum的缩写,表示:动量, 等于质量乘以速度 p=mv
  let px: number = 0;
  let py: number = 0;
  let pz: number = 0;

  // 累加计算整个系统中,各个天体在三维各矢量方向的动量
  for (let i: number = 0; i < bodies.length; i++) {
    const body: Body = bodies[i];
    const mass: number = body.mass;
    px += body.vx * mass;
    py += body.vy * mass;
    pz += body.vz * mass;
  }

  // 太阳预设速度为0,通过动量守恒定律,反推太阳各矢量方向速度
  const body: Body = bodies[0];
  body.vx = -px / Constants.SOLAR_MASS;
  body.vy = -py / Constants.SOLAR_MASS;
  body.vz = -pz / Constants.SOLAR_MASS;
}

/**
 * 更新天体在按指定的时间变化后的位置信息
 * @param dt - delta time 时间变化
 */
export function advance(dt: number): void {
  const size = bodies.length;

  // 两两配对计算各天体瞬时速度
  for (let i = 0; i < size; i++) {
    const iBody = bodies[i];
    let vxi: number = iBody.vx;
    let vyi: number = iBody.vy;
    let vzi: number = iBody.vz;
    for (let j: number = i + 1; j < size; j++) {
      const jBody: Body = bodies[j];

      // 天体间距离差
      const dx: number = iBody.x - jBody.x;
      const dy: number = iBody.y - jBody.y;
      const dz: number = iBody.z - jBody.z;

      const d2: number = dx * dx + dy * dy + dz * dz;
      const mag: number = dt / (d2 * Math.sqrt(d2));

      // 由天体距离计算引力对速度的相互影响
      const jMass: number = jBody.mass;
      vxi -= dx * jMass * mag;
      vyi -= dy * jMass * mag;
      vzi -= dz * jMass * mag;

      const iMass: number = iBody.mass;
      jBody.vx += dx * iMass * mag;
      jBody.vy += dy * iMass * mag;
      jBody.vz += dz * iMass * mag;
    }
    iBody.vx = vxi;
    iBody.vy = vyi;
    iBody.vz = vzi;
  }

  // 更新天体的位置信息
  for (let i: number = 0; i < size; i++) {
    const body: Body = bodies[i];
    body.x += dt * body.vx; // 位置 = 时间 * 速度
    body.y += dt * body.vy;
    body.z += dt * body.vz;
  }
}

/**
 * 在程序开始和结束后调用,通过计算机械能,判断机械能守恒与否,以检查程序的运行正确性
 * @returns 返回系统机械能
 */
export function energy(): number {
  let energy: number = 0.0;
  const size: number = bodies.length;

  // 计算各天体的机械能总和,机械能公式:机械能=动能+势能
  for (let i: number = 0; i < size; i++) {
    const iBody: Body = bodies[i];

    // 对每个天体的动能进行加和,动能公式为:动能=1/2×物体质量×运动速度的平方
    energy += 0.5 * iBody.mass * (iBody.vx * iBody.vx + iBody.vy * iBody.vy + iBody.vz * iBody.vz);

    // 计算当前遍历到的天体和其他天体间的势能,势能公式为:引力势能=-G*物理A质量*物理B质量/距离
    for (let j: number = i + 1; j < size; j++) {
      const jBody: Body = bodies[j];
      const dx: number = iBody.x - jBody.x;
      const dy: number = iBody.y - jBody.y;
      const dz: number = iBody.z - jBody.z;

      const distance: number = Math.sqrt(dx * dx + dy * dy + dz * dz);
      energy -= (iBody.mass * jBody.mass) / distance;
    }
  }
  return energy;
}

  • offsetMomentum() 建立孤立系统的动量守恒。
  • advance() 更新天体在按指定的时间变化后的位置信息。
  • energy() 判断系统计算前后机械能守恒。

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!

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

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

相关文章

计科录取75人!常州大学计算机考研考情分析!

常州大学&#xff08;Changzhou University&#xff09;&#xff0c;简称“常大”&#xff0c;位于江苏省常州市&#xff0c;是江苏省人民政府与中国石油天然气集团有限公司、中国石油化工集团有限公司及中国海洋石油集团有限公司共建的省属全日制本科院校&#xff0c;为全国深…

⼤模型在⽹络安全⽅⾯的应⽤汇总

引⾔ ⼤语⾔模型&#xff08;Large Language Models, LLMs&#xff09;的横空出世&#xff0c;为传统计算机科学的各个细分领域带来了颠覆性的变⾰。这种变⾰的浪潮同样席卷了⽹络安全领域&#xff0c;引发了⼀系列深刻的变化和影响。GPT-4、Gemini、Llama 2 等⼤模型以其卓越的…

7月22日学习笔记 文件共享服务nfs,SAMBA文件共享与DNS域名服务

任务背景 由于业务驱动&#xff0c;为了提⾼⽤户的访问效率&#xff0c;现需要将原有web服务器上的静态资源 ⽂件分离出来&#xff0c;单独保存到⼀台⽂件服务器上。 任务要求 1. ⼀台应⽤服务器web-server部署apache&#xff0c;静态⽹⻚资源存放在另外⼀台NFS服 务器上 …

深入理解计算机系统 CSAPP 家庭作业11.7

静态内容是指在不同请求中访问到的数据都相同的静态文件。例如&#xff1a;图片、视频、网站中的文件&#xff08;html、css、js&#xff09;、软件安装包、apk文件、压缩包文件等。 /** get_filetype - derive file type from file name*/ void get_filetype(char *filename,…

12_TypeScript 模块 以及 模块化封装DB 库

TypeScript 模块 1、模块中暴露方法12、模块中暴露方法23、模块中暴露方法34、封装[上一节的db 库](https://blog.csdn.net/qq_46143850/article/details/140664100)5、TypeScript 命名空间 模块的概念&#xff08;官方&#xff09;&#xff1a; 关于术语的一点说明&#xff1a…

Linux网络-wget命令

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注我&#xff0c;我尽量把自己会的都分享给大家&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux服务器作为一个常用的网络服务器&#xff0c;主要的作用就是向客户端提供网络…

【工具推荐】强大的JS渗透测试工具:URLFinder

一、下载地址 https://github.com/pingc0y/URLFinder 二、工具原理 从表现中JS中提取URL或者敏感数据&#xff0c;一款用于快速提取检测页面中JS与URL的工具。功能类似于JSFinder&#xff0c;但JSFinder好久没更新了。 三、工具介绍 1、下载解压出来包含下面两个文件 2、直…

内网渗透—内网穿透工具NgrokFRPNPSSPP

前言 主要介绍一下常见的隧道搭建工具&#xff0c;以此来达到一个内网穿透的目的。简单说一下实验滴环境吧&#xff0c;kali作为攻击机&#xff0c;winserver2016作为目标靶机。 kali 192.168.145.171 winserver2016 10.236.44.127 显然它们处于两个不同的局域网&#xff0c…

SQLException:Operation not allowed after ResultSet closed

运行代码时出现的错误&#xff1a; 这是在运行简单的JDBC访问数据库时出现的问题&#xff0c;原因是在ResultSet方法中添加了close()关闭方法,如图&#xff1a; ResultSet 是通过 query 方法获得的&#xff0c;并且在 try-catch 块中没有显式地关闭它。这实际上是 一个常见的…

ServletContainerInitializer接口详解

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhlServletContainerInitializer概述 ServletContainerInitializer是Servlet 3.0规范中引入的一个接口,它的主要目的是允许开发者在Servlet容器(如Tomcat、Jetty等)启动时执行一些自定义的初始化代…

池化层pytorch最大池化练习

神经网络构建 class Tudui(nn.Module):def __init__(self):super(Tudui, self).__init__()self.maxpool1 MaxPool2d(kernel_size3, ceil_modeFalse)def forward(self, input):output self.maxpool1(input)return output Tensorboard 处理 writer SummaryWriter("./l…

Linux系统上安装Redis

百度网盘&#xff1a; 通过网盘分享的文件&#xff1a;redis_linux 链接: https://pan.baidu.com/s/1ZcECygWA15pQWCuiVdjCtg?pwd8888 提取码: 8888 1.把安装包拖拽到/ruanjian/redis/文件夹中&#xff08;自己选择&#xff09; 2.进入压缩包所在文件夹&#xff0c;解压压缩…

生信技能54 - WisecondorX多线程并行分析CNV

WisecondorX分析CNV,默认单样本分析,batch_analysis参数设置为True可启动多样本并行分析。 WisecondorX基本使用方法以及npz文件转换和reference构建参考文章: 生信技能53 - wiseconrdoX自动化批量npz转换和reference构建 github: https://github.com/CenterForMedicalGe…

微服务:解决复杂业务的妙方

1 微服务介绍 1)什么是微服务 ​ 微服务&#xff08;Microservices&#xff09;是一种软件架构风格&#xff0c;它将一个大型应用程序拆分成许多较小的、松散耦合的、独立运行的服务。这些服务通常围绕特定功能或业务领域组织&#xff0c;可以独立开发、部署、扩展和更新。微…

【网络安全学习】 SQL注入01:基础知识

&#x1f4bb; 1. 什么是SQL注入 SQL注入是一种针对Web程序中数据库层的安全漏洞的攻击方式。它利用了程序对用户输入数据合法性的判断或过滤不严&#xff0c;允许攻击者在设计不良的程序中添加额外的SQL语句&#xff0c;从而执行计划外的命令或访问未授权的数据。攻击者可以通…

[Unity] ShaderGraph实现不同贴图素材的同一材质球复用

无意间发现的ShaderGraph小技巧&#xff0c; 可以实现同一个ShaderGraph&#xff0c;同一个Material材质球&#xff0c; 但使用不同的Texture贴图&#xff0c;而Sprite显示不会相互覆盖。 具体实现方法如下&#xff1a; 声明Texture2D时&#xff0c;把名字命名成&#xff1a…

超燃!纯AI生成《泰坦尼克号》大片!浙大阿里发布MovieDreamer:超长电影生成“梦工厂“

论文链接&#xff1a;https://arxiv.org/pdf/2407.16655 项目主页&#xff1a;https://aim-uofa.github.io/MovieDreamer/ github链接&#xff1a;https://github.com/aim-uofa/MovieDreamer 亮点直击 MovieDreamer&#xff0c;一个新颖的分层框架&#xff0c;将自回归模型与扩…

人工智能算法工程师(中级)课程19-模型的量化与部署之模型部署和存储方式与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程19-模型的量化与部署之模型部署和存储方式与代码详解本文全面介绍了神经网络模型在实际应用中的部署与存储策略&#xff0c;重点覆盖了两大主流框架&#xff1a;LibTorch和TensorRT。L…

JavaScript模拟滑动手势

双击回到顶部 左滑动 右滑动 代码展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Gesture…

linux命令更新-文本处理awk

awk命令简介 awk是一种强大的文本处理工具&#xff0c;可以对文本文件进行格式化、统计、计算等操作。它逐行读取文本文件&#xff0c;并对每一行进行处理。awk的语法相对简单&#xff0c;但功能非常强大&#xff0c;是Linux系统中常用的文本处理工具之一。 awk命令基本语法 …