【香橙派系列教程】(十六) 语音模块与阿里云结合

news2024/11/17 4:21:52

【十六】语音模块与阿里云结合

本小节实现功能使用语音模块和摄像头在香橙派上做垃圾智能分类识别。

大致流程:说出“识别垃圾类型”口令,语音模块接收到之后通过串口发送字符串指令,随后进行拍照(请确保香橙派已经驱动起来了摄像头,我们需要将此步骤设置为开机自启动)将照片发送到阿里云进行识别,返回字符串做出判断,由香橙派再次发送字符串给语音模块,反馈出得到的垃圾类型。

文章目录

  • 【十六】语音模块与阿里云结合
    • 1.环境准备
    • 2.代码实现
      • uartTool.h
      • uartTool.c
      • garbage.py
      • garbage.c
      • garbage.h
      • main.c
    • 3.编译运行
    • 附录:
    • 1.如何打开串口5
    • 2.popen函数
    • 3.fgets函数
    • 4.remove函数
    • 5.assess函数
    • 6.awk
    • 7.写个shell脚本用于杀死运行的进程

1.环境准备

  1. 将语音模块接在UART5的位置

image-20240721211956152

  1. 在orange pi 3.0.6上确认已经配置开启了uart5:(overlays=uart5)

串口5默认是关闭的,如果没有打开请看附录部分

orangepi@orangepizero2:~/garbage$ cat /boot/orangepiEnv.txt
verbosity=1
bootlogo=false
console=both
disp_mode=1920x1080p60
overlay_prefix=sun50i-h616
rootdev=UUID=15a0010c-94e1-412f-b030-199e90c16cb1
rootfstype=ext4
overlays=uart5 i2c3
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u
  1. 同时将USB摄像头接到香橙派上
  2. 确认已经运行了mjpg-streamer服务
orangepi@orangepizero2:~/garbage$ ps ax | grep mjpg
1704 ? S 0:00 /bin/bash /home/orangepi/mjpg.sh
1710 ? Sl 0:29 ./mjpg_streamer -i ./input_uvc.so -d /dev/video1 -u -f 30 -o ./output_http.so -w
./www
5594 pts/0 S+ 0:00 grep --color=auto mjpg
orangepi@orangepizero2:~/garbage$

2.代码实现

阿里云与语音模块交互

  1. 首先创建garbage目录,将garbage.c、garbage.h、 garbage.py三个文件拷贝进来。

  2. 参照《基于官方外设开发,Linux原生串口开发》代码实现, 修改uartTool.h:

uartTool.h

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "wiringSerial.h"

#ifndef __UARTTOOL__H
#define __UARTTOOL__H

int my_serialOpen (const char *device, const int baud) ;
void my_serialSendstring (const int fd, const unsigned char *s, int len) ;
int my_serialGetstring (const int fd, unsigned char *buffer) ;

#define SERIAL_DEV "/dev/ttyS5"
#define BAUD 115200

#endif

uartTool.c

#include "wiringSerial.h"
#include "uartTool.h"

int my_serialOpen (const char *device, const int baud)
{
	struct termios options ;   // 创建一个termios结构体,用于串口参数设置
	speed_t myBaud ;   // 创建一个速度类型的变量 myBaud,用于保存波特率
	int status, fd ;   // 创建整数类型的变量 status 和 fd,用于保存状态和文件描述符
 
	switch (baud){   // 根据传入的波特率参数选择合适的波特率常数
		case   9600: myBaud =   B9600 ; break ; 
		case 115200: myBaud = B115200 ; break ; 
	}
	if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)   // 打开串口设备,设置打开选项
	return -1 ;   // 如果打开失败,返回错误代码 -1
	
	fcntl (fd, F_SETFL, O_RDWR) ;   // 设置文件状态标志
	
