大家好,我是杂烩君。
在嵌入式开发中,修改配置文件是一个高频操作。比如切换 WiFi 热点、调整串口波特率、更新服务器地址……这些参数往往分散在各种配置文件中。
今天就来分享一种简洁高效的配置文件修改方法——借助 sed 工具,一行命令搞定,还能轻松集成到 C 代码中。
一、常见配置文件格式及其局限
平时我们接触到的配置文件,大致分为两类:
标准格式:如 JSON、INI、XML、YAML 等。这些格式都有成熟的解析库,例如 C 语言中的 cJSON、iniparser 等,使用起来比较方便。
非标准格式:很多开源组件或系统工具使用的配置文件并不遵循上述标准格式,而是采用自定义的 key=value 或其他自由格式。例如 Linux 下常见的 wpa_supplicant.conf、hostapd.conf、resolv.conf 等,格式各异,没有通用的解析库可用。
对于非标准格式的配置文件,我们当然可以自己写解析代码——打开文件、逐行读取、字符串匹配、替换、写回文件。但这套流程写下来代码量不小,而且容易出 bug。
有没有更优雅的方式?有,那就是 sed。
二、sed 工具简介
sed(Stream Editor)是 Linux 下一个强大而轻量的流式文本编辑器,可以对文本进行查找、替换、删除、插入等操作,而无需打开文件进行交互式编辑。
sed 最常用的功能就是文本替换,其基本语法为:
sed -i 's/原始内容/替换内容/g' 文件路径
各参数含义:
| 参数 | 说明 |
|---|---|
-i |
直接修改源文件(in-place),不加此参数则只输出结果,不改原文件 |
s |
替换(substitute)命令 |
/ |
分隔符,用于分隔匹配模式和替换内容 |
g |
全局替换(global),一行中有多个匹配时全部替换 |
理解了基本语法,我们来看一个嵌入式开发中的实战案例。
三、实战:修改 WiFi 配置文件
如修改 WiFi 配置文件 wpa_supplicant.conf:
ctrl_interface=/var/run/wpa_supplicant
update_config=1
ap_scan=1
network={
ssid="test"
psk="12345678"
scan_ssid=1
key_mgmt=WPA-PSK
priority=1
}
现在我们需要将 ssid 修改为 **LinuxZn**,psk 修改为 **88888888**。
3.1 命令行操作
sed -i 's/ssid="[^"]*"/ssid="LinuxZn"/g' ./wpa_supplicant.conf
sed -i 's/psk="[^"]*"/psk="88888888"/g' ./wpa_supplicant.conf
3.2 正则表达式解析
这里的核心在于匹配模式 ssid="[^"]*",我们逐层拆解:
ssid=" :匹配字面量
ssid="[^"]* :这是一个正则表达式,[^"] 表示匹配除双引号以外的任意字符,* 表示匹配零次或多次。组合起来就是匹配双引号之间的任意内容
" :匹配结尾的双引号
所以 ssid="[^"]*" 能够匹配到 ssid="test"、ssid="MyWiFi" 等任意 ssid 值,然后用 ssid="LinuxZn" 进行替换。psk 字段同理。
这种写法的好处是:无论原来的值是什么,都能正确匹配并替换,具有良好的通用性。
四、在 C 代码中集成 sed
在嵌入式 Linux 应用中,我们往往需要在程序运行时动态修改配置。可以通过 system() 函数调用 sed 命令来实现:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define SHELL_CMD_MODIFY_WIFI_SSID "sed -i 's/ssid="[^"]*"/ssid="%s"/g' ./wpa_supplicant.conf"
#define SHELL_CMD_MODIFY_WIFI_PSW "sed -i 's/psk="[^"]*"/psk="%s"/g' ./wpa_supplicant.conf"
#define SHELL_CMD_RESET_WIFI_CONF "cp default_wpa_supplicant.conf wpa_supplicant.conf"
static int modify_wifi_config(const char *ssid, const char *password)
{
char shell_cmd[256] = {0};
int ret = 0;
/* 重置WiFi配置文件,保证格式正确 */
ret = system(SHELL_CMD_RESET_WIFI_CONF);
if (ret != 0)
{
printf("reset wifi config failed!n");
return-1;
}
/* 修改WiFi名称 */
snprintf(shell_cmd, sizeof(shell_cmd), SHELL_CMD_MODIFY_WIFI_SSID, ssid);
ret = system(shell_cmd);
if (ret != 0)
{
printf("modify ssid failed!n");
return-1;
}
/* 修改WiFi密码 */
memset(shell_cmd, 0, sizeof(shell_cmd));
snprintf(shell_cmd, sizeof(shell_cmd), SHELL_CMD_MODIFY_WIFI_PSW, password);
ret = system(shell_cmd);
if (ret != 0)
{
printf("modify psk failed!n");
return-1;
}
return0;
}
int main(int argc, char *argv[])
{
if (modify_wifi_config("LinuxZn", "88888888") == 0)
{
printf("WiFi config modified successfully!n");
}
else
{
printf("WiFi config modified failed!n");
}
return0;
}
运行结果:
相比原始写法,优化后的代码有以下改进:
五、注意
在实际项目中使用这种方式,有几点需要特别注意:
1. 操作前先备份
在修改配置文件之前,建议先保留一份默认配置文件(如示例中的 default_wpa_supplicant.conf),用于在修改出错时恢复。这在嵌入式设备上尤其重要——一旦配置损坏又没有备份,设备可能无法正常工作。
2. 注意命令注入风险
上面的代码中,ssid 和 password 通过 %s 拼接进 shell 命令。如果这些参数来自外部输入(如用户通过网页或串口输入),恶意用户可能构造特殊字符串来执行任意命令。例如输入 "; rm -rf / ; " 作为 ssid,后果不堪设想。
在实际产品中,务必对输入参数进行合法性校验,例如:
static int is_valid_input(const char *str)
{
while (*str)
{
if (*str == ';' || *str == '&' || *str == '|' ||
*str == '`' || *str == '$' || *str == ''' || *str == '"')
{
return 0;
}
str++;
}
return 1;
}
3. 分隔符冲突处理
如果要替换的内容中包含 / 字符(如文件路径),可以使用其他字符作为 sed 的分隔符,例如用 # 或 |:
sed -i 's#/old/path#/new/path#g' config.txt
六、总结
这种方法不仅适用于 WiFi 配置,在嵌入式开发中还有很多场景可以复用:
修改网络配置:如 IP 地址、DNS、网关等(/etc/network/interfaces)
调整串口参数:修改波特率、数据位等配置
更新服务器地址:OTA 升级服务器、MQTT Broker 地址等
切换运行模式:修改应用启动参数或功能开关
只要配置文件是文本格式,都可以用 sed 来处理。
本文介绍了一种利用 sed 工具修改配置文件的实用方法。核心思路是:通过正则表达式匹配目标字段,用 sed 的替换功能一行命令完成修改,并可在 C 代码中通过 system() 调用实现自动化。
这种方式的优点是简洁高效、无需引入额外的解析库,特别适合嵌入式 Linux 环境下对非标准配置文件的修改场景。
当然,如果项目中配置项较多、结构复杂,建议还是采用标准格式(如 JSON + cJSON),便于长期维护。选择合适的方案,才是工程实践的最优解。
如果觉得本文对你有帮助,欢迎点赞、在看、转发支持一波,我们下期见!
230