What Linux Shell Should I Use

Which shell should be used for Linux/MacOS/UNIX best compatibility?

You won't find a shell implementation that will be installed on every of these OSes, however, all of them are either POSIX compliant or more or less close to being compliant.

You should then restrict your shell scripts to stick to the POSIX standard as far as possible.

However, there is no simple way to tell a script is to be executed in a POSIX context, and in particular to specify what shebang to set. I would suggest to use a postinstaller script that would insert the correct shebang on the target platform retrieved using this command:

#!/bin/sh
printf "#!%s\n" `PATH=\`getconf PATH\` command -v sh`

You scripts should also include this instruction once and before calling any external command:

export PATH=$(getconf PATH):$PATH

to make sure the utilities called are the POSIX ones. Moreover, beware that some Unix implementations might require an environment variable to be set for them to behave a POSIX way (eg BIN_SH=xpg4 is required on Tru64/OSF1, XPG_SUS_ENV=ON on AIX, ...).

To develop your script, I would recommend to use a shell that has the less extensions to the standard, like dash. That would help to quickly detect errors caused by bashisms (or kshisms or whatever).

PS: beware that despite popular belief, /bin/sh is not guaranteed to be POSIX compliant even on a POSIX compliant OS.

A better Linux shell?

You can run PowerShell on Linux via Pash. It uses Mono the way PowerShell uses .NET.

bash vs csh vs others - which is better for application maintenance?

These days, just about any non-embedded (or large embedded) operating system has a POSIX:2001 a.k.a. Single Unix v3 compatibility layer. This is native on unix platforms (Linux, Mac OS X, Solaris, *BSD, etc.) and installable on other platforms such as Windows and Android. POSIX specifies a shell language, usually known as POSIX sh. This language is derived from the Bourne shell.

Most unix systems have one of two implementations of POSIX sh: ksh or bash, which have additional useful features compared to POSIX. However some less mainstream systems (especially embedded ones) may have only POSIX-mandated features.

Given your objectives, I see three choices:

  • Restrict yourself to POSIX sh. Pro: you don't have to worry about differing variants, since there's a standard and compliant implementations are readily available. Con: you don't benefit from bash and ksh's extensions.
  • Use the intersection of ksh and bash. This is attractive in appearance, but it does mean you have to use two reference documents instead of just one — and even the features that bash and ksh have in common don't always use the same syntax. Figuring out which one you want to use on a given system is also a pain.
  • Choose one of ksh or bash. Both bash and ksh are available on all unix-like platforms and on Windows. Both have an open source implementation (the only one for bash, ATT ksh93 for ksh) that can be installed on most platforms. I'd go for bash over ksh for two reasons. First, it's the default on Linux, so you'll find more people who're used to it. Second, there are systems that come with an older, less-featured implementation of ksh; even if you can install ksh93, it's another thing you have to think about when deploying.

Forget about csh for scripting, and forget about zsh if you want common default availability.

See also What are the fundamental differences between the mainstream *NIX shells?, particularly the “for scripting” part of my answer.

Note that shell programming involves other utilities beyond the shell. POSIX specifies those other utilities. “Bash plus other POSIX utilities” is a reasonable choice, distinct from “POSIX utilities (including sh)”.

