I have Bash file in which I have constructs that include (@)_ as part of the variable name. For example:


The Bash files are pre-processed and the literal string (@)_ is replaced with characters so that the final result is valid Bash. The sequence (@)_, if present, will always be at the beginning of a variable name.

I'd like to modify the sh.vim syntax file so that variable names are considered to be valid in identifier highlighting and not shown with shDerefWordError highlighting. I've tried modifying:

syn match shDerefVar contained "{\@<=!\k\+" nextgroup=@shDerefVarList


syn match shDerefVar contained "{\@<=\((@)_\)\?!\k\+" nextgroup=@shDerefVarList

but this has not worked.

Note that this allowance might reasonably be made conditional to a variable such as b:is_eggsh.

Can anyone suggest a solution?

1 Answer 1


You are close. The problem is that you took the syntax definition for Bash's special ${!varname}, and that doesn't match.

I would also define a separate syntax group, used exclusively for your placeholder variables. The sh.vim syntax script is extensive via syntax clusters, that's how you install it as an additional dereference:

syn match shDerefPlaceholder contained "{\@<=[!#]\?(@)_\k\+" nextgroup=@shDerefVarList
syn cluster shDerefList add=shDerefPlaceholder


Integrating with an existing syntax can be messy... especially with an advanced one like shell (which supports several sub-modes). An alternative would be using :match Preproc /(@)_[a-zA-Z_][a-zA-Z0-9_]*/; this always applies over existing syntax highlighting. Only complication: this is window-local, so you'd need some :autocmds to install it, like this:

au BufNewFile,BufRead * if &syntax == 'sh' | match Preproc /(@)_[a-zA-Z_][a-zA-Z0-9_]*/ | endif
  • Hi Ingo... that almost does it. I still see error highlighting in the case of ${#(@)_VariableName[@]} or ${!(@)_VariableName[@]}. Would you mind updating your answer to address these as well? Many thanks!! Commented Oct 17, 2016 at 14:36
  • Great! Supporting those should be a matter of inserting [!#]\? at the right place; see my edit. Commented Oct 17, 2016 at 14:40
  • That's what I thought, too. But, they're still showing up with the error highlighting. Is it possible that escaping is needed? Commented Oct 17, 2016 at 14:47
  • I previously didn't say that the code was working for the other cases. Almost there! Is it possible that the syn match shDerefSpecial is taking priority or something like that? Commented Oct 17, 2016 at 14:58
  • 1
    Great! I've added the following rule and it is satisfactory! au BufNewFile,BufRead * if &syntax == 'sh' | match shEggsh /(@)_[a-zA-Z_][a-zA-Z0-9_]*/ | endif Commented Oct 18, 2016 at 11:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.