智能家居代碼構建編寫、簡單工廠模式、樹莓派攝像頭視頻監控功能實現

根據上一節內容的代碼框架開始編寫代碼:

首先編寫controlDevices.h這個頭文件里面的代碼,這個是設備工廠每一個結點的結構體類型,而且還要在這個頭文件里面進行函數的聲明,也就是創建的那些設備.c文件里面的函數(為了將設備添加至設備鏈表的函數),其中這個頭文件里面的結構體內容根據功能提前設定。同樣然后再編寫inputCommand.h這個頭文件里面的內容,這個是指令工廠里面的頭文件,也是指令鏈表里面的每一個結點的類型。編寫完這兩個頭文件,然后再進行設備工廠設備文件、指令工廠指令文件和main.c文件的編寫。

  • controlDevices.h是指令工廠頭文件代碼,結點結構體的聲明,這里面的東西不一定夠用,可以先寫上,等不夠的時候在進行添加。
#include<wiringPi.h> //包含wiringPi庫
#include<string.h>
struct Devices
{int status;  //表示開關的狀態int pinNum;char devicesName[128]; //存放設備的名稱int (*open)(int pinNum);int (*close)(int pinNum);int (*deviceInit)(int pinNum);int (*readStatus)(int pinNum);int (*changStatus)(int status);struct Devices*next;
};
//以下幾行將設備添加至設備鏈表的函數聲明,便于以后的查找引用
struct Devices* addBathroomLightToDeviceLink(struct Devices* phead);
struct Devices* addSecondFlootLightToDeviceLink(struct Devices* phead);
struct Devices* addRestaurantLightToDeviceLink(struct Devices* phead);
struct Devices* addLivingRoomLightToDeviceLink(struct Devices* phead);
struct Devices* addFireContrlToDeviceLink(struct Devices* phead);
  • inputCommand.h是設備工廠頭文件代碼,里面有設備鏈表每一個結點的結構體類型的聲明,和指令工廠頭文件類似。
#include<wiringPi.h>
#include<string.h>
#include<wiringSerial.h>
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
struct InputCommander
{int fd;int socketfd;char port[12];           //端口號char ipAdress[32];       //ip地址char command[32];	     //存放指令信息char log[1024];          //存放日志信息char devicesname[128];   //存放串口設備名字char commandName[128];int (*getCommand)(struct InputCommander* voicer); //接收指令函數int (*Init)(struct InputCommander* voicer,char* ipAdress,char* port);struct InputCommander* next;
};
//將指令結點添加至指令鏈表中的函數聲明
struct InputCommander* addVoiceToDeviceLink(struct InputCommander* phead);
struct InputCommander* addSocketToDeviceLink(struct InputCommander* phead);
  • 首先編寫設備工廠的設備文件bathroomLight.c
#include"contrlDevices.h"  //包含頭文件
int bathroomLightOpen(int pinNum)
{digitalWrite(pinNum,LOW);//將引腳電平拉低,點亮浴室燈
}
int bathroomLightClose(int pinNum)
{digitalWrite(pinNum,HIGH);//將引腳電平拉高,熄滅浴室燈
}
int bathroomLightInit(int pinNum)
{pinMode(pinNum,OUTPUT);digitalWrite(pinNum,HIGH);//初始化引腳功能
}
struct Devices bathroomLight={.pinNum=22,             //浴室燈繼電器控制IO口引腳.devicesName="bathroomLight", //通過這個設備名進行浴室燈結點的查找,然后再進行結構體函數的調用.deviceInit=bathroomLightInit,.open=bathroomLightOpen,.close=bathroomLightClose,
};
struct Devices* addBathroomLightToDeviceLink(struct Devices* phead) //將浴室燈結點插入到設備工廠鏈表里面,采用頭插法
{if (phead==NULL){return &bathroomLight;}else{bathroomLight.next=phead;phead=&bathroomLight;}
}
  • fire.c文件代碼,代碼框架和浴室燈代碼框架相同,不同的是這個文件里面要有讀取引腳狀態的函數,同時引腳也要設置為輸入模式,當檢測到或火災是火災傳感器的引腳會被拉為低電平。
