111

Considering that POSIX is the closest thing to a common standard among all unices, I'm interested in knowing if there's a shell that supports it exclusively. While most modern shells provide support for POSIX (and will run POSIX compliant scripts without any problem), they don't do a good job at pointing out non-compliant features.

Is there any shell that implements POSIX and POSIX only, in such a way that it'd throw an error for any non compliant feature?

EDIT I want to clarify that I'm not asking for general tips for writing portable shell scripts. The related question mentioned in the comments already covered this. I thought of this question when I found out that bash has a --posix option but only to discover that it only affects some intialization behaviors which is not exactly what I'm looking for.

tripleee
  • 7,699
rahmu
  • 20,023

4 Answers4

69

You can use ShellCheck (GitHub) as a linter for your shell scripts. There is also an online version.

To detect POSIX compatibility issues (e.g. SC2039), the shebang line of your shell script should be #!/bin/sh. You can also pass --shell=sh to shellcheck.

Example (test.sh):

#!/bin/sh
if [[ $HOSTNAME == test ]]; then
    echo fail &> foo
fi

Result (shellcheck test.sh):

In test.sh line 2:
if [[ $HOSTNAME == test ]]; then
   ^-- SC2039: In POSIX sh, [[ ]] is undefined.
      ^-- SC2039: In POSIX sh, HOSTNAME is undefined.    

In test.sh line 3:
    echo fail &> foo
              ^-- SC2039: In POSIX sh, &> is undefined.
johnLate
  • 3,034
55

Unfortunately, 'portable' is usually a stronger requirement than 'POSIX-compliant' for shell scripts. That is, writing something that runs on any POSIX shell isn't too hard, but getting it to run on any real-world shell is harder.

You can start by installing every shell in your package manager, in particular debian's posh sounds like what you want (Policy-compliant Ordinary SHell). Debian's policy is POSIX with a few exceptions (echo -n specified, local...).

Beyond that though, testing has to cover a few shells (/bin/sh especially) on a range of platforms. I test on Solaris (/bin/sh and xpg4/sh), and BSD. AIX and HP-UX are very compliant and don't cause problems. bash is a little world of its own.

I'd recommend the Autoconf guide to portable shell, which is absolutely brilliant and saves a lot of time. Large chunks of it are obsolete, but that's OK; just skip TruUnix and Ultrix and so on if you don't care!

Nicholas Wilson
  • 1,068
  • 1
  • 11
  • 19
  • posh does indeed sound like what I'm asking for. I'll do some tests as soon as I can. – rahmu Sep 21 '12 at 13:29
  • 1
    I answered before spotting the related question! posh is a debian thing, so won't be packaged on every system. Also, the shell isn't necessarily the thing to be most worried about; sed incompatibilities are a big problem, for example. – Nicholas Wilson Sep 21 '12 at 13:47
  • 1
    I wouldn't bother porting to Solaris /bin/sh aka Bourne shell (which is not POSIX). Solaris like all modern unices has a POSIX shell, it just happens not to be in the usual location (/usr/xpg4/bin/sh) – Stéphane Chazelas Sep 21 '12 at 14:31
  • Sadly, we ship scripts that have to run on at least every /bin/sh. It's not as bad as all that... I'd rather not have to do it though. – Nicholas Wilson Sep 21 '12 at 14:52
  • @sch: I'm on Solaris 10 now. I suspect /usr/xpg4/bin/sh and /bin/ksh to be the same executable. It's not 2 hardlinks to the same file and they do not share a similar hash (md5sum). However their size is the same (to the byte), their behavior is exactly the same, exactly similar error messages and exactly similar outputs, notable when I press ^V in emacs-mode: Version M-11/16/88i. How can I test to see if /usr/xpg4/bin/sh is really the Bourne shell like you say? (I tripled check: It's not a link, symbolic nor hard). – rahmu Sep 21 '12 at 15:51
  • 1
    The Bourne shell is not a POSIX conformant shell, ksh88 is. The Bourne shell used to be Unix's sh in the pre-POSIX days, but it's a museum piece now, just like the Thomson shell was before it. – Stéphane Chazelas Sep 21 '12 at 16:38
  • 2
    The reason I treat /bin/sh as important is because it's invoked from the shebang in so many scripts. "/usr/bin/env sh" is hardly an improvement. If you're going to put any effort into making something work on non-POSIX shells, I feel I may as well prioritise each system's /bin/sh. It won't be long before some customer runs your script with the default shell, which is not all that unreasonable. – Nicholas Wilson Sep 21 '12 at 17:07
  • The Autoconf documentation contains flaws such as "To detect whether you are running Bash, test whether BASH_VERSION is set." This is false; it will not work when Bash has been invoked as /bin/sh. – Kaz Apr 17 '20 at 17:07
  • Why wouldn't POSIX-compliant syntax be very portable? – Melab Feb 03 '21 at 19:09
15

Bash will run in POSIX-compliant mode if the POSIXLY_CORRECT environment variable is set. From the manpage:

   POSIXLY_CORRECT
          If  this  variable  is  in the environment when bash starts, the
          shell enters posix mode before reading the startup files, as  if
          the  --posix  invocation option had been supplied.  If it is set
          while the shell is running, bash enables posix mode, as  if  the
          command set -o posix had been executed.

Many other GNU utilities will also honor POSIXLY_CORRECT, so if you're on a system with predominantly GNU tools (e.g. most Linux systems), this is a good start if your goal is POSIX conformance.

2

Is there any shell that implements POSIX and POSIX only, in such a way that it'd throw an error for any non compliant feature?

Dash fits the bill here.

DASH is a POSIX-compliant implementation of /bin/sh that aims to be as small as possible. It does this without sacrificing speed where possible. In fact, it is significantly faster than bash (the GNU Bourne-Again SHell) for most tasks.

When writing shell scripts that should conform to the POSIX Shell Command Language, I use Dash as the interpreter when testing my scripts as it implements only POSIX shell features and throws an error if it encounters any Bashisms such as array variables or process substitution.

Dash is a port of the Almquist shell (hence its name, Debian Almquist shell) and on Debian-based systems, /bin/sh is a symbolic link to /bin/dash. For more information, see Almquist shell - Wikipedia.

  • 1
    I added this answer because none of the other (excellent) answers mention Dash. It's only mentioned in the question comments but users (including those who find this question via a search engine) shouldn't have to check through comments to see all the valid answers. – Anthony Geoghegan Jul 19 '21 at 17:10
  • 7
    The design of dash priortizes practicality over strict conformance. I would not trust it to identify all deviations from POSIX the way posh specifically does. – tripleee Apr 15 '22 at 08:34