当前位置: 首页 > news >正文

Linux小课堂: 流、重定向与 cut 命令进阶

从命令行基础到流式数据处理


在 Linux 系统中,命令行不仅是执行任务的工具,更是一个强大的数据处理流水线。我们此前已掌握如 lscatcut 等基本命令的使用方式,但真正实现高效自动化与脚本化运维的关键,在于理解并掌握“流(Stream)”、“管道(Pipeline)”和“重定向(Redirection)”三大核心机制

这些概念构成了 Unix/Linux 设计哲学的核心——“一切皆流”,即程序之间的通信不是通过复杂的接口,而是通过简单、统一的数据流进行传递与处理

流(Stream) 是一个贯穿整个系统设计的核心抽象。它是一种时间上连续的数据序列,可以类比为传送带上的物品——每次只传输一个元素,而非批量打包发送。这种模型广泛应用于视频流、音频流、字节流等场景

从终端命令行的角度来看,每一个命令的执行都涉及三种基本的标准流(Standard Streams):

  • stdin(标准输入,文件描述符 0):程序从键盘或管道接收输入数据。
  • stdout(标准输出,文件描述符 1):程序正常运行结果的输出通道。
  • stderr(标准错误输出,文件描述符 2):用于输出错误信息和诊断消息,独立于 stdout。

默认情况下,这三种流均绑定到终端(Terminal),因此我们既能看见命令结果,也能看到报错信息。但真正的强大之处在于——我们可以对这些流进行重定向(Redirection),将它们指向文件、黑洞设备 /dev/null 或其他命令,从而实现自动化、日志记录和复杂的数据处理链。

重点提炼:

  • 所有命令本质上都是“处理流”的工具
  • stdoutstderr 可分别重定向,也可合并处理
  • 重定向符号是控制流走向的关键操作符

本节将围绕 cut 命令的进阶用法展开,并深入剖析输出重定向机制,包括标准输出(stdout)、标准错误输出(stderr)以及如何精准控制它们的流向,最终构建可复用、高可靠性的命令组合体系

cut 命令进阶:基于分隔符提取字段(Field-based Extraction)


1 ) 背景:为何需要 -d-f 参数?

传统的 cut -c 参数依据字符位置剪切文本,适用于固定宽度格式。但在实际应用中,因为每行字段长度不一致,往往不实用,大多数结构化文本(如日志、报表、配置文件)采用分隔符分隔字段的形式,典型代表是 CSV 文件(Comma-Separated Values)

此时,若仍使用 -c 按字符数截取,会因字段长度不一导致错位。因此必须引入:

参数含义示例
-d delimiter指定分隔符(必须紧跟分隔符字符)-d, 表示以逗号分隔
-f field-list提取指定字段(支持单个、列表、范围)-f1, -f1,3, -f2-

示例场景:有一个名为 notes.csv 的成绩记录文件,每行包含三项信息,以逗号分隔:

mark,95/100,很不错 
matthew,30/100,需努力
alice,88/100,有进步

每一行由三个字段构成:姓名、分数、评语

2 ) 实际操作示例

提取第一列:学生姓名
cut -d',' -f1 notes.csv提取第三列:评语
cut -d',' -f3 notes.csv提取第一和第三列(多字段)
cut -d',' -f1,3 notes.csv提取第二列至末尾(字段范围)
cut -d',' -f2- notes.csv

上面 -d',' 可以 简写成 -d,

注意事项:

  • 分隔符前后的空格会影响匹配结果,建议确保数据整洁
  • 若原始文件含有空字段(如 , ,),需配合其他工具(如 sedawk)预处理
  • -d 后面的分隔符必须紧跟参数,中间无空格(语法要求)

输出重定向基础:改变命令输出的目标路径 之 stdout 与 stderr 的流向


默认情况下,所有命令的正常输出都会显示在终端(stdout)
然而,我们常常希望将其保存到文件中以便后续分析或归档。这就需要用到输出重定向符号