// Get and modify current options: 获取并修改当前的串口参数:
	tcgetattr (fd, &options) ;   // 获取当前的串口参数
	cfmakeraw (&options) ;   // 初始化 termios 结构体为原始模式
	cfsetispeed (&options, myBaud) ;  // 设置输入波特率
    cfsetospeed (&options, myBaud) ;  // 设置输出波特率
    options.c_cflag |= (CLOCAL | CREAD) ;  // 本地连接和使能接收
    options.c_cflag &= ~PARENB ;  // 禁用奇偶校验
    options.c_cflag &= ~CSTOPB ;  // 1位停止位
    options.c_cflag &= ~CSIZE ;  // 用数据位掩码清空数据位设置
    options.c_cflag |= CS8 ;  // 设置8位数据位
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;   // 禁用规范输入
	options.c_oflag &= ~OPOST ;   // 禁用输出处理
	options.c_cc [VMIN] = 0 ;   // 读取数据的最小字符数
	options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds) 超时等待时间(十分之一秒100ms)
	tcsetattr (fd, TCSANOW, &options) ;   // 设置新的串口参数
	ioctl (fd, TIOCMGET, &status);   // 获取串口控制模式状态
	status |= TIOCM_DTR ;   // 设置 DTR(数据终端就绪)位
	status |= TIOCM_RTS ;   // 设置 RTS(请求发送)位
 
	ioctl (fd, TIOCMSET, &status);   // 设置串口控制模式状态
	
	usleep (10000) ;  // 暂停 10 毫秒
	return fd ;   // 返回串口文件描述符
}
//会传递0XAA 0X55 0X46 0x00 0X55 0XAA类似的参数
void my_serialSendstring (const int fd, const unsigned char *s, int len)
{
	int ret ;
	ret = write (fd,s,len); 	
	if (ret < 0) 
		printf ("Serial Sendstring Error\n") ;
}

int my_serialGetstring (const int fd, unsigned char *buffer)
{
	int n_read ;
	n_read = read (fd, buffer, 32) ; 	
	return n_read ;
}

garbage.py

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_imagerecog20190930

import os
import io
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions

config = Config(
  # 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
  # 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html
  # 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
  access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
  access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
  # 访问的域名
  endpoint='imagerecog.cn-shanghai.aliyuncs.com',
  # 访问的域名对应的region
  region_id='cn-shanghai'
)
#封装成函数
def alibaba_garbage():
    #场景一:文件在本地
    img = open(r'/home/orangepi/trash/test.jpg', 'rb')#以后把拍到的垃圾文件统一放到这个文件里面,不用跟着改了
    #场景二:使用任意可访问的url
   # url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'
    #img = io.BytesIO(urlopen(url).read())

    classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()
    classifying_rubbish_request.image_urlobject = img
    runtime = RuntimeOptions()
    try:
      # 初始化Client
      client = Client(config)
      response = client.classifying_rubbish_advance(classifying_rubbish_request, runtime)
      # 获取整体结果
      print(response.body.to_map()['Data']['Elements'][0]['Category'])
      return response.body.to_map()['Data']['Elements'][0]['Category']
    except Exception as error:
      return '获取失败'

        
if __name__ == "__main__":
    alibaba_garbage()

garbage.c

第一种写法:

#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void garbage_init(){
    Py_Initialize();
    // 将当前路径添加到sys.path中
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyUnicode_FromString(".")); 
}
void garbage_final()
{
    // 关闭Python解释器
    Py_Finalize();
}
char *garbage_category(char *category)
{

    // 导入para模块
    PyObject *pModule = PyImport_ImportModule("garbage");
    if (!pModule)
    {
        PyErr_Print();
        printf("Error: failed to load garbage.py\n");
        goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE
    }
    //获取say_funny函数对象
    PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_garbage");
    if (!pFunc)
    {
        PyErr_Print();
        printf("Error: failed to load say_funny\n");
        goto FAILED_FUNC;
    }
    //创建一个字符串作为参数
    //char *category = "comedy";
    //PyObject *pArgs = Py_BuildValue("(s)", category);//字符串加括号表示他是一个包含字符串的元组
    
    //调用say_funny函数并获取返回值
    PyObject *pValue = PyObject_CallObject(pFunc, NULL);
    if (!pValue)
    {
        PyErr_Print();
        printf("Error: function call failed\n");
        goto FAILED_VALUE;
    }
    //将返回值转换为C类型
    char *result = NULL;
    if (!PyArg_Parse(pValue, "s", &result))
    {
        PyErr_Print();
        printf("Error: parse failed\n");
        goto FAILED_RESULT;
    }
    //打印返回值
    //printf("pValue=%s\n", result);
    // 为垃圾分类信息分配内存,复制返回值
    category = (char *)malloc(sizeof(char) * (strlen(result) + 1));
    memset(category, 0, (strlen(result) + 1));
    strncpy(category, result, (strlen(result) + 1));
    //释放所有引用的Python对象
FAILED_RESULT:
    Py_DECREF(pValue);
FAILED_VALUE:
    Py_DECREF(pFunc);
FAILED_FUNC:
    Py_DECREF(pModule);
FAILED_MODULE:
    return category;
}

