Function: sh-mode

sh-mode is an autoloaded, interactive and byte-compiled function defined in sh-script.el.gz.

Signature

(sh-mode)

Documentation

Major mode for editing shell scripts.

This mode works for many shells, since they all have roughly the same syntax, as far as commands, arguments, variables, pipes, comments etc. are concerned. Unless the file's magic number indicates the shell, your usual shell is assumed. Since filenames rarely give a clue, they are not further analyzed.

This mode adapts to the variations between shells (see sh-set-shell) by means of an inheritance based feature lookup (see sh-feature). This mechanism applies to all variables (including skeletons) that pertain to shell-specific features. Shell script files can use the sh-shell local variable to indicate the shell variant to be used for the file.

The default style of this mode is that of Rosenblatt's Korn shell book. The syntax of the statements varies with the shell being used. The following commands are available, based on the current shell's syntax:
C-c C-c (sh-case) case statement
C-c C-f (sh-for) for loop
C-c ( (sh-function) function definition
C-c TAB (sh-if) if statement
C-c C-l (sh-indexed-loop) indexed loop from 1 to n
C-c C-o (sh-while-getopts) while getopts loop
C-c C-r (sh-repeat) repeat loop
C-c C-s (sh-select) select loop
C-c C-u (sh-until) until loop
C-c C-w (sh-while) while loop

For sh and rc shells indentation commands are:
C-c ? (smie-config-show-indent) Show the rules controlling this line's indentation.
C-c < (smie-config-set-indent) Change the rules controlling this line's indentation.
C-c > (smie-config-guess) Try to tweak the indentation rules so the
buffer indents as it currently is indented.


DEL (backward-delete-char-untabify) Delete backward one position, even if it was a tab.
M-e (sh-end-of-command) Go to end of successive commands.
M-a (sh-beginning-of-command) Go to beginning of successive commands.
C-c : (sh-set-shell) Set this buffer's shell, and maybe its magic number.
C-M-x (sh-execute-region) Have optional header and region be executed in a subshell.

sh-electric-here-document-mode(var)/sh-electric-here-document-mode(fun) controls whether insertion of two unquoted < insert a here document. You can control this behavior by modifying sh-mode-hook.

If you generally program a shell different from your login shell you can set sh-shell-file accordingly. If your shell's file name doesn't correctly indicate what shell it is use sh-alias-alist to translate.

If your shell gives error messages with line numbers, you can use C-c C-x (executable-interpret) with your script for an edit-interpret-debug cycle.

Probably introduced at or before Emacs version 24.4.

Key Bindings

Aliases

shell-script-mode

Source Code

;; Defined in /usr/src/emacs/lisp/progmodes/sh-script.el.gz
;;;###autoload
(define-derived-mode sh-mode prog-mode "Shell-script"
  "Major mode for editing shell scripts.
This mode works for many shells, since they all have roughly the same syntax,
as far as commands, arguments, variables, pipes, comments etc. are concerned.
Unless the file's magic number indicates the shell, your usual shell is
assumed.  Since filenames rarely give a clue, they are not further analyzed.

This mode adapts to the variations between shells (see `sh-set-shell') by
means of an inheritance based feature lookup (see `sh-feature').  This
mechanism applies to all variables (including skeletons) that pertain to
shell-specific features.  Shell script files can use the `sh-shell' local
variable to indicate the shell variant to be used for the file.

The default style of this mode is that of Rosenblatt's Korn shell book.
The syntax of the statements varies with the shell being used.  The
following commands are available, based on the current shell's syntax:
\\<sh-mode-map>
\\[sh-case]	 case statement
\\[sh-for]	 for loop
\\[sh-function]	 function definition
\\[sh-if]	 if statement
\\[sh-indexed-loop]	 indexed loop from 1 to n
\\[sh-while-getopts]	 while getopts loop
\\[sh-repeat]	 repeat loop
\\[sh-select]	 select loop
\\[sh-until]	 until loop
\\[sh-while]	 while loop

For sh and rc shells indentation commands are:
\\[smie-config-show-indent]	Show the rules controlling this line's indentation.
\\[smie-config-set-indent]	Change the rules controlling this line's indentation.
\\[smie-config-guess]  Try to tweak the indentation rules so the
buffer indents as it currently is indented.


\\[backward-delete-char-untabify]	 Delete backward one position, even if it was a tab.
\\[sh-end-of-command]	 Go to end of successive commands.
\\[sh-beginning-of-command]	 Go to beginning of successive commands.
\\[sh-set-shell]	 Set this buffer's shell, and maybe its magic number.
\\[sh-execute-region]	 Have optional header and region be executed in a subshell.

`sh-electric-here-document-mode' controls whether insertion of two
unquoted < insert a here document.  You can control this behavior by
modifying `sh-mode-hook'.

If you generally program a shell different from your login shell you can
set `sh-shell-file' accordingly.  If your shell's file name doesn't correctly
indicate what shell it is use `sh-alias-alist' to translate.

If your shell gives error messages with line numbers, you can use \\[executable-interpret]
with your script for an edit-interpret-debug cycle."
  (make-local-variable 'sh-shell-file)
  (make-local-variable 'sh-shell)

  (setq-local skeleton-pair-default-alist
	      sh-skeleton-pair-default-alist)

  (setq-local paragraph-start (concat page-delimiter "\\|$"))
  (setq-local paragraph-separate (concat paragraph-start "\\|#!/"))
  (setq-local comment-start "# ")
  (setq-local comment-start-skip "#+[\t ]*")
  (setq-local local-abbrev-table sh-mode-abbrev-table)
  (setq-local comint-dynamic-complete-functions
	      sh-dynamic-complete-functions)
  (add-hook 'completion-at-point-functions #'comint-completion-at-point nil t)
  ;; we can't look if previous line ended with `\'
  (setq-local comint-prompt-regexp "^[ \t]*")
  (setq-local imenu-case-fold-search nil)
  (setq font-lock-defaults
	`((sh-font-lock-keywords
	   sh-font-lock-keywords-1 sh-font-lock-keywords-2)
	  nil nil
	  ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
	  (font-lock-syntactic-face-function
	   . ,#'sh-font-lock-syntactic-face-function)))
  (setq-local syntax-propertize-function #'sh-syntax-propertize-function)
  (add-hook 'syntax-propertize-extend-region-functions
            #'syntax-propertize-multiline 'append 'local)
  (setq-local skeleton-pair-alist '((?` _ ?`)))
  (setq-local skeleton-pair-filter-function #'sh-quoted-p)
  (setq-local skeleton-further-elements
	      '((< '(- (min sh-basic-offset (current-column))))))
  (setq-local skeleton-filter-function #'sh-feature)
  (setq-local skeleton-newline-indent-rigidly t)
  (setq-local defun-prompt-regexp
              (concat
               "^\\("
               "\\(function[ \t]\\)?[ \t]*[[:alnum:]_]+[ \t]*([ \t]*)"
               "\\|"
               "function[ \t]+[[:alnum:]_]+[ \t]*\\(([ \t]*)\\)?"
               "\\)[ \t]*"))
  (setq-local add-log-current-defun-function #'sh-current-defun-name)
  (add-hook 'completion-at-point-functions
            #'sh-completion-at-point-function nil t)
  (setq-local outline-regexp "###")
  ;; Parse or insert magic number for exec, and set all variables depending
  ;; on the shell thus determined.
  (sh-set-shell
   (cond ((save-excursion
            (goto-char (point-min))
            (looking-at auto-mode-interpreter-regexp))
          (match-string 2))
         ((not buffer-file-name) sh-shell-file)
         ;; Checks that use `buffer-file-name' follow.
         ((string-match "\\.m?spec\\'" buffer-file-name) "rpm")
         ((string-match "[.]sh\\>"     buffer-file-name) "sh")
         ((string-match "[.]bash\\>"   buffer-file-name) "bash")
         ((string-match "[.]ksh\\>"    buffer-file-name) "ksh")
         ((string-match "[.]mkshrc\\>" buffer-file-name) "mksh")
         ((string-match "[.]t?csh\\(rc\\)?\\>" buffer-file-name) "csh")
         ((string-match "[.]zsh\\(rc\\|env\\)?\\>" buffer-file-name) "zsh")
	 ((equal (file-name-nondirectory buffer-file-name) ".profile") "sh")
         (t sh-shell-file))
   nil nil)
  (add-hook 'hack-local-variables-hook
    #'sh-after-hack-local-variables nil t))