如何通过Bash脚本调用Tree-sitter删除Lua配置文件中的代码块?
用Tree-sitter在Bash中稳健删除Lua配置块的方案
完全可以通过Tree-sitter操作AST来替代sed,实现更可靠的Lua配置块删除,且能整合到Bash脚本中。以下是具体实现步骤:
1. 安装Tree-sitter及Lua语法支持
首先需要在系统中安装Tree-sitter核心工具,以及Lua语言的语法解析器:
- 通过包管理器安装Tree-sitter(比如Debian/Ubuntu用
sudo apt install tree-sitter,macOS用brew install tree-sitter) - 安装Lua语法解析器:
tree-sitter install lua
如果要在Python脚本中调用Tree-sitter(更方便操作AST),还需安装Python绑定:
pip install tree-sitter tree-sitter-lua
2. 编写Tree-sitter查询定位目标配置块
假设你的ssh_domains.lua中,目标配置是数组里的table结构,比如:
ssh_domains = { { name = "dev-server", remote_host = "dev.example.com", -- 其他配置 }, { name = "prod-server", remote_host = "prod.example.com", -- 其他配置 } }
要删除指定name对应的table块,需要编写Tree-sitter查询语句,匹配包含目标域名的table节点:
(table (field name: (identifier) @name value: (string) @domain_name (#eq? @domain_name "\"TARGET_DOMAIN\"") ) ) @target_block
这里TARGET_DOMAIN是占位符,后续脚本中会替换为实际要删除的域名。
3. 编写Python脚本处理AST修改
直接用Bash操作AST比较繁琐,我们可以写一个轻量的Python脚本(利用Tree-sitter的Python API)来解析、修改并输出Lua代码,然后在Bash中调用它。示例脚本remove_ssh_domain.py:
import sys import tree_sitter_lua from tree_sitter import Language, Parser def remove_target_domain(target_name, file_path): # 构建Lua语言库 Language.build_library( 'build/my-languages.so', [tree_sitter_lua.__path__[0]] ) lua_lang = Language('build/my-languages.so', 'lua') parser = Parser() parser.set_language(lua_lang) # 读取原文件内容 with open(file_path, 'r') as f: raw_code = f.read() tree = parser.parse(raw_code.encode()) # 替换占位符,执行查询 query_str = f""" (table (field name: (identifier) @name value: (string) @domain_name (#eq? @domain_name "\"{target_name}\"") ) ) @target_block """ query = lua_lang.query(query_str) captures = query.captures(tree.root_node) if not captures: print(f"未找到名称为 {target_name} 的配置块", file=sys.stderr) return raw_code # 获取目标块的字节范围 target_node = captures[0][0] start_idx = target_node.start_byte end_idx = target_node.end_byte # 处理数组元素删除后的多余逗号问题 # 向前查找前置逗号 prev_comma_pos = start_idx - 1 while prev_comma_pos >= 0 and raw_code[prev_comma_pos] in [' ', '\t', '\n']: prev_comma_pos -= 1 has_prev_comma = prev_comma_pos >= 0 and raw_code[prev_comma_pos] == ',' # 向后查找后置逗号 next_comma_pos = end_idx while next_comma_pos < len(raw_code) and raw_code[next_comma_pos] in [' ', '\t', '\n']: next_comma_pos += 1 has_next_comma = next_comma_pos < len(raw_code) and raw_code[next_comma_pos] == ',' # 生成修改后的代码 if has_prev_comma: modified_code = raw_code[:prev_comma_pos] + raw_code[end_idx:] elif has_next_comma: modified_code = raw_code[:start_idx] + raw_code[next_comma_pos+1:] else: modified_code = raw_code[:start_idx] + raw_code[end_idx:] return modified_code if __name__ == '__main__': if len(sys.argv) != 3: print("用法: python3 remove_ssh_domain.py <目标域名> <Lua文件路径>", file=sys.stderr) sys.exit(1) target_domain = sys.argv[1] lua_file = sys.argv[2] print(remove_target_domain(target_domain, lua_file))
4. 整合到Bash脚本中
编写Bash脚本remove_ssh_config.sh,调用Python脚本完成删除操作,并添加语法校验:
#!/bin/bash # 检查参数 if [ $# -ne 2 ]; then echo "用法: $0 <目标域名> <Lua配置文件路径>" exit 1 fi DOMAIN="$1" LUA_FILE="$2" TMP_FILE="${LUA_FILE}.tmp" # 调用Python脚本生成修改后的内容 python3 remove_ssh_domain.py "$DOMAIN" "$LUA_FILE" > "$TMP_FILE" # 校验修改后的Lua语法 if tree-sitter parse "$TMP_FILE" > /dev/null 2>&1; then # 语法合法,替换原文件 mv "$TMP_FILE" "$LUA_FILE" echo "成功删除域名 ${DOMAIN} 的配置块" else # 语法错误,丢弃临时文件 rm "$TMP_FILE" echo "删除失败:修改后的内容存在语法错误" exit 1 fi
注意事项
- 首次运行Python脚本会生成
build/my-languages.so,确保当前目录有写入权限 - 根据你的实际Lua配置结构,调整Tree-sitter查询语句的匹配规则(比如如果配置块的字段名或结构不同,修改查询逻辑)
- 操作前建议备份原配置文件,避免意外情况
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