garbage.c

第二种写法:

#include <stdio.h>
#include <Python.h>
#include "garbage.h"

void garbage_init()
{  
    Py_Initialize();// 初始化Python解释器
    PyObject* sysPath = PySys_GetObject("path");// 获取sys.path对象
    // 将当前路径添加到sys.path中
    PyList_Append(sysPath, PyUnicode_DecodeFSDefault(".")); // PyUnicode_FromString将c字符串转换成Python字符串
}

void garbage_final()
{
    // 关闭Python解释器
    Py_Finalize();
}

char *garbage_category(char *category) 
{
    // 导入Python模块
    PyObject* pModule = PyImport_ImportModule("garbage");

    if (pModule != NULL) {
        // 获取Python函数对象
        PyObject* pFunction = PyObject_GetAttrString(pModule, "alibaba_garbage");

        if (pFunction != NULL && PyCallable_Check(pFunction)) {
            // 调用Python函数,这里是无参数调用
            PyObject* pArgs = PyTuple_New(0);  // 传递空参数元组
            PyObject* pResult = PyObject_CallObject(pFunction, pArgs);

            if (pResult != NULL) {
              // 将返回值转换为C类型
              char *result = NULL;
  
              if (!PyArg_Parse(pResult, "s", &result)) {
                PyErr_Print();
                printf("Error: parse failed\n");
              }
  
                // 打印返回值
                printf("pResult = %s\n", result);
                
                // 为垃圾分类信息分配内存,复制返回值
                category = (char *)malloc(sizeof(char) * (strlen(result) + 1));
                memset(category, 0, (strlen(result) + 1));
                strncpy(category, result, (strlen(result) + 1));
                 
                Py_DECREF(pResult);
            } else {
                PyErr_Print(); // 打印Python错误信息
            }

            Py_DECREF(pFunction);
            Py_DECREF(pArgs);
        } else {
            PyErr_Print();
        }

        Py_DECREF(pModule);
    } else {
        PyErr_Print();
    }
    return category;
}

garbage.h

#ifndef __GARBAGE__H
#define __GARBAGE__H

void garbage_init();
void garbage_final();
char *garbage_category(char *category);

// 增加拍照指令和照片路径宏定义
#define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg"
#define GARBAGE_FILE "/tmp/garbage.jpg"

#endif

main.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include "uartTool.h"
#include "garbage.h"

 // 判断进程是否在运行
static int detect_process(const char * process_name)
{
	int n = -1;
	FILE *strm;
	char buf[128] = {0};
	
	// 构造命令字符串,通过ps命令查找进程
	sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);
	// 使用popen执行命令并读取输出
	if ((strm = popen(buf, "r")) != NULL) {
		if (fgets(buf, sizeof(buf), strm) != NULL) {
			n = atoi(buf); // 将进程ID字符串转换为整数
		}
	}
	else {
		return -1; // popen失败
	}	
	pclose(strm); // 关闭popen打开的文件流

	return n;
}