1 ) 单大于号 >:覆盖写入新文件或清空已有文件

cut -d',' -f1 notes.csv > students.txt

该命令将 cut 的输出结果写入 students.txt 文件:

  • 若文件不存在,则自动创建;
  • 若文件已存在,则原有内容被完全覆盖,且不会提示确认。

风险提醒:误用 > 可能导致重要数据丢失,请谨慎操作

验证效果:

cat students.txt
输出:
mark 
matthew
alice

同时可通过 ls 查看目录新增文件:

ls redirect/
显示:notes.csv  students.txt

双大于号 >>:追加写入文件末尾

当需要持续记录日志或累积数据时,应使用 >> 符号:

cut -d',' -f1 notes.csv >> students.txt

此命令的作用是:

  • 将输出内容追加到目标文件末尾
  • 若文件不存在,同样会自动创建
  • 已有内容保留不变

应用场景举例:定时任务中将每次运行结果追加至日志文件:

some_command >> /var/log/results.log
符号含义行为说明
>覆盖写入若文件存在则清空内容再写入;若不存在则创建
>>追加写入总是在文件末尾添加新内容,保留原有数据

代码示例:

将学生名字保存到 students.txt(覆盖模式)
cut -d, -f1 notes.csv > students.txt再次执行,追加相同内容(模拟日志记录)
cut -d, -f1 notes.csv >> students.txt

风险提示:> 具有破坏性,误用可能导致数据丢失,建议配合 set -o noclobber 防止意外覆盖

对比两种符号行为:

操作是否创建文件是否覆盖原内容是否追加
>
>>

标准错误重定向(stderr → 文件)

由于 >>> 默认仅作用于 stdout,要捕获错误信息需显式指定文件描述符 2

符号含义
2>将 stderr 覆盖写入指定文件
2>>将 stderr 追加写入指定文件

示例:捕获不存在文件的错误

错误不会被 > 捕获(仍显示在终端)
cat not_exist.csv > results.txt正确方式:使用 2> 捕获 stderr
cat not_exist.csv 2> errors.log查看错误内容
cat errors.log
输出:cat: not_exist.csv: No such file or directory

特殊文件 /dev/null:黑洞设备与静默丢弃输出

有时我们既不想让输出显示在终端,也不想写入磁盘文件——例如调试脚本时临时屏蔽某些无关信息
Linux 提供了一个特殊的设备文件:/dev/null,俗称“黑洞文件”

任何写入该文件的数据都会被立即丢弃,无法恢复,读取则始终为空

完全静默运行命令,既不显示也不保存输出
command > /dev/null 2>&1 

此技巧常用于定时任务(cron jobs)或后台服务中,避免产生冗余输出

cut -d',' -f1 notes.csv > /dev/null

执行后,终端无输出,系统也未生成文件,整个过程“悄无声息”

技术本质解析:

  • /dev/null 是一个字符设备文件
  • 其 inode 特性决定了它永远为空
  • 常用于清理不需要的标准输出或错误输出

典型用途:

屏蔽某命令的所有输出(含错误)
command > /dev/null 2>&1

标准流详解:stdin、stdout、stderr 与文件描述符(File Descriptor)


1 ) 三大标准流定义

Linux 中每个进程默认拥有三个标准 I/O 流:

名称缩写文件描述符(FD)作用说明
标准输入stdin0接收输入,程序接收输入的通道(通常来自键盘)
标准输出stdout1输出正常结果,正常运行结果输出(默认显示在终端)
标准错误输出stderr2输出错误信息,错误信息输出通道(独立于 stdout)

关键点:stdoutstderr 是两个独立的流,即使都显示在终端上,也可分别重定向
当程序调用 open()creat() 系统调用时,内核返回最小可用的非负整数作为 FD
标准流在进程初始化时已被占用(0,1,2),后续打开的文件通常从 3 开始编号
在 Unix/Linux 系统中,一切皆可视为“文件”:普通文件、管道、套接字、设备等均可通过 FD 访问

