" b:is_kornshell if defined, enhance with kornshell syntax
" b:is_bash if defined, enhance with bash syntax
" is_kornshell if neither b:is_kornshell or b:is_bash is
" defined, then if is_kornshell is set
" b:is_kornshell is default
" is_bash if none of the previous three variables are
" defined, then if is_bash is set b:is_bash is default
" g:sh_fold_enabled if non-zero, syntax folding is enabled
"
" This file includes many ideas from â•”ric Brunet (eric.brunet@ens.fr)
" For version 5.x: Clear all syntax items
" For version 6.x: Quit when a syntax file was already loaded
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
" b:is_sh is set when "#! /bin/sh" is found;
" However, it often is just a masquerade by bash (typically Linux)
" or kornshell (typically workstations with Posix "sh").
" So, when the user sets "is_bash" or "is_kornshell",
" a b:is_sh is converted into b:is_bash/b:is_kornshell,
" respectively.
if !exists("b:is_kornshell") && !exists("b:is_bash")
if exists("is_kornshell")
let b:is_kornshell= 1
if exists("b:is_sh")
unlet b:is_sh
endif
elseif exists("is_bash")
let b:is_bash= 1
if exists("b:is_sh")
unlet b:is_sh
endif
else
let b:is_sh= 1
endif
endif
if !exists("g:sh_fold_enabled")
let g:sh_fold_enabled= 0
endif
" sh syntax is case sensitive
syn case match
setlocal isk+=.
" Clusters: contains=@... clusters
"==================================
syn cluster shCaseEsacList contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseSingleQuote,shCaseDoubleQuote,shSpecial
syn cluster shCommandSubList1 contains=shArithmetic,shDeref,shDerefSimple,shNumber,shPosnParm,shSpecial,shSingleQuote,shDoubleQuote,shStatement,shVariable,shSubSh
syn cluster shCommandSubList2 contains=@shCommandSubList1
syn cluster shDblQuoteList contains=shCommandSub,shDeref,shDerefSimple,shSpecial,shPosnParm
syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError
syn cluster shDerefVarList contains=shDerefOp,shDerefVarArray,shDerefOpError
syn cluster shIdList contains=shCommandSub,shWrapLineOperator,shIdWhiteSpace,shDeref,shDerefSimple,shSpecial,shRedir,shSingleQuote,shDoubleQuote
syn cluster shCaseList contains=@shCommandSubList1,shCaseEsac,shColon,shCommandSub,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,bkshFunction,shSpecial
syn cluster shColonList contains=@shCaseList
syn cluster shEchoList contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shExpr,shSingleQuote,shDoubleQuote,shSpecial
syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shSingleQuote,shDoubleQuote,shSpecial,shExpr,shDblBrace,shDeref,shDerefSimple
syn cluster shExprList2 contains=@shExprList1,@shCaseList
syn cluster shLoopList contains=@shCaseList,shTestOpr,shExpr,shDblBrace,shConditional,shCaseEsac
syn cluster shSubShList contains=@shCaseList
syn cluster shTestList contains=shCharClass,shComment,shDeref,shDerefSimple,shDoubleQuote,shExpr,shExpr,shNumber,shOperator,shSingleQuote,shSpecial,shTestOpr
syn cluster shFunctionList contains=@shCaseList,shOperator
" Echo:
" ====
" This one is needed INSIDE a CommandSub, so that `echo bla` be correct
syn region shEcho matchgroup=shStatement start="\<echo\>" skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|()]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList
syn region shEcho matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|()]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=@shEchoList
" This must be after the strings, so that bla \" be correct
syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shOperator end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="#"me=e-1 contains=shNumber,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shSpecial,shOperator,shDoubleQuote,shCharClass
" Error Codes
" ===========
syn match shDoError "\<done\>"
syn match shIfError "\<fi\>"
syn match shInError "\<in\>"
syn match shCaseError ";;"
syn match shEsacError "\<esac\>"
syn match shCurlyError "}"
syn match shParenError ")"
if exists("b:is_kornshell")
syn match shDTestError "]]"
endif
syn match shTestError "]"
" Options interceptor
" ===================
syn match shOption "\s[\-+][a-zA-Z0-9]\+\>"ms=s+1
syn match shOption "\s--\S\+"ms=s+1
" Operators:
" =========
syn match shOperator "[!&;|]"
syn match shOperator "\[[[^:]\|\]]"
syn match shOperator "!\==" skipwhite nextgroup=shPattern
syn match shPattern contained "\<\S\+\())\)\@=" contains=shSingleQuote,shDoubleQuote,shDeref
" Subshells:
" =========
syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2
syn region shSubSh transparent matchgroup=shSubShRegion start="(" end=")" contains=@shSubShList
" Tests
"======
syn region shExpr transparent matchgroup=shRange start="\[" skip=+\\\\\|\\$+ end="\]" contains=@shTestList
syn region shExpr transparent matchgroup=shStatement start="\<test\>" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
syn match shTestOpr contained "<=\|>=\|!=\|==\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!=<>]"
if exists("b:is_kornshell") || exists("b:is_bash")
syn region shDblBrace matchgroup=Delimiter start="\[\[" skip=+\\\\\|\\$+ end="\]\]" contains=@shTestList
syn region shDblParen matchgroup=Delimiter start="((" skip=+\\\\\|\\$+ end="))" contains=@shTestList
endif
" Character Class in Range
" ========================
syn match shCharClass contained "\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]"
" Loops: do, if, while, until
" =====
syn region shDo transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
syn region shIf transparent matchgroup=shConditional start="\<if\>" matchgroup=shConditional end="\<;\_s*then\>" end="\<fi\>" contains=@shLoopList,shDblBrace,shDblParen
syn region shFor matchgroup=shLoop start="\<for\>" end="\<in\>" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shCaseList add=shRepeat
syn region shRepeat matchgroup=shLoop start="\<while\>" end="\<in\>" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen,shDblBrace
syn region shRepeat matchgroup=shLoop start="\<until\>" end="\<in\>" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen,shDblBrace
syn region shCaseEsac matchgroup=shConditional start="\<select\>" matchgroup=shConditional end="\<in\>" end="\<do\>" contains=@shLoopList
else
syn region shRepeat matchgroup=shLoop start="\<while\>" end="\<do\>"me=e-2 contains=@shLoopList
syn region shRepeat matchgroup=shLoop start="\<until\>" end="\<do\>"me=e-2 contains=@shLoopList
endif
" Case: case...esac
" ====
syn match shCaseBar contained skipwhite "[^|"`'()]\{-}|"hs=e nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseSingleQuote,shCaseDoubleQuote
syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBar
syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="[^$()]\{-})"ms=s,hs=e end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,,shComment
syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList
syn keyword shCaseIn contained skipwhite skipnl in nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseSingleQuote,shCaseDoubleQuote
syn region shCaseSingleQuote matchgroup=shOperator start=+'+ skip=+\\\\\|\\.+ end=+'+ contains=shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
syn region shCaseDoubleQuote matchgroup=shOperator start=+"+ skip=+\\\\\|\\.+ end=+"+ contains=@shDblQuoteList,shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained
syn region shCaseCommandSub start=+`+ skip=+\\\\\|\\.+ end=+`+ contains=@shCommandSubList1 skipwhite skipnl nextgroup=shCaseBar contained
" Misc
"=====
syn match shWrapLineOperator "\\$"
syn region shCommandSub start="`" skip="\\\\\|\\." end="`" contains=@shCommandSubList1
" $(..) is not supported by sh (Bourne shell). However, apparently
" some systems (HP?) have as their /bin/sh a (link to) Korn shell
" (ie. Posix compliant shell). /bin/ksh should work for those
" systems too, however, so the following syntax will flag $(..) as
" an Error under /bin/sh. By consensus of vimdev'ers!
if exists("b:is_kornshell") || exists("b:is_bash")
syn region shCommandSub matchgroup=shCmdSubRegion start="\$(" skip='\\\\\|\\.' end=")" contains=@shCommandSubList2
syn region shArithmetic matchgroup=shArithRegion start="\$((" skip='\\\\\|\\.' end="))" contains=@shCommandSubList2
syn match shSkipInitWS contained "^\s\+"
else
syn region shCommandSub matchgroup=Error start="$(" end=")" contains=@shCommandSubList2
endif
if exists("b:is_bash")
syn cluster shCommandSubList1 add=bashSpecialVariables,bashStatement
syn cluster shCaseList add=bashAdminStatement,bashStatement
syn keyword bashSpecialVariables contained BASH HISTCONTROL LANG OPTERR PWD
syn keyword bashSpecialVariables contained BASH_ENV HISTFILE LC_ALL OPTIND RANDOM
syn keyword bashSpecialVariables contained BASH_VERSINFO HISTFILESIZE LC_COLLATE OSTYPE REPLY
syn keyword bashSpecialVariables contained BASH_VERSION HISTIGNORE LC_MESSAGES PATH SECONDS
syn keyword bashSpecialVariables contained CDPATH HISTSIZE LINENO PIPESTATUS SHELLOPTS
syn keyword bashSpecialVariables contained DIRSTACK HOME MACHTYPE PPID SHLVL
syn keyword bashSpecialVariables contained EUID HOSTFILE MAIL PROMPT_COMMAND TIMEFORMAT
syn keyword bashSpecialVariables contained FCEDIT HOSTNAME MAILCHECK PS1 TIMEOUT
syn keyword bashSpecialVariables contained FIGNORE HOSTTYPE MAILPATH PS2 UID
syn keyword bashSpecialVariables contained GLOBIGNORE IFS OLDPWD PS3 auto_resume
syn keyword bashSpecialVariables contained GROUPS IGNOREEOF OPTARG PS4 histchars
syn keyword bashSpecialVariables contained HISTCMD INPUTRC
syn keyword bashStatement chmod fgrep install rm sort
syn keyword bashStatement clear find less rmdir strip
syn keyword bashStatement du gnufind ls rpm tail
syn keyword bashStatement egrep gnugrep mkdir sed touch
syn keyword bashStatement expr grep mv sleep complete
syn keyword bashAdminStatement daemon killproc reload start stop
syn keyword bashAdminStatement killall nice restart status
endif
if exists("b:is_kornshell")
syn cluster shCommandSubList1 add=kshSpecialVariables,kshStatement
syn cluster shCaseList add=kshStatement
syn keyword kshSpecialVariables contained CDPATH HISTFILE MAILCHECK PPID RANDOM
syn keyword kshSpecialVariables contained COLUMNS HISTSIZE MAILPATH PS1 REPLY
syn keyword kshSpecialVariables contained EDITOR HOME OLDPWD PS2 SECONDS
syn keyword kshSpecialVariables contained ENV IFS OPTARG PS3 SHELL
syn keyword kshSpecialVariables contained ERRNO LINENO OPTIND PS4 TMOUT
syn keyword kshSpecialVariables contained FCEDIT LINES PATH PWD VISUAL
syn keyword kshSpecialVariables contained FPATH MAIL
syn keyword kshStatement cat expr less printenv strip
syn keyword kshStatement chmod fgrep ls rm stty
syn keyword kshStatement clear find mkdir rmdir tail
syn keyword kshStatement cp grep mv sed touch
syn keyword kshStatement du install nice sort tput
syn keyword kshStatement egrep killall
endif
syn match shSource "^\.\s"
syn match shSource "\s\.\s"
syn region shColon start="^\s*:" end="$\|" end="#"me=e-1 contains=@shColonList
" String and Character constants
"===============================
syn match shNumber "-\=\<\d\+\>"
syn match shSpecial contained "\\\d\d\d\|\\[abcfnrtv0]"
syn region shSingleQuote matchgroup=shOperator start=+'+ end=+'+ contains=shStringSpecial
syn region shDoubleQuote matchgroup=shOperator start=+"+ skip=+\\"+ end=+"+ contains=@shDblQuoteList,shStringSpecial
syn match shStringSpecial contained "[^[:print:]]"
syn match shSpecial "\\[\\\"\'`$()#]"
" Comments
"=========
syn cluster shCommentGroup contains=shTodo
syn keyword shTodo contained TODO
syn match shComment "#.*$" contains=@shCommentGroup
" File redirection highlighted as operators
"==========================================
syn match shRedir "\d\=>\(&[-0-9]\)\="
syn match shRedir "\d\=>>-\="
syn match shRedir "\d\=<\(&[-0-9]\)\="
syn match shRedir "\d<<-\="
" Shell Input Redirection (Here Documents)
if version < 600
syn region shHereDoc matchgroup=shRedir start="<<\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<-\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir end="^\s*END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<\s*\**EOF\**" matchgroup=shRedir end="^EOF$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<-\s*\**EOF\**" matchgroup=shRedir end="^\s*EOF$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<\s*\**\.\**" matchgroup=shRedir end="^\.$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\.\**" matchgroup=shRedir end="^\s*\.$" contains=@shDblQuoteList
elseif g:sh_fold_enabled
syn region shHereDoc matchgroup=shRedir fold start="<<\s*\**\z(\S*\)\**" matchgroup=shRedir end="^\z1$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\**\z(\S*\)\**" matchgroup=shRedir end="^\s*\z1$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir fold start="<<\s*\**\z(["'\\]\S*\)\**" matchgroup=shRedir end="^\z1$"
syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\**\z(["'\\]\S*\)\**" matchgroup=shRedir end="^\s*\z1$"
else
syn region shHereDoc matchgroup=shRedir start="<<\s*\**\z(\S*\)\**" matchgroup=shRedir end="^\z1$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\z(\S*\)\**" matchgroup=shRedir end="^\s*\z1$" contains=@shDblQuoteList
syn region shHereDoc matchgroup=shRedir start="<<\s*\**\z(["'\\]\S*\)\**" matchgroup=shRedir end="^\z1$"
syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\z(["'\\]\S*\)\**" matchgroup=shRedir end="^\s*\z1$"
endif
" Identifiers
"============
syn match shVariable "\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze=" nextgroup=shSetIdentifier
syn match shIdWhiteSpace contained "\s"
syn match shSetIdentifier contained "=" nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shSingleQuote
if exists("b:is_bash")
syn region shSetList matchgroup=shSet start="\<\(declare\|typeset\|local\|export\|unset\)\>[^/]"me=e-1 end="$" end="[|)]"me=e-1 matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="#\|="me=e-1 contains=@shIdList
syn region shSetList matchgroup=shSet start="\<set\>[^/]"me=e-1 end="$" end="[|)]"me=e-1 matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
syn match shSet "\<\(declare\|typeset\|local\|export\|set\|unset\)$"
elseif exists("b:is_kornshell")
syn region shSetList matchgroup=shSet start="\<\(typeset\|export\|unset\)\>[^/]"me=e-1 end="$" end="[)|]"me=e-1 matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
syn region shSetList matchgroup=shSet start="\<set\>[^/]"me=e-1 end="$" end="[)|]"me=e-1 matchgroup=shOperator end="[;&]"me=e-1 matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
syn match shSet "\<\(typeset\|set\|export\|unset\)$"
else
syn region shSetList matchgroup=shSet start="\<\(set\|export\|unset\)\>[^/]"me=e-1 end="$" end="[)|]"me=e-1 matchgroup=shOperator end="[;&]" matchgroup=NONE end="[#=]"me=e-1 contains=@shIdList
syn match shStatement "\<\(set\|export\|unset\)$"
endif
" handles functions which start: Function () {
" Apparently Bourne shell accepts functions too,
" even though it isn't documented by my man pages
" for it.
syn cluster shCommandSubList1 add=bkshFunction
syn match bkshFunction "^\s*\<\h\w*\>\s*()" skipwhite skipnl nextgroup=bkshFunctionRegion contains=bkshFunctionParen
if g:sh_fold_enabled
syn region bkshFunctionRegion transparent contained fold matchgroup=Delimiter start="{" end="}" contains=@shFunctionList
else
syn region bkshFunctionRegion transparent contained matchgroup=Delimiter start="{" end="}" contains=@shFunctionList
endif
syn match bkshFunctionParen contained "[()]"
" Parameter Dereferencing
" =======================
syn match shDerefSimple "\$\w\+" nextgroup=shDerefVarArray
syn region shDeref matchgroup=PreProc start="\${" end="}" contains=@shDerefList nextgroup=shDerefVarArray
syn match shDerefWordError contained "[^}$[]"
syn match shDerefSimple "\$[-_#*@!?]"
if exists("b:is_bash") || exists("b:is_kornshell")
syn match shDerefSimple "\$\$"
syn region shDeref matchgroup=PreProc start="\${##\=" end="}" contains=@shDerefList
endif
" bash : ${!prefix*}
" bash : ${#parameter}
if exists("b:is_bash")
syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOp
syn match shDerefVar contained "{\@<=!\w\+" nextgroup=@shDerefVarList
endif
syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOpError
syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp
syn match shDerefVar contained "{\@<=\w\+" nextgroup=@shDerefVarList
" sh ksh bash : ${var[... ]...} array reference
syn region shDerefVarArray contained matchgroup=shDeref start="\[" end="]" contains=@shCommandSubList2 nextgroup=shDerefOp,shDerefOpError
" sh ksh bash : ${parameter:-word} word is default value
" sh ksh bash : ${parameter:=word} assign word as default value
" sh ksh bash : ${parameter:?word} display word if parameter is null
" sh ksh bash : ${parameter:+word} use word if parameter is not null, otherwise nothing
" ksh bash : ${parameter#pattern} remove small left pattern
" ksh bash : ${parameter##pattern} remove large left pattern
" ksh bash : ${parameter%pattern} remove small right pattern
" ksh bash : ${parameter%%pattern} remove large right pattern
syn cluster shDerefPatternList contains=shDerefPattern,shDerefString
syn match shDerefOpError contained ":[[:punct:]]"
syn match shDerefOp contained ":\=[-=?]" nextgroup=@shDerefPatternList
syn match shDerefOp contained ":\=+" nextgroup=@shDerefPatternList
if exists("b:is_bash") || exists("b:is_kornshell")
syn match shDerefOp contained "#\{1,2}" nextgroup=@shDerefPatternList
syn match shDerefOp contained "%\{1,2}" nextgroup=@shDerefPatternList
syn match shDerefPattern contained "[^{}]\+" contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub nextgroup=shDerefPattern
syn region shDerefPattern contained start="{" end="}" contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
endif
syn region shDerefString contained matchgroup=shOperator start=+'+ end=+'+ contains=shStringSpecial
syn region shDerefString contained matchgroup=shOperator start=+"+ skip=+\\"+ end=+"+ contains=@shDblQuoteList,shStringSpecial
syn match shDerefString contained "\\["']"
" bash : ${parameter:offset}
" bash : ${parameter:offset:length}
" bash : ${parameter//pattern/string}
" bash : ${parameter//pattern}
if exists("b:is_bash")
syn region shDerefOp contained start=":[$[:alnum:]_]"me=e-1 end=":"me=e-1 end="}"me=e-1 contains=@shCommandSubList2 nextgroup=shDerefPOL
syn match shDerefPOL contained ":[^}]\{1,}" contains=@shCommandSubList2
syn match shDerefOp contained "/\{1,2}" nextgroup=shDerefPat
syn match shDerefPat contained "[^/}]\{1,}" nextgroup=shDerefPatStringOp
syn match shDerefPatStringOp contained "/" nextgroup=shDerefPatString
syn match shDerefPatString contained "[^}]\{1,}"
endif
" A bunch of useful sh keywords
syn keyword shStatement break eval newgrp return ulimit
syn keyword shStatement cd exec pwd shift umask
syn keyword shStatement chdir exit read test wait
syn keyword shStatement continue kill readonly trap
syn keyword shConditional contained elif else then
syn keyword shCondError elif else then
if exists("b:is_kornshell") || exists("b:is_bash")
syn keyword shFunction function
syn keyword shStatement alias fg integer printf times
syn keyword shStatement autoload functions jobs r true
syn keyword shStatement bg getopts let stop type
syn keyword shStatement false hash nohup suspend unalias
syn keyword shStatement fc history print time whence
if exists("b:is_bash")
syn keyword shStatement bind disown local popd shopt
syn keyword shStatement builtin enable logout pushd source