Use dash as /bin/sh
Table of Contents
This article only slightly expands on what the archwiki says about it.
Linking
/bin/sh
to dash instead of bash is not the same as "changing your default shell".
I want startup scripts and everything that has a #!/bin/sh
shebang to use the lightest possible shell by default, but I still want my trusty bash in interactive terminal sessions, and for complex scripts.
Checkbashisms-up-
As the article suggests, I install checkbashisms
and run it against all installed files, first in /usr/bin
and then all other files (I could have done this in one command, but it's easier to divide it into several steps):
sh$> find /usr/bin/ -type f -perm -o=r -print0 | \ xargs -0 gawk '/^#!.*( |[/])sh/{printf "%s\0", FILENAME} {nextfile}' 2>/dev/null | \ xargs -0 checkbashisms $> pacman -Qlq | grep -v '/usr/bin/' | \ xargs -d'\n' gawk '/^#!.*( |[/])sh/{printf "%s\0", FILENAME} {nextfile}' 2>/dev/null | \ xargs -0 checkbashisms
This finds all scripts that have a #!/bin/sh
or similar shebang and checks them with checkbashisms
.
It finds a lot of false positives as far as dash
is concerned.
From the man page:
Note that the definition of a bashism in this context roughly equates to "a shell feature that is not required to be supported by POSIX"; this means that some issues flagged may be permitted under optional sections of POSIX, such as XSI or User Portability.
These are definitely false positives for dash
:
(local foo)
(local foo=bar)
(local x y)
(test -a/-o)
(trap with signal numbers)
(type)
($_)
This one is problematic though:
(echo -e)
dash
will echo a literal -e
in this case. I had a closer look at a few scripts that use this but decided it's nothing to worry about (famous last words).
Then, some scripts deliberately test for bashisms in their scripts, creating more false positives (I particularly noticed various libtool scripts).
Others use $RANDOM
, which is undefined in dash
, but prove fallbacks.
The only one that really seems to be problematic is xdg-settings
, which uses [[ expr1 == expr2 ]]
instead of [ expr1 = expr2 ]
a few times (but strangely only in KDE-specific functions).
Additionally, I've seen (alternative test command ([[ foo ]] should be [ foo ]))
or ([^] should be [!])
when it's inside a grep
or sed
pattern, hence false positive.
Then I continued to test my own scripts:
sh$> find "$HOME" -type f -perm -o=r -print0 | \ xargs -0 gawk '/^#!.*( |[/])sh/{printf "%s\0", FILENAME} {nextfile}' 2>/dev/null | \ xargs -0 checkbashisms
This is where I had to open scripts in an editor and decide to either change the shebang to #!/bin/bash
or fix the script.
Link /bin/sh to dash-up-
Now the most important step:
# ln -sfT dash /usr/bin/sh
Reboot-up-
I had to fix only one thing - my ~/.xinitrc
which already has the #!/bin/sh
shebang needs the HOSTNAME
environment variable which bash
sets automatically.
So, in my ~/.bash_profile
, which still gets executed (my login shell is still bash
), I just need to export HOSTNAME
.
Totally unscientific performance comparison - before the change:
$> systemd-analyze
Startup finished in 2.468s (kernel) + 8.171s (userspace) = 10.639s
graphical.target reached after 2.545s in userspace
After the change:
$> systemd-analyze
Startup finished in 2.506s (kernel) + 2.563s (userspace) = 5.070s
graphical.target reached after 2.558s in userspace
Of course I'd have to test more thoroughly, but this looks good enough.
Make it permanent-up-
I cannot prevent bash
updates from over writing the symlink, but I can add a pacman hook that immediately reverts it after every update.
I put this in /etc/pacman.d/hooks/dash-as-sh.hook
:
cfg[Trigger] Type = Package Operation = Install Operation = Upgrade Target = bash [Action] Description = Re-pointing /bin/sh symlink to dash... When = PostTransaction Exec = /usr/bin/ln -sfT dash /usr/bin/sh Depends = dash
Now I can rewrite my regularly executed conky scripts to use dash! Because conky has /bin/sh hardcoded.