int main(int argc, char *argv[])
{
	int serial_fd = -1;
	int ret = -1;
	unsigned char buffer[6] = {0xAA, 0X55, 0x00, 0x00, 0x55, 0xAA};
	int len = 0;
	char *category = NULL;

	// 初始化串口和垃圾分类模块
	garbage_init ();

	// 用于判断mjpg_streamer服务是否已经启动
	ret = detect_process ("mjpg_streamer");
	if (-1 == ret) {
		printf("detect process failed\n");
        goto END;
	}
	
	// 打开串口
	serial_fd = my_serialOpen (SERIAL_DEV, BAUD);
	if (-1 == serial_fd) {
		goto END;
	}

	while (1) {
		// 从串口读取数据
		len = my_serialGetstring (serial_fd, buffer);
		printf("lend = %d, buf[2] = 0x%x\n", len, buffer[2]);
		
		if (len > 0 && buffer[2] == 0x46) {
			buffer[2] = 0x00;

			// 在执行wget命令之前添加调试输出
			printf("Executing wget command...\n");
			// 使用系统命令拍照
			system(WGET_CMD);
			// 在执行wget命令之后添加调试输出
			printf("Wget command executed.\n");
			
			// 判断垃圾种类
			if (0 == access(GARBAGE_FILE, F_OK)) {
				category = garbage_category (category);
				if (strstr(category, "干垃圾")) {
					buffer[2] = 0x41;
				}
				else if (strstr(category, "湿垃圾")) {
					buffer[2] = 0x42;
				}
				else if (strstr(category, "可回收垃圾")) {
					buffer[2] = 0x43;
				}
				else if (strstr(category, "有害垃圾")) {
					buffer[2] = 0x44;
				}
				else {
					buffer[2] = 0x45; //未识别到垃圾类型
				}
			}
			else {
                buffer[2] = 0x45; //识别失败
            }
			// 发送分类结果到串口
			my_serialSendstring (serial_fd, buffer, 6);
			buffer[2] = 0x00;
			remove(GARBAGE_FILE); // 删除拍照文件
		}
	}
END:
    close(serial_fd);
	// 释放垃圾分类资源
	garbage_final();

	return 0;
}

3.编译运行

编译 
gcc -o test *.c *.h -I /usr/include/python3.10 -l python3.10

执行 
sudo -E ./test
sudo -E 命令用于在以超级用户权限运行命令的同时,保留环境变量

查看进程 
ps -ax | grep mjpg_streamer | grep -v grep
ps -ax | grep ./test | grep -v grep
ps aux | grep './test' | grep -v grep | awk '{print $2}'

杀死进程 
kill -9 pid (-9——-SIGKILL)

附录:

1.如何打开串口5

  1. 由 26pin 接口的原理图可知,Orange Pi Zero 2 可用的 uart 为 uart5

image-20240721212335060

如果使用的为 Linux5.16 内核的系统,uart5 默认是关闭的,需要手动打开才能
使用。
在/boot/orangepiEnv.txt 中加入下面红色字体部分的配置,然后重启 Linux 系统就
可以打开 uart5。
orangepi@orangepi:~$ sudo vim /boot/orangepiEnv.txt
overlays=uart5

  1. 进入 linux 系统后,先确认下/dev 下是否存在 uart5 的设备节点
root@orangepi:~# ls /dev/ttyS5
/dev/ttyS5
  1. 然后开始测试 uart5 接口,先使用杜邦线短接要测试的 uart5 接口的 rx 和 tx
  1. 使用 wiringOP 中的 gpio 命令测试串口的回环功能如下所示,如果能看到下面的打印,说明串口通信正常
orangepi@orangepi:~$ gpio serial /dev/ttyS5
Out: 0: -> 0
Out: 1: -> 1
Out: 2: -> 2
Out: 3: -> 3^C

2.popen函数

popen 是一个在 C 语言中用于打开进程管道的函数,它允许你创建一个进程,并与该进程进行通信。这个函数通常用于执行一个命令,并从其标准输出读取数据,或者向其标准输入写入数据。

函数原型如下:

FILE *popen(const char *command, const char *type);
  • command:要执行的命令字符串。
  • type:指定了打开管道的方式,可以是以下两种模式之一:
    • "r":读取模式,从子进程的标准输出读取数据。
    • "w":写入模式,向子进程的标准输入写入数据。

如果函数调用成功,它会返回一个指向新打开的进程文件的指针,你可以使用标准的文件I/O函数(如 freadfwritefclose 等)来操作这个文件。如果调用失败,它会返回 NULL

使用 popen 时需要注意几点:

  • 必须使用 pclose 函数来关闭通过 popen 打开的管道,以确保子进程正确终止。
  • 管道的读写操作是阻塞的,即如果管道的缓冲区满了,写操作会等待直到有空间可用;如果缓冲区空了,读操作会等待直到有数据可读。
  • 使用 popen 时要小心处理错误和异常情况,确保资源被正确释放。

下面是一个简单的使用 popen 的例子:

#include <stdio.h>

int main() {
    FILE *fp;
    char buffer[128];

    // 打开一个管道来读取 ls 命令的输出
    fp = popen("ls", "r");
    if (fp == NULL) {
        perror("popen");
        return 1;
    }

    // 读取输出
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }

    // 关闭管道
    pclose(fp);

    return 0;
}

