Add /bin/bash to avoid unnecessary pain

I’ve been using NixOS as a daily driver for more than a year now, and I think I have enough experience to say that the lack of /bin/bash is a huge pain in the ass. The web is scattered with dozens of issues of NixOS users pleading with upstream to use /usr/bin/env bash instead. I just came across this issue on https://github.com/broadinstitute/cromwell/issues/3201.

Is there an argument against having /bin/bash that I’m not thinking of? It seems like dogmatism over pragmatism, but perhaps there’s a previous discussion that I did not find with a search.

Edit: is there any reason not to do sudo ln -s /run/current-system/sw/bin/bash /bin/bash?

2 Likes

IIRC, /bin/sh and /usr/bin/env were added due to POSIX compliance.

Pretty dogmatic, but it’s a form of global system state. Could have issues with bash_4 vs bash_5, etc. Ultimately, most programs shouldn’t assume filepaths (Yes, I’m aware shebangs needs an absolute path).

Not sure how persistent this is with nixos-rebuild.

If you want a quick hack, you could always do nix-shell -p steam; steam-run <prog>

1 Like

It can be a pain, but most upstream maintainers are willing to accept a patch that moves them from /bin/bash to /usr/bin/env bash, and if they aren’t a great thing about Nix is that it’s trivial to patch their software :slight_smile: That’s the sort of patch that’s unlikely to bitrot with merge conflicts, as well.

2 Likes

It’s not even a NixOS-specific issue. This affects some BSDs too, where bash is not in /bin/.

2 Likes

Unfortunately it’s not always that simple. For example, in the issue I linked it’s a .jar file, which as I understand means re-compiling from scratch. My symbolic link was a few second fix, while packaging this software for NixOS would take me at least an hour. Upstream maintainers are often incredulous when this issue is reported. Even with upstream willingness, there are often valid reasons not to switch to /usr/bin/env bash–namely lack of support for multiple arguments.

POSIX specifies that env should exist, but not the path itself, so /usr/bin/env is usually present, just as /bin/bash is usually present.

A quick search shows more than 5,000 issues on github for NixOS & /bin/bash. It seems like this is just a waste of everyone’s time with no derived benefit, outside of some noble feeling of purity.

2 Likes

Java .jar files are just archives (Java ARchive) so extract, change and repack, but the problem in this case might require at least recompiling that file, as the string is embedded in a compiled .class file (as opposed to say a text file in a .jar archive).

But I do also feel like we might be more than a bit annoying with not having /bin/bash. However, I think if we do end up with having ‘/bin/bash’, it needs to be compatible with the macOS bash which is version 3.2.57 (due to licensing issues with newer bash)

1 Like

I think a good design decision would be to have a user configurable /bin/bash. That way those that care for macOS compatibility could choose 3, most of us could choose 5, and those that were so inclined could elect to have none at all.

I wholeheartedly like and appreciate that NixOS’s /bin is so sparse, but bash is a special case used by many in the same way as /bin/sh

3 Likes

The problem with that is then we won’t be reproducible, e.g. suppose we have /bin/bash in one computer, and none in another. If the package maintainer uses /bin/bash and the user doesn’t then the package won’t work. Worse with multiple versions, supposing (this is a silly conjecture but the principle holds) that the only terminal emulator requires bash 3 at runtime, and the only text editor requires bash 5 at runtime and both are incompatible… In reality it’s probably not this serious, but we still want to avoid stuff like this.

Caveat: We probably do some of this by using /bin/sh but perhaps in theory only rely on a minimum compatible set of features?

2 Likes

In reality the Bash API is highly stable and backwards compatible. The vast majority of scripts that ran on bash 4.4 will also run on Bash 5.0. I’ve personally never encountered a situation where a script didn’t run because it was designed for an earlier version of bash. You do occasionally hear issues with mac users, but that’s a very different situation of being on an old version of bash lacking features used in modern scripts.

On the reproducibility spectrum between instruction set versions and python versions, bash is much closer to instruction sets.

1 Like

Here’s a module do this in a NixOS configuration, in the same way /usr/bin/env works. Perhaps we should consider it for NixOS?

{ config, lib, pkgs, ... }:

with lib;

