ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Universal Ctags readtags 命令完全指南:用 Scheme 表达式过滤、排序与格式化 tags 文件

Universal Ctags readtags 命令完全指南:用 Scheme 表达式过滤、排序与格式化 tags 文件 开发工具CLI【免费下载链接】ctagsA maintained ctags implementation项目地址https://gitcode.com/gh_mirrors/ct/ctags点击查看免费下载本篇技术指南围绕 Universal Ctags 项目中的readtags命令行工具展开。readtags用于读取 ctags 生成的 tags 文件按名称查找、列出、过滤、排序并格式化其中的标签条目是编写编辑器插件、构建代码导航与索引类客户端工具时最常打交道的配套程序。读完本文你将掌握readtags的全部 Action 与 Option 用法并能熟练使用其基于 Scheme 风格的表达式语言完成复杂查询、自定义排序与格式化输出。readtags 是什么readtags是 Universal Ctags 仓库中随主程序一起构建的命令行工具其功能定义在 docs/man/readtags.1.rst即本文所依据的 man 手册页。从命令行层面看它负责从 tags 文件中筛选、排序并打印标签条目基础筛选通过Action完成列出全部常规标签、列出伪标签pseudo tags或列出与指定名字匹配的常规标签进一步的处理通过后处理器post processors完成过滤器表达式filter、排序器表达式sorter与格式化器表达式formatter。与多数读者熟悉的grep tags不同readtags针对 ctags 生成的 tags 文件结构做了专门优化对有序sortedtags 文件NAME Action 采用二分查找即使面对超大 tags 文件也能在毫秒级返回结果同时它内置了一整套可编程的表达式语言把按语言过滤按文件排序输出函数声明这类需求变成了单条命令。从实现角度看readtags命令本身位于 extra-cmds/readtags-cmd.c其底层文件读取能力来自同仓库的libreadtags库libreadtags/readtags.h、libreadtags/readtags.c后者由 Exuberant-ctags 的 readtags.c/readtags.h 演化而来如今作为独立库维护其构建与独立使用方式见 libreadtags/README.md。命令语法SYNOPSISreadtags -h | --help readtags (-H | --help-expression) (filter|sorter|formatter) readtags -v | --version readtags [OPTION]... ACTION-h/--help打印帮助信息并退出-H/--help-expression打印某类表达式filter/sorter/formatter中可用的全部函数与变量清单是探索表达式语言的内置文档-v/--version打印版本号。从 extra-cmds/readtags-cmd.c#L741-L747 可以看到readtags 复用了 ctags 的版本号PROGRAM_VERSION[OPTION]... ACTION执行具体动作这是日常使用的主要形式。Action让 readtags 干什么Action说明-l,--list列出全部常规标签[-] NAME列出名字匹配 NAME 的常规标签当 NAME 以-开头时需要用单独的-作分隔符表示其后的参数一律当作 NAME 处理-D,--list-pseudo-tags列出伪标签可与-Q过滤器组合只提取指定的伪标签在 extra-cmds/readtags-cmd.c#L657-L685 的run()函数中可以看到 Action 的实际执行流程先打开 tags 文件ACTION_LIST_PTAGS伪标签、ACTION_FIND按名查找与ACTION_LIST列表依次处理若指定了排序器则所有条目先复制进一个ptrArray经ptrArraySortR()排序后再逐个交给 walker 回调打印。另外命令参数解析extra-cmds/readtags-cmd.c#L749-L820中凡是第一个字符不是-的参数都会被当作NAME动作的查找名处理这与手册描述一致。控制读取行为的选项-t TAGFILE/--tag-file TAGFILE指定要读取的 tags 文件默认值为tags。特殊值-表示从标准输入读取 tags 内容。注意手册特别提醒-只是让命令行更简洁并不代表高效——readtags 会把标准输入的数据先写入一个临时文件再对该临时文件执行 Action。这一点在 extra-cmds/readtags-cmd.c#L337-L381 的openTags()中有直接体现当tagFileName等于-时调用tempFileFP()创建临时文件把 stdin 内容经copyFile()拷贝进去后再打开。-s[0|1|2]/--override-sort-detection METHOD覆盖对 tags 文件排序方式的自动检测。METHOD取值为unsorted对应-s0未排序sorted对应-s1按字节序排序foldcase对应-s2忽略大小写排序。为什么需要覆盖扩展格式format 2的 tags 文件会在头部用伪标签标明排序方式但旧格式format 1的 tags 文件即使有序也没有该标记libreadtags无法自动利用二分查找。若你确认这样的文件确实有序就可以用-s显式告知。对应库层 API 是tagsSetSortType()其注释libreadtags/readtags.h#L181-L194明确警告如果实际未排序却标记为 sorted查找结果将是错误的。手册指出NAME Action 在 sorted含 foldcasetags 文件上执行二分查找比在 unsorted 文件上快得多。libreadtags的tagsFind()文档libreadtags/readtags.h#L224-L249也印证了这一点使用 C locale 排序的文件走二分查找一旦启用忽略大小写TAG_IGNORECASE二分查找就会被禁用退化为线性扫描。控制 NAME Action 匹配行为的选项选项说明-i,--icase-matchNAME Action 执行大小写不敏感的匹配-p,--prefix-matchNAME Action 执行前缀匹配这两者对应libreadtags中tagsFind()的匹配选项位TAG_PARTIALMATCH前缀匹配与TAG_IGNORECASE忽略大小写见 libreadtags/readtags.h#L42-L47。在命令解析处extra-cmds/readtags-cmd.c#L811-L814它们被按位或进matchOpts后传给tagsFind()。控制输出的选项默认情况下readtags 的输出只包含标签的 name、input 与 pattern 三个字段。以下选项用于调整输出选项说明-A,--absolute-input与-C相同但只使用绝对路径形式-C,--canonicalize-input解析常规标签 input 字段中的..与.得到路径的唯一表示仅当 tags 文件包含!_TAG_PROC_CWD伪标签时有效-d,--debug打开调试输出-E,--escape-output按 tags(5) 所述规则转义输出中的制表符等字符-e,--extension-fields在输出中包含扩展字段-n,--line-number与-e一起使用时额外输出行号字段-P,--with-pseudo-tags像-D一样先列出伪标签但不退出继续执行后续处理即便同时指定-Q与-P-Q也只作用于常规标签不影响伪标签关于-E的深入说明tags 文件为了机器可读会对某些字符做转义例如保证 pattern 字段以外的字段不出现制表符。默认情况下 readtags 会把这些转义还原成人类可读的形式但当你把 readtags 的输出喂给脚本或客户端工具时应当使用-E保持转义形式。相关讨论见 ctags-client-tools(7)。关于-E下的 input 字段即 tags(5) 中的{tagfile}readtags 总是按 tags 文件中的原样literal打印 input 字段而 ctags 写入 tags 文件时只有在TAG_OUTPUT_MODE伪标签为u-ctags且TAG_OUTPUT_FILESEP为slash时才会对 input 字段套用转义规则。这一判断逻辑在 extra-cmds/readtags-cmd.c#L424-L430 中有实现命令先用tagsFindPseudoTag()检查这两个伪标签二者同时满足才置位escapingInputField。关于-C/-A的限制-C的路径规范化依赖!_TAG_PROC_CWD伪标签记录 ctags 运行时的当前工作目录。实现extra-cmds/readtags-cmd.c#L275-L310要求该值必须以/开头Windows 风格盘符路径如C:\Somewhere目前不被接受遇到时会直接报错退出。-A与-C的区别在于前者只产出绝对路径形式对应canonFnameCacheTableNew(cwd, absoluteOnly)中的absoluteOnly参数。过滤、排序与格式化-Q EXP, --filter EXP # 打印前用 EXP 过滤 Action 列出的标签 -S EXP, --sorter EXP # 打印前用 EXP 对标签排序 -F EXP, --formatter EXP # 打印时用 EXP 格式化标签三者统一使用下面的EXPRESSION一节介绍的表达式语言。仓库中对应的三套独立实现分别位于 dsl/qualifier.cfilter、dsl/sorter.csorter与 dsl/formatter.cformatterreadtags 命令通过-H filter/-H sorter/-H formatter直接调用各自的q_help()/s_help()/f_help()打印可用函数清单见 extra-cmds/readtags-cmd.c#L581-L600。基础示例# 列出 /path/to/tags 中的全部标签 $ readtags -t /path/to/tags -l # 列出 tags 中所有以 mymethod 开头的标签前缀匹配 $ readtags -p - mymethod # 列出所有匹配 mymethod 的标签忽略大小写 $ readtags -i - mymethod # 列出所有以 myvar 开头的标签并打印全部字段整行输出 $ readtags -p -ne - myvar最后一个命令把-p前缀匹配、-n行号与-e扩展字段组合使用等价于输出 tags 文件中的完整行。表达式语言EXPRESSION-Q、-S、-F三者的表达式是Scheme 风格的。对不了解 Scheme/Lisp 的读者只需记住三条规则函数调用用一对括号包裹括号内第一个元素是函数/运算符名其余是参数函数调用可以嵌套缺失值和布尔假统一用#f表示#t以及除#f外的所有值都被视为真。于是( 1 ( 2 3))表示先算 23再把结果与 1 相加(and string 1 #t)对string、1、#t做逻辑与因为没有#f结果为真。表达式在命令行中通常需要加引号传给 shell。readtags 读取表达式时会先经es_read_from_string()解析为 S 表达式再交给对应编译器编译若表达式语法有误或在末尾残留垃圾字符都会报错退出见 extra-cmds/readtags-cmd.c#L602-L640。过滤Filtering使过滤器表达式产生真值的标签条目才会被 readtags 打印。基础过滤运算符eq?、prefix?、suffix?、substr?以及正则字面量#/PATTERN/。以$开头的变量用于访问语言通用字段例如$language表示语言字段。# 列出 Python 代码文件中所有以 myfunc 开头的标签 $ readtags -p -Q (eq? $language Python) - myfuncdowncase/upcase运算符可辅助实现大小写不敏感匹配# 列出所有包含 my 的标签大小写不敏感 $ readtags -Q (substr? (downcase $name) my) -l逻辑运算符有and、or、not。缺失字段的值为#f因此可以据此处理字段缺失的情况# 列出名字包含 impl 的标签要求若是 Python 文件则保留 # 同时允许 language: 字段缺失 $ readtags -Q (and (substr? $name impl)\ (or (not $language)\ (eq? $language Python))) -l正则表达式#/PATTERN/当字符串谓词prefix?、suffix?、substr?不够用时可用#/PATTERN/PATTERN 为POSIX 扩展正则表达式。# 列出所有继承自类 A 的标签 $ readtags -Q (#/(^|,) ?A(,|$)/ $inherits) -l这里$inherits是逗号分隔的类名列表形如A,B,C、P, A, Q或单独的A。该表达式同时兼容逗号后有空格与逗号后无空格两种格式。大小写不敏感匹配使用#/PATTERN/i# 列出所有继承自类 A 或 a 的标签 $ readtags -Q (#/(^|,) ?A(,|$)/i $inherits) -l若 PATTERN 中要包含/字符需在前面加\转义。注意上述继承查询正则只是演示#/PATTERN/用法的示例。各 tags 文件生成器对inherits:字段的格式并无统一约定例如逗号后是否带空格甚至 ctags 自身的各个解析器之间也不一致。做此类查询前需要先确认目标语言inherits:字段的实际格式。string-regexp别名#/PATTERN/与#/PATTERN/i是为交互使用提供的便捷语法其底层函数是string-regexp#/PATTERN/等价于(string-regexp PATTERN)#/PATTERN/i等价于(string-regexp PATTERN :case-fold #t)。string-regexp不需要对/加\转义因此在客户端工具中动态拼接表达式时更简单详见 ctags-client-tools(7)。缺失字段的安全处理tags 文件中可能有些条目没有inherits:字段此时$inherits为#f而字符串运算符只接受字符串直接对其做正则匹配会报错。解决办法是先用and确认字段存在# 安全地列出所有继承自类 A 的标签 $ readtags -Q (and $inherits (#/(^|,) ?A(,|$)/ $inherits)) -l先保证$inherits非缺失再对它做正则匹配。反向需求同样常见——保留字段缺失的标签。例如要排除引用标签由extras:字段标记但没有extras:字段的标签同样不是引用标签需要保留# 列出除引用标签外的所有标签 $ readtags -Q (or (not $extras) (#/(^|,) ?reference(,|$)/ $extras)) -l(not $extras)在$extras缺失时产生#t从而让整个or表达式为真。与 ctags 管道组合的实战模式ctags -o -与readtags -t -的组合非常适合快速检查较短源码文件ctags 把 tags 写到标准输出readtags 从标准输入读取。# 列出某文件中所有长度超过 100 行的函数 $ ctags -o - --fieldsneKz input.c \ | ./readtags -t - -en \ -Q (and (eq? $kind function) $end $line ( (- $end $line) 100)) \ -l该命令依赖--fieldsneKz输出的end:字段$end与$line分别表示函数结束行与起始行二者之差大于 100 即为大函数。# 列出某文件中覆盖第 80 行的所有标签 $ ctags -o - --fieldsneKz input.c \ | readtags -t - -ne \ -Q (and $line (or (eq? $line 80) (and $end ( $line 80) ( 80 $end)))) \ -l运行readtags -H filter可查看过滤器表达式中全部可用的函数与变量。过滤器表达式的仓库级验证Tmain 测试目录 Tmain/readtags-qualifier.d 提供了大量可实际运行验证的过滤器用例。其 run.sh 依次执行多组-Q查询例如readtags -e -t output.tags -Q (suffix? $name q) -l readtags -e -t output.tags -Q (and (eq? $kind member) (eq? Baz $scope-name)) -l readtags -e -t output.tags -Q (and $inherits (#/(^|,)Foo(,|$)/ $inherits) (eq? $kind class)) -l readtags -e -n -t roles.tags -Q (and $roles (#/(^|,)superClass(,|$)/ $roles)) -l readtags -e -t c.tags -Q (and (eq? $scope-kind function) (eq? $kind local)) -l对应期望输出见 stdout-expected.txt覆盖了$scope-name、$scope-kind、$inherits、$roles、$signature等字段的访问以及or表达式把缺失字段当作#f处理的语义。排序Sorting排序器表达式对两个标签条目求值以决定谁排在前面直到所有条目顺序确定。排序表达式中$与分别用于访问两个条目的字段称它们为 $-entry 与 -entry。表达式的值必须是 -1、0 或 1-1$-entry 应排在 -entry 之前1反之0两者在输出中的先后不确定。排序的核心运算符是用于比较两个字符串或两个数字数字指line:、end:字段。在( a b)中a b得 -1a b得 1a b得 0。字符串比较使用strcmp见 strcmp(3)。# 按名字排序较短或按字母序更小的名字排在前面 $ readtags -S ( $name name) -l读作如果 $-entry 的标签名更小它就排在 -entry 之前。or运算符用于串联多个表达式直到某一个返回 -1 或 1。例如先按输入文件名排序、同一文件内再按行号排序$ readtags -S (or ( $input input) ( $line line)) -l*-运算符用于翻转比较结果(*- ( a b))等价于( b a)。过滤器表达式可以嵌进排序表达式。常用技巧是用if把过滤结果转成可比较的整数( (if filter-expr-on-$-entry -1 1) (if filter-expr-on--entry -1 1))如果 $-entry 满足过滤条件而 -entry 不满足相当于( -1 1)结果为 -1即满足条件的条目排在前面。例如想把kind为file的标签排到其他标签之后( (if (eq? $kind file) 1 -1) (if (eq? kind file) 1 -1))快速解读若 $-entry 是 file 而 -entry 不是排序器变成( 1 -1)结果为 1于是 $-entry 被放到 -entry 之后——正是想要的file 标签沉底效果。格式化Formatting格式化器表达式决定 readtags 如何打印标签条目。表达式可以产生字符串、布尔值、整数或列表字符串与整数按原样打印#f不打印任何内容#t打印一个换行。列表可以包含任意数量的字符串、布尔值、整数和/或嵌套列表readtags 会按顺序递归地打印列表元素。过滤器中的所有运算符在格式化器表达式中同样可用。此外格式化器还提供list运算符用于构造列表例如(list 1 #f hello)生成包含1、#f、hello的列表。注意与真正的 Lisp 不同反引号backquote构造在这里不可用。以下面的 tags 文件output.tags作为示例输入M input.c 4; macro file: N input.c 3; macro file: bar input.c 11; f typeref:typename:void file: signature:(char ** argv,int * r) foo input.c 6; f typeref:typename:int file: signature:(int v) main input.c 16; f typeref:typename:int signature:(int argc,char ** argv)只打印函数名$ readtags -t output.tags -Q (eq? $kind function) -F (list $name #t) -l bar foo main只用格式化器完成同样的事if的假分支返回#f即不输出$ readtags -t output.tags -F (if (eq? $kind function) (list $name #t) #f) -l bar foo main生成函数声明利用$file字段判断是否为文件内函数以决定是否加static并用$typeref-name、$signature拼出原型$ readtags -t output.tags -F \ (if (eq? $kind function) (list (if $file static #f) $typeref-name $name $signature ; #t) #f) -l static void bar(char ** argv,int * r); static int foo(int v); int main(int argc,char ** argv);检查表达式的行为print运算符可以打印某个表达式的值$ readtags -Q (print $name) -l它会在每个标签条目前先打印其名字。由于print的返回值不是#f所有条目都会被打印。若想控制这一点可以使用begin或begin0运算符begin返回其最后一个参数的值begin0返回其第一个参数的值。例如$ readtags -Q (begin0 #f (print (prefix? ctags ct))) -l这会打印出一堆#t数量取决于 tags 文件有多少行而真正的标签条目不会被打印。readtags 背后的 libreadtagsreadtags 命令的读取能力来自libreadtags一个用于读取 ctags 生成 tags 文件的 C 库其公共 API 定义在 libreadtags/readtags.h。核心函数包括tagsOpen()打开 tags 文件返回句柄并填充tagFileInfo格式号、排序方式、生成程序信息等tagsFirst()/tagsNext()顺序遍历常规标签tagsFirstPseudoTag()/tagsNextPseudoTag()顺序遍历伪标签tagsFind()/tagsFindNext()按名字查找有序文件走二分查找可通过TAG_PARTIALMATCH、TAG_IGNORECASE等选项位组合控制匹配方式tagsFindPseudoTag()按名查找伪标签与tagsFind()不同即使文件有序也使用线性查找tagsField()取条目指定扩展字段的值tagsSetSortType()覆盖排序方式检测对应-s选项tagsGetErrno()区分找不到标签返回 0与发生错误返回系统 errno 或tagErrno枚举值。libreadtags支持 GNU Autotools 与 CMake 两种构建方式并可作为独立库集成进其他项目详见 libreadtags/README.md。其测试目录 libreadtags/tests 提供了对tagsOpen、tagsFind、tagsFindPseudoTag、tagsSetSortType、字段反转义unescaping等行为的单元测试例如针对错误格式、错误排序标记、损坏的line:字段、超多字段、空文件等边界情况的 tags 样本与用例。总结readtags是 Universal Ctags 体系中面向消费侧的关键工具Action 层解决列出/查找-Q/-S/-F三层表达式解决过滤/排序/格式化底层由libreadtags提供针对有序文件的二分查找与伪标签支持。无论是写一个像readtags那样的命令行封装、在编辑器里实现 goto-definition还是构建批量处理 tags 的脚本理解这套 Scheme 风格表达式——$/字段访问、#/PATTERN/正则、or/*-/if组合、list/print/begin0输出控制——都是最核心的一步。需要进一步了解 tags 文件格式细节可阅读 tags(5)面向客户端工具的开发建议可阅读 ctags-client-tools(7)。赞分享开发工具CLI【免费下载链接】ctagsA maintained ctags implementation项目地址https://gitcode.com/gh_mirrors/ct/ctags点击查看免费下载相关推荐网盘直链下载助手快速拿到9大网盘真实直链IDM满速下载网盘直链下载助手快速拿到9大网盘真实直链IDM满速下载 你是不是也遇过网盘网页版拖一个2GB文件非会员只有几十KB/s网盘直链下载助手是个油猴脚本在前端Universal Ctags 完全指南ctags.1 手册精解——从命令行选项到标签文件格式Universal Ctags 完全指南ctags.1 手册精解——从命令行选项到标签文件格式 导读 本文是对 Universal Ctags 官方手册 do开发工具CLIUniversal Ctags输出格式详解TAGS文件结构分析Universal Ctags输出格式详解TAGS文件结构分析 引言你还在为TAGS文件解析烦恼吗 作为开发者你是否曾因无法正确解析TAGS文件格式而错开发工具CLI上一篇知网文献批量下载工具完整指南一条命令把检索、下载与 Zotero 入库全搞定下一篇Rufus 一次做对 U 盘启动盘GPT/MBR 选对老机器照样装 Win11创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表