这个例子中,我们使用 popen 来执行 ls 命令,并读取其输出,然后逐行打印出来。最后,使用 pclose 来关闭管道。

3.fgets函数

fgets 是C语言标准库中的一个函数,用于从文件或输入流中读取字符串。这个函数会从指定的输入流中读取字符,直到遇到换行符 \n 或达到指定的字符数为止,并将读取的字符串存储在提供的缓冲区中。

函数原型如下:

char *fgets(char *str, int num, FILE *stream);
  • str:指向字符数组的指针,用于存储读取的字符串。
  • num:指定最多要读取的字符数,包括空字符(\0)。
  • stream:指向 FILE 结构的指针,表示要从哪个文件或输入流读取数据。

如果 fgets 成功读取数据,它会返回 str 指向的字符串。如果发生错误或提前遇到文件结束(EOF),它会返回 NULL

以下是 fgets 函数的一些关键点:

  • fgets 会将读取到的第一个换行符包含在返回的字符串中,但在字符串的末尾会添加一个空字符(\0)。
  • 如果读取的字符数达到 num - 1 之前没有遇到换行符,fgets 将停止读取,并将空字符添加到字符串末尾。
  • 如果 num 大于1,str 必须有足够的空间来存储至少 num - 1 个字符加上空字符。

下面是一个使用 fgets 的示例代码:

#include <stdio.h>

int main() {
    char buffer[100];
    FILE *file = fopen("example.txt", "r"); // 打开文件以供读取

    if (file == NULL) {
        perror("Error opening file");
        return 1;
    }

    // 从文件中读取一行
    if (fgets(buffer, sizeof(buffer), file) != NULL) {
        printf("Read line: %s", buffer);
    }

    // 关闭文件
    fclose(file);

    return 0;
}

在这个例子中,我们使用 fgets 从文件 example.txt 中读取一行文本,并将读取的内容存储在 buffer 数组中。然后,我们打印这行文本。最后,我们使用 fclose 函数关闭文件。如果文件打开失败,我们使用 perror 打印错误信息。

4.remove函数

在C语言中,remove 函数用于删除文件系统中的一个文件。这个函数定义在 <stdio.h> 头文件中,其原型如下:

int remove(const char *filename);
  • filename:要删除的文件的路径名。

函数返回值:

  • 如果成功删除文件,返回0。
  • 如果失败,返回-1,并设置 errno 以指示错误类型。

使用示例

#include <stdio.h>

int main() {
    const char *filename = "example.txt";
    
    if (remove(filename) == 0) {
        printf("File '%s' has been successfully deleted.\n", filename);
    } else {
        perror("Failed to delete file");
    }

    return 0;
}

在这个示例中,我们尝试删除名为 example.txt 的文件。如果删除成功,会打印一条成功消息;如果失败,则使用 perror 函数打印错误信息。

注意事项

  1. 权限问题:确保程序有足够的权限来删除指定的文件。
  2. 文件状态:如果文件正在被使用(例如,被其他程序打开),可能无法删除。
  3. 目录项限制:某些文件系统可能有删除文件的限制,例如文件名长度或字符限制。
  4. 错误处理:始终检查 remove 函数的返回值,并适当处理错误。

remove 函数是处理文件删除的简单方法,但应谨慎使用,特别是在生产环境中,以避免意外删除重要文件。

5.assess函数

在C语言中,access 函数用于检查进程是否可以对文件进行特定的访问。这个函数定义在 <unistd.h> 头文件中(在Windows上是 io.h),其原型如下:

int access(const char *pathname, int mode);
  • pathname:要检查访问权限的文件或目录的路径。
  • mode:指定要检查的访问权限类型,可以是以下宏的组合:
    • F_OK:文件是否存在。
    • R_OK:文件是否可读。
    • W_OK:文件是否可写。
    • X_OK:文件是否可执行(在UNIX系统中,这个标志检查文件是否具有执行权限;在Windows系统中,这个标志被忽略)。

函数返回值:

  • 如果指定的文件存在并且具有相应的访问权限,返回0。
  • 如果文件不存在或没有相应的访问权限,返回-1,并设置 errno 以指示错误类型。

使用示例

#include <stdio.h>
#include <unistd.h>