What is the preferred Bash shebang (#!)?

You should use #!/usr/bin/env bash for portability: different *nixes put bash in different places, and using /usr/bin/env is a workaround to run the first bash found on the PATH. And sh is not bash.

Why do you need to put #!/bin/bash at the beginning of a script file?

It's a convention so the *nix shell knows what kind of interpreter to run.

For example, older flavors of ATT defaulted to sh (the Bourne shell), while older versions of BSD defaulted to csh (the C shell).

Even today (where most systems run bash, the "Bourne Again Shell"), scripts can be in bash, python, perl, ruby, PHP, etc, etc. For example, you might see #!/bin/perl or #!/bin/perl5.

PS:
The exclamation mark (!) is affectionately called "bang". The shell comment symbol (#) is sometimes called "hash".

PPS:
Remember - under *nix, associating a suffix with a file type is merely a convention, not a "rule". An executable can be a binary program, any one of a million script types and other things as well. Hence the need for #!/bin/bash.

Should I use a Shebang with Bash scripts?

On UNIX-like systems, you should always start scripts with a shebang line. The system call execve (which is responsible for starting programs) relies on an executable having either an executable header or a shebang line.

From FreeBSD's execve manual page:

 The execve() system call transforms the calling process into a new
process. The new process is constructed from an ordinary file, whose
name is pointed to by path, called the new process file.
[...]

This file is
either an executable object file, or a file of data for an interpreter.

[...]

An interpreter file begins with a line of the form:

#! interpreter [arg]

When an interpreter file is execve'd, the system actually execve's the
specified interpreter. If the optional arg is specified, it becomes the
first argument to the interpreter, and the name of the originally
execve'd file becomes the second argument

Similarly from the Linux manual page:

execve() executes the program pointed to by filename. filename must be
either a binary executable, or a script starting with a line of the
form:

#! interpreter [optional-arg]

In fact, if a file doesn't have the right "magic number" in it's header, (like an ELF header or #!), execve will fail with the ENOEXEC error (again from FreeBSD's execve manpage):

[ENOEXEC] The new process file has the appropriate access
permission, but has an invalid magic number in its
header.


If the file has executable permissions, but no shebang line but does seem to be a text file, the behaviour depends on the shell that you're running in.

Most shells seem to start a new instance of themselves and feed it the file, see below.

Since there is no guarantee that the script was actually written for that shell, this can work or fail spectacularly.

From tcsh(1):

   On  systems which do not understand the `#!' script interpreter conven‐
tion the shell may be compiled to emulate it; see the version shell
variable. If so, the shell checks the first line of the file to see if
it is of the form `#!interpreter arg ...'. If it is, the shell starts
interpreter with the given args and feeds the file to it on standard
input.

From FreeBSD's sh(1):

If the program is not a normal executable file (i.e., if it
does not begin with the “magic number” whose ASCII representation is
“#!”, resulting in an ENOEXEC return value from execve(2)) but appears to
be a text file, the shell will run a new instance of sh to interpret it.

From bash(1):

   If this execution fails because the file is not in  executable  format,
and the file is not a directory, it is assumed to be a shell script, a
file containing shell commands. A subshell is spawned to execute it.

You cannot always depend on the location of a non-standard program like bash. I've seen bash in /usr/bin, /usr/local/bin, /opt/fsf/bin and /opt/gnu/bin to name a few.

So it is generally a good idea to use env;

#!/usr/bin/env bash

If you want your script to be portable, use sh instead of bash.

#!/bin/sh

While standards like POSIX do not guarantee the absolute paths of standard utilities, most UNIX-like systems seem to have sh in /bin and env in /usr/bin.

What is the difference b/w Bash and Shell script

"Shell script" is generic term for a script that's executed by a shell.

"Bash script" is a more specific term; it refers to a script that's executed by one specific shell, the Bash shell.

A shell is a command interpreter program. It can be used interactively (where the user types commands at a prompt, and the shell executes them), or as an interpreter for a script (where a series of commands are written in a file).

The Bourne shell is one of the older shells on UNIX (not the oldest, but we needn't worry about ancient history). Several other shells have been implemented as replacements for, or extensions of, the Bourne shell.

In particular, GNU Bash is perhaps the most commonly used shell these days. It implements the same features as the Bourne shell, plus a number of extensions.

A Bourne shell script typically starts with "Shebang" line:

#!/bin/sh

A Bash script typically starts with a Shebang that specifies the Bash shell:

#!/bin/bash

and may depend on features implemented by Bash but not by the Bourne shell.

(On some operating systems, /bin/sh might be the same command as /bin/bash.)

Not all Unix shells are based on the Bourne shell. In particular, csh and its derivative tcsh are largely incompatible with the Bourne-derived shells.

Bash has very little to do with Windows cmd scripts, except that both Bash and cmd.exe are both command interpreters.

What does $@ mean in a shell script?

$@ is nearly the same as $*, both meaning "all command line arguments". They are often used to simply pass all arguments to another program (thus forming a wrapper around that other program).

The difference between the two syntaxes shows up when you have an argument with spaces in it (e.g.) and put $@ in double quotes:

wrappedProgram "$@"
# ^^^ this is correct and will hand over all arguments in the way
# we received them, i. e. as several arguments, each of them
# containing all the spaces and other uglinesses they have.
wrappedProgram "$*"
# ^^^ this will hand over exactly one argument, containing all
# original arguments, separated by single spaces.
wrappedProgram $*
# ^^^ this will join all arguments by single spaces as well and
# will then split the string as the shell does on the command
# line, thus it will split an argument containing spaces into
# several arguments.

Example: Calling

wrapper "one two    three" four five "six seven"

will result in:

"$@": wrappedProgram "one two    three" four five "six seven"
"$*": wrappedProgram "one two three four five six seven"
^^^^ These spaces are part of the first
argument and are not changed.
$*: wrappedProgram one two three four five six seven


Related Topics



Leave a reply



Submit