Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
[PATCH] _sh: support more shells + options
- X-seq: zsh-workers 54763
- From: dana <dana@xxxxxxx>
- To: zsh-workers@xxxxxxx
- Subject: [PATCH] _sh: support more shells + options
- Date: Sat, 13 Jun 2026 03:02:53 +0000
- Archived-at: <https://zsh.org/workers/54763>
- Feedback-id: i9be146f9:Fastmail
- List-id: <zsh-workers.zsh.org>
better support dash, yash, various kshes, and osh/ysh (oil). also remove
some pointless nonsense i added before
dana
diff --git a/Completion/Unix/Command/_sh b/Completion/Unix/Command/_sh
index f0f18d4bb..d50b81ccc 100644
--- a/Completion/Unix/Command/_sh
+++ b/Completion/Unix/Command/_sh
@@ -1,36 +1,151 @@
-#compdef sh ash csh dash ksh ksh88 ksh93 mksh oksh pdksh rc tcsh yash
-
-local bourne argv0
-local -a args all_opts=( -{{0..9},{A..Z},{a..z}} )
-
-[[ $service == (csh|?csh|fish|rc) ]] || bourne=1
-
-# Bourne-style shells support +x variants
-# @todo Uncomment when workers/45422 is fixed
-# (( bourne )) && all_opts+=( ${all_opts/#-/+} )
-# Bourne-style shells take argv[0] as the second argument to -c
-(( bourne )) && argv0=':argv[0]:'
-
-# All of the recognised shells support at least these arguments
-args=(
- "(1 -)-c[execute specified command string]: :_cmdstring$argv0"
- '-e[exit immediately on non-zero return]'
- '-i[act as interactive shell]'
- '-l[act as login shell]'
- '-x[print command trace]'
- '1:script file:_files'
- '*:: :_files'
-)
-# Bourne-style shells support -o/+o option. Not all of them support -ooption in
-# the same argument, but we'll allow it here for those that do
-(( bourne )) && args+=(
- '-o+[set specified option]:option:'
- '+o+[unset specified option]:option:'
-)
-# Since this is a generic function we don't know what other options these shells
-# support, but we don't want them to break the ones listed above, so we'll just
-# ignore any other single-alphanumeric option. Obviously this doesn't account
-# for long options
-args+=( '!'${^${all_opts:#(${(~j<|>)${(@M)${(@M)args#(*[\*\)]|)[+-]?}%[+-]?}})}} )
-
-_arguments -s -S -A '-*' : $args
+#compdef sh ash csh dash ksh ksh88 ksh93 lksh mksh oksh osh pdksh posh rc rksh rksh93 tcsh yash ysh
+
+(( $+functions[_sh_set_options] )) ||
+_sh_set_options() {
+ local MATCH MBEGIN MEND k_ v_
+ local -a opts_ tmp_ expl_
+
+ tmp_=( ${(f)"$( _call_program options $words[1] -c '"set -o"' )"} )
+
+ # remove heading
+ [[ $tmp_[1] == (#i)*'current option'* ]] && tmp_=( $tmp_[2,-1] )
+ # convert osh/ysh syntax
+ [[ $tmp_[1] == set\ [+-]o\ * ]] && {
+ tmp_=( ${(@)tmp_##set [+-]o } )
+ tmp_=( ${tmp_:^tmp_} )
+ }
+ # loop since some shells arrange the options in columns
+ for k_ v_ in $=tmp_; do
+ opts_+=( $k_ )
+ done
+
+ _wanted set-options expl_ 'set option' compadd -a "$@" - opts_
+}
+
+local variant apat='[+-]?*'
+local -a args oopts
+
+case ${${(Q)words[1]}:t} in
+ r#ksh)
+ _pick_variant -r variant \
+ ksh93=showme \
+ mksh='utf8-#mode' \
+ oksh='csh-#history' \
+ '' \
+ -c '"set -o"'
+ ;;
+ sh)
+ _pick_variant -r variant \
+ zsh=junkie \
+ bash=onecmd \
+ ksh93=showme \
+ mksh='utf8-#mode' \
+ oksh='csh-#history' \
+ ksh=keyword \
+ dash=debug \
+ '' \
+ -c '"set -o"'
+ ;;
+esac
+case $variant in
+ bash|zsh) _$variant "$@"; return ;;
+ ?*) service=$variant ;;
+esac
+
+# bourne/ksh/posix-style shells. not meant to be totally accurate, but mostly
+if [[ $service != (csh|?csh|rc) ]]; then
+ args=(
+ # invocation options
+ '(1 -)-c[execute specified command string]: :_cmdstring:argv[0]:'
+ '-i[act as interactive shell]'
+ '-l[act as login shell]' # not defined by posix but very widely supported
+ '-o+[set specified option]: :_sh_set_options'
+ '+o+[unset specified option]: :_sh_set_options'
+ '(-c)-s[read commands from standard input]'
+ # set options. these are all defined by posix
+ '(-a +a)'{'-a[',"+a[don't "}'mark all variables for export]'
+ '(-b +b)'{'-b[',"+b[don't "}'report status of terminated background jobs immediately]'
+ '(-C +C)'{'-C[',"+C[don't "}'prevent output redirection from overwriting existing files]'
+ '(-e +e)'{'-e[',"+e[don't "}'exit immediately on non-zero return]'
+ '(-f +f)'{'-f[',"+f[don't "}'disable file globbing]'
+ '(-h +h)'{'-h[',"+h[don't "}'hash commands]'
+ '(-m +m)'{'-m[',"+m[don't "}'enable job control]'
+ '(-n +n)'{'-n[',"+n[don't "}'read (syntax-check) commands only]'
+ '(-u +u)'{'-u[',"+u[don't "}'treat unset variables as an error during parameter expansion]'
+ '(-v +v)'{'-v[',"+v[don't "}'print shell input lines as they are read]'
+ '(-x +x)'{'-x[',"+x[don't "}'print command trace]'
+ )
+
+ case $service in
+ dash)
+ args=(
+ ${args:#\(*\)[+-]h\[*}
+ '(-E +E -V +V)'{'-E[',"+E[don't "}'use emacs-style command-line editing]'
+ '(-I +I)'{'-I[',"+I[don't "}'ignore EOF]'
+ '(-E +E -V +V)'{'-V[',"+V[don't "}'use vi-style command-line editing]'
+ )
+ ;;
+ yash)
+ PREFIX= _sh_set_options -O oopts
+ oopts=( ${oopts:#cmdline} )
+ args+=(
+ '(- : *)--help[display help information]'
+ '(- : *)'{-V,--version}'[display version information]'
+ "(--profile)--noprofile[don't load ~/.yash_profile]"
+ "(--rcfile)--norcfile[don't load ~/.yashrc]"
+ '(--noprofile)--profile=[load specified file instead of ~/.yash_profile]:.yash_profile file:_files'
+ '(--norcfile)--rcfile=[load specified file instead of ~/.yashrc]:.yashrc file:_files'
+ ${${(M)args:#\(*\)-c\[*}/-c/--cmdline}
+ --$^oopts
+ ++$^oopts
+ )
+ ;;
+ *ksh*)
+ args+=(
+ '(-k +k)'{'-k[',"+k[don't "}'recognise parameter assignments anywhere in a command]'
+ '(-p +p)'{'-p[',"+p[don't "}'enable privileged mode]'
+ '(-r +r)'{'-r[',"+r[don't "}'enable restricted mode]'
+ )
+ ;|
+ oksh|r#[lm]ksh)
+ args+=(
+ '(-X +X)'{'-X[',"+X[don't "}'mark directories with trailing / when globbing]'
+ )
+ ;|
+ ksh93)
+ PREFIX= _sh_set_options -O oopts
+ oopts=( ${${oopts:#rc}//[_-]/} )
+ args+=(
+ '(-B +B)'{'-B[',"+B[don't "}'enable brace expansion]'
+ '(-D +D)'{'-D[',"+D[don't "}'display strings subject to language translation]'
+ '(-E +E --rc)'{'--rc[','-E[',"+E[don't "}'load $ENV or ~/.kshrc]'
+ '(-G +G)'{'-G[',"+G[don't "}'enable ** pattern (globstar)]'
+ '(-H +H)'{'-H[',"+H[don't "}'enable history expansion]'
+ '(-t +t)'{'-t[',"+t[don't "}'read one command and exit]'
+ --$^oopts
+ )
+ [[ $PREFIX == --n* ]] && args+=( --no$^oopts )
+ ;;
+ r#[lm]ksh)
+ args+=(
+ '(-U +U)'{'-U[',"+U[don't "}'enable UTF-8 support]'
+ '-T+[spawn on specified tty]:tty:_files -g "*(%c)"'
+ )
+ ;;
+ esac
+
+# others. these options are common to all of those currently enumerated
+else
+ apat='-?*'
+ args=(
+ '(1 -)-c[execute specified command string]: :_cmdstring'
+ '-e[exit immediately on non-zero return]'
+ '-i[act as interactive shell]'
+ '-l[act as login shell]'
+ '-x[print command trace]'
+ )
+fi
+
+args+=( '1:script file:_files' '*:: :_files' )
+
+_arguments -s -S -A $apat : $args
Messages sorted by:
Reverse Date,
Date,
Thread,
Author