{

  options = {
    environment.binbash = mkOption {
      default = null;
      type = types.nullOr types.path;
      description = ''
        Include a /bin/bash in the system.
      '';
    };
  };

  config = {

    system.activationScripts.binbash = if config.environment.binbash != null
      then ''
        mkdir -m 0755 -p /bin
        ln -sfn ${config.environment.usrbinenv} /bin/.bash.tmp
        mv /bin/.bash.tmp /usr/bin/bash # atomically replace /usr/bin/env
      ''
      else ''
        rm -f /bin/bash
        rmdir -p /bin || true
      '';

  };

}
6 Likes

True, but the mac issue will happen, especially if we make the bash version selectable. What I am saying, is that if we make this a thing, we then have to make it a thing for all installations with the same bash version[1]. And we need version compatibility both ways[1]. Otherwise I could write a derivation that works on my system because I have /bin/bash, but won’t work on your system if you don’t have /bin/bash (either build time or run time)!

[1] Okay, the same version thing is an impurity we already have, i.e. the caveat of /bin/sh can be different on non-NixOS installations. E.g. if I do

$ nix-shell --pure -A bash # or something
nix-shell $ /bin/sh --version
# I get 3.2.57(1)-release on mac
# I get 4.4.23(1)-release on NixOS 19.09

@matthewbauer that’s awesome, thanks for making!

@KoviRobi we could still require that submissions to nixpkgs do not depend on /bin/bash for purity reasons if we want users to be able to ‘opt-in’ to /bin/bash on NixOS without forcing it on everyone.

Otherwise, defaulting to Bash 4.4 seems reasonable given that all of nixpkgs uses this. bash_5 is not used anywhere, and Bash 3 is not in nixpkgs.

1 Like

@KoviRobi we could still require that submissions to nixpkgs do not depend on /bin/bash for purity reasons if we want users to be able to ‘opt-in’ to /bin/bash on NixOS without forcing it on everyone.

The problem is that then PR feedback from people who opted in to having /bin/bash will become slightly suspect, sometimes without the person doing the testing realising it.

2 Likes

Completely agreed. One thing I really appreciate about NixOS is reproducibility on bug reports. I think /bin/bash and things like that which compromise reproducibility would have such a big impact that in the worst possible way. It’s almost as if NixOS becomes 2 distros at that point.

5 Likes

Perhaps there’s a way for useSandbox to exclude /bin/bash using a bind mount or otherwise?

Sure, but then it still applies for a program’s runtime, should that use /bin/bash. Arguably we should have replaced all /bin/bash instances in the fixupPhase.

In reality the Bash API is highly stable and backwards compatible. The
vast majority of scripts that ran on bash 4.4 will also run on Bash
5.0
. I’ve personally
never encountered a situation where a script didn’t run because it was
designed for an earlier version of bash.

I have, and it was in fact the Nixpkgs stdenv, which doesn’t work (or
didn’t at the time, with Bash 5).

1 Like

We actually need to go the other way and explicitly allow /bin/bash in the sandbox on macOS in order to fix sandbox compatibility with macOS Catalina as /bin/sh on Catalina now executes /bin/bash (it’s got a mechanism by which it can execute bash or zsh depending on certain other global configuration; if that other config is unreachable, e.g. because the sandbox is on, it defaults to /bin/bash).

See https://github.com/NixOS/nix/issues/3223 for more info.

eew, that‘s kinda disgusting. So that means #!/bin/sh doesn‘t necessarily mean a script needs to be strictly POSIX-compliant?

Anyway it would be great if there was a way to restrict anything in /nix from touching /bin/bash, while stuff anywhere else can use it.
Maybe make /bin/bash a wrapper script which does something like

if [ comesfromnix ]; then
  echo "programs from the nix store aren't allowed to use this file, please patch the code!"
  exit 1
fi
/run/current-system/sw/bin/bash "$@"

This could also easily be extended to e.g. warn the user when this wrapper is used for the first time…

When /bin/sh invokes /bin/bash on macOS it still does so with argv[0] == "sh", meaning bash still runs in POSIX sh mode.

Edit: I also forgot to mention, on macOS 10.14 and below, /bin/sh is literally just bash with a different name (for some reason it’s 64 bytes larger than /bin/bash though). You can verify this with /bin/sh -c 'echo $BASH_VERSION'. So what macOS 10.15 is doing now isn’t functionally different except in that the sandbox needs to allow access to /bin/bash.

1 Like