#include"contrlDevices.h"
int fireContrlInit(int pinNum)
{pinMode(pinNum,INPUT);digitalWrite(pinNum,HIGH);
}
int fireReadstatus(int pinNum)
{int ret;ret=digitalRead(pinNum);return ret;
}
struct Devices fireContrl={.pinNum=25,             //火災報警器輸入IO口.devicesName="fire",.deviceInit=fireContrlInit,.readStatus=fireReadstatus
};
struct Devices* addFireContrlToDeviceLink(struct Devices* phead)
{if (phead==NULL){return &fireContrl;}else{fireContrl.next=phead;phead=&fireContrl;}
}
  • 由于其他幾個燈光控制文件里面的代碼和第一個浴室燈控制代碼大同小異,所以這里不再贅述,下面是語音指令輸入文件代碼,這個文件里面的函數就要添加讀取指令函數和初始化函數,所謂的初始化函數就是將串口打開然后設置相應的波特率,讀取指令函數需要注意的是在讀取指令前需要將緩存區初始化防止有亂碼,讀指令函數主要調用read函數進行指令的讀取,在沒有指令到來的時候,輸出:usart for voice read over time,其實代碼框架和設備工廠的框架基本類似,只是文件里面包含的函數有所差異,但都有一個設備結點插入函數。