2 ) 文件描述符(File Descriptor, FD)机制

文件描述符是一个非负整数,用于操作系统内核标识进程打开的文件资源

  • 0: stdin(标准输入)
  • 1: stdout(标准输出)
  • 2: stderr(标准错误输出)

当你执行如 open()pipe() 等系统调用时,内核返回一个可用的 FD。关闭文件后,FD 被释放回收。

类比解释:FD 类似于图书馆的借书编号,每个读者(进程)对每本书(文件/设备)都有唯一的编号来引用。

重定向标准错误输出(stderr):精准捕获错误信息


1 ) 问题提出:为什么 > 无法捕获错误?

尝试以下命令:

cat not_exist_file.csv > results.txt 

尽管使用了 >,终端仍然显示错误:

cat: not_exist_file.csv: No such file or directory

results.txt 内容为空。

原因在于:> 默认只重定向 stdout (FD=1),而错误信息属于 stderr (FD=2),并未被捕获。

2 ) 解决方案:使用 2> 重定向 stderr

语法格式:

command 2> error_file.log

示例:

cat not_exist_file.csv 2> errors.log

执行后:

  • 终端不再显示错误
  • 错误信息被写入 errors.log 文件

查看内容:

cat errors.log
输出:
cat: not_exist_file.csv: No such file or directory

3 ) 追加模式:2>>

>> 类似,2>> 将错误输出追加至文件末尾:

cat another_missing.csv 2>> errors.log

适合长期监控脚本中的异常情况

合并输出流:将 stdout 与 stderr 重定向至同一目标

有时我们需要将正常输出与错误信息统一收集到同一个日志文件中,便于集中排查问题。

1 ) 使用 2>&1 实现合并重定向

语法:

command > output.log 2>&1

含义分解:

  • >:重定向 stdout 到 output.log
  • 2>&1:将 stderr(FD=2)重定向到与 stdout(FD=1)相同的位置

解析 2>&1

  • 2> 表示重定向 stderr;
  • &1 表示“指向 FD=1 所指向的地方”;
  • 合起来就是“让 stderr 指向 stdout 的目的地”。

示例:

cat possibly_missing.txt > all_output.txt 2>&1

无论文件是否存在,所有输出(包括错误)都将进入 all_output.txt

2 ) 追加模式下的合并输出

只需将 > 改为 >>

cat test.log >> combined.log 2>> combined.log

等价写法(推荐):

cat test.log >> combined.log 2>&1

注意:此处 2>&1 必须放在最后,否则可能绑定错误的目标。

综合图解与符号总结

基本重定向符号

符号名称行为说明
>覆盖重定向将 stdout 写入指定文件,若文件存在则清空原内容
>>追加重定向将 stdout 追加到文件末尾,若文件不存在则创建
2>错误输出覆盖重定向将 stderr 写入指定文件(覆盖)
2>>错误输出追加重定向将 stderr 追加到文件末尾
&>>&全部输出重定向将 stdout 和 stderr 统一重定向到同一文件
2>&1合并错误输出将 stderr 重定向至当前 stdout 所指位置

示例

重定向形式语法功能描述
覆盖写入 stdout> file写入文件,存在则清空
追加写入 stdout>> file写入文件末尾
覆盖写入 stderr2> file错误信息写入文件
追加写入 stderr2>> file错误信息追加
合并 stdout 与 stderr> file 2>&1两者均写入同一文件
静默丢弃所有输出> /dev/null 2>&1完全屏蔽输出

基于 NestJS + TypeScript 的日志重定向模拟实现


1 ) 方案1

虽然 Shell 层面的重定向由操作系统完成,但在 Node.js/NestJS 应用中,我们可以模拟类似行为,尤其适用于 CLI 工具开发或日志服务设计。

