13

I have a large shell script that uses a mixture of spaces and tabs. I want to re-indent the whole file based on syntax, like Eclipse's Format. Is there a program (beautify ?) that will do this ?

I'm having a hard time figuring out the logic with everything jammed together e.g.

   if [ "$CANCELLATION" ]
   then
   while test $num -gt 0
    do
    if [ "$cjb" -gt 0 ]

Learned how to call functions in Vim but that didn't work.

Emacs - lost all the newlines

4 Answers 4

13

Emacs can do that:

  • load the file into Emacs
  • press Ctrl-space at the top of the file
  • move the cursor to the bottom of the file
  • press Alt-X and type untabify then return
  • press Alt-X and type indent-region then return

This will get rid of tabs and indent everything properly.

If you need to do this more often and do not use Emacs as your editor, you might want to pack it all into a script:

#!/usr/bin/emacs --script

(setq require-final-newline 'visit)

(defun indent-files (files)
  (cond (files
         (find-file (car files))
         (untabify (point-min) (point-max))
         (indent-region (point-min) (point-max))
         (save-buffer)
         (kill-buffer)
         (indent-files (cdr files)))))

(indent-files command-line-args-left)

;; EOF ;;
4
  • 1
    not really an emacs guy - but if the super shell vim doesn't work. I'll try that.
    – jsymolon
    Sep 10, 2009 at 14:21
  • 1
    M-> is the usual binding for end-of-buffer. Because taking your hands off the keyboard is evil. Sep 10, 2009 at 21:52
  • 3
    Also marking the entire buffer can be done with C-x h, which runs mark-whole-buffer.
    – Victor
    Feb 18, 2013 at 16:14
  • Brilliant! Two things: (i) I have to re-select the region after untabify as everything gets unselected, and (2) did this not just use to be in the menu as well? It deserves to be!
    – alle_meije
    Jul 26, 2018 at 12:15
7

In Vi/Vim once you've set the correct file syntax (:set syntax=sh), you can press: gg=G (or 1G=G) to correct indentation of the entire file.

Here is the command to format the whole file in-place:

ex +"set syn=sh" +"norm gg=G" -cwq foobar.sh

Note: Make a backup before running above command.

To test the formatting first, edit the file in Vim and run: :norm gg=G.

See also: Re-indenting badly indented code at Vi SE

3
  • 2
    I just tried this (Vim 8.0) and it didn't work correctly. My guess is that it tried to indent the shell script as if it were C code; it handled curly braces correctly but not if and fi keywords. Perhaps some plugin is required? Feb 22, 2018 at 22:36
  • Actually, you should be setting filetype=sh (or ft).
    – lingfish
    Mar 5, 2019 at 6:39
  • This does not work well at all. It makes a mess of my complex Bash script. Apr 18, 2020 at 0:48
2

It looks like shfmt could do the trick for you. It formats shell/bash code, which includes indentation.

shfmt -l -w script.sh
3
  • 1
    While this may answer the question, it would be a better answer if you could provide some explanation why it does so. Pehaps you can give an example of how to use it?
    – DavidPostill
    Apr 8, 2018 at 15:08
  • GREAT! This worked! I used two spaces as ident, so my command was shfmt -i 2 -ci ~/mariadb-qa/pquery-run.sh Apr 18, 2020 at 0:51
  • Strangely, shfmt doesnt actually seem to touch the indentation. If you indent an entire block and run the formatter, itll leave it like it is.
    – oarfish
    Oct 27, 2022 at 11:40
0

You probably want Super Shell Indent. Save it to ~/.vim/indent and when you're next in vim, execute :source ~/.vim/indent/sh.vim

You also probably want to setup file based smart indenting in your .vimrc

" Turn on smart indenting
filetype indent on
set smartindent

If you don't mind vim messing with your formatting, add this line to vimrc too.

" When you load/save a shell script, auto indent it
autocmd BufRead,BufWritePre *.sh normal gg=G

Anyway, here is what you've been waiting for. When you're next editing a shell script in vim, assuming you've turned on smart indenting and installed super shell ident, just hit the follow keys to reindent your script: gg=G

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .