Link scripts to bin home-manager

I’m trying to integrate my custom scripts into the system’s PATH on NixOS with WSL, using Home Manager (unstable). My file structure includes:

  • configuration.nix
  • flake.lock
  • flake.nix
  • home.nix
  • README.md
  • scripts/
    • myscript.sh

I want to symbolically link my scripts or add the path to the scripts directory to $PATH. I’ve searched extensively but haven’t found a clear solution. This NixOS Discourse post discusses a similar issue but uses Git to fetch scripts. It references a Reddit comment that also didn’t solve my problem.

My configurations are available in this GitHub repo.

You can use the nixpkgs function writeShellScriptBin. Here’s an example:

home.packages = with pkgs; [
  (writeShellScriptBin "hi" ''
    echo "hi!"
  '')
];

This script will automatically be put in your path with the name hi

Edit:

If you want to read it from a separate .sh file, I think this should work.

home.packages = with pkgs; [
  (writeShellScriptBin "hi" (builtins.readFile ./relpath/to/script.sh)
];
3 Likes

I see a few ways of doing this:

1 - You can add you repository’s scripts dir to path and install dependencies manually:

{ pkgs, }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [ lazydocker ];
}

But this has the downside of not being very reproducible, since you will need to have your repository cloned at the declared location for this to work.

2 - You can install each script as a package:

{ pkgs, ... }: {
  home.packages = with pkgs; [
    (writeShellScriptBin {
      name = "lzd";
      runtimeInputs = [ lazydocker ];
      text = import ./lzd;
    })
  ];
}

The downside here is that you will need to run home-manager switch to tinker with your scripts.

3 - You can also do a mix of the two, trying the cloned repo first, and falling back to the installed scripts.

{ pkgs, ... }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [
    lazydocker
    (writeShellScriptBin {
      name = "lzd";
      text = import ./lzd;
    })
  ];
}
3 Likes

What about:

  home-manager.users.username = {
    home.stateVersion = "23.11";

    home.file = {
      ".local/bin" = {
        source = ./scripts;
        recursive = true;
      };
    };

Which should make a ~/.local/bin directory, and copy over the contents from sources/scripts/.

For more info, see: NixOS for the Impatient.

1 Like

it created copy of scipts in .local/bin but I can’t call it from terminal for some reason, it’s look like .local/bin not have in $PATH :grinning:. I first thought it don’t have execution access but it has

can I pass some arguments while using it through file? like ${home.username}

Can you add ~/.local/bin to PATH? Or, can you change .local/bin to a path available in PATH?

If you write the script inside a nix file you can you ${home.username} inside the script.

I used something like

{ pkgs, ... }: {
  home.sessionPath = [ "/home/sul/your-repository-path-here/scripts" ];
  home.packages = with pkgs; [
    lazydocker
    (writeShellScriptBin {
      name = "lzd";
      text = import ./lzd;
    })
  ];
}

in my flake but this leads to this error.

A definition for option home-manager.users.<me>.home.packages."[definition 1-entry 1]"' is not of type package’. Definition values:

apparently writeShellScriptBin does not create a package?