日志服务类(LogService.ts)

// src/log/log.service.ts
import * as fs from 'fs';
import * as path from 'path';export class LogService {private readonly logPath: string;constructor(logFileName: string = 'app.log') {this.logPath = path.join(process.cwd(), 'logs', logFileName);this.ensureLogDirectory();}private ensureLogDirectory(): void {const dir = path.dirname(this.logPath);if (!fs.existsSync(dir)) {fs.mkdirSync(dir, { recursive: true });}}/* 模拟 '>':覆盖写入日志 */write(output: string): void {fs.writeFileSync(this.logPath, output + '\n');}/* 模拟 '>>':追加写入日志*/append(output: string): void {fs.appendFileSync(this.logPath, output + '\n');}/* 模拟 '2>':单独记录错误*/error(errorMessage: string): void {const timestamp = new Date().toISOString();const formatted = `[ERROR] ${timestamp} - ${errorMessage}`;fs.appendFileSync(this.logPath.replace('.log', '.error.log'), formatted + '\n');}/* 模拟 '> file 2>&1':合并输出与错误 */logEverything(message: string, isError = false): void {const timestamp = new Date().toISOString();const prefix = isError ? '[ERROR]' : '[INFO]';const line = `${prefix} ${timestamp} - ${message}\n`;fs.appendFileSync(this.logPath, line);}
}

控制器调用示例(LoggerController.ts)

// src/logger/logger.controller.ts
import { Controller, Get } from '@nestjs/common';
import { LogService } from './log.service';@Controller('logger')
export class LoggerController {private readonly logService = new LogService('results.log');@Get('success')handleSuccess() {const data = 'Processing completed successfully';this.logService.logEverything(data);return { message: data };}@Get('error')handleError() {try {throw new Error('File not found');} catch (err) {this.logService.logEverything((err as Error).message, true);return { error: (err as Error).message };}}@Get('silent')silentRun() {// 类似 > /dev/null 2>&1 console.log('This will not be logged externally');return { status: 'done silently' };}
}

shell 脚本调用 NestJS API 并重定向输出

假设启动 NestJS 服务后监听 http://localhost:3000

请求成功接口并将输出追加到日志
curl http://localhost:3000/logger/success >> api_results.log 2>&1 请求错误接口并合并记录 
curl http://localhost:3000/logger/error >> api_results.log 2>&1 静默执行(不输出任何内容)
curl http://localhost:3000/logger/silent > /dev/null 2>&1

2 ) 方案2

构建一个简易的“CSV 字段提取与日志重定向”服务的完整实现

项目结构概览

src/
├── app.controller.ts
├── app.service.ts
├── dto/
│   └── extract-field.dto.ts 
└── utils/└── stream-redirect.util.ts

DTO 定义:字段提取请求体

// src/dto/extract-field.dto.ts
export class ExtractFieldDto {readonly filePath: string;readonly delimiter: string;readonly fields: number[];readonly outputFile?: string;readonly errorFile?: string;readonly appendMode?: boolean;
}

工具类:模拟 Shell 流重定向行为

