如何在Vim的map/filter中正确使用expand修饰符获取缓冲区绝对路径
问题根源说明
你当前的代码逻辑出错,和map/filter的表达式规则没有关系,核心是两个使用误区,也是你得到['.vim/vimrc:p']这类错误输出的直接原因:
- 对
expand()的能力边界理解错配:expand()只负责解析Vim内置的特殊路径占位符(比如%当前文件、#上次活动文件、<cfile>光标下的文件名这类),不会对你手动拼接到普通文件名字符串末尾的:p修饰符做解析——这类手动拼的:p会被当成文件名的普通字符直接返回。 - 代码执行逻辑错位:你写的
expand(bufname(v:val).":p"),是先把bufname(v:val)返回的相对路径字符串和":p"拼成完整的待处理字符串,再传给expand展开。最终expand看到的内容就是类似.vim/vimrc:p的普通路径,自然会把这部分当成文件名的一部分输出,不会触发绝对路径转换。
正确的实现方式
结合你现在用的“字符串作为map/filter回调”的写法,有两种可直接落地的修正方案:
- *最稳定的通用写法(推荐)
改用专门处理路径修饰符的fnamemodify()函数替代错误的expand拼接。这个函数的设计目的就是给已拿到的路径字符串应用路径修饰规则,完全匹配“把缓冲区相对路径转成绝对路径”的需求:
这里的let l:buffers = filter(range(1, bufnr('$')), 'buflisted(v:val)') let l:buffiles = map(copy(l:buffers), 'fnamemodify(bufname(v:val), ":p")')":p"是fnamemodify的第二个参数,是独立的路径修饰符规则,不是拼在路径后面的字符串,执行时会先拿到缓冲区的文件名,再自动转换成对应绝对路径,不会把:p混进文件名结果里。 - 如果你坚持要使用
expand()实现
不要手动拼接文件名字符串,要让expand拿到它能识别的缓冲区占位符格式(#<缓冲区编号>:p),再传入处理:
这种写法的兼容性不如上面的fnamemodify方案:如果对应缓冲区已经被卸载,Vim的缓冲区占位符可能失效,无法正确返回路径,而let l:buffiles = map(copy(l:buffers), 'expand("#" . v:val . ":p")')bufname()是直接读取Vim内部存储的缓冲区文件名,不受缓冲区是否加载的影响。
补充说明
很多人会误以为map/filter的字符串回调有特殊的语法规则,实际上没有。你写在这个回调字符串里的逻辑,和你直接在Vim命令行逐行执行的代码逻辑完全等价——你单独在命令行敲echo expand(bufname(1) . ":p"),同样会输出带:p后缀的错误结果,和map的运行规则没有任何关联。
如果是Vim 8.0及以上版本,更推荐用lambda语法替代字符串回调,从根源上避免引号转义出错的问题:
let l:buffiles = map(copy(l:buffers), {bid -> fnamemodify(bufname(bid), ':p')})
内容的提问来源于stack exchange,提问作者Meitham
相关产品推荐
相关产品推荐