int main() {
    const char *filename = "example.txt";
    
    // 检查文件是否存在
    if (access(filename, F_OK) != -1) {
        // 检查文件是否可读
        if (access(filename, R_OK) != -1) {
            printf("File '%s' exists and is readable.\n", filename);
        } else {
            perror("File is not readable");
        }
    } else {
        perror("File does not exist");
    }

    return 0;
}

在这个示例中,我们首先检查 example.txt 文件是否存在,然后检查它是否可读。如果文件存在但不可读,我们使用 perror 函数打印错误信息。

注意事项

  1. 权限问题access 函数检查的是文件的访问权限,这可能受到操作系统安全策略和用户权限的影响。
  2. 符号链接:在UNIX系统中,如果 pathname 是一个符号链接,access 函数的行为取决于编译时定义的宏(如 AT_SYMLINK_NOFOLLOW)。
  3. 文件状态:即使文件当前具有访问权限,文件的状态也可能随时改变,例如被其他用户删除或修改权限。
  4. 错误处理:始终检查 access 函数的返回值,并适当处理错误。

access 函数是检查文件访问权限的简单方法,但应谨慎使用,特别是在需要高度安全性的应用程序中。

6.awk

awk 是一种强大的文本处理工具,广泛用于数据提取、报告生成、数据过滤等任务。在 UNIX 和类 UNIX 系统中,awk 是一种常用的命令行程序。

当你使用命令 awk '{print $2}' 时,你告诉 awk 读取输入文件的每一行,并打印出每行的第二个字段。这里的 $2 表示每行的第二个字段,字段之间通常由空白字符(如空格或制表符)分隔。

命令解释

  • awk:命令本身。
  • {print $2}:这是 awk 的动作部分,告诉 awk 要执行的操作。print $2 表示打印每行的第二个字段。

使用示例

假设你有一个名为 data.txt 的文件,内容如下:

apple 10
banana 20
cherry 30

运行以下命令:

awk '{print $2}' data.txt

输出将会是:

10
20
30

更多选项

  • 如果你只想打印第二列,并且每行的字段由逗号分隔,你可以使用 awk -F, '{print $2}'
  • 你还可以通过指定不同的字段分隔符来处理更复杂的数据格式。例如,使用 awk -F"\t" '{print $2}' 来处理制表符分隔的字段。

其他用途

  • awk 也可以用于更复杂的文本处理任务,如条件判断、循环、数学运算等。例如,打印每行的第二字段,但只打印值大于15的行:
    awk '{if ($2 > 15) print $2}' data.txt
    

awk 是一个非常灵活的工具,适合处理各种文本数据。

7.写个shell脚本用于杀死运行的进程

当你运行程序时,shell脚本可以使用ps命令查找到特定进程的PID并使用kill命令杀死该进程。
以下是一个简单的Shell脚本示例,假设你的程序名为test

#!/bin/bash

# 查找进程PID
PID=$(ps aux | grep './test' | grep -v grep | awk '{print $2}')

if [ -n "$PID" ]; then
    # 杀死进程
    kill -SIGKILL $PID
    echo "Process ./test (PID $PID) killed."
else
    echo "Process ./test not found."
fi

这段脚本是一个 Bash 脚本,用于查找并可能终止名为 ./test 的进程。下面是对脚本的逐行分析:

  1. #!/bin/bash

    • 这是一个 shebang 行,告诉操作系统使用 /bin/bash 来执行这个脚本。bash 是 Bourne Again Shell 的缩写,是许多 UNIX 和 Linux 系统上默认的 shell。
  2. # 查找进程PID

    • 这是一行注释,解释接下来的命令的作用。
  3. PID=$(ps aux | grep './test' | grep -v grep | awk '{print $2}')

    • 这行命令执行了一系列管道操作来查找进程 ID(PID)。
      • ps aux:列出当前运行的所有进程。
      • grep './test':过滤出包含 ./test 的行,假设 ./test 是你想要查找的进程名称。
      • grep -v grep:过滤掉包含 grep 的行,避免 grep 命令本身也被匹配。
      • awk '{print $2}':使用 awk 打印每行的第二个字段,通常是进程的 PID。
    • 这个命令的输出(即找到的 PID)被赋值给变量 PID
  4. if [ -n "$PID" ]; then

    • 这是一个条件语句,检查变量 PID 是否不为空(即是否找到了进程)。
  5. # 杀死进程

    • 这是另一行注释,说明接下来的命令的作用。
  6. kill -SIGKILL $PID

    • 如果找到了进程(即 PID 不为空),则发送 SIGKILL 信号强制终止进程。SIGKILL 是一个强制杀死进程的信号,进程无法捕获或忽略。
  7. echo "Process ./test (PID $PID) killed."

    • 输出一条消息,告知用户进程已被杀死,并显示进程的 PID。
  8. else

    • 如果条件语句中的条件不满足(即没有找到进程),则执行 else 块中的命令。
  9. echo "Process ./test not found."

    • 输出一条消息,告知用户没有找到名为 ./test 的进程。
  10. fi

    • 结束 if 条件语句。