// src/utils/stream-redirect.util.ts
import { createReadStream, createWriteStream, existsSync } from 'fs';
import { parse } from 'csv-parse/sync';
import { Transform } from 'stream';export class StreamRedirectUtil {static async extractCsvFields(dto: {filePath: string;delimiter: string;fields: number[];outputFile?: string;errorFile?: string;appendMode?: boolean;},captureStderr = true,): Promise<{ stdout: string[]; stderr: string[] }> {const stdout: string[] = [];const stderr: string[] = [];// 检查文件是否存在if (!existsSync(dto.filePath)) {const errorMsg = `No such file or directory: ${dto.filePath}`;if (captureStderr) {stderr.push(errorMsg);}return { stdout, stderr };}try {const content = await fs.promises.readFile(dto.filePath, 'utf-8');const records = parse(content, {delimiter: dto.delimiter,skip_empty_lines: true,});const extracted = records.map((row) =>dto.fields.map((f) => row[f - 1] || '').join(dto.delimiter).trim(),);stdout.push(...extracted);// 输出重定向逻辑 if (dto.outputFile) {const flag = dto.appendMode ? 'a' : 'w';const ws = createWriteStream(dto.outputFile, { flags: flag });ws.write(extracted.join('\n') + '\n');ws.end();}// 错误重定向(此处仅为演示,实际无错误)if (dto.errorFile && stderr.length > 0) {const flag = dto.appendMode ? 'a' : 'w';const errWs = createWriteStream(dto.errorFile, { flags: flag });errWs.write(stderr.join('\n') + '\n');errWs.end();}} catch (err) {const msg = `Error reading/parsing file: ${(err as Error).message}`;stderr.push(msg);if (dto.errorFile) {const flag = dto.appendMode ? 'a' : 'w';const errWs = createWriteStream(dto.errorFile, { flags: flag });errWs.write(msg + '\n');errWs.end();}}return { stdout, stderr };}
}

主服务层:封装业务逻辑

// src/app.service.ts
import { Injectable } from '@nestjs/common';
import { ExtractFieldDto } from './dto/extract-field.dto';
import { StreamRedirectUtil } from './utils/stream-redirect.util';@Injectable()
export class AppService {async extractFields(dto: ExtractFieldDto): Promise<any> {const result = await StreamRedirectUtil.extractCsvFields({filePath: dto.filePath,delimiter: dto.delimiter || ',',fields: dto.fields,outputFile: dto.outputFile,errorFile: dto.errorFile,appendMode: dto.appendMode,});return {count: result.stdout.length,output: result.stdout,errors: result.stderr,redirected: !!dto.outputFile,loggedErrors: !!dto.errorFile,};}
}

控制器接口:接收外部请求

// src/app.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AppService } from './app.service';
import { ExtractFieldDto } from './dto/extract-field.dto';@Controller('csv')
export class AppController {constructor(private readonly appService: AppService) {}@Post('extract')extract(@Body() dto: ExtractFieldDto) {return this.appService.extractFields(dto);}
}

使用示例(CURL 请求模拟)

发送提取请求:提取第1、3字段,追加写入 students.txt,错误记录到 errors.log
curl -X POST http://localhost:3000/csv/extract \
-H "Content-Type: application/json" \
-d '{"filePath": "/path/to/notes.csv","delimiter": ",","fields": [1, 3],"outputFile": "students.txt","errorFile": "errors.log","appendMode": true 
}'

响应示例:

{"count": 3,"output": ["mark,很不错", "matthew,需要努力", "alice,有进步"],"errors": [],"redirected": true,"loggedErrors": true
}

3 )方案3

虽然 Node.js 不直接操作底层文件描述符,但我们可以通过 child_process 模块精确控制子进程的标准流,模拟 Linux 重定向行为。

安装依赖

pnpm add @nestjs/core @nestjs/common rxjs

创建重定向执行服务

