优化SSH配置文件转JSON格式的JavaScript代码
SSH配置转JSON代码优化
需求说明
需要将本地~/.ssh/config配置文件(含注释)转换为指定结构的JSON,要求移除所有#开头的注释行,每行首元素作为键、后续内容作为对应值。现有代码存在两个问题:
reduce循环无法自动处理最后一个Host条目,需要额外手动添加- 未考虑值中包含空格的配置项(如部分特殊路径或
ForwardAgent这类带参数的项)
原配置文件
# Personal account - default config Host github.com-user1 HostName github.com User git IdentityFile ~/.ssh/id_rsa_user1 # Official account Host github.com-user2 HostName github.com User git IdentityFile ~/.ssh/id_rsa_user2
目标JSON结构
[ { "Host": "github.com-user1", "HostName": "github.com", "User": "git", "IdentityFile": "~/.ssh/id_rsa_user1" }, { "Host": "github.com-user2", "HostName": "github.com", "User": "git", "IdentityFile": "~/.ssh/id_rsa_user2" } ]
原代码
import { exec as cExec } from "child_process"; import { promisify } from "util"; const exec = promisify(cExec); async function ssh() { const cmd = 'while IFS= read -r line; do echo "$line"; done < "$HOME/.ssh/config"' const { stdout, stderr } = await exec(cmd); if (stderr) throw new Error(stderr); const a = stdout.replace("\n", ""); let obj = {}; const b = stdout.trim().split("\n").reduce((r, a) => { if (!a.includes("#")) { let [name, value] = a.trim().split(" "); if (name === "Host") { if (Object.keys(obj).length > 0) r.push(obj); obj = {}; obj[name] = value; } else { obj[name] = value; } } return r; }, []); if (Object.keys(obj).length > 0) b.push(obj); return b }
优化后的代码
import { readFile } from "fs/promises"; import { homedir } from "os"; async function parseSshConfig() { // 直接读取ssh配置文件,替代shell exec方式 const configPath = `${homedir()}/.ssh/config`; const content = await readFile(configPath, "utf8"); return content.split("\n").reduce((acc, line) => { const trimmedLine = line.trim(); // 跳过空行和注释行 if (!trimmedLine || trimmedLine.startsWith("#")) return acc; // 用正则分割键和值,兼容值中包含空格的情况,只分割第一个空白符后的内容 const [key, ...valueParts] = trimmedLine.split(/\s+/); const value = valueParts.join(" "); if (key === "Host") { // 如果当前已有未提交的配置项,先推入结果数组 if (acc.currentHost && Object.keys(acc.currentHost).length > 0) { acc.result.push(acc.currentHost); } // 初始化新的Host配置 acc.currentHost = { [key]: value }; } else { // 追加其他配置项到当前Host acc.currentHost[key] = value; } return acc; }, { result: [], currentHost: {} }) // 最后处理剩余的未提交配置项 .then(acc => { if (acc.currentHost && Object.keys(acc.currentHost).length > 0) { acc.result.push(acc.currentHost); } return acc.result; }); }
优化点说明
- 替换文件读取方式:用
fs.promises.readFile直接读取文件,避免调用shell命令,更高效且安全 - 移除外部变量依赖:将当前Host配置对象放到reduce的累积器中,使reduce逻辑更纯粹,避免外部状态污染
- 兼容值含空格的配置:使用
split(/\s+/)分割后再拼接,确保值中包含空格的配置项(如带空格的路径、ForwardAgent yes等)能正确解析 - 自动处理最后一个Host:在reduce结束后的后续处理中统一检查并推送剩余的配置项,无需额外的判断代码
- 代码结构更清晰:函数命名更具语义化,逻辑分层明确
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

