Nix 备忘清单

Nix 快速参考备忘单,旨在帮助编写基础的 Nix 代码。Nix 是一个纯函数式包管理器和 NixOS 系统的基础语言。

类型与语法

字符串

let
  x = "单行字符串";
  y = ''
    多行字符串
    支持换行和缩进
  '';
  z = ''
    前导空格会被自动处理
      这行会保持缩进
  '';
in

字符串插值

let
  name = "世界";
  greeting = "你好,${name}!";
in

转义字符

let
  escaped = ''
    引号:\"hello\",换行:\n,制表符:\t
  '';
in

整数

let
  x = -123;
  y = 123;
  # 支持不同进制
  hex = 0xFF;          # 十六进制: 255
  bin = 0b1010;        # 二进制: 10
  oct = 0o755;         # 八进制: 493
in

浮点数

let
  x = -0.32;
  y = 0.45;
  scientific = 1.23e-4;    # 科学记数法
  inf = 1.0 / 0.0;         # 无穷大
in

布尔值

let
  x = true;
  y = false;
  # 布尔运算
  and_result = true && false;   # false
  or_result = true || false;    # true
  not_result = !true;           # false
in

空值

let
  x = null;
  # null 表示缺失值
  # 类似于其他语言的 None 或 undefined
in

路径

let
  absolute = /usr/bin/env;      # 绝对路径
  relative = ./config.nix;      # 相对路径
  home = ~/Documents;           # 家目录路径
  # 路径会在构建时自动复制到 Nix store
in

属性集

let
  x = {
    name = "张三";
    age = 25;
    address = {
      city = "北京";
      district = "朝阳区";
    };
  };
  y = { c = 3; };
  # 递归属性集
  rec_set = rec {
    x = 1;
    y = x + 1;    # 可以引用同级属性
  };
in

参见 属性集操作

列表

let
  numbers = [ 1 2 3 4 5 ];
  mixed = [
    1
    "这是字符串"
    23.0
    null
    { name = "属性集"; }
  ];
  # 列表可以包含任意类型的元素
  nested = [ [ 1 2 ] [ 3 4 ] ];
in

注释

↓ 单行注释

# 这是单行注释
let x = 1; in x  # 行末注释

↓ 多行注释

/*
  这是多行注释
  可以跨越多行
  /* 可以嵌套 */
*/

作用域管理

定义局部变量

let
  x = 1;
  y = 2;
  z = x + y;    # 可以引用前面定义的变量
in
  x + y + z     # 返回 6

嵌套 let 表达式

let
  outer = 10;
in
  let
    inner = 5;
  in
    outer + inner   # 返回 15

将变量添加到作用域

let
  x = 1;
  y = 2;
in
  { inherit x y; }    # 继承多个变量

↓ 等价于

let
  x = 1;
  y = 2;
in
  { x = x; y = y; }

从属性集继承属性

let
  config = { 
    host = "localhost"; 
    port = 8080;
    ssl = true;
  };
in
  { 
    inherit (config) host port;
    # 只继承特定属性
  }

↓ 等价于

let
  config = { 
    host = "localhost"; 
    port = 8080;
    ssl = true;
  };
in
  { 
    host = config.host; 
    port = config.port; 
  }

将所有属性引入作用域

let
  config = { 
    host = "localhost"; 
    port = 8080;
    database = "myapp";
  };
in
  with config;
  "${host}:${toString port}/${database}"    # 返回 "localhost:8080/myapp"

注意事项

let
  x = 1;
  attrs = { x = 2; y = 3; };
in
  with attrs;
  x + y    # x = 1 (局部变量优先级更高), y = 3

条件表达式

基本条件表达式

let
  x = 10;
  result = if x > 0
    then "正数"
    else if x < 0
    then "负数"
    else "零";
in
  result

布尔条件判断

let
  isEnabled = true;
  config = if isEnabled
    then { port = 8080; debug = true; }
    else { port = 80; debug = false; };
in
  config

字符串条件判断

let
  env = "production";
  database = if env == "production"
    then "prod-db.example.com"
    else if env == "staging"
    then "staging-db.example.com"
    else "localhost";
in
  database

复杂条件判断

let
  user = { name = "张三"; age = 25; isAdmin = true; };
  message = if user.isAdmin && user.age >= 18
    then "管理员用户: ${user.name}"
    else "普通用户: ${user.name}";
in
  message

