Jump to content

Nix-shell shebang: Difference between revisions

From NixOS Wiki
imported>JakeHillion
Python: Had no luck running this and [this](https://discourse.nixos.org/t/nix-shell-shebang-default-nix-no-such-file-or-directory/24246) suggested you need at least one package. Added `-p python3` and it worked.
YoshiRulz (talk | contribs)
C#: New section
Tag: 2017 source edit
(8 intermediate revisions by 7 users not shown)
Line 7: Line 7:
The first shebang line is always <code>#! /usr/bin/env nix-shell</code>.<br>
The first shebang line is always <code>#! /usr/bin/env nix-shell</code>.<br>
The second shebang line declares the script language and the script dependencies.
The second shebang line declares the script language and the script dependencies.
As of Nix 2.19.0 you can also use the new CLI <code>nix shell</code> and flakes to define shebangs. See [https://nixos.org/manual/nix/stable/command-ref/new-cli/nix.html?highlight=shebang#shebang-interpreter docs].


== Examples ==
== Examples ==
Line 30: Line 32:
convert "$1" -scale 50% "$1.s50.jpg" &&
convert "$1" -scale 50% "$1.s50.jpg" &&
cowsay "done $1.q50.jpg"
cowsay "done $1.q50.jpg"
</syntaxHighlight>
=== C# ===
Using <code>dotnet run app.cs</code>, new in .NET 10:
<syntaxHighlight lang="csharp">
#!/usr/bin/env nix-shell
/*
#! nix-shell -i "dotnet run" -p dotnetCorePackages.dotnet_10.sdk
*/
#:package Humanizer@2.14.1
using Humanizer;
var dotNet9Released = DateTimeOffset.Parse("2024-12-03");
var since = DateTimeOffset.Now - dotNet9Released;
Console.WriteLine($"It has been {since.Humanize()} since .NET 9 was released.");
</syntaxHighlight>
</syntaxHighlight>


Line 98: Line 119:
</syntaxHighlight>
</syntaxHighlight>


=== Haskell ===
<syntaxHighlight lang="haskell">
#! /usr/bin/env nix-shell
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [turtle])" -i runghc
{-# LANGUAGE OverloadedStrings #-}
import Turtle
main = echo "Hello world!"
</syntaxHighlight>


== Pinning nixpkgs ==
== Pinning nixpkgs ==
Line 106: Line 138:
#! /usr/bin/env nix-shell
#! /usr/bin/env nix-shell
#! nix-shell -i bash
#! nix-shell -i bash
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-18.03.tar.gz
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/aed4b19d312525ae7ca9bceb4e1efe3357d0e2eb.tar.gz


echo hello world
echo hello world
</syntaxHighlight>
</syntaxHighlight>
== Flake ==
It is also possible to make it work for flake like in:
<syntaxHighlight lang="bash">
#!/usr/bin/env -S nix shell nixpkgs#bash nixpkgs#hello nixpkgs#cowsay --command bash
hello | cowsay
</syntaxHighlight>
The [https://nix.dev/manual/nix/2.19/command-ref/new-cli/nix3-shell doc] mentions that it should be possible to run more complex commands using multiple lines, but it does not work for me as reported [https://github.com/NixOS/nixpkgs/issues/280033 here].


== Performance ==
== Performance ==
Line 117: Line 161:
* [https://discourse.nixos.org/t/speeding-up-nix-shell-shebang/4048 Speeding up nix-shell shebang]
* [https://discourse.nixos.org/t/speeding-up-nix-shell-shebang/4048 Speeding up nix-shell shebang]
* [https://github.com/xzfc/cached-nix-shell cached-nix-shell] - Instant startup time for nix-shell
* [https://github.com/xzfc/cached-nix-shell cached-nix-shell] - Instant startup time for nix-shell
* [https://www.tweag.io/blog/2020-06-25-eval-cache/ Nix Flakes, Part 2: Evaluation caching - Tweag]


== See also ==
== See also ==
Line 124: Line 169:
* [https://gist.github.com/travisbhartwell/f972aab227306edfcfea nix-shell and Shebang Lines]
* [https://gist.github.com/travisbhartwell/f972aab227306edfcfea nix-shell and Shebang Lines]
* [https://notes.yukiisbo.red/posts/2021/07/Spice_up_with_Nix_Scripts.html Spice up with Nix: Scripts with magical dependencies]
* [https://notes.yukiisbo.red/posts/2021/07/Spice_up_with_Nix_Scripts.html Spice up with Nix: Scripts with magical dependencies]
[[Category:Nix]]
[[Category:Shell]]

Revision as of 07:31, 4 July 2025

You can use nix-shell as a script interpreter to

  • run scripts in arbitrary languages
  • provide dependencies with Nix

To do this, start the script with multiple shebang (#!) lines.
The first shebang line is always #! /usr/bin/env nix-shell.
The second shebang line declares the script language and the script dependencies.

As of Nix 2.19.0 you can also use the new CLI nix shell and flakes to define shebangs. See docs.

Examples

Bash

To run bash scripts, set the interpreter with -i bash

#! /usr/bin/env nix-shell
#! nix-shell -i bash -p bash

echo hello world

You can use nix-shell -p ... to add dependencies:

#! /usr/bin/env nix-shell
#! nix-shell -i bash -p imagemagick cowsay

# scale image by 50%
convert "$1" -scale 50% "$1.s50.jpg" &&
cowsay "done $1.q50.jpg"

C#

Using dotnet run app.cs, new in .NET 10:

#!/usr/bin/env nix-shell
/*
#! nix-shell -i "dotnet run" -p dotnetCorePackages.dotnet_10.sdk
*/
#:package Humanizer@2.14.1

using Humanizer;

var dotNet9Released = DateTimeOffset.Parse("2024-12-03");
var since = DateTimeOffset.Now - dotNet9Released;

Console.WriteLine($"It has been {since.Humanize()} since .NET 9 was released.");

Python

#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p python3

print("hello world")
#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p python3Packages.pillow python3Packages.ansicolor

# scale image by 50%
import sys, PIL.Image, ansicolor
path = sys.argv[1]
image = PIL.Image.open(path)
factor = 0.5
image = image.resize((round(image.width * factor), round(image.height * factor)))
path = path + ".s50.jpg"
image.save(path)
print(ansicolor.green(f"done {path}"))

Rust

No dependencies

#!/usr/bin/env nix-shell
#![allow()] /*
#!nix-shell -i bash -p rustc
rsfile="$(readlink -f $0)"
binfile="/tmp/$(basename "$rsfile").bin"
rustc "$rsfile" -o "$binfile" --edition=2021 && exec "$binfile" $@ || exit $?
*/
fn main() {
    for argument in std::env::args().skip(1) {
        println!("{}", argument);
    };
    println!("{}", std::env::var("HOME").expect(""));
}

With dependencies

uses rust-script

#!/usr/bin/env nix-shell
//! ```cargo
//! [dependencies]
//! time = "0.1.25"
//! ```
/*
#!nix-shell -i rust-script -p rustc -p rust-script -p cargo
*/
fn main() {
    for argument in std::env::args().skip(1) {
        println!("{}", argument);
    };
    println!("{}", std::env::var("HOME").expect(""));
    println!("{}", time::now().rfc822z());
}

Haskell

#! /usr/bin/env nix-shell
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [turtle])" -i runghc

{-# LANGUAGE OverloadedStrings #-}

import Turtle

main = echo "Hello world!"

Pinning nixpkgs

To pin nixpkgs to a specific version, add a third shebang line:

#! /usr/bin/env nix-shell
#! nix-shell -i bash
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/aed4b19d312525ae7ca9bceb4e1efe3357d0e2eb.tar.gz

echo hello world

Flake

It is also possible to make it work for flake like in:

#!/usr/bin/env -S nix shell nixpkgs#bash nixpkgs#hello nixpkgs#cowsay --command bash

hello | cowsay

The doc mentions that it should be possible to run more complex commands using multiple lines, but it does not work for me as reported here.


Performance

TODO ... why the startup delay? how to make it faster?

See also