如何在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
相关产品推荐
相关产品推荐

