一個精簡的控制台工具,展示 密碼驗證 → 命令列參數分派 → 文本文件讀取/命令匹配/執行 的完整流程。適用於嵌入式裝置維護介面、自動化腳本前置驗證等場景。
test,最多 15 字元,錯誤即退出argc < 2)→ 列印 command.txt 全部內容argv[1],成功則拼接所有參數並模擬執行command.txt,逐行 fgets 處理/* cmd_tool.c */
#define _POSIX_C_SOURCE 200809L /* 確保 strdup 等可用 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* isatty, read */
#define MAX_LINE 1024
#define PASSWD "test"
static void secure_getpass(char *buf, size_t sz) {
/* 簡易版:關閉回顯(POSIX) */
if (isatty(STDIN_FILENO)) {
system("stty -echo");
fgets(buf, sz, stdin);
system("stty echo");
putchar('\n');
} else {
fgets(buf, sz, stdin);
}
buf[strcspn(buf, "\r\n")] = '\0'; /* 去除換行 */
}
int main(int argc, char *argv[]) {
char input[32];
/* 1. 密碼驗證 */
while (1) {
printf("please input password to login: ");
secure_getpass(input, sizeof(input));
if (strcmp(input, PASSWD) == 0) {
puts("Enjoy it!");
break;
}
puts("your password can't login.");
return EXIT_FAILURE;
}
/* 2. 開啟命令檔 */
FILE *fp = fopen("command.txt", "r");
if (!fp) {
perror("command.txt");
return EXIT_FAILURE;
}
char line[MAX_LINE];
/* 3. 無參數 → 列表模式 */
if (argc < 2) {
puts("Command List:");
while (fgets(line, sizeof(line), fp))
fputs(line, stdout);
fclose(fp);
return EXIT_SUCCESS;
}
/* 4. 有參數 → 匹配模式 */
int found = 0;
while (fgets(line, sizeof(line), fp)) {
line[strcspn(line, "\r\n")] = '\0'; /* 去除換行再比較 */
if (strcmp(argv[1], line) == 0) {
found = 1;
break;
}
}
fclose(fp);
if (!found) {
fprintf(stderr, "sorry, can't find your command: %s\n", argv[1]);
return EXIT_FAILURE;
}
/* 5. 拼接完整命令列並輸出(實際專案可改為 execvp 或系統 API 呼叫) */
char cmd[MAX_LINE] = {0};
for (int i = 1; i < argc; ++i) {
strncat(cmd, argv[i], sizeof(cmd) - strlen(cmd) - 1);
if (i + 1 < argc)
strncat(cmd, " ", sizeof(cmd) - strlen(cmd) - 1);
}
printf("find the command.\nthe command is: %s\n", cmd);
/* TODO: 實際執行邏輯,例如:
* pid_t pid = fork();
* if (pid == 0) { execvp(argv[1], &argv[1]); perror("exec"); _exit(127); }
* waitpid(pid, NULL, 0);
*/
return EXIT_SUCCESS;
}
| 原版問題 | 改進方案 |
|---|---|
scanf("%s") 會截斷空白、無緩衝區保護 | 自訂 secure_getpass,支援關閉回顯、防溢出、跨平台 fgets 回退 |
strncmp(argv[1], Buffer, strlen(Buffer)-1) 邏輯錯誤(長度含換行) | 先 strcspn 去除換行,再用 strcmp 精確匹配 |
ReceiveCommand 未初始化導致 UB | 初始化 char cmd[MAX_LINE] = {0};,改用 strncat 防溢出 |
硬編碼 exit(0/1) | 改用 EXIT_SUCCESS/FAILURE,語意更清晰 |
| 無錯誤碼區分 | perror 輸出系統錯誤碼,便於診斷 |
$ gcc -Wall -Wextra -std=c11 cmd_tool.c -o cmd_tool
$ echo -e "backup\nrestart\nstatus" > command.txt
$ ./cmd_tool
please input password to login: test
Enjoy it!
Command List:
backup
restart
status
$ ./cmd_tool backup now
please input password to login: test
Enjoy it!
find the command.
the command is: backup now
crypt(3) / argon2 雜湊存儲,避免明文硬編碼fork()+execvp() 或 posix_spawn() 真正執行子程式# 註解、key=value 格式、INI/JSON 解析syslog 或輕量級日誌庫(如 log.c)