条件表达式中的属性集

let
  env = "production";
  config = if env == "development"
    then {
      debug = true;
      logLevel = "debug";
      database.host = "localhost";
    }
    else {
      debug = false;
      logLevel = "info";
      database.host = "prod-db.example.com";
    };
in
  config

属性集

定义属性集

let
  person = {
    name = "李四";
    age = 30;
    skills = [ "编程" "设计" "管理" ];
    contact = {
      email = "lisi@example.com";
      phone = "138-0000-0000";
    };
  };
in
  person

动态属性名

let
  key = "dynamicKey";
  attrs = {
    ${key} = "动态值";
    "static-key" = "静态值";
  };
in
  attrs    # { dynamicKey = "动态值"; static-key = "静态值"; }

更新属性集

let
  base = { x = 1; y = 2; };
  updated = base // { y = 3; z = 4; };   # 合并属性集
in
  updated    # { x = 1; y = 3; z = 4; }

深度合并

let
  config1 = {
    server = { host = "localhost"; port = 8080; };
    database = { name = "test"; };
  };
  config2 = {
    server = { port = 9000; ssl = true; };
    cache = { enabled = true; };
  };
  # 注意:// 操作符只进行浅合并
  merged = config1 // config2;
  # server 属性会被完全替换,而不是深度合并
in
  merged

检查属性是否存在

let
  person = { name = "王五"; age = 28; };
in
  {
    hasName = person ? name;      # true
    hasEmail = person ? email;    # false
    hasNested = person ? contact.email;  # false (嵌套检查)
  }

访问属性

let
  config = {
    server = {
      host = "localhost";
      port = 8080;
    };
  };
in
  {
    host = config.server.host;     # 访问嵌套属性
    port = config.server.port;
  }

可选属性访问

let
  config = { server = { host = "localhost"; }; };
in
  {
    port = config.server.port or 3000;        # 如果不存在则使用默认值
    timeout = config.timeout or 30;           # 30
    ssl = config.server.ssl or false;         # false
  }

连接与插值

连接列表

let
  list1 = [ 1 2 3 ];
  list2 = [ 4 5 6 ];
  combined = list1 ++ list2;     # [ 1 2 3 4 5 6 ]
  
  # 多个列表连接
  all = [ 0 ] ++ list1 ++ list2 ++ [ 7 8 ];
in
  all    # [ 0 1 2 3 4 5 6 7 8 ]

连接路径与字符串

let
  base = /usr/bin;
  executable = base + /python3;           # /usr/bin/python3
  
  # 路径与字符串连接
  config_path = /etc + "/nginx/nginx.conf";   # /etc/nginx/nginx.conf
  
  # 字符串连接
  greeting = "你好" + ",世界!";            # "你好,世界!"
in
  { inherit executable config_path greeting; }

字符串插值

let
  name = "张三";
  age = 25;
  score = 95.5;
in
  {
    greeting = "你好,${name}!";
    info = "姓名:${name},年龄:${toString age}";
    result = "考试成绩:${toString score}分";
    
    # 复杂表达式插值
    status = "用户 ${name} ${if age >= 18 then "已成年" else "未成年"}";
    
    # 路径插值
    config_file = "${./configs}/${name}.conf";
  }

函数

简单函数

let
  # 单参数函数
  increment = x: x + 1;
  double = x: x * 2;
  
  # 使用函数
  result1 = increment 5;    # 6
  result2 = double 3;       # 6
in
  { inherit result1 result2; }

命名参数

let
  # 基本命名参数
  createUser = {name, age, email}: {
    inherit name age email;
    id = builtins.hashString "md5" email;
  };
  
  user = createUser {
    name = "张三";
    age = 25;
    email = "zhangsan@example.com";
  };
in
  user

忽略额外参数

let
  # 使用 ... 忽略额外的参数
  processConfig = {host, port, ...}: {
    url = "${host}:${toString port}";
  };
  
  result = processConfig {
    host = "localhost";
    port = 8080;
    ssl = true;        # 会被忽略
    timeout = 30;      # 会被忽略
  };
in
  result    # { url = "localhost:8080"; }

默认参数值

