做過(guò)嵌入式Linux開(kāi)發(fā)或使用過(guò)桌面Linux系統(tǒng)的童鞋們,肯定對(duì)shell命令交互印象比較深刻,然而我們大多數(shù)搞嵌入式軟件開(kāi)發(fā)的碼農(nóng)都是基于單片機(jī),比如51、STM32等進(jìn)行開(kāi)發(fā)的,在單片機(jī)上能否做個(gè)shell命令行交互?答案當(dāng)然是可以的,在網(wǎng)上類(lèi)似的文章和代碼一搜一籮筐, 基本原理: 監(jiān)測(cè)用戶(hù)的輸入,然后到一個(gè)命令查找表里過(guò)濾是否可以找到該命令,如果可以則調(diào)用對(duì)應(yīng)的處理函數(shù),當(dāng)然做的好點(diǎn)的話還可以向處理函數(shù)傳遞參數(shù)。
主要的數(shù)據(jù)結(jié)構(gòu)及解析函數(shù)定義如下,注意這里函數(shù)指針的定義,Argc代表參數(shù)的個(gè)數(shù),可以為0、1、2...,Argv用于存放具體的參數(shù),可能有些童鞋要問(wèn)為啥定義成CHAR **,這是因?yàn)槲覀冊(cè)趕hell交互窗口輸入的內(nèi)容都會(huì)被當(dāng)作ASCII碼字符串,所以只能用CHAR *來(lái)指向它們,另外又因?yàn)槲覀兛赡軙?huì)輸入多個(gè)字符串參數(shù)(多個(gè)參數(shù)以空格進(jìn)行間隔),所以要使用二級(jí)指針CHAR **,可能有的同學(xué)會(huì)發(fā)現(xiàn),我們平時(shí)見(jiàn)的標(biāo)準(zhǔn)main函數(shù)的原型就是這樣定義的
int main(int argc, char *argv[])
char *argv[]與char **argv是等價(jià)的,這個(gè)就不需要解釋了吧,采用這種定義方式可以非常靈活,具體見(jiàn)下面的用法示例:
#define SHELL_MAX_PARA_NUM 20 //最多支持20個(gè)命令參數(shù)
// 函數(shù)指針
typedef UINT8 (* Cmd_Analys_Fun_P)(UINT8 Argc, CHAR **Argv);
typedef struct
{
CHAR *pName;
Cmd_Analys_Fun_P pCmdFunc; // 命令解析函數(shù)
} S_Shell_Cmd;
/***************************************************************
* 函數(shù)名稱(chēng): Shell_Proc
* 功能描述: Shell交互處理
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返 回 值:
****************************************************************/
UINT8 Shell_Proc(CHAR *ucCmd, UINT8 ucCmdLength)
{
UINT8 Result;
Result = Cmd_Analys(Shell_Cmd, S_NUM(Shell_Cmd), ucCmd, ucCmdLength);
if ((Result EQ 1) || (Result EQ 2))
{
//vConsoleLog("[shell]#");
}
return Result;
}
/***************************************************************
* 函數(shù)名稱(chēng): Cmd_Analys
* 功能描述: 命令解析
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返 回 值:
****************************************************************/
LOCAL UINT8 Cmd_Analys(CONST S_Shell_Cmd Shell_Cmd[], UINT8 Num, CHAR Cmd[], UINT8 Len)
{
UINT8 i, j;
UINT8 Argc, Cmd_Len;
CHAR *(Argv[SHELL_MAX_PARA_NUM]);
Cmd_Len = 0;
for (i = 0; i < Len; i++)
{
if ((Cmd[i] EQ 'r') || (Cmd[i] EQ 'n')) // 找到回車(chē)換行鍵, 說(shuō)明已經(jīng)輸入了一條完整命令
{
Cmd_Len = i; // 記錄命令長(zhǎng)度
Cmd[i] = '?';
break;
}
else if (Cmd[i] EQ ' ') // 空格全部替換成'?'
{
Cmd[i] = '?';
}
}
if (i EQ Len) // 沒(méi)有找到命令
{
return 0;
}
if (Cmd_Len EQ 0) // 全部輸入的是空格或者回車(chē)
{
vConsoleLog("rnShell:"); // 提示輸入新的命令
return 1;
}
for (i = 0; i < Num; i++)
{
if (strcmp(Shell_Cmd[i].pName, Cmd) != 0)
{
continue;
}
j = (UINT8)strlen(Cmd);
Argc = 0;
while (j < Cmd_Len)
{
if (Cmd[j] EQ '?' && Cmd[j + 1] != '?') // 前一個(gè)是空格,后一個(gè)非空格,說(shuō)明是一個(gè)新參數(shù)
{
if (Argc < SHELL_MAX_PARA_NUM)
{
Argv[Argc] = &Cmd[j + 1];
Argc++;
}
else
{
break;
}
}
j++;
}
// 執(zhí)行命令
(*Shell_Cmd[i].pCmdFunc)(Argc, Argv);
break;
}
if (i EQ Num)
{
vConsoleLog("Cmd Error!");
return 2;
}
return 1;
}
**用法1:**只有命令,沒(méi)有參數(shù)
/***************************************************************
* 函數(shù)名稱(chēng): RebootTerminal
* 功能描述: 重啟終端
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返 回 值:
****************************************************************/
LOCAL UINT8 RebootTerminal(UINT8 argc, CHAR **argv)
{
//發(fā)起復(fù)位請(qǐng)求
udwResetTimeCounter = 0;
blResetRequestFlag = TRUE;
vConsoleLog("Terminal Prepare Reboot ...");
return 1;
}
**用法2:**命令+1個(gè)參數(shù)
/***************************************************************
* 函數(shù)名稱(chēng): ConsoleOutputRedirect
* 功能描述: console輸出重定向
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返 回 值:
****************************************************************/
LOCAL UINT8 ConsoleOutputRedirect(UINT8 argc, CHAR **argv)
{
if (argc != 1)
{
vConsoleLog("miss argumentrn");
return 0;
}
if (!strcmp(argv[0], "on"))
{
ucConsoleRedirectFlag = 1;
vConsoleLog("console output redirect to tcpconsolern");
}
else if (!strcmp(argv[0], "off"))
{
ucConsoleRedirectFlag = 0;
vConsoleLog("console output redirect to localconsolern");
}
else
{
vConsoleLog("error argumentrn");
return 0;
}
return 1;
}
**用法3:**命令+N個(gè)參數(shù)
/***************************************************************
* 函數(shù)名稱(chēng): SetTerminalTime
* 功能描述: 設(shè)置終端時(shí)間
* 輸入?yún)?shù):
* 輸出參數(shù):
* 返 回 值:
****************************************************************/
LOCAL UINT8 SetTerminalTime(UINT8 argc, CHAR **argv)
{
UINT8 ucTime[6];
if (argc != 6)
{
vConsoleLog("Param Err! argc = %d", argc);
return 0;
}
ucTime[0] = strtoul(argv[0], NULL, 0);
ucTime[1] = strtoul(argv[1], NULL, 0);
ucTime[2] = strtoul(argv[2], NULL, 0);
ucTime[3] = strtoul(argv[3], NULL, 0);
ucTime[4] = strtoul(argv[4], NULL, 0);
ucTime[5] = strtoul(argv[5], NULL, 0);
ucTimeTestFlag = 1;
stCurrentTime.ucYear = ucTime[0];
stCurrentTime.ucMonth = ucTime[1];
stCurrentTime.ucDay = ucTime[2];
stCurrentTime.ucHour = ucTime[3];
stCurrentTime.ucMin = ucTime[4];
stCurrentTime.ucSec = ucTime[5];
vConsoleLog("SetTerminalTime: %02d/%02d/%02d %02d:%02d:%02d", ucTime[0], ucTime[1], ucTime[2],
ucTime[3], ucTime[4], ucTime[5]);
return 1;
}
以上三種用法,基本可以涵蓋現(xiàn)實(shí)中的各種使用需求!
以上就是shell命令的基本用法,至于如何捕捉用戶(hù)的輸入,方式和方法就很多了,不過(guò)常用的就下面的幾種情況:
- 終端設(shè)備上的串口(這種最常見(jiàn))
- 終端設(shè)備上的網(wǎng)口(稍微有點(diǎn)門(mén)檻,后面會(huì)專(zhuān)門(mén)寫(xiě)一篇STM32的文章介紹這種用法)
- 如果終端設(shè)備已經(jīng)登錄了后臺(tái)主站云平臺(tái),直接在云平臺(tái)上給終端設(shè)備下發(fā)shell命令
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19126瀏覽量
305249 -
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209524 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62622 -
系統(tǒng)
+關(guān)注
關(guān)注
1文章
1017瀏覽量
21347
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論