You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NixOS配置中声明式引用systemd服务单元名称?

问题描述

我定义了一个services.nix文件并将其导入到NixOS配置中,代码大致如下:

{ config, pkgs, ... }:

let
...
in
rec {
  services.postgresql = ...;

  tgsendPostgres = import ./tgsend.nix { 
    pkgs = pkgs;
    lll = "postgresql"; # <------ 此处的值
  }; 
}

我不想将postgresql以字符串形式传入,而是希望声明式/静态地引用services.postgresql,以便在构建时捕获拼写错误。原本以为它是services.postgresql.name这样的属性值,但发现它似乎仅存储在services的属性键中,请问如何从服务定义中获取postgresql这个值?

解决方法

在NixOS中,服务定义本身并没有直接暴露对应的属性键名称,你可以通过以下两种方式实现静态引用:

1. 显式定义服务名称常量

在let块中定义一个常量存储服务名称,同时用这个常量定义services中的属性和传入参数:

{ config, pkgs, ... }:

let
  postgresServiceName = "postgresql";
in
rec {
  services.${postgresServiceName} = ...;

  tgsendPostgres = import ./tgsend.nix { 
    pkgs = pkgs;
    lll = postgresServiceName;
  }; 
}

这样只要postgresServiceName的定义正确,后续所有引用都会保持一致,构建时如果出现拼写错误会直接报错,完全满足声明式需求。

2. 通过属性集操作提取键名

如果已经定义了services.postgresql,可以利用Nix的内置函数从services属性集中筛选出对应的键名,但这种方式不如第一种直观高效:

{ config, pkgs, ... }:

let
  # 从services中筛选出与postgresql服务匹配的键名
  postgresServiceName = builtins.head (builtins.attrNames (builtins.filterAttrs (k: v: v == services.postgresql) services));
in
rec {
  services.postgresql = ...;

  tgsendPostgres = import ./tgsend.nix { 
    pkgs = pkgs;
    lll = postgresServiceName;
  }; 
}

注意这种方法需要确保services中只有一个与services.postgresql完全匹配的属性,否则会得到错误结果。

优先推荐第一种方式,它简洁直观,能在编写阶段就避免拼写错误。

内容的提问来源于stack exchange,提问作者Chris Stryczynski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:33:24