#include "inputCommand.h"
int voiceInit(struct InputCommander* voicer)//就是對串口的初始化
{int fd;if((fd=serialOpen(voicer->devicesname,115200))==-1){   //open serial,波特率115200printf("usrat open fail\n");exit(-1);}voicer->fd=fd;return fd;
}
int voiceGetCommand(struct InputCommander* voicer)
{int nread=0;memset(voicer->command,'\0',sizeof(voicer->command));nread=read(voicer->fd,voicer->command,sizeof(voicer->command));if(nread==0){printf("usart for voice read over time\n");}else{return nread;}
}
struct InputCommander voiceContrl={.commandName="voice",.command={'\0'},.devicesname="/dev/ttyAMA0",.next=NULL,.getCommand=voiceGetCommand,.Init=voiceInit,.log={'\0'}
};
struct InputCommander* addVoiceToDeviceLink(struct InputCommander* phead)
{if(phead==NULL){return &voiceContrl;}else{voiceContrl.next=phead;phead=&voiceContrl;}	
}
  • 下面是socket指令文件代碼,這個里面不需要getCommmand這個函數因為在這里寫了,對后面多線程的處理不是特別的方便,計劃的是連接進來一個客戶端然后起一個線程去對接,但是客戶端發送完一條消息后,需要斷開連接然后重新連接,因為代碼里面采用的是點對點的方式。《socket知識補充》
#include "inputCommand.h"
int socketInit(struct InputCommander* socketMes)//就是對socket的初始化
{int socketfd;int bindre;int listenre;int len=sizeof(struct sockaddr_in);struct sockaddr_in IP;memset(&IP,'\0',len);IP.sin_family=AF_INET; //協議IP.sin_port=htons(atoi(socketMes->port));IP.sin_addr.s_addr=inet_addr(socketMes->ipAdress);socketfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//建立套接字if(socketfd==-1){printf("socket create fail\n");perror("socket");exit(-1);}else{printf("socket create successful\n");}bindre=bind(socketfd,(struct sockaddr*)&IP,len); //綁定服務器IP地址和端口號listenre=listen(socketfd,10);   //監聽printf("socket server listening.........\n");socketMes->socketfd=socketfd;return socketfd;
}
struct InputCommander socketContrl={.commandName="socketServer",.command={'\0'},.next=NULL,.Init=socketInit,               //socket初始化函數,建立套接字,然后綁定、監聽、等待客戶端的連接.log={'\0'},.port="8088",                 //服務器端口號.ipAdress="192.168.43.136"   //服務端IP地址
};
struct InputCommander* addSocketToDeviceLink(struct InputCommander* phead)
{if(phead==NULL){return &socketContrl;}else{socketContrl.next=phead;phead=&socketContrl;}	
}
  • 最后進行main函數代碼的編寫,main函數里面涉及到設備工廠、指令工廠頭結點的插入和設備文件、指令文件分別插入到設備鏈表和指令鏈表。同時還要有結點查找函數:包括設備結點查找函數、指令結點查找函數,查找后返回結點指針然后對特定結點進行操作即可。同時main函數里面還涉及到線程的創建,socket_thread這個函數里面在有客戶端接入的時候又進行了線程的創建,用來對接接入的客戶端。《線程知識補充》
#include<stdio.h>
#include<string.h>
#include <unistd.h>
#include <pthread.h>
#include "inputCommand.h"
#include"contrlDevices.h"
int newfd;
struct Devices *pdeviceHead=NULL;//將設備鏈表的頭結點設置為全局變量
struct InputCommander* socketHandler=NULL; //這個是查找到的的socket指令結點,將它設為全局變量是因為socket_thread這個函數有用到這個節點//除此之外,socket_thread的子線程read_thread也有用到這個節點指針,雖然可以通過創建線程傳參,但是不建議那么做。
struct InputCommander* pcommandHead=NULL;//將指令鏈表的頭結點設置為全局變量
struct Devices *findDevicesByName(char*name,struct Devices*phead)//查找設備結點函數
{struct Devices *tmp=phead;if(phead==NULL){return NULL;}else{while(tmp!=NULL){if(strcmp(tmp->devicesName,name)==0)return tmp;tmp=tmp->next;}return NULL;}
}
struct InputCommander *findCommandByName(char*name,struct InputCommander*phead)//查找指令結點函數
{struct InputCommander *tmp=phead;if(phead==NULL){return NULL;}else{while(tmp!=NULL){if(strcmp(tmp->commandName,name)==0)return tmp;tmp=tmp->next;}return NULL;}
}
void* read_thread(void *data)//當有新的客戶端接入的時候,創建線程去對接,這個函數就是線程對接函數,用于讀取客戶端指令
{int n_read;memset(socketHandler->command,'\0',sizeof(socketHandler->command));n_read=read(newfd,socketHandler->command,sizeof(socketHandler->command));if(n_read==-1)perror("read");else if(n_read>0){printf("\n get:%d,%s\n",n_read,socketHandler->command);}else{printf("client quit\n");}
}
void* voice_thread(void*data)//語音線程函數,用于等待語音指令,在這里設置為一個包含有while(1)的線程
{int nread;struct InputCommander* voiceHandler;voiceHandler=findCommandByName("voice",pcommandHead);if(voiceHandler==NULL){printf("find voiceHandler error!\n");pthread_exit(NULL);   //查找指令工廠語音部分失敗退出當前線程}else{printf("%s find successful\n",voiceHandler->commandName);if(voiceHandler->Init(voiceHandler)<0){printf("voice init error\n");pthread_exit(NULL);   //初始化失敗退出當前線程}else{printf("%s init successful!\n",voiceHandler->commandName);}while(1){nread=voiceHandler->getCommand(voiceHandler);if(nread==0){printf("nodata from voice\n");}else{printf("do divece contrl:%s\n",voiceHandler->command);}}}
}
void* socket_thread(void*data)//socket線程,用于與客戶端對接,這個是main函數里面創建線程函數
{pthread_t readthread; int len=sizeof(struct sockaddr_in);struct sockaddr_in CLI;//客戶端信息memset(&CLI,'\0',len);socketHandler=findCommandByName("socketServer",pcommandHead);if(socketHandler==NULL){printf("find socketHandler error!\n");pthread_exit(NULL);   //查找指令工廠socket部分失敗退出當前線程}else{printf("%s find successful!\n",socketHandler->commandName);}socketHandler->Init(socketHandler);while(1){newfd=accept(socketHandler->socketfd,(struct sockaddr*)&CLI,&len);pthread_create(&readthread,NULL,read_thread,NULL);}
}
int main()
{if(wiringPiSetup()==-1){return -1;}//初始化樹莓派硬件,這個只需要執行一次所以放在main函數里面即可pthread_t voicetd;pthread_t sockettd;//指令工廠初始化pcommandHead=addVoiceToDeviceLink(pcommandHead);			//插入語音指令結點pcommandHead=addSocketToDeviceLink(pcommandHead);			//插入socket指令結點//設備控制工廠初始化pdeviceHead=addBathroomLightToDeviceLink(pdeviceHead);    //插入浴室燈pdeviceHead=addRestaurantLightToDeviceLink(pdeviceHead);  //插入餐廳燈pdeviceHead=addSecondFlootLightToDeviceLink(pdeviceHead); //插入二樓浴室燈pdeviceHead=addLivingRoomLightToDeviceLink(pdeviceHead);  //插入客廳燈pdeviceHead=addFireContrlToDeviceLink(pdeviceHead);       //插入火災報警器控制//線程池建立、語音線程、socket線程pthread_create(&voicetd,NULL,voice_thread,NULL);pthread_create(&sockettd,NULL,socket_thread,NULL);pthread_join(voicetd,NULL);pthread_join(sockettd,NULL);//等待指定線程退出return 0;
}

代碼編寫完成后通過ftp工具傳輸到樹莓派編譯(我這里用的是FileIlla)
在這里插入圖片描述

  • 使用指令:gcc *.c -o test -lpthread -lwiringPi進行編譯,然后執行可以看到下圖:(在語音沒有指令的時候打印nodata from voice)
    在這里插入圖片描述
  • 然后進行串口接收信息功能的測試,使用串口前按照這篇博文進行串口的設置,我這里使用的是USB轉ttl連接電腦進行測試測試結果如下:(測試成功)
    在這里插入圖片描述
    在這里插入圖片描述
  • 然后進行socket客戶端連接的測試,在電腦端使用網絡調試助手,輸入IP地址和端口號進行連接。下圖是發送和接受的結果顯示。
    在這里插入圖片描述
    在這里插入圖片描述

樹莓派mjpg-streamer監控功能調試:

使用監控功能使用樹莓派現成的庫mjpg-streamer,樹莓派利用pi Camera模塊,通過mjpg-streamer軟件獲取視頻,通過手機端或電腦端瀏覽實時視頻。mjpg-streamer是一個開源的攝像頭媒體流,通過本地獲取攝像頭的數據,再通過http通訊發出來,然后再通過瀏覽器訪問樹莓派的ip地址和對應的端口號就能看到對應的視頻流。mjpg-streamer是一個比較好的軟件框架,他用的是插件的思想,它將相應的功能編譯成相應的.so庫,然后通過代碼里面的delsy將.so庫里面的API拿來用。

  • 下載前先下載一下幾個工具和庫:
    sudo apt-get install libjpeg8-dev #JPEG支持庫,圖像處理庫
    sudo apt-get install imagemagick
    sudo apt-get install libv4l-dev #4l是小寫"L",這個是底層攝像頭驅動的上層的一個應用庫,底層是value for linuxv4表示value 4l表示linux,這是一個開源的底層視頻設備驅動的一個庫。
    sudo apt-get install cmake #下載編譯工具
  • git clone https://github.com/jacksonliam/mjpg-streamer.git下載mjpg-streamer庫,
  • 下載好在這個庫之后cd mjpg-streamer/mjpg-streamer-experimental //進入下載目錄后進入左側路徑,然后使用指令:make all #進行編譯,出現下圖錯誤,表示樹莓派里面沒有cmake編譯工具,make指令會調用cmake的東西,sudo apt-get install cmake進行安裝即可。
    在這里插入圖片描述
    出現下圖表示編譯成功:
    在這里插入圖片描述
  • 然后使用指令:sudo make install #進行安裝,結果如圖:
    ,
  • 然后打開啟動腳本start.sh,這里面有啟動腳本,如下圖所示:input_uvc是使用uvc攝像頭,也就是usb口的攝像頭,output_http表示使用http輸出。而實際上樹莓派的應該使用input_raspicam.so,所依要進行修改。將input_uvc.so改為 input_raspicam.so即可 。在這里插入圖片描述在這里插入圖片描述 在這里插入圖片描述
  • 然后使用指令:sudo raspi-config打開設置再將攝像頭打開即可。
    在這里插入圖片描述在這里插入圖片描述
    在這里插入圖片描述
  • 最后使用指令:./start.sh執行腳本即可,然后通過瀏覽器輸入 http://IP地址:8080,回車 顯示如下頁面,點擊頁面左側,Stream欄,顯示監視畫面。
    在這里插入圖片描述

智能家居人臉識別方案:

  • 對于人臉識別這個功能的實現我采用人工智能開放平臺——祥云平臺,只要掌握了這一個平臺后臺API的開發,同樣就可以使用其他平臺的方案去開發車牌識別、人臉識別、圖片識別等等功能。下面是翔云平臺的產品:
    在這里插入圖片描述

  • 先試用一下人臉識別功能,注冊登錄后開始使用人臉識別功能,比對結果有JSON數據( JSON 是一種輕量級的傳輸數據格式 , 用于數據交互 ,json是一種與語言無關的數據交換的格式.),這種在網頁上點擊進行的識別是進行的BS(browser serve,就是瀏覽器服務)的識別,每一次網頁訪問都是BS模式,這種通用的協議是http的協議,人臉識別就是讓代碼完成剛才點擊的一系列操作,就是讓代碼發起http請求,不一定要掉瀏覽器發起http請求,因為瀏覽器的后臺也是通過http的請求來獲取數據。既然要使用代碼發起http請求就要了解linux如何使用C語言發起http請求。在之后的文章里面會有如何使用智能云平臺。
    在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272130.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272130.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272130.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux中Shell的命令替換用法筆記

命令替換主要是指將命令的標準輸出值賦值給某個變量。命令替換屬于Shell編程中非常重要的功能&#xff0c;需要熟悉掌握。 命令替換的方式 1、反引號:命令 2、$()&#xff1a;$(命令) 用法說明&#xff1a;date1date #將date命令值給date1變量 date2$(date) #將date命令值給dat…

采樣次數不同平均值不一樣_網絡推廣采取的方式不同,效果也不一樣

網絡推廣是基于互聯網而進行&#xff0c;針對目標客戶群體&#xff0c;加之以有效的網絡鏈接形式使其形成迅速大的購買力的一種營銷模式&#xff0c;是目前很多企業已經認識到的&#xff0c;但是在推廣過程之中往往會出現不同的狀況&#xff0c;比如說模式是完全不一樣的。 …

rocketmq 同步刷盤和異步刷盤以及主從復制之同步復制和異步復制你理解了嗎

同步刷盤、異步刷盤 RocketMQ的消息是存儲到磁盤上的&#xff0c;這樣既能保證斷電后恢復&#xff0c;又可以讓存儲的消息量超出內存的限制。 RocketMQ為了提高性能&#xff0c;會盡可能地保證磁盤的順序寫。消息在通過Producer寫入RocketMQ的時候&#xff0c;有兩種 寫磁盤方…

七層網絡模型、TCP/IP四層模型、網絡數據包、交換機路由器區別

七層網絡模型&#xff08;簡稱OSI模型&#xff09;&#xff1a; OSI 模型(Open System Interconnection model)是一個由國際標準化組織提出的概念模型,試圖供一個使各種不同的計算機和網絡在世界范圍內實現互聯的標準框架。它將計算機網絡體系結構劃分為七層,每層都可以提供抽…

Linux中Shell的算數運算符和位運算符用法筆記

1、算數運算符 算數運算符主要是加、減、乘、除、余、冪等常見的算術運算&#xff0c;以及加等、減等、乘等、除等、余等復合算術運算。 注意&#xff1a;Shell只支持整數運算&#xff0c;小數部分會舍去。一般情況下算術運行需要個let命令一起來使用。注意除法運算&#xff0c…

ostu閾值分割python實現_PIL大津法閾值分割Python代碼

[Python]代碼#codingutf8"""大津法閾值分割:A Threshold Selection Method from Gray-Level Histograms需要PIL庫"""import Imageimport sysdef sigma(im,i,debug False):"""閾值為i時,圖像im中兩組的方差"""c0_p…

php執行一條insert插入兩條數據其中一條亂碼

顯然這就是編碼問題&#xff0c;但是問題從哪來的呢&#xff0c; 我把文件編碼以及代碼的編碼都設置成utf-8了&#xff0c;為什么還有這個問題于是我就開始寫測試腳本 第一條 mysql_query(insert into table value(1,1,"思考思考123")) 測試沒有問題 第二條 $name$_G…

TCP三次握手、四次揮手、socket,tcp,http三者之間的區別和原理

接著上一篇文章敘述&#xff1a; TCP/IP連接(在互聯網的通信中&#xff0c;永遠是客戶端主動連接到服務端)&#xff1a; 手機能夠使用聯網功能是因為手機底層實現了TCP/IP協議&#xff0c;可以使手機終端通過無線網絡建立TCP連接。TCP協議可以對上層網絡提供接口&#xff0c;使…

小程序接入h5頁面_小程序-實現怎么跳轉打開 H5 網頁鏈接(或跳轉至公眾號文章)...

背景有時候,因為業務需求,在小程序當中,需要跳轉到 h5 網頁,或跳轉到公眾號,形成流量的閉環,那在小程序當中怎么實現呢?實例效果前提條件該小程序與需要跳轉至的公眾號(訂閱號/服務號)進行了綁定關聯使用小程序開放能力web-view實現跳轉(承載網頁的容器。會自動鋪滿整個小程序…

NSUserDefaults的用法

NSUserDefaults適合存儲輕量級的本地數據&#xff0c;比如要保存一個登陸界面的數據&#xff0c;用戶名、密碼之類的&#xff0c;個人覺得使用NSUserDefaults是首選。下次再登陸的時候就可以直接從NSUserDefaults里面讀取上次登陸的信息咯。 因為如果使用自己建立的plist文件什…

http、https、密碼學基礎、GET和POST區別

http協議相關&#xff1a; http協議的特性: http協議是建立在TCP/IP協議之上應用層協議&#xff0c;默認端口為80或者8080。http協議的的特點是無狀態&#xff0c;無連接&#xff08;并不是真的沒有連接&#xff0c;而是在請求數據的時候有連接&#xff0c;在數據回來的時候就…

apache 配置文件內使用 8080 端口_【SpringBoot 框架】- SpringBoot 配置文件

一、SpringBoot配置文件類型SpringBoot是基于約定的&#xff0c;所以很多配置都有默認值&#xff0c;但如果想使用自己的配置替換默認配置的話 &#xff0c;就可以自己編寫配置文件進行相應配置&#xff0c;起步依賴spring-boot-starter-parent 中&#xff0c;有配置文件的引入…

Linux中while循環的用法筆記

Shell中可以采用while循環來實現需要進行循環的操作。 語法結構如下&#xff1a; while exp do command done 執行過程&#xff1a;while將測試exp的返回值&#xff0c;如果返回值為true則執行循環體的命令&#xff0c;返回值為false則不執行循環。循環完成后會進入下一次循環之…

IOS9.0 導航欄狀態欄

狀態欄高度20px 豎屏是導航欄高度44px 橫屏時導航欄高度32px 獲取當前屏幕狀態&#xff1a;UIApplication.sharedApplication().statusBarOrientation轉載于:https://www.cnblogs.com/iOSboyYang/p/5328886.html

libcurl庫的安裝和使用

libcurl簡介: libcurl是一個跨平臺的網絡協議庫&#xff0c;支持http, https, ftp, gopher, telnet, dict, file, 和ldap 協議。libcurl同樣支持HTTPS證書授權&#xff0c;HTTP POST, HTTP PUT, FTP 上傳, HTTP基本表單上傳&#xff0c;代理&#xff0c;cookies,和用戶認證。li…

android 微信縮小通話界面_安卓如何做出微信那樣的界面仿微信“我”的界面2/5...

本系列目標通過安卓編程仿寫微信“我”的界面,讓大家也能做出類似微信界面.效果圖如下:本文目標做出支付部分(其他部分在后續文章中逐步分享).效果圖如下:實現方案通過截圖工具或者下載一張微信支付照片,放到工程的src/main/res/drawable目錄下,命名為pay.png;同樣獲取一張向右…

Shell腳本中函數的定義和調用筆記

Shell腳本函數的作用主要是把一些可以通用的功能封裝起來、避免腳本中出現大量重復的腳本代碼&#xff0c;同時可以大大增強腳本的可讀性、和可維護性。Shell函數定義的語法格式&#xff1a;function FUNCTION_NAME(){command1command2command3}也可以省略function 關鍵字FUNCT…