let
  # 为参数提供默认值
  createServer = {
    host ? "localhost", 
    port ? 8080, 
    ssl ? false
  }: {
    inherit host port ssl;
    url = 
      let proto = if ssl then "https" else "http";
      in "${proto}://${host}:${toString port}";
  };
  
  # 使用所有默认值
  server1 = createServer {};
  # 只覆盖 port  
  server2 = createServer {port = 3000;};
  # 覆盖多个参数
  server3 = createServer {                      
    host = "example.com";
    port = 443;
    ssl = true;
  };
in
  { inherit server1 server2 server3; }

绑定到变量

let
  # 将整个参数集绑定到变量,同时解构部分参数
  logMessage = {
    level, 
    message, 
    timestamp ? null
  }@args: {
    formatted = "[${level}] ${message}";
    raw = args;  # 保留原始参数集
    hasTimestamp = 
      args ? timestamp && timestamp != null;
  };
  
  log = logMessage {
    level = "INFO";
    message = "服务器启动成功";
    user = "admin";  # 额外参数也会保存在 args 中
  };
in
  log

多参数函数

let
  # 柯里化函数
  add = x: y: x + y;
  multiply = x: y: z: x * y * z;
  
  # 部分应用
  add10 = add 10;  # 等价于 y: 10 + y
  
  # 使用示例
  result1 = add 3 4;        # 7
  result2 = add10 5;        # 15
  result3 = multiply 2 3 4; # 24
in
  { inherit result1 result2 result3; }

高阶函数

let
  # 接受函数作为参数的高阶函数
  applyTwice = f: x: f (f x);
  
  # 返回函数的高阶函数
  createMultiplier = factor: x: x * factor;
  
  # 函数组合
  compose = f: g: x: f (g x);
  
  # 使用示例
  double = x: x * 2;
  increment = x: x + 1;
  
  result1 = applyTwice double 3;    # 12
  triple = createMultiplier 3;
  result2 = triple 5;               # 15
  doubleAndIncrement = 
    compose increment double;
  result3 = doubleAndIncrement 4;   # 9
in
  { inherit result1 result2 result3; }

递归函数

let
  # 计算阶乘
  factorial = n:
    if n <= 1
    then 1
    else n * factorial (n - 1);
  
  # 斐波那契数列
  fibonacci = n:
    if n <= 1
    then n
    else fibonacci (n - 1) + fibonacci (n - 2);
  
  # 列表长度计算
  listLength = list:
    if list == []
    then 0
    else 1 + listLength (builtins.tail list);
  
  # 使用示例
  fact5 = factorial 5;        # 120
  fib7 = fibonacci 7;         # 13
  len = listLength [1 2 3 4]; # 4
in
  { inherit fact5 fib7 len; }

条件函数

