Vim 不一致地对 bash 文件进行语法高亮。

30
当我使用vim打开一些bash脚本文件时,有时会将它们识别为"conf"文件,没关系,我只需通过使用":setf sh"命令将文件类型设置为"sh"即可更正。
这很好,但我注意到这并不能完全解决问题:

Two files side-by-side with different highlighting

请注意,在左侧,shopt 得到了正确的高亮显示,但是在右侧,我手动将文件类型设置为 sh 后没有得到高亮显示。这意味着当 vim 将文件识别为 bashsh 时,它会将文件类型设置为 sh,但是当我手动设置文件类型时,vim 进行了一些额外的步骤,而我并没有执行这些步骤。有人知道可能是什么问题,以及我该如何解决吗?
4个回答

33

Vim已经默认识别了许多文件类型。大多数情况下是通过文件扩展名来实现的,但在某些情况下,Vim还会分析文件内容来猜测正确的类型。

Vim会自动为特定文件名设置文件类型,例如.bashrc.tcshrc等。但是,扩展名为.sh的文件将被识别为csh、ksh或bash脚本。为了确定这是哪种类型的脚本,Vim会读取文件的第一行以查看#!行。

如果第一行包含单词bash,则文件将被识别为bash脚本。通常,如果脚本是直接执行的,你会看到#!/bin/bash,对于其他一些shell配置文件,你应该使用文件扩展名.bash

Vim中的帮助也解释了这一点,可以使用:help ft-bash-syntax。你还可以在.vimrc中使用let g:is_bash=1,将bash语法突出显示设置为所有filetype=sh的文件的默认值。如果想了解详细信息,可以查看$VIMRUNTIME/filetype.vim实现。


1
我将此标记为答案,因为它很好地解释了为什么,同时提供了一个完全合理的解决方案。我还添加了一个回答,详细说明了我的解决方案。 - Peter Coulton
1
“唉。”简介很有帮助,但所提供的解决方案(例如,在“.vimrc”中添加“let g:is_bash=1”)是无用的。为什么呢?因为这样做会强制非Bash shell脚本缓冲区(例如Bourne、Korn)使用Bash特定的语法高亮。请考虑Tim Friske的出色解决方案。 - Cecil Curry
这正是我为什么添加了公平警告,指出这会影响到所有 filetype=sh 的文件。实际上,我只是复制了 :help ft-bash-syntax 推荐在你的 .vimrc 中添加以使其成为全局默认选项的内容。它不会强制将此语法用于所有 shell 文件。更具体的 shebang(比如 #!/bin/ksh)仍将覆盖此“默认”选择,因此我无法重现你的担忧。 - raimue

8
原文:It turns out that syntax/sh.vim includes specific highlighting for Korn, Bash and sh, you just have to tell it which you're using. This is done with b:is_kornshell, b:is_bash and b:is_sh respectively.
Depending on the situation I figure I'll use the following:
ftdetect/bash.vim:
翻译:原来syntax/sh.vim包含了特定的Korn、Bash和sh语法高亮,你只需要告诉它你使用哪个。分别使用b:is_kornshell、b:is_bash和b:is_sh完成这一操作。
根据情况,我认为我会使用以下内容:
ftdetect/bash.vim:
au BufRead,BufNewFile *bash* let g:is_bash=1
au BufRead,BufNewFile *bash* setf sh

模型行:

# vim:let g:is_bash=1:set filetype=sh:

按键映射

nmap <silent> <leader>b :let g:is_bash=1<cr> :setf sh<cr> 

1
+1 很好!模型线方法是我最喜欢的。(如果我足够在意这些微小的偏差 :)) - sehe
@Peter Coulton:你是不是想把 b:is_bash 变量设置为缓冲区本地变量,而不是全局变量 g:is_bash。至少你的代码片段没有反映出这种情况。此外,你可以通过在第一个命令结尾处添加 let b:is_bash = 1 | setfiletype sh 来连接这两个 au 命令。 - Tim Friske
1
我不认为每次加载文件名中带有“bash”的文件时都设置全局变量比在.vimrc中设置一次并完成更有益。 - dash-tom-bang
2
很遗憾,对于这种用例,在模型行中设置变量是不可能的。 - John Freeman
非理想情况。全局启用 g:is_bash=1 会为所有非 Bash shell 脚本缓冲区(例如 Bourne、Korn)启用 Bash 特定的语法高亮,这是不好的。局部启用 b:is_bash=1 是更好的选择。同样,针对文件类型的 ftdetect/ 文件已经被一个统一所有文件类型检测的 filetype.vim 文件所取代。有一个解决方案可以同时解决这两个问题,请参见 Tim Friske优秀解决方案 - Cecil Curry
@Peter-Coulton 我正在使用vim v8.1,但是你的modeline会返回一个错误:处理modelines时检测到错误: 第1行: E518: 未知选项:let - SebMa

4

与Peter Coulton的解决方案相似,并且在“filetype” Vim帮助文件的“new-filetype”部分中也有记录作为替代方法,~/.vim/filetype.vim文件可以包含以下代码:

if exists("did_load_filetypes")
  finish
endif

augroup filetypedetect
  au! BufRead,BufNewFile *bash* let b:is_bash = 1 | setfiletype sh
augroup END

这种方法有以下影响:
  1. 只有一个~/.vim/filetype.vim文件,而不是在~/.vim/ftdetect目录下为每个文件类型都有一个。
  2. b:is_bash变量设置为缓冲区本地变量,而不是全局变量,通过将其称为g:is_bash来引用。

3

尝试查看有效语法设置

:windo echo b:current_syntax

我希望第一个窗口显示,第二个窗口显示sh。你也可以尝试调整语法同步:
:windo syn sync fromstart
:windo syn sync minlines=300

通常情况下

:he syn-sync

想要了解更多信息


PS.

有一种可能是其他的高亮标记造成干扰:

:windo se @/=''
:match none
:2match none
:3match none

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接