bash: Make interactive the default

The status quo of `bash` not being interactive is frustrating for many users,
because trying to use it interactively is just messed up, and
`bashInteractive` is not intuitive and barely discoverable.

This was brought to my (and many others) attention by @stahnma in his
[talk at CfgMgmtCamp 2025](https://cfp.cfgmgmtcamp.org/ghent2025/talk/YUVUTN/),
where he highlighted this as one of the frustrations he ran into when
learning Nix.

Why this is fine:
- No reason for not making interactive the default was given in the original commit (6c6ff6f36f), but probably it was due to the increase in closure size
- The closure size only increases by 6.9MiB (19.5%) today, with the
  added dependency on the store paths for readline and ncurses, which
  are needed on systems in almost all cases anyways
- If somebody really needs to get a more minimal system, they can use
  the newly-introduced `bashNonInteractive` instead now
- Though to apply it consistently, they'll need to do that in an
  overlay like
  ```
  final: prev: {
    bash = self.bashNonInteractive;
  }
  ```

  Or alternatively using the `system.replaceDependencies.replacements`
  NixOS option approach.

While there's also other such `*Interactive` packages that could use the
same treatment, `bash` is a great start.

This was already attempted before in
https://github.com/NixOS/nixpkgs/pull/151227, but was not continued for
unknown reason.

To avoid stdenv becoming bigger, all uses of bash in the (working)
stdenv's are changed to the explicitly non-interactive version here.

This commit will however still cause a mass rebuild for all packages (and reverse deps)
making use of the default bash.
This commit is contained in:
Silvan Mosberger
2025-02-05 00:31:46 +01:00
parent 10053c5dfd
commit e3491c9e40
13 changed files with 44 additions and 44 deletions
+2 -2
View File
@@ -22,7 +22,7 @@
pkg-config, pkg-config,
texinfo, texinfo,
bison, bison,
bash, bashNonInteractive,
}: }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {
@@ -79,7 +79,7 @@ stdenv.mkDerivation rec {
buildInputs = buildInputs =
[ [
perl perl
bash bashNonInteractive
] ]
++ lib.optionals enableGhostscript [ ++ lib.optionals enableGhostscript [
ghostscript ghostscript
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
lib, lib,
autoreconfHook, autoreconfHook,
bash, bashNonInteractive,
libtool, libtool,
fetchFromGitHub, fetchFromGitHub,
nix-update-script, nix-update-script,
@@ -48,7 +48,7 @@ stdenv.mkDerivation (finalAttrs: {
buildInputs = [ buildInputs = [
autoreconfHook autoreconfHook
# For patchShebangs in postInstall # For patchShebangs in postInstall
bash bashNonInteractive
perl perl
]; ];
@@ -24,7 +24,7 @@
, zlib , zlib
# platform-specific dependencies # platform-specific dependencies
, bash , bashNonInteractive
, darwin , darwin
, windows , windows
@@ -235,7 +235,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
inherit nativeBuildInputs; inherit nativeBuildInputs;
buildInputs = lib.optionals (!stdenv.hostPlatform.isWindows) [ buildInputs = lib.optionals (!stdenv.hostPlatform.isWindows) [
bash # only required for patchShebangs bashNonInteractive # only required for patchShebangs
] ++ buildInputs; ] ++ buildInputs;
prePatch = optionalString stdenv.hostPlatform.isDarwin '' prePatch = optionalString stdenv.hostPlatform.isDarwin ''
@@ -329,7 +329,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
postPatch = optionalString (!stdenv.hostPlatform.isWindows) '' postPatch = optionalString (!stdenv.hostPlatform.isWindows) ''
substituteInPlace Lib/subprocess.py \ substituteInPlace Lib/subprocess.py \
--replace-fail "'/bin/sh'" "'${bash}/bin/sh'" --replace-fail "'/bin/sh'" "'${bashNonInteractive}/bin/sh'"
'' + optionalString mimetypesSupport '' '' + optionalString mimetypesSupport ''
substituteInPlace Lib/mimetypes.py \ substituteInPlace Lib/mimetypes.py \
--replace-fail "@mime-types@" "${mailcap}" --replace-fail "@mime-types@" "${mailcap}"
@@ -611,7 +611,7 @@ in with passthru; stdenv.mkDerivation (finalAttrs: {
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [ ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
# Ensure we don't have references to build-time packages. # Ensure we don't have references to build-time packages.
# These typically end up in shebangs. # These typically end up in shebangs.
pythonOnBuildForHost buildPackages.bash pythonOnBuildForHost buildPackages.bashNonInteractive
]; ];
separateDebugInfo = true; separateDebugInfo = true;
@@ -3,7 +3,7 @@
lib, lib,
fetchurl, fetchurl,
libiconv, libiconv,
bash, bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook, updateAutotoolsGnuConfigScriptsHook,
}: }:
@@ -84,7 +84,7 @@ stdenv.mkDerivation rec {
]; ];
buildInputs = buildInputs =
lib.optionals (!stdenv.hostPlatform.isMinGW) [ lib.optionals (!stdenv.hostPlatform.isMinGW) [
bash bashNonInteractive
] ]
++ lib.optionals (!stdenv.hostPlatform.isLinux && !stdenv.hostPlatform.isCygwin) [ ++ lib.optionals (!stdenv.hostPlatform.isLinux && !stdenv.hostPlatform.isCygwin) [
# HACK, see #10874 (and 14664) # HACK, see #10874 (and 14664)
@@ -5,7 +5,7 @@
fetchurl, fetchurl,
perl, perl,
libintl, libintl,
bash, bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook, updateAutotoolsGnuConfigScriptsHook,
gnulib, gnulib,
gawk, gawk,
@@ -83,7 +83,7 @@ stdenv.mkDerivation {
nativeBuildInputs = [ updateAutotoolsGnuConfigScriptsHook ]; nativeBuildInputs = [ updateAutotoolsGnuConfigScriptsHook ];
buildInputs = buildInputs =
[ [
bash bashNonInteractive
libintl libintl
] ]
++ optionals stdenv.hostPlatform.isSunOS [ ++ optionals stdenv.hostPlatform.isSunOS [
@@ -6,7 +6,7 @@
fetchurl, fetchurl,
perl, perl,
libintl, libintl,
bash, bashNonInteractive,
updateAutotoolsGnuConfigScriptsHook, updateAutotoolsGnuConfigScriptsHook,
gnulib, gnulib,
gawk, gawk,
@@ -57,7 +57,7 @@ let
xz xz
libintl libintl
libiconv libiconv
bash bashNonInteractive
gnulib gnulib
gawk gawk
freebsd freebsd
+1 -2
View File
@@ -6,8 +6,7 @@
, bison , bison
, util-linux , util-linux
# patch for cygwin requires readline support , interactive ? true
, interactive ? stdenv.hostPlatform.isCygwin
, readline , readline
, withDocs ? null , withDocs ? null
, forFHSEnv ? false , forFHSEnv ? false
+11 -11
View File
@@ -153,7 +153,7 @@ let
runtimeShell = prevStage.ccWrapperStdenv.shell; runtimeShell = prevStage.ccWrapperStdenv.shell;
}; };
bash = prevStage.bash or bootstrapTools; bashNonInteractive = prevStage.bashNonInteractive or bootstrapTools;
thisStdenv = import ../generic { thisStdenv = import ../generic {
name = "${name}-stdenv-darwin"; name = "${name}-stdenv-darwin";
@@ -168,7 +168,7 @@ let
inherit extraNativeBuildInputs; inherit extraNativeBuildInputs;
preHook = preHook =
lib.optionalString (!isBuiltByNixpkgsCompiler bash) '' lib.optionalString (!isBuiltByNixpkgsCompiler bashNonInteractive) ''
# Don't patch #!/interpreter because it leads to retained # Don't patch #!/interpreter because it leads to retained
# dependencies on the bootstrapTools in the final stdenv. # dependencies on the bootstrapTools in the final stdenv.
dontPatchShebangs=1 dontPatchShebangs=1
@@ -181,9 +181,9 @@ let
export PATH_LOCALE=${prevStage.darwin.locale}/share/locale export PATH_LOCALE=${prevStage.darwin.locale}/share/locale
''; '';
shell = bash + "/bin/bash"; shell = bashNonInteractive + "/bin/bash";
initialPath = [ initialPath = [
bash bashNonInteractive
prevStage.file prevStage.file
bootstrapTools bootstrapTools
]; ];
@@ -328,7 +328,7 @@ let
# SDK packages include propagated packages and source release packages built during the bootstrap. # SDK packages include propagated packages and source release packages built during the bootstrap.
sdkPackages = prevStage: { sdkPackages = prevStage: {
inherit (prevStage) inherit (prevStage)
bash bashNonInteractive
libpng libpng
libxml2 libxml2
libxo libxo
@@ -408,7 +408,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
# stage should only access the stage that came before it. # stage should only access the stage that came before it.
ccWrapperStdenv = self.stdenv; ccWrapperStdenv = self.stdenv;
bash = bootstrapTools // { bashNonInteractive = bootstrapTools // {
shellPath = "/bin/bash"; shellPath = "/bin/bash";
}; };
@@ -600,7 +600,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
(llvmLibrariesPackages prevStage) (llvmLibrariesPackages prevStage)
{ {
inherit (prevStage) inherit (prevStage)
bash bashNonInteractive
cctools cctools
coreutils coreutils
cpio cpio
@@ -847,14 +847,14 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
inherit (prevStage) ccWrapperStdenv; inherit (prevStage) ccWrapperStdenv;
# Avoid an infinite recursion due to the SDKs including ncurses, which depends on bash in its `dev` output. # Avoid an infinite recursion due to the SDKs including ncurses, which depends on bash in its `dev` output.
bash = super.bash.override { stdenv = self.darwin.bootstrapStdenv; }; bashNonInteractive = super.bashNonInteractive.override { stdenv = self.darwin.bootstrapStdenv; };
# Avoid pulling in a full python and its extra dependencies for the llvm/clang builds. # Avoid pulling in a full python and its extra dependencies for the llvm/clang builds.
libxml2 = super.libxml2.override { pythonSupport = false; }; libxml2 = super.libxml2.override { pythonSupport = false; };
# Use Bash from this stage to avoid propagating Bash from a previous stage to the final stdenv. # Use Bash from this stage to avoid propagating Bash from a previous stage to the final stdenv.
ncurses = super.ncurses.override { ncurses = super.ncurses.override {
stdenv = self.darwin.bootstrapStdenv.override { shell = lib.getExe self.bash; }; stdenv = self.darwin.bootstrapStdenv.override { shell = lib.getExe self.bashNonInteractive; };
}; };
darwin = super.darwin.overrideScope ( darwin = super.darwin.overrideScope (
@@ -1162,7 +1162,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
extraAttrs = { extraAttrs = {
inherit bootstrapTools; inherit bootstrapTools;
libc = prevStage.darwin.libSystem; libc = prevStage.darwin.libSystem;
shellPackage = prevStage.bash; shellPackage = prevStage.bashNonInteractive;
}; };
disallowedRequisites = [ bootstrapTools.out ]; disallowedRequisites = [ bootstrapTools.out ];
@@ -1172,7 +1172,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
with prevStage; with prevStage;
[ [
apple-sdk apple-sdk
bash bashNonInteractive
bzip2.bin bzip2.bin
bzip2.out bzip2.out
cc.expand-response-params cc.expand-response-params
+4 -4
View File
@@ -74,7 +74,7 @@ let
expand-response-params = ""; expand-response-params = "";
bsdcp = linkBootstrap { paths = [ "bin/bsdcp" ]; }; bsdcp = linkBootstrap { paths = [ "bin/bsdcp" ]; };
patchelf = linkBootstrap { paths = [ "bin/patchelf" ]; }; patchelf = linkBootstrap { paths = [ "bin/patchelf" ]; };
bash = linkBootstrap { bashNonInteractive = linkBootstrap {
paths = [ paths = [
"bin/bash" "bin/bash"
"bin/sh" "bin/sh"
@@ -376,13 +376,13 @@ let
gawk gawk
diffutils diffutils
patch patch
bash bashNonInteractive
xz xz
gzip gzip
bzip2 bzip2
bsdcp bsdcp
]; ];
shell = "${prevStage.bash}/bin/bash"; shell = "${prevStage.bashNonInteractive}/bin/bash";
stdenvNoCC = import ../generic { stdenvNoCC = import ../generic {
inherit inherit
config config
@@ -471,7 +471,7 @@ in
# we CAN'T import LLVM because the compiler built here is used to build the final compiler and the final compiler must not be built by the bootstrap compiler # we CAN'T import LLVM because the compiler built here is used to build the final compiler and the final compiler must not be built by the bootstrap compiler
inherit (bootstrapTools) inherit (bootstrapTools)
patchelf patchelf
bash bashNonInteractive
curl curl
coreutils coreutils
diffutils diffutils
+1 -1
View File
@@ -10,7 +10,7 @@
pkgs.gzip pkgs.gzip
pkgs.bzip2.bin pkgs.bzip2.bin
pkgs.gnumake pkgs.gnumake
pkgs.bash pkgs.bashNonInteractive
pkgs.patch pkgs.patch
pkgs.xz.bin pkgs.xz.bin
+3 -3
View File
@@ -770,7 +770,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
bzip2 bzip2
xz xz
zlib zlib
bash bashNonInteractive
binutils binutils
coreutils coreutils
diffutils diffutils
@@ -806,7 +806,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
gzip gzip
bzip2 bzip2
xz xz
bash bashNonInteractive
binutils.bintools binutils.bintools
coreutils coreutils
diffutils diffutils
@@ -866,7 +866,7 @@ assert bootstrapTools.passthru.isFromBootstrapFiles or false; # sanity check
gzip gzip
bzip2 bzip2
xz xz
bash bashNonInteractive
coreutils coreutils
diffutils diffutils
findutils findutils
+2 -2
View File
@@ -3,7 +3,7 @@
stdenv, stdenv,
fetchFromGitHub, fetchFromGitHub,
cmake, cmake,
bash, bashNonInteractive,
gnugrep, gnugrep,
fixDarwinDylibNames, fixDarwinDylibNames,
file, file,
@@ -37,7 +37,7 @@ stdenv.mkDerivation rec {
}; };
nativeBuildInputs = [ cmake ] ++ lib.optional stdenv.hostPlatform.isDarwin fixDarwinDylibNames; nativeBuildInputs = [ cmake ] ++ lib.optional stdenv.hostPlatform.isDarwin fixDarwinDylibNames;
buildInputs = lib.optional stdenv.hostPlatform.isUnix bash; buildInputs = lib.optional stdenv.hostPlatform.isUnix bashNonInteractive;
patches = [ patches = [
# This patches makes sure we do not attempt to use the MD5 implementation # This patches makes sure we do not attempt to use the MD5 implementation
+8 -7
View File
@@ -5586,17 +5586,18 @@ with pkgs;
### SHELLS ### SHELLS
runtimeShell = "${runtimeShellPackage}${runtimeShellPackage.shellPath}"; runtimeShell = "${runtimeShellPackage}${runtimeShellPackage.shellPath}";
runtimeShellPackage = bash; runtimeShellPackage = bashNonInteractive;
bash = lowPrio (callPackage ../shells/bash/5.nix { }); bash = callPackage ../shells/bash/5.nix { };
bashNonInteractive = lowPrio (callPackage ../shells/bash/5.nix {
interactive = false;
});
# WARNING: this attribute is used by nix-shell so it shouldn't be removed/renamed # WARNING: this attribute is used by nix-shell so it shouldn't be removed/renamed
bashInteractive = callPackage ../shells/bash/5.nix { bashInteractive = bash;
interactive = true; bashFHS = callPackage ../shells/bash/5.nix {
};
bashInteractiveFHS = callPackage ../shells/bash/5.nix {
interactive = true;
forFHSEnv = true; forFHSEnv = true;
}; };
bashInteractiveFHS = bashFHS;
carapace = callPackage ../shells/carapace { carapace = callPackage ../shells/carapace {
buildGoModule = buildGo123Module; buildGoModule = buildGo123Module;