这个脚本是一个简单的例子,展示了如何在 Bash 中使用条件语句和管道来处理系统任务。然而,需要注意的是,使用 kill -SIGKILL 强制终止进程可能会导致数据丢失或其他问题,因此在生产环境中应谨慎使用。此外,脚本中的 grep 命令可能需要根据实际情况调整,以确保正确匹配进程名称。

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

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

相关文章

2024/8/25 Nacos本机配置

目录 一、nacos下载 二、修改配置文件 2.1、配置数据库 2.2、配置鉴定密钥 2.3、配置启动脚本 三、nacos启动 3.1、启动运行脚本 3.2、新增配置 3.3、服务列表 记录一下本机nacos2.2.3配置全过程 一、nacos下载 下载地址&#xff1a;https://github.com/alibaba/nacos/r…

安灯系统赋能照明行业打造高效智能的生产管理体系

在当今竞争激烈的照明行业中&#xff0c;提高生产效率、确保产品质量以及实现智能化管理已成为企业生存和发展的关键。安灯系统作为一种先进的生产管理工具&#xff0c;正逐渐在照明行业中发挥着重要作用&#xff0c;为企业打造高效智能的生产管理体系提供有力支持。 一、照明行…

LeetCode31

206.反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#…

秋招TCP篇(较全的TCP网络知识,通俗理解底层逻辑)

TCP详细知识 计算机网络八股-局域网和广域网详解八股-OSI七层模型和TCP/IP五层模型八股-数据在网络之间传递的过程八股-UDP详解根据协议格式详解TCPSocket详解八股-TCP可靠性机制确认应答超时重传超时重传等待时间数据去重 八股-三次握手和四次挥手三次握手四次挥手为什么要三次…

系统之家官网网址:一键直达,安全无忧!

有很多用户反映自己搜搜系统之家&#xff0c;会出现很多网址&#xff0c;不知道哪个才是真正的系统之家官网网址&#xff1f;对于想要访问真正的系统之家官网&#xff0c;获取更多优质系统资源及解决相关系统问题的用户而言&#xff0c;记住系统之家的官网网址是至关重要。以下…

4款文章生成器,自动写作优质文章

在当今信息爆炸的时代&#xff0c;内容创作已经成为网络世界中不可或缺的一部分。然而&#xff0c;随着人们对高质量内容的需求不断增加&#xff0c;传统的手动创作已经无法满足市场的需求。因此&#xff0c;文章生成器应运而生&#xff0c;成为许多从业者和企业的利器。在本文…

公司图纸文件加密超详细的三大步骤,图纸加密软件最新推荐

随着企业信息化建设的不断深入&#xff0c;图纸文件成为了企业核心数据的重要组成部分。特别是在制造、建筑、设计等行业&#xff0c;CAD图纸文件承载了大量的商业秘密和技术机密。一旦这些图纸被泄露或未经授权地访问&#xff0c;企业将面临巨大的经济损失和声誉损害。因此&am…

【已解决】JS Uncaught DOMException: Failed to construct ‘Worker’ 所有场景

【已解决】JS Uncaught DOMException: Failed to construct ‘Worker’ 所有场景 概述 在JavaScript中&#xff0c;Web Workers允许我们运行后台脚本&#xff0c;这些脚本不会影响到页面的性能。然而&#xff0c;当我们尝试创建一个新的Worker时&#xff0c;有时会遇到“Uncau…

时钟同步方案的参考方案

网络时间服务器是为网络设备提供精确、标准、安全、可靠和多功能的时间服务的最佳解决方案&#xff0c;能提供精确的同步时钟信号&#xff0c;支持标准的NTP和SNTP网络对时协议&#xff0c;提供高精度的网络同步时钟直接来自于GPS系统中各个卫星的原子钟。 那么时钟同步方案我…

