int system(const char *command) 是 C 標準庫提供的執行 Shell 命令接口。流程:fork() 子進程 → execl("/bin/sh", "sh", "-c", command, NULL) → 父進程 waitpid() 等待。返回值:Shell 退出碼(高 8 位),-1 表示 fork 失敗。
/**********************************************************************
* 文件: TestShellCommand.c
* 功能: 演示 C 語言執行 shell 命令刪除文件
* 編譯: gcc -Wall -o TestShellCommand TestShellCommand.c
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main() {
char file_index[100] = {0};
char file_dir[100] = "TestFile"; // 目標目錄
char del_cmd[256] = {0};
char *ext_ptr;
int ret;
printf("請輸入要刪除的文件索引(不含路徑):");
if (scanf("%99s", file_index) != 1) {
fprintf(stderr, "輸入讀取失敗\n");
return 1;
}
// 判斷是否包含擴展名
ext_ptr = strrchr(file_index, '.');
if (ext_ptr == NULL) {
// 無擴展名:刪除該前綴所有文件
snprintf(del_cmd, sizeof(del_cmd), "rm -rf %s/%s.*", file_dir, file_index);
} else {
// 有擴展名:精確刪除
snprintf(del_cmd, sizeof(del_cmd), "rm -rf %s/%s", file_dir, file_index);
}
printf("執行命令: %s\n", del_cmd);
ret = system(del_cmd);
if (ret == -1) {
perror("system() 執行失敗");
return 1;
}
// 解析退出碼:WEXITSTATUS 需包含
if (WEXITSTATUS(ret) != 0) {
fprintf(stderr, "命令執行異常退出,碼=%d\n", WEXITSTATUS(ret));
// 注意:rm -rf 文件不存在也返回 0,需自行判斷業務邏輯
}
printf("操作完成\n");
return 0;
}
TestShellCommand: TestShellCommand.c
gcc -Wall -g -o release/TestShellCommand TestShellCommand.c
clean:
rm -f release/TestShellCommand *.o
項目根目錄/
├── TestShellCommand.c
├── Makefile
└── release/
├── TestShellCommand # 編譯產物
└── TestFile/ # 待操作文件目錄
├── 123456.r
└── 123457.txt
$ make
$ cd release
$ ./TestShellCommand
請輸入要刪除的文件索引(不含路徑):123456
執行命令: rm -rf TestFile/123456.*
$ ls TestFile/ # 123456.r 已刪除
123457.txt
| 風險 | 對策 |
|---|---|
| 命令注入 | 嚴格校驗輸入,禁止 ; & | $ ` 等字符,使用白名單 |
| 路徑遍歷 | 規範化路徑,限制在允許目錄下(realpath + 前綴檢查) |
| 權限提升 | 最小權限運行,避免 setuid,必要時用 capabilities |
| 競爭條件 | 避免 TOCTOU,操作前後復查文件狀態 |
| 資源耗盡 | 限制並發 system() 調用,超時控制 |
FILE* 可流式讀取/寫入。unlink()/remove(),目錄操作用 mkdir()/rmdir()/opendir(),完全繞過 Shell,更安全高效。
#include <sys/wait.h>
int status = system(cmd);
if (status == -1) {
// fork 失敗
} else if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status); // 正常退出碼 0-255
} else if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status); // 被信號終止
} else if (WIFSTOPPED(status)) {
int sig = WSTOPSIG(status); // 被信號停止
}
set -x; 開啟 Shell 追蹤