Jump to content

Shell scripts/zh: Difference between revisions

From Official NixOS Wiki
Ardenet (talk | contribs)
Created page with "== 外部 builder.sh 脚本 =="
Ardenet (talk | contribs)
Created page with "较长的 Bash 脚本通常会保存为外部脚本文件,并在 Nix 中调用:"
Line 23: Line 23:
== 外部 builder.sh 脚本 ==
== 外部 builder.sh 脚本 ==


<div lang="en" dir="ltr" class="mw-content-ltr">
较长的 Bash 脚本通常会保存为外部脚本文件,并在 Nix 中调用:
Longer Bash scripts are usually stored as external script files, and called from Nix:
</div>


{{file|default.nix|nix|3=
{{file|default.nix|nix|3=

Revision as of 18:06, 2 August 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
    '';
  };

外部 builder.sh 脚本

较长的 Bash 脚本通常会保存为外部脚本文件,并在 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

另见:

runCommand + builder.sh

除了使用 stdenv.mkDerivation 之外,还可以通过 runCommand 来调用外部的 Bash 脚本:

❄︎ 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);
}

打包

示例:

# 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 会将原始脚本移动到 .github-downloader.sh-wrapped

命令未找到

例如,脚本会抛出错误 svn: command not found,这是因为缺少依赖包 subversion

当某个命令缺失时,可以使用 nix-locate 来查找对应的软件包名称。例如,stat 命令:

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

调试嵌入脚本

当 Bash 脚本执行失败时,它只会输出错误信息,而不会显示出错的代码位置。

要跟踪命令和行号,可以使用

❄︎ 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

某些环境(例如基于 BusyBox 的 OpenWRT)仅提供“受限” Shell(使用 sh 而非 bash

在 NixOS 上,POSIX shells 由软件包 dashposh 提供。

另见