Interactive Bash with `nix develop` Flake

Consider the following bare Nix shell environment. shell.nix:

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { }

And this flake.nix:

{
  outputs = inputs@{ self, nixpkgs, ... }:
    let
      pkgs = import nixpkgs { system = "x86_64-linux"; };
    in
  {
    devShell."x86_64-linux" = import ./shell.nix { inherit pkgs; };
  };
}

The nix-shell will yield an interactive Bash:

$ nix-shell

[nix-shell:/tmp/x]$ echo $SHELL
/nix/store/lz9s7v9xxkr3jf4wlah9vw1knmic2nda-bash-interactive-5.1-p8/bin/bash

while nix develop does not:

$ nix develop
$ echo $SHELL
/nix/store/wadmyilr414n7bimxysbny876i2vlm5r-bash-5.1-p8/bin/bash

This leads to issues when launching applications like Vim and Visual Studio Code, which launch integrated terminals that will be ‘dumb’. This means the arrow keys and DEL will not behave like expected… This issue refers to when nix-shell also had this problem, it seems: Vim in nix-shell starts non-interactive bash on :shell and :terminal · Issue #2034 · NixOS/nix · GitHub


Is there a way to make nix develop behave like nix-shell?

(And as a side question: Is it possible for nix develop to show a prompt similar to nix-shell to be able to distinguish we’re in a sub shell?)


[Update: does not work.] A solution that I came up with is to add the following shellHook to mkShell for the Flake:

shellHook = ''
  SHELL=${pkgs.bashInteractive}/bin/bash
'';

But, I am not very experienced with Nix to know if this is the right direction. Or perhaps I am solving the wrong problem here.

My solution didn’t work as I kept debugging this with Visual Studio Code. The following does work, but I do not understand how and whether it’s a solid solution. I am not sure why nix-shell doesn’t require this, but nix develop then does:

buildInputs = [ pkgs.bashInteractive ];

To more reliably check the Bash binary used, we can use /proc/[pid]/exe. Combined with the previous solution, I end up with an interactive Bash:

$ nix develop
$ readlink /proc/$$/exe
/nix/store/yx24h6ywbkg7rm5x7lrm8mx8ypcf4ywi-bash-interactive-5.1-p8/bin/bash

nix-shell still works too:

$ nix-shell

[nix-shell:/tmp/x]$ readlink /proc/$$/exe
/nix/store/lz9s7v9xxkr3jf4wlah9vw1knmic2nda-bash-interactive-5.1-p8/bin/bash
2 Likes

I think there is a related issue in the Nix repo for this now:

https://github.com/NixOS/nix/issues/6091

4 Likes

Thanks for linking to that issue. Not 100% sure it’s the same issue, but seems like it. The author of the issue doesn’t state what he means by ‘broken’.

My issue is about an non-interactive/dumb shell that inserts literal tabs. There is a similar issue where auto-completion doesn’t work for any software not specified in mkShell. So in that case git would not auto-complete anyway, because its share directory is not listed in $XDG_DATA_DIRS.

I am not sure what the underlying problem is, if there is any real problem here. Is nix develop pure (or nix-shell)? You can access tools from the ‘parent’ environment, like git, which begs the question why git is in $PATH, but its share directory not in $XDG_DATA_DIRS.

Also related: bash autocompletion in nix shell (nix command) ¡ Issue #4453 ¡ NixOS/nix ¡ GitHub

@b-zee @cdepillabout The above line used to work for me (although I put it under nativeBuildInputs, but I don’t think it makes any difference for final.myHaskellPkgs.shellFor).

But it no longer works. I’m now on a later nixpkgs commit from June 2024.

Did you find a more recent working solution to the problem? The problem, to recap, is a dysfunctional bash shell within a “nix develop” shell (strange chars showing up, no tab completion, etc.).

1 Like

I have no alternative. I am on a commit from 2 weeks ago, and it seems to be working still.

What does $SHELL say in your shell?

1 Like

Also experiencing this issue on nixos-unstable. Weirdly, this used to just work, but this morning I launched a nix develop and my starship prompt blew up. Simply adding the bashInteractive package to the build deps doesn’t solve it. It seems to work ok if I just run nix develop and work directly in there, but I was running zellij and having it launch a sub-shell causes the problem.

1 Like

Something like

SHELL=$(nix build nixpkgs#bash^out --no-link --print-out-paths)/bin/bash nix shell

should work instead of nix shell.

Alternatively, you can modify the shell:

  pkgs.mkShell {
    packages = [ ... ];
    shellHook = ''
      export SHELL=${lib.getExe pkgs.bash}
    '';
  }

(Make sure to use bashInteractive instead of bash if you’re on 24.11 or earlier.)

This shellHook solution was mentioned in the OP. I’ve now checked (3,5 years) later and it seems like some behavior has changed. Setting shellHook will indeed make the nix develop shell use interactive Bash.

However, the problem with VS Code et al still exists as long as the bashInteractive package is not included in the shell’s PATH.

The problem is that shells spawned from nix develop will use a noninteractive shell.

$ nix develop
$ type -a bash
bash is /nix/store/4fvc5fm8bszmkydng1ivrvr5cbvr1g60-bash-5.2p37/bin/bash
bash is /run/current-system/sw/bin/bash
$ bash
# 'Dumb' shell, e.g. pressing arrow keys yields the following:
$ ^[[D^[[A^[[C^[[B

While if the buildInputs includes bashInteractive, it works (notice the interactive Bash in the type output):

$ nix develop
$ type -a bash
bash is /nix/store/c3ym86hnrsh03k11mhbvvcw7f0y403vr-bash-interactive-5.2p37/bin/bash
bash is /nix/store/4fvc5fm8bszmkydng1ivrvr5cbvr1g60-bash-5.2p37/bin/bash
bash is /run/current-system/sw/bin/bash
$ bash
# Arrow keys (history) and autocompletion (using tabs) works!

(edit)

I have just learned the common way to spawn a subshell in a generic way is to call $SHELL. That makes sense as it will use the same as the parent shell. Perhaps VS Code is at fault here, as it apparently does not call $SHELL but somehow detects and regularly calls bash.