let
```nix
let
  # 根据条件选择不同的处理函数
  processData = condition: data:
    let
      uppercaseProcessor = str: 
        builtins.replaceStrings 
          ["a" "e" "i" "o" "u"] 
          ["A" "E" "I" "O" "U"] str;
      
      lengthProcessor = str: 
        toString (builtins.stringLength str);
      
      reverseProcessor = str: 
        let
          len = builtins.stringLength str;
          chars = builtins.genList 
            (i: builtins.substring 
              (len - 1 - i) 1 str) len;
        in
          builtins.concatStringsSep "" chars;
    in
      if condition == "uppercase"
      then uppercaseProcessor data
      else if condition == "length"
      then lengthProcessor data
      else if condition == "reverse"
      then reverseProcessor data
      else data;
  
  # 使用示例
  text = "hello world";
  result1 = processData "uppercase" text;
  result2 = processData "length" text;
  result3 = processData "reverse" text;
in
  { inherit result1 result2 result3; }

函数工厂

let
  # 创建验证器函数的工厂
  createValidator = rules: value:
    let
      checkRule = rule:
        if rule.type == "minLength"
        then 
          builtins.stringLength value >= rule.value
        else if rule.type == "maxLength"
        then 
          builtins.stringLength value <= rule.value
        else if rule.type == "contains"
        then 
          builtins.match ".*${rule.value}.*" value 
          != null
        else true;
      
      results = map checkRule rules;
      allValid = builtins.all (x: x) results;
    in
      { 
        valid = allValid; 
        value = value;
        errors = builtins.filter 
          (rule: !checkRule rule) rules;
      };
  
  # 创建特定的验证器
  emailValidator = createValidator [
    { type = "minLength"; value = 5; }
    { type = "contains"; value = "@"; }
    { type = "contains"; value = "."; }
  ];
  
  passwordValidator = createValidator [
    { type = "minLength"; value = 8; }
    { type = "maxLength"; value = 32; }
  ];
  
  # 使用示例
  email_result = emailValidator "user@example.com";
  password_result = passwordValidator "mypassword123";
in
  { inherit email_result password_result; }

函数式编程工具

let
  # 映射函数
  mapList = f: list:
    if list == []
    then []
    else 
      let head = f (builtins.head list);
          tail = mapList f (builtins.tail list);
      in [ head ] ++ tail;
  
  # 过滤函数
  filterList = predicate: list:
    if list == []
    then []
    else
      let
        head = builtins.head list;
        tail = builtins.tail list;
        filtered_tail = 
          filterList predicate tail;
      in
        if predicate head
        then [ head ] ++ filtered_tail
        else filtered_tail;
  
  # 归约函数
  reduceList = f: initial: list:
    if list == []
    then initial
    else 
      let head = builtins.head list;
          tail = builtins.tail list;
      in reduceList f (f initial head) tail;
  
  # 使用示例
  numbers = [ 1 2 3 4 5 ];
  doubled = mapList (x: x * 2) numbers;
  evens = filterList (x: x mod 2 == 0) numbers;
  sum = reduceList (acc: x: acc + x) 0 numbers;
  product = reduceList (acc: x: acc * x) 1 numbers;
in
  { inherit doubled evens sum product; }

内置函数

类型检查函数

let
  value = "hello";
in
  {
    isString = builtins.isString value;      
          # true
    isInt = builtins.isInt value;            
          # false
    isBool = builtins.isBool true;           
          # true
    isList = builtins.isList [1 2 3];        
          # true
    isAttrs = builtins.isAttrs {x = 1;};     
          # true
    isFunction = builtins.isFunction (x: x); 
          # true
  }

字符串操作函数

let
  text = "Hello, Nix World!";
in
  {
    length = builtins.stringLength text;     # 18
    substring = builtins.substring 7 3 text; # "Nix"
    split = builtins.split " " text;         # 分割字符串
    replace = builtins.replaceStrings ["Nix"] ["世界"] text;  # "Hello, 世界 World!"
    
    # 大小写转换需要自定义实现
    toLower = builtins.replaceStrings 
      ["A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" 
       "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"]
      ["a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" 
       "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"] 
      "HELLO";
  }

列表操作函数

let
  numbers = [1 2 3 4 5];
  strings = ["apple" "banana" "cherry"];
in
  {
    length = builtins.length numbers;        # 5
    head = builtins.head numbers;            # 1
    tail = builtins.tail numbers;            # [2 3 4 5]
    
    # 映射函数
    doubled = map (x: x * 2) numbers;        # [2 4 6 8 10]
    # 过滤函数
    evens = builtins.filter (x: x mod 2 == 0) numbers;  # [2 4]
    # 折叠函数
    sum = builtins.foldl' (acc: x: acc + x) 0 numbers;  # 15
    # 元素查找
    hasThree = builtins.elem 3 numbers;      # true
    # 排序
    sorted = builtins.sort (a: b: a < b) [3 1 4 1 5 9];  # [1 1 3 4 5 9]
  }

属性集操作函数

let
  attrs = { a = 1; b = 2; c = 3; };
in
  {
    keys = builtins.attrNames attrs;         # ["a" "b" "c"]
    values = builtins.attrValues attrs;      # [1 2 3]
    hasAttr = builtins.hasAttr "b" attrs;    # true
    
    # 映射属性值
    doubled = builtins.mapAttrs (name: value: value * 2) attrs;
    # { a = 2; b = 4; c = 6; }
    
    # 过滤属性
    filtered = builtins.filterAttrs (name: value: value > 1) attrs;
    # { b = 2; c = 3; }
    
    # 移除属性
    removed = builtins.removeAttrs attrs ["b"];  # { a = 1; c = 3; }
  }

导入与模块

导入文件

let
  # 导入其他 Nix 文件
  utils = import ./utils.nix;
  config = import ./config.nix;
  
  # 带参数导入
  lib = import <nixpkgs/lib>;
  
  # 导入并传递参数
  myModule = import ./module.nix {
    pkgs = import <nixpkgs> {};
    config = { enableFeature = true; };
  };
in
  {
    inherit utils config lib myModule;
  }

模块定义

# module.nix 示例
{ pkgs, config, ... }:

{
  # 模块选项
  options = {
    services.myapp = {
      enable = lib.mkOption {
        type = lib.types.bool;
        default = false;
        description = "启用 MyApp 服务";
      };
      
      port = lib.mkOption {
        type = lib.types.int;
        default = 8080;
        description = "MyApp 监听端口";
      };
    };
  };
  
  # 模块配置
  config = lib.mkIf config.services.myapp.enable {
    systemd.services.myapp = {
      description = "MyApp 服务";
      after = ["network.target"];
      serviceConfig = {
        ExecStart = "${pkgs.myapp}/bin/myapp --port ${toString config.services.myapp.port}";
        Restart = "always";
      };
    };
  };
}

包管理

基本包定义

# default.nix 或 shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.stdenv.mkDerivation {
  pname = "my-app";
  version = "1.0.0";
  
  src = ./.;  # 当前目录作为源码
  
  buildInputs = with pkgs; [
    nodejs
    yarn
  ];
  
  buildPhase = ''
    yarn install
    yarn build
  '';
  
  installPhase = ''
    mkdir -p $out/bin
    cp -r dist/* $out/
    makeWrapper ${pkgs.nodejs}/bin/node $out/bin/my-app \
      --add-flags "$out/index.js"
  '';
  
  meta = with pkgs.lib; {
    description = "我的应用程序";
    homepage = "https://example.com";
    license = licenses.mit;
    maintainers = with maintainers; [ "your-name" ];
  };
}

开发环境

# shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    # 开发工具
    git
    nodejs
    yarn
    python3
    
    # 系统库
    pkg-config
    openssl
    
    # 数据库
    postgresql
    redis
  ];
  
  # 环境变量
  shellHook = ''
    echo "欢迎进入开发环境!"
    export DATABASE_URL="postgresql://localhost/myapp"
    export REDIS_URL="redis://localhost:6379"
    
    # 自动启动服务
    if ! pgrep -f "postgres" > /dev/null; then
      echo "启动 PostgreSQL..."
      postgres -D $HOME/postgres-data &
    fi
  '';
}

常用工具函数

递归函数

let
  # 计算阶乘
  factorial = n:
    if n <= 1
    then 1
    else n * factorial (n - 1);
  
  # 列表求和
  sumList = list:
    if list == []
    then 0
    else builtins.head list + sumList (builtins.tail list);
  
  # 深度合并属性集
  deepMerge = a: b:
    let
      mergeAttr = name:
        let
          aVal = a.${name};
          bVal = b.${name};
        in
          if builtins.isAttrs aVal && builtins.isAttrs bVal
          then deepMerge aVal bVal
          else bVal;
    in
      a // b // builtins.listToAttrs (
        map (name: { inherit name; value = mergeAttr name; })
        (builtins.filter (name: builtins.hasAttr name a) (builtins.attrNames b))
      );
in
  {
    fact5 = factorial 5;           # 120
    listSum = sumList [1 2 3 4 5]; # 15
    merged = deepMerge 
      { a = { x = 1; y = 2; }; c = 3; }
      { a = { y = 9; z = 4; }; d = 5; };
  }

错误处理

断言

let
  version = "1.2.3";
  
  # 基本断言
  validVersion = assert builtins.stringLength version > 0; version;
  
  # 带消息的断言(通过 throw 实现)
  checkPort = port:
    if port < 1 || port > 65535
    then throw "端口号必须在 1-65535 之间,当前值: ${toString port}"
    else port;
  
  # 条件检查
  config = {
    port = checkPort 8080;
    host = if builtins.getEnv "HOST" != "" 
           then builtins.getEnv "HOST" 
           else "localhost";
  };
in
  config

异常处理

let
  # 安全的属性访问
  safeGet = attrs: path: default:
    let
      keys = builtins.split "\\." path;
      getValue = obj: keyList:
        if keyList == [] then obj
        else if !builtins.isAttrs obj then default
        else if !builtins.hasAttr (builtins.head keyList) obj then default
        else getValue (obj.${builtins.head keyList}) (builtins.tail keyList);
    in
      getValue attrs keys;
  
  config = {
    database = {
      host = "localhost";
      port = 5432;
    };
  };
  
  # 安全访问嵌套属性
  dbHost = safeGet config "database.host" "default-host";      # "localhost"
  dbUser = safeGet config "database.user" "default-user";      # "default-user"
in
  { inherit dbHost dbUser; }

来源