Jump to content

Shell scripts: Difference between revisions

From Official NixOS Wiki
Roxwize (talk | contribs)
Roxwize (talk | contribs)
Marked this version for translation
 
Line 2: Line 2:


<translate>
<translate>
<!--T:1-->
The package <code>writeShellScript</code> can be used to add shell scripts to Nix expressions.
The package <code>writeShellScript</code> can be used to add shell scripts to Nix expressions.
</translate>
</translate>
Line 21: Line 22:


<translate>
<translate>
== External builder.sh script ==
== External builder.sh script == <!--T:2-->


<!--T:3-->
Longer Bash scripts are usually stored as external script files, and called from Nix:
Longer Bash scripts are usually stored as external script files, and called from Nix:
</translate>
</translate>
Line 48: Line 50:


<translate>
<translate>
<!--T:4-->
See also:
See also:


<!--T:5-->
* [<tvar name="1">https://github.com/NixOS/nixpkgs/issues/23099</tvar> Make a derivation with no source]
* [<tvar name="1">https://github.com/NixOS/nixpkgs/issues/23099</tvar> Make a derivation with no source]
* [<tvar name="2">https://nixos.org/guides/nix-pills/working-derivation.html</tvar> Nix Pills: Chapter 7. Working Derivation]
* [<tvar name="2">https://nixos.org/guides/nix-pills/working-derivation.html</tvar> Nix Pills: Chapter 7. Working Derivation]


=== runCommand + builder.sh ===
=== runCommand + builder.sh === <!--T:6-->


<!--T:7-->
Instead of <code>stdenv.mkDerivation</code>, <code>runCommand</code> can also be used to call an external Bash script:
Instead of <code>stdenv.mkDerivation</code>, <code>runCommand</code> can also be used to call an external Bash script:
</translate>
</translate>
Line 71: Line 76:


<translate>
<translate>
== Packaging ==
== Packaging == <!--T:8-->


<!--T:9-->
Example:
Example:
</translate>
</translate>
Line 108: Line 114:


<translate>
<translate>
<!--T:10-->
<code>wrapProgram</code> will move the original script to <code>.github-downloader.sh-wrapped</code>.
<code>wrapProgram</code> will move the original script to <code>.github-downloader.sh-wrapped</code>.


=== Command not found ===
=== Command not found === <!--T:11-->


<!--T:12-->
For example, the script throws the error <code>svn: command not found</code>, because the dependency <code>subversion</code> is missing.
For example, the script throws the error <code>svn: command not found</code>, because the dependency <code>subversion</code> is missing.


<!--T:13-->
When a command is missing, you can use <code>nix-locate</code> to find the package name. for example, the <code>stat</code> command:
When a command is missing, you can use <code>nix-locate</code> to find the package name. for example, the <code>stat</code> command:
</translate>
</translate>
Line 123: Line 132:


<translate>
<translate>
== Debugging embedded scripts ==
== Debugging embedded scripts == <!--T:14-->


<!--T:15-->
When a bash script fails, it prints only an error message, but no code location.
When a bash script fails, it prints only an error message, but no code location.


<!--T:16-->
To trace commands and line numbers, one can use
To trace commands and line numbers, one can use
</translate>
</translate>
Line 155: Line 166:


<translate>
<translate>
== POSIX shell ==
== POSIX shell == <!--T:17-->


<!--T:18-->
Some environments (like OpenWRT, via BusyBox) offer only a "limited" shell (<code>sh</code> instead of <code>bash</code>).
Some environments (like OpenWRT, via BusyBox) offer only a "limited" shell (<code>sh</code> instead of <code>bash</code>).


<!--T:19-->
On NixOS, POSIX shells are provided by the packages <code>dash</code> and <code>posh</code>.
On NixOS, POSIX shells are provided by the packages <code>dash</code> and <code>posh</code>.


== See also ==
== See also == <!--T:20-->


<!--T:21-->
* [[<tvar name="1">Nix-shell shebang</tvar>|Nix-shell shebang]]
* [[<tvar name="1">Nix-shell shebang</tvar>|Nix-shell shebang]]
* [<tvar name="2">https://nixos.org/manual/nixpkgs/stable/#ssec-stdenv-functions</tvar> Shell functions section in the Nixpkgs manual]
* [<tvar name="2">https://nixos.org/manual/nixpkgs/stable/#ssec-stdenv-functions</tvar> Shell functions section in the Nixpkgs manual]

Latest revision as of 01:28, 28 July 2026

The package writeShellScript can be used to add shell scripts to Nix expressions.

someBuildHelper = { name, sha256 }:
  stdenv.mkDerivation {
    inherit name;
    outputHashMode = "recursive";
    outputHashAlgo = "sha256";
    outputHash = sha256;
    builder = writeShellScript "builder.sh" ''
      echo "hi, my name is ''${0}" # escape bash variable
      echo "hi, my hash is ${sha256}" # use nix variable
      echo "hello world" >output.txt
    '';
  };

External builder.sh script

Longer Bash scripts are usually stored as external script files, and called from Nix:

❄︎ default.nix
{
  outputTxtDrv = stdenv.mkDerivation rec {
    name = "output.txt";
    # disable unpackPhase etc
    phases = "buildPhase";
    builder = ./builder.sh;
    nativeBuildInputs = [ coreutils jq ];
    PATH = lib.makeBinPath nativeBuildInputs;
    # only strings can be passed to builder
    someString = "hello";
    someNumber = builtins.toString 42;
    someJson = builtins.toJSON { dst = "world"; };
  };
}
≡︎ builder.sh
 jq -r '.dst') $someNumber" >$out

See also:

runCommand + builder.sh

Instead of stdenv.mkDerivation, runCommand can also be used to call an external Bash script:

❄︎ default.nix
{
  outputTxtDrv = runCommand "output.txt" {
    nativeBuildInputs = [ coreutils jq ];
    # only strings can be passed to builder
    someString = "hello";
    someNumber = builtins.toString 42;
    someJson = builtins.toJSON { dst = "world"; };
  } (builtins.readFile ./builder.sh);
}

Packaging

Example:

# nix-build -E 'with import <nixpkgs> { }; callPackage ./default.nix { }'

{ stdenv
, lib
, fetchFromGitHub
, bash
, subversion
, makeWrapper
}:
  stdenv.mkDerivation {
    pname = "github-downloader";
    version = "08049f6";
    src = fetchFromGitHub {
      # https://github.com/Decad/github-downloader
      owner = "Decad";
      repo = "github-downloader";
      rev = "08049f6183e559a9a97b1d144c070a36118cca97";
      sha256 = "073jkky5svrb7hmbx3ycgzpb37hdap7nd9i0id5b5yxlcnf7930r";
    };
    buildInputs = [ bash subversion ];
    nativeBuildInputs = [ makeWrapper ];
    installPhase = ''
      mkdir -p $out/bin
      cp github-downloader.sh $out/bin/github-downloader.sh
      wrapProgram $out/bin/github-downloader.sh \
        --prefix PATH : ${lib.makeBinPath [ bash subversion ]}
    '';
  }

wrapProgram will move the original script to .github-downloader.sh-wrapped.

Command not found

For example, the script throws the error svn: command not found, because the dependency subversion is missing.

When a command is missing, you can use nix-locate to find the package name. for example, the stat command:

$ nix-locate bin/stat | grep 'bin/stat$'
coreutils.out       0 s /nix/store/vr96j3cxj75xsczl8pzrgsv1k57hcxyp-coreutils-8.31/bin/stat

Debugging embedded scripts

When a bash script fails, it prints only an error message, but no code location.

To trace commands and line numbers, one can use

❄︎ test-trace.nix
{ runCommand, coreutils }:
runCommand "output.txt" { nativeBuildInputs = [ coreutils ]; } ''
  # line 5 in nix file = line 1 in bash script -> offset 4
  PS4='+ Line $(expr $LINENO + 4): '
  set -o xtrace # print commands

  echo hello >$out # line 9 in nix file

  set +o xtrace # hide commands
''
$ nix-build -E 'with import <nixpkgs> { }; callPackage ./test-trace.nix { }'
this derivation will be built:
  /nix/store/2v5biwny8plpyk2bv6cfr41ppp0a1i4k-output.txt.drv
building '/nix/store/2v5biwny8plpyk2bv6cfr41ppp0a1i4k-output.txt.drv'...
++ Line 9: echo hello
++ Line 11: set +o xtrace
/nix/store/ppidmnpd5m762x9kqj8jd3g7df7dknrz-output.txt

POSIX shell

Some environments (like OpenWRT, via BusyBox) offer only a "limited" shell (sh instead of bash).

On NixOS, POSIX shells are provided by the packages dash and posh.

See also