Shell scripts: Difference between revisions
imported>Milahu add link: nix-shell and Shebang Lines |
Marked this version for translation |
||
| (12 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
<languages/> | |||
<translate> | |||
<!--T:1--> | |||
The package <code>writeShellScript</code> can be used to add shell scripts to Nix expressions. | |||
</translate> | |||
<syntaxHighlight lang="nix"> | <syntaxHighlight lang="nix"> | ||
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 | |||
''; | |||
}; | |||
</syntaxHighlight> | </syntaxHighlight> | ||
== External builder.sh script == | <translate> | ||
== External builder.sh script == <!--T:2--> | |||
Longer | <!--T:3--> | ||
Longer Bash scripts are usually stored as external script files, and called from Nix: | |||
</translate> | |||
{{file|default.nix|nix|3= | |||
{ | { | ||
outputTxtDrv = stdenv.mkDerivation rec { | outputTxtDrv = stdenv.mkDerivation rec { | ||
| Line 36: | Line 43: | ||
}; | }; | ||
} | } | ||
}} | |||
{{file|builder.sh|sh|3= | |||
echo "$someString $(echo "$someJson" | jq -r '.dst') $someNumber" >$out | echo "$someString $(echo "$someJson" | jq -r '.dst') $someNumber" >$out | ||
}} | |||
See also | <translate> | ||
<!--T:4--> | |||
See also: | |||
* [https://github.com/NixOS/nixpkgs/issues/23099 | <!--T:5--> | ||
* [https://nixos.org/guides/nix-pills/working-derivation.html Nix Pills: Chapter 7. Working Derivation] | * [<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] | |||
=== runCommand + builder.sh === | === runCommand + builder.sh === <!--T:6--> | ||
Instead of <code>stdenv.mkDerivation</code>, | <!--T:7--> | ||
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= | |||
{ | { | ||
outputTxtDrv = runCommand "output.txt" { | outputTxtDrv = runCommand "output.txt" { | ||
| Line 63: | Line 73: | ||
} (builtins.readFile ./builder.sh); | } (builtins.readFile ./builder.sh); | ||
} | } | ||
}} | |||
== Packaging == | <translate> | ||
== Packaging == <!--T:8--> | |||
<!--T:9--> | |||
Example: | |||
</translate> | |||
<syntaxHighlight lang="nix"> | <syntaxHighlight lang="nix"> | ||
| Line 100: | Line 113: | ||
</syntaxHighlight> | </syntaxHighlight> | ||
<code>wrapProgram</code> will move the original script to <code>.github-downloader.sh-wrapped</code> | <translate> | ||
<!--T:10--> | |||
<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. | |||
<!--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: | |||
</translate> | |||
<syntaxHighlight lang="console"> | <syntaxHighlight lang="console"> | ||
$ nix-locate bin/stat | grep 'bin/stat$' | $ nix-locate bin/stat | grep 'bin/stat$' | ||
coreutils.out | coreutils.out 0 s /nix/store/vr96j3cxj75xsczl8pzrgsv1k57hcxyp-coreutils-8.31/bin/stat | ||
</syntaxHighlight> | </syntaxHighlight> | ||
== Debugging embedded scripts == | <translate> | ||
== 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. | ||
To trace commands and line numbers, | <!--T:16--> | ||
To trace commands and line numbers, one can use | |||
</translate> | |||
{{file|test-trace.nix|nix|3= | |||
{ runCommand, coreutils }: | { 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 | |||
'' | |||
}} | |||
<syntaxHighlight lang="console"> | <syntaxHighlight lang="console"> | ||
| Line 145: | Line 165: | ||
</syntaxHighlight> | </syntaxHighlight> | ||
== | <translate> | ||
== 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>). | |||
<!--T:19--> | |||
On NixOS, POSIX shells are provided by the packages <code>dash</code> and <code>posh</code>. | |||
== See also == <!--T:20--> | |||
== | <!--T:21--> | ||
* [[<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="3">https://gist.github.com/travisbhartwell/f972aab227306edfcfea</tvar> nix-shell and Shebang Lines] | |||
* [<tvar name="4">https://ertt.ca/nix/shell-scripts/</tvar> Shell Scripts with Nix] | |||
</translate> | |||
[[Category:Development{{#translation:}}|Shell scripts]] | |||
[[Category:Shell{{#translation:}}|Shell scripts]] | |||
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:
{
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"; };
};
}
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:
{
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
{ 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.