idea发送请求提示 无法保留 Cookie,cookie storage file is included in ignored list:

你们好&#xff0c;我是金金金。 场景 IDEA插件httpclient发起请求&#xff0c;提示如下 翻译如下&#xff1a;cookie存储文件被列入忽略列表 解决 查看下是不是你把.idea文件忽略显示了&#xff0c;忽略后会不检索 索引不知道所在位置&#xff0c;所以需要把.idea从忽略列表…

【python】如何通过Python中的http.server搭建文件上传下载服务

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

C++系列-泛型编程之类模板

泛型编程之类模板 类模板类模板的定义方式类模板的举例 类模板和函数模板的区别类模板中的成员函数创建时机类模板的对象做函数的参数指定传入类型类中的参数模板化整个类模板化 类模板与继承类模板中成员函数的类外实现类模板分文件编写类模板分文件编写的问题问题原因解决方案…

Java框架myBatis(二)

一、#{}和${}的区别 #{}所采取的时占位符方式&#xff08;底层是预编译模式&#xff09;&#xff0c;与JDBC中的&#xff1f;方式相同&#xff0c;传参更加方便安全 &#xff0c;防止了sql注入。当我们需要向sql传值&#xff0c;使用#{}&#xff1b; ${}是将内容直接拼接到sql…

Uniapp 微信小程序,实现页面滚动Tab悬停吸顶,点击tab内容跟随滚动

Uniapp 微信小程序&#xff0c;实现页面滚动Tab悬停吸顶&#xff0c;点击tab内容跟随滚动 页面股东tab悬停原理&#xff1a; 运用uniapp原生提供方法uni.createSelectorQuery()获取滚动对应节点的信息&#xff0c;即节点距离页面顶部的距离&#xff0c;再通过uniapp原生监听页…

【C语言】深入理解指针(一)

指针1 1.内存和地址2.指针变量和地址2.1取地址操作符&#xff08;&&#xff09;2.2指针变量和将引用操作符2.3解引用操作符2.4指针变量的大小 3.指针变量类型的意义3.1指针变量类型3.2void*指针 4.const修饰指针5.指针运算5.1 指针- 整数5.2指针-指针3.指针的关系运算 6.野…

《高等代数》最大公因式典型例题

说明&#xff1a;此内容用于本人复习巩固&#xff0c;如果也能帮助到大家那就更加有意义了。 注&#xff1a;这道题主要从 1)公因式整除多项式的线性组合 2)最大公因式能够被其它公因式整除 3)如果两个多项式互相整除&#xff0c;那就说明这两个多项式相等 这三个知识点出…

Hadoop的HA配置与实现(ZooKeeper)

目录 一、Hadoop的HA架构二、配置实现Hadoop的HA三、效果 一、Hadoop的HA架构 集群规划 112&#xff1a;NameNode1 ResourceManager1 JournalNode1 113&#xff1a;NameNode2 ResourceManager2 JournalNode2 114&#xff1a;DataNode1 NodeManager1 115&#xff1a;DataNode2 N…

如何快速建30个文件夹

文件夹的快速新建只能通过专门的软件来实现&#xff0c;快速建30个&#xff0c;我们可以使用批处理命令&#xff0c;也可以用第三方软件&#xff0c;批处理的命令&#xff0c;需要我们熟悉如何编写&#xff0c;而第三方软件的话不需要那么多的知识&#xff0c;直接在软件上一键…

货运搬家小程序app定制开发过程中需要的主要功能介绍

货运搬家小程序是一个集成了物流服务全过程的数字化解决方案&#xff0c;它通过移动平台提供注册与登录、货物管理、车辆管理、路线规划、货物分配、订单管理、实时追踪、评价与反馈、价格透明等功能。 具体的功能&#xff1a; 注册与登录用户可以通过小程序注册账号并登录&am…

inflight 守恒算法的实现和仿真

前面介绍过&#xff0c;只要某条流的 inflt 在 bdp 之外再增加一个相等的余量 I&#xff0c;即 inflt bdp I&#xff0c;比如 I 2&#xff0c;I 3&#xff0c;…&#xff0c;就一定会收敛到公平&#xff0c;且不会占据过多 buffer&#xff0c;因此 rtt 不会膨胀&#xff0c…