Jump to content

Shell scripts: Difference between revisions

From Official NixOS Wiki
Roxwize (talk | contribs)
No edit summary
Roxwize (talk | contribs)
prepare for translation
Line 1: Line 1:
<languages/>
<translate>
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>


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
Line 16: Line 20:
</syntaxHighlight>
</syntaxHighlight>


<translate>
== External builder.sh script ==
== External builder.sh script ==


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>


{{file|default.nix|nix|3=
{{file|default.nix|nix|3=
Line 41: Line 47:
}}
}}


See also
<translate>
See also:


* [https://github.com/NixOS/nixpkgs/issues/23099 make a derivation with no source]
* [<tvar name="1">https://github.com/NixOS/nixpkgs/issues/23099</tvar> Make a derivation with no source]
* [https://nixos.org/guides/nix-pills/working-derivation.html 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 ===


Instead of <code>stdenv.mkDerivation</code>, we can also use <code>runCommand</code> 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>


{{file|default.nix|nix|3=
{{file|default.nix|nix|3=
Line 62: Line 70:
}}
}}


<translate>
== Packaging ==
== Packaging ==


Example:
Example:
</translate>


<syntaxHighlight lang="nix">
<syntaxHighlight lang="nix">
Line 97: Line 107:
</syntaxHighlight>
</syntaxHighlight>


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


=== Command not found ===
=== Command not found ===
Line 104: Line 115:


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>


<syntaxHighlight lang="console">
<syntaxHighlight lang="console">
Line 110: Line 122:
</syntaxHighlight>
</syntaxHighlight>


<translate>
== Debugging embedded scripts ==
== Debugging embedded scripts ==


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.


To trace commands and line numbers, we can use
To trace commands and line numbers, one can use
</translate>


{{file|test-trace.nix|nix|3=
{{file|test-trace.nix|nix|3=
Line 140: Line 154:
</syntaxHighlight>
</syntaxHighlight>


<translate>
== POSIX shell ==
== POSIX shell ==


Line 148: Line 163:
== See also ==
== See also ==


* [[Nix-shell shebang]]
* [[<tvar name="1">Nix-shell shebang</tvar>|Nix-shell shebang]]
* [https://nixos.org/manual/nixpkgs/stable/#ssec-stdenv-functions 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]
* [https://gist.github.com/travisbhartwell/f972aab227306edfcfea nix-shell and Shebang Lines]
* [<tvar name="3">https://gist.github.com/travisbhartwell/f972aab227306edfcfea</tvar> nix-shell and Shebang Lines]
* [https://ertt.ca/nix/shell-scripts/ Shell Scripts with Nix]
* [<tvar>https://ertt.ca/nix/shell-scripts/</tvar> Shell Scripts with Nix]
</translate>


[[Category:Development]]
[[Category:Development]]
[[Category:Shell]]
[[Category:Shell]]

Revision as of 01:26, 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