// redirect.service.ts
import { Injectable } from '@nestjs/common';
import { spawn, SpawnOptions } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';interface RedirectOptions {stdoutFile?: string;stderrFile?: string;append?: boolean;mergeStderrIntoStdout?: boolean;
}@Injectable()
export class RedirectService {private readonly BASE_DIR = path.resolve(__dirname, '../logs');constructor() {if (!fs.existsSync(this.BASE_DIR)) {fs.mkdirSync(this.BASE_DIR, { recursive: true });}}/* 执行命令并根据选项重定向输出 */async executeCommand(command: string,args: string[],options: RedirectOptions = {},): Promise<void> {const { stdoutFile, stderrFile, append = false, mergeStderrIntoStdout = false } = options;const stdoutFlag = append ? 'a' : 'w';const stderrFlag = append ? 'a' : 'w';const stdoutPath = stdoutFile ? path.join(this.BASE_DIR, stdoutFile) : null;const stderrPath = stderrFile ? path.join(this.BASE_DIR, stderrFile) : null;const spawnOptions: SpawnOptions = {stdio: ['ignore', 'pipe', 'pipe'],};const child = spawn(command, args, spawnOptions);// 处理 stdoutif (stdoutPath) {const stdoutStream = fs.createWriteStream(stdoutPath, { flags: stdoutFlag });child.stdout.pipe(stdoutStream);} else {child.stdout.pipe(process.stdout);}// 处理 stderrif (mergeStderrIntoStdout && stdoutPath) {child.stderr.pipe(fs.createWriteStream(stdoutPath, { flags: stdoutFlag }));} else if (stderrPath) {const stderrStream = fs.createWriteStream(stderrPath, { flags: stderrFlag });child.stderr.pipe(stderrStream);} else {child.stderr.pipe(process.stderr);}return new Promise((resolve, reject) => {child.on('close', (code) => {console.log(`命令退出码: ${code}`);resolve();});child.on('error', (err) => {console.error('子进程启动失败:', err);reject(err);});});}
}

控制器调用示例

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { RedirectService } from './redirect.service';@Controller()
export class AppController {constructor(private readonly redirectService: RedirectService) {}@Get('cut-names')async cutNames() {await this.redirectService.executeCommand('cut',['-d', ',', '-f', '1', 'notes.csv'],{ stdoutFile: 'students.txt', append: true },);return { message: '已将姓名追加至 students.txt' };}@Get('run-with-error')async runWithError() {await this.redirectService.executeCommand('cat',['not_exist_file.csv'],{stdoutFile: 'output.log',stderrFile: 'errors.log',append: true,},);return { message: '命令执行完成,输出已分离记录' };}@Get('merge-output')async mergeOutput() {await this.redirectService.executeCommand('cat',['not_exist_file.csv'],{stdoutFile: 'all.log',append: true,mergeStderrIntoStdout: true,},);return { message: '标准输出与错误输出已合并至 all.log' };}
}

功能特性说明

特性实现方式
模拟 >>>使用 'w''a' 文件标志
模拟 2>2>>分别处理 stderr
模拟 2>&1stderr 管道接入 stdout 目标流
模拟 /dev/null设置 .pipe() 到空流或忽略
日志目录自动创建初始化时检查并创建
异步安全执行返回 Promise,便于集成

为何掌握流与重定向至关重要?


1 ) 【自动化基石】构建无人值守脚本的核心能力

  • 日志轮转、定时备份、异常监控均依赖精确的输出控制
  • 结合 cron>> /var/log/app.log 2>&1 可实现生产级日志管理
  1. 【管道哲学】Unix 设计精髓的体现
  • “一个程序只做一件事,并做好” —— 通过 | 管道连接多个小型工具,形成强大工作流
  • 示例:ps aux | grep node | awk '{print $2}' | xargs kill
  1. 【调试利器】分离正常输出与错误信息提升排查效率
  • 开发阶段将 stderr 单独保存便于分析问题根源
  • 生产环境中可选择性丢弃冗余输出(>/dev/null 2>&1
  1. 【安全意识】警惕 > 的覆盖风险
  • 建议启用 set -o noclobber 并使用 >> 明确意图
  • 在脚本中加入确认逻辑或使用临时文件缓冲
  1. 【跨语言迁移】流的思想贯穿所有编程范式
  • Node.js 的 Readable/Writable
  • Java 的 InputStream/OutputStream
  • Python 的 sys.stdin, sys.stdout
  • 均源自同一理论基础

关键要点凝练


主题核心要点
cut 命令进阶必须使用 -d 指定分隔符,-f 指定字段;支持单个字段、多字段(,)、范围(-)提取
标准流本质stdin(0), stdout(1), stderr(2) 是文件描述符,代表不同数据流向
重定向原理通过修改文件描述符的指向,改变输出目的地
> vs >>前者覆盖,后者追加;误用可能导致重要日志丢失
错误输出独立性stderr 默认不参与普通重定向,需显式处理
2>&1 的时机必须放在 > 之后,否则会复制旧的 stdout 目标
黑洞 /dev/null丢弃数据的理想工具,常用于清理噪音输出
工程实践意义在 CI/CD、监控脚本、日志轮转中广泛应用

掌握重定向是迈向高级 Shell 编程的关键一步


本文梳理了 流(Stream)、管道(Pipeline) 和 重定向(Redirection)
这三者构成了 Shell 编程的核心基础,也是实现自动化脚本、日志分析、错误监控等任务的关键技术

尤其是 cut 命令基于分隔符的高级用法,全面解析了 stdin/stdout/stderr 的本质与重定向符号(>, >>, 2>, 2>&1)的工作原理

通过对 cut 命令的进阶使用与输出重定向机制的系统梳理,我们不仅掌握了如何精确提取结构化文本字段,更重要的是理解了 Linux 中“流”的抽象模型及其背后的文件描述符机制

梳理如下:

  • cut 命令基于分隔符的高级剪切技巧
  • 标准输入(stdin)、标准输出(stdout)、标准错误输出(stderr)的本质
  • 各类重定向符号的工作原理与使用场景
  • 文件描述符的概念及其在重定向中的作用
  • 实际操作示例与代码验证

这些知识为后续管道(|)、进程间通信(IPC)、Shell 脚本编写乃至系统级监控与自动化部署打下坚实基础,始终记住:“命令不是孤立的工具,而是数据流中的节点。”

http://www.dtcms.com/a/506936.html

相关文章:

  • 虚拟内存核心常识
  • ubuntu配置mysql8.0并设置Navicat网络连接
  • 深圳网站维护一般多少钱网址大全黄免费片
  • 从若依框架看权限设计与数据字典:背后的工程化思考
  • 邦策网站建设平台网站建设文化咨询
  • ASTM D7033-2022 定向刨花板检测
  • 使用ThreadLocal的一些注意事项
  • Kotion 常见用法注意事项(持续更新...)
  • 如何使用思维导图提升信息整理效率
  • K-VXE-TABLE二次封装,含table‘自定义列功能
  • 基于 GEE 开发的一种利用 OTSU 算法实现水体提取的便捷工具
  • Linux小课堂: 深入解析 top、htop、glances 及进程终止机制
  • 建设协会网站洛阳伟创科技
  • MongoDB 提供的 `GridFSTemplate` 操作 GridFS 大文件系统的常用查询方式
  • 2025年ASOC SCI2区TOP,基于模糊分组的多仓库多无人机电力杆巡检模因算法,深度解析+性能实测
  • 无人机地面站中不同的飞行模式具体含义释义(开源飞控常用的5种模式)
  • Inventor 转换为 3DXML 全流程技术指南:附迪威模型网在线方案
  • Maven POM 简介
  • pytorch踩坑记录
  • seo每天一贴博客南宁网站排名优化电话
  • 手机端网站开发书籍徐州vi设计公司
  • STM32F1和STM32F4在配置硬件SPI1时有什么不同?
  • 衣柜灯橱柜灯MCU方案开发
  • 数据访问对象模式(Data Access Object Pattern)
  • 滚动显示效果
  • Spring Cloud - Spring Cloud 微服务概述 (微服务的产生与特点、微服务的优缺点、微服务设计原则、微服务架构的核心组件)
  • YOLOv4:目标检测领域的 “速度与精度平衡大师”
  • agent设计模式:第二章节—路由
  • 玩转Docker | 使用Docker安装uptime-kuma监控工具
  • flutter开发小结