Should I put *.sh and *.rb file extensions on all my scripts?

I have a bunch of hand-rolled executable scripts in my $HOME/bin directory. Some are written in bash, some in Ruby. All of them have the shebang line at the top telling the shell what interpreter to use (bash or Ruby, in my case).

I'm wondering if it is better to put file extensions on these scripts to indicate what scripting language they are written in? For example, the scripts in Ruby would have the *.rb suffix, and the bash ones would have the *.sh suffix.

Currently, these scripts just have simple names with no file extension.

What's the best practice?


You can do wildcard commands like ls *.rb or cp *.sh if you want to organize your scripts in the future.

Start early or regret later, in my opinion.

Editors like vim will also be able to apply the correct syntax highlighting based on shebang or file extension.

This can also be accomplished by using modelines in various editors. E.g. for vim:

# vim: ft=sh

Well - as most things in life: It depends on your needs.

You stated that these scripts reside in a bin-directory and I assume that these scripts are to be called from the command line. As user I consider it as annoying if I have to type bla.ksh or foo.bash. Also if the coder decides to move to annother interpreter the command name would change too and I would have to amend other scripts that make use of these tools - very annoying, even if user and coder are the very same person.

But on the other hand I use extensions like .sh or .tcl in my project build directories. This way I can make use of make features to deploy the files into their destination directories - but at this stage I remove the file suffix.


Obviously there are some differences between executable files in bin directories, and editable "source" files.

  • For source files, it is helpful to have a suffix so you can see what is what, and to help some less-intelligent tools that fail to scan the #! line.
  • For modules, they're only used by a related set of programs, all of which use the same interpreter (or no interpreter), and it is conventional to include .pm, .sh or .so in such cases.
  • For executable programs, its name is part of the "programming contract" by which users and other scripts invoke it. It is important that the name does not change even if the implementation does; so obviously the filename should not have a suffix

In the case of a compiled program, the difference between "source" and "executable" is obvious: one contains source code, the other contains machine language or interpreted bytecode. In the case of a script, there's no obvious difference, but the make command maintains a notional separation between the "source code for a script" and the "executable version of a script": its default "compiler" for "shell script" is simply cp.

I would recommend keeping a separate $HOME/source directory, and either:

  • keeping a symlink, such as made by ln -s ../source/foo.sh $HOME/bin/foo; or
  • manually copy them after making changes (and testing them), using install -m 755 foo.sh ../bin/foo; or
  • using a Makefile rule to perform a syntax check before copying the source file from $HOME/source into $HOME/bin

Footnote: a shell script module is only usable by another shell script, and modifies the internal context of that script using the . or source built-in commands. This is different from an executable script, which (like any program) runs as a separate process and cannot modify its parent process. As a rough convention, modules go in /usr/lib/name_of_program/name_of_module.sh whereas commands go in /usr/bin/name_of_command (without any suffix).