尼克斯家族文雅介绍(2019)

2020-05-07 19:59:53

不要担心别人要做什么。预测未来的最好方法就是发明它。--艾伦·凯(Alan Kay)。

改变我们进行计算方式的想法很少出现。我们现在使用的很多技术都是旧技术的翻版--一层层的化妆品包裹着旧的概念。整个产品线都是建立在这种缺乏创造力和独创性的基础上的。老问题没有解决。相反,这些所谓的创新解决方案只是将问题抛诸脑后,同时用新的色调来描绘它,声称至少它们让它变得更加丰富多彩。这种心态对进步的危害是多方面的。这给人一种错误的印象,即解决方案实际上正在执行。这造成了一种对改进的错误把握。

几年前,Eelco Dolstra撰写了开创性的论文,描述了激进的软件部署方式。这些论文有效地构成了NIX的基石,NIX是一种纯粹的函数式包管理器语言,解决了长期困扰计算的问题-包管理不善。在本文中,我将讨论Nix家族,以及如何利用它们为您服务。

$符号将用于指示常规用户的shell提示符,而#符号将表示root用户的shell。有些情况下,由于使用sudo,命令的EUID将为零(0)。

您有多少次因为升级了其他组件所依赖的软件而导致系统损坏?你有过多少次深夜停留,因为你必须让一个应用程序正常工作,因为你安装的新软件包破坏了它?有多少次,当您感到沮丧时,您放弃了修复您的系统,而只是决定从头开始重新安装您的系统?恢复数据文件很容易;但是,从上一个工作状态恢复系统配置是通往地狱的单程票。

NixOS是一个Linux发行版,它通过利用NIX的确定性并使用单个声明性配置文件来解决这些问题,该文件将所有设置和旋钮包含在一个位置-/etc/nixos/configuration ation.nix。此文件包含有关文件系统、用户、服务、网络配置、输入设备、内核参数等的信息。这意味着您可以获取某个人的配置.nix,并拥有他的确切系统配置!在NixOS中,您不必手动摆弄整个系统来进行您想要的配置。您不必使用临时解决方案来指定所需的配置状态。您不需要安装额外的软件来管理系统配置。

NixOS不遵循FHS,有效地防止了额外的脑损伤。这为灵活性和独创性提供了很大的空间。它没有/usr/和/opt/。它确实有/bin/和/usr/bin/,分别只包含sh和env-这两个实际上都是指向/nix/store/中某个地方的实际程序的符号链接。由管理员显式安装的系统二进制文件的顶级位置位于/run/current-system/sw/bin/and/run/current-system/sw/sbin/。另一方面,用户安装的程序可在其各自的m~/.nix-profile/bin/处获得。这些位置不能通过正常方式修改;必须使用专用程序写入这些树。

安装NixOS非常简单。对于裸机系统,请从nixos.org/nixos/download.html下载安装程序。也可以从该页面获得VM映像。对于我的上一次安装,我使用以下设置进行了安装:

在UEFI模式下从USB驱动器引导。在登录提示符下,以root身份登录。

为了让您抢先一步,您可以使用我的配置的精简版本,如下所示。根据您的需要替换这些值。此处提供所有可用的配置旋钮。

{config,lib,pkgs,.}:{Imports=[./Hardware-Configuration.nix];boot={loader={systemd-boot.enable=true;efi.canTouchEfiVariables=true;};initrd.availableKernelModules=[";xhci_PCI";";EHCI_PCI";";AHCI";";USB_。initrd.luks.device=[{device=";/dev/vg/root";;name=";root";;preLVM=false;}];leanTmpDir=true;};fileSystems=[{device=";/dev/disk/by-uuid/6106-6BF8";;fsType=";vFAT";;mount Point=";/dev/disk/by-uuid/6106-6BF8";;fsType=";vFAT";;mount Point="。fsType=";ext4";;mount Point=";/";;}];swapDevices=[{device=";/dev/vg/exchange";;}];Networking={hostname=";mehfoo";;hostID=";7B1548AE";;enableIPv6=true;networkmanager er.enable=true;};Environment={systemPack.enable=true;}。security.sudo={enable=true;configFile=';';Defaults env_Reset root all=(ALL:ALL)ALL%WELL ALL=(ALL)SETENV:NOPASSWD:ALL';';;};services={xserver={autorun=true;defaultDepth=24;enable=true;displayManager.kdm.enable=true;desktopManager.kde5.enable=true。ExtraGroups=[";车轮";";网络管理器";";码头";];};defaultUserShell=";/run/current-system/sw/bin/zsh";;};}。

用您现有的UUID替换磁盘的UUID。使用命令blKID获取UUID。对于networking.hostID的值,请使用以下命令:

该命令将解析/etc/nixos/configuration ation.nix,确保没有错误。此命令将下载所有必要的软件包以匹配规范。

安装后,更新现有配置非常简单。您所要做的就是编辑配置文件,然后重新构建系统:

如果您犯了错误,系统会通知您,而不是继续进行不正确的配置。系统完成引导后,使用Ctrl+Alt+F1切换到控制台,然后以root身份登录,然后为我们在configuration中指定的用户设置密码。nix:

构成NixOS和Nixpkgs核心的组件是Nix语言。它是一种旨在处理包的声明性语言。

为了更容易理解该语言,让我们安装Nix REPL:

接下来,让我们运行它。您将看到版本号和nix-repl提示符。在撰写本文时,最新的稳定版本是1.11.8:

声明字符串的另一种方法是使用两对单引号。请不要将其与双引号混淆:

与使用";相比,使用';的优点是允许在其内部存在";:

然后它返回的值将被正确引用。这在稍后我们要构建复杂表达式时很有用。

哎呀!这不是我们预想的。由于NIX在设计时考虑了文件和目录,因此它做了一个特例,即当/字符被非空格字符包围时,它会将其解释为目录路径,从而产生绝对路径。要实际执行除法,请在/字符前后至少添加一个空格:

顺便说一下,NIX中没有浮点数。因此,如果您尝试评估一个,您将得到:

NIX-repl>;1.0error:语法错误,意外的int,需要ID或OR_KW或美元_CURLY或';";';,位于(字符串):1:3';";';

此表达式返回部分应用的函数的闭包。我们需要另一个值来完全应用它:

NIX中的=运算符用于绑定值。在此示例中,它用于定义部分应用程序。要使用该功能,请执行以下操作:

Nix-repl&>1<;2truenix-repl>;1&>1==1truenix-repl>;1==1truenix-repl>;";foo";==";foo";truenix-repl>;";foo";<;";bar";Falsenix-repl>;

nix-repl>;[1";foo";true]++[false(6/2)][1";foo";true false 3]。

nix-repl>;builtins.head([1";foo";true(6/2)]++[false(6/2)])1。

nix-repl>;builtins.ail([1";foo";true(6/2)]++[false(6/2)])[";foo";true 3 false 3]。

列表从0开始编制索引。要获取第1个元素,请使用builtins.elemAt运算符:

NIX中的一个重要数据结构是集合。它们是用分号分隔的关键字值对:

nix-repl>;{a=0;b=";bar";;c=true;d=(6/2);}{a=0;b=";Kato";;c=true;d=3;}。

集合与列表的不同之处在于,从集合中提取值是通过引用名称来完成的。要提取b的值,请使用。经营者:

nix-repl>;{a=0;b=";bar";;c=true;d=(6/2);}.b";bar";

nix-repl>;{a=0;b=";bar";;c=true;d=(6/2);}。";b";";bar";

nix-repl>;rec{a=0;b=";bar";;c=true;d=(6/2);e=b;}.E";bar";

在NIX中,所有路径都转换为绝对路径。如果引用当前目录中的文件:

同样,如果引用绝对路径内的相对路径,它仍会转换为绝对路径:

nix-repl&>;/error:语法错误,意外';/';,at(String):1:1nix-repl&>;。/error:语法错误,意外';.';,at(String):1:1

如果这些名词没有动词可用,那会有什么意思呢?NIX中的函数与其他语言有相似之处,但也有自己独特的特点。

此表达式创建一个返回其参数的匿名函数-Identity函数。第一个x后面的冒号表示它是函数的参数,就像在lambda演算中一样。此外,由于字母的等价性,名称并不重要:

这些函数没有多大用处,因为它们没有被捕获用于应用程序。例如,如果我们要将其与参数";foo";一起使用,则需要用括号将其括起来:

让我们创建一个将";ugh&34;附加到其输入的函数,然后应用它:

要定义接受另一个参数的函数,让我们使用以下形式:

nx-repl>;ugh=s:t:s+tnix-repl>;ugh&34;+tnix-repl>;ugh";Me";";You";";

当集与函数一起使用时,可以实现更强大的抽象。我们可以将集合作为参数传递给函数,然后该函数将使用该集合中的数据:

nix-repl>;poof={a,b}:x:a+";";+b+x

此函数有两个参数:{a,b}-具有两个元素的集合的参数规范,以及x-常规参数。请注意,参数规范不是实数集,而仅仅是匹配参数的一种方式;它使用逗号作为值分隔符。在此函数中,我们使用+运算符组合输入。要使用此函数,我们需要执行以下操作:

当一个函数声明一个set作为它的参数时,您需要在调用使用它们的函数时指定关键字。在本例中,关键字名称是a和b。

我们在这里使用了一个常规的、未设置的参数,以便它可以将集合引用为一个值。请注意以下内容:

也可以指定默认值。当不使用带有默认值的参数时,将使用默认值。它们在Common Lisp中的声明类似:

*(deful foof(a&;可选(b";oo";))(连接#39;string a b))*(foof";o.o";)";o.o。o";*(foof";o.o";";^_^";)";o.o^_^";

nix-repl>;foof={a,b?";oo";}:a+bnix-repl>;foof{a=";goo";;}";gooO.o&34;nix-repl>;foof{a=";goo";;b=";oog";;}";gooog"。

为了增加更大的灵活性,NIX支持使用伪静态参数。让我们从上面修改该函数:

一样的。那么,我们如何利用这种灵活性呢?我们将为属性集创建一个标签,以便可以引用‘Extra’值:

nix-repl&>foof=[电子邮件受保护]{a,b,c?";C";,.}:a+b+c+attrs.dnix-repl>;foof{a=";A";;b=";B";;d=";D";;}";abcd";nix-repl>;foof{a=。;c=";X";;d=";D";;}";ABXD";

关键字let让我们(不是有意使用双关语)在局部范围内定义变量。例如,要使标识符x和y仅在本地范围内可见,请执行以下操作:

让x=";foo";;y=";bar&34;in x+poof{a=";hm";;b=";real";;}";HMM";+y";foohreallyhmmbar";

请注意最后一个;在与let一起使用的in关键字之前-它标志着let主体的开始。let结构的行为方式类似于Lisp和Haskell等语言中的let关键字。

{x=#34;foo&34;;y=";bar";;};poof{a=y;b=x;}";xyz";";bar foo xyz";

这里发生的情况是,该集合中的值被“公开”,以使它们在with正文中可用。

条件表达式由if关键字完成。它与主流语言有相似的形式:

如果为FALSE,则";TRUE&34;ELSE";FALSE";";FALSE";

将文件导入NIX表达式的想法与其他语言略有不同。NIX的进口与套装密切相关。假设我们有包含以下内容的文件meh.nix:

let表达式将名称meh绑定到接受一个参数的函数。在let的主体中,它返回一个包含名为meh的成员的集合-=左侧的那个成员。此成员的值是刚刚定义的函数。这里需要记住的重要概念是,这个let表达式返回一个属性集。

我们再次看到熟悉的lambda术语。如图所示,这里的meh名称是一个函数。现在,我们如何才能取消引用这个值呢?通过使用。接线员!

我们必须使用括号,因为当前目录中没有meh.nix.meh这样的文件。如果我们要一步一步通过它,它会有以下几点:

这很好地概括了有关NIX语言的入门概念。其余的毛茸茸的细节可以在手册中找到。

Nixpkgs是由全球用户管理和维护的包的集合。由于源代码在GitHub中,因此它能够利用该平台提供的强大协作模型。在撰写本文时,该集合中有42583个包。它包含广泛的软件包,从生产力应用程序到定理证明器,应有尽有。

大多数流行的操作系统都能很好地处理软件包,直到它们不能。只要你独自一人沿着直线前进,你就会没事的。当你在路上介绍其他人时,情况就不同了。为了让所有的演员一致行动,每个人都必须严格地相互联系。如果一名演员决定离开,自己走,整个剧组就会变得残缺不全。然而,如果该成员克隆了自己,使得离开的副本变得独立,那么原始的行走演员就不会受到干扰。

让我们以目标为多用户生产开发环境的分发为例。安装Firefox版本100时,主二进制文件位于/usr/bin/Firefox或/usr/local/bin/Firefox。然后,该系统中的所有用户都将能够从该路径访问应用程序;John、Mary和Peter都很高兴。但是,当John将其升级到版本200时,Mary和Peter正在使用的同一应用程序也会升级!如果他们更喜欢与他们一起工作的旧版本,这不是一件好事!Nixpkgs允许您拥有一个软件的多个版本,而不会与其他版本发生冲突。John、Mary和Peter都可以拥有自己的Firefox版本,而不会与其他版本冲突。Nixpkgs是怎么做到的?它通过根据计算出的校验和命名组件,而不使用通用的全局位置来实现这一点。

每个用户都有自己的~/.nix-profile版本,这些目录中的所有内容都不包含常规文件。相反,它们都是位于/nix/store/中的实际文件的符号文件。此目录是实际安装程序及其依赖项的位置。写入该目录的唯一方式是通过特定于Nix的程序。无法通过正常方式修改该目录的内容。因此,当普通用户john安装Vim8时,该程序的安装格式类似于/nix/store/w4cr4j13lqzry2b8830819vdz3sdypfa-vim-8.0.0329.。程序包名前的字符是用于构建程序包的所有输入的校验和。然后,文件/HOME/john/.nix-profile/bin/vim指向指向/nix/store/中的文件的符号链接,该符号链接将指向/nix/store/w4cr4j13lqzry2b8830819vdz3sdypfa-vim-8.0.0329/bin/vim.中的实际Vim二进制文件。

如果您使用的是NixOS,请跳过此步骤,因为Nixpkgs已经随附了。要在GNU/Linux或MacOS上安装Nixpkgs,请运行:

系统将提示您输入通过sudo进行root访问的凭据,因为它会将资源安装到/nix/。安装之后,可能还会要求您在shell初始化文件中附加一行命令。当您生成一个新的shell实例时,特定于Nix的命令将可供使用。

使用Nixpkgs安装软件包有两种方式:git checkout,这是最先进的最新更新版本,或者使用通道。git存储库非常适合那些想要使用包的最新和最好的可用版本的人,或者那些想要测试东西的人。另一方面,通道本质上是较早版本的git存储库的快照。

对git存储库的更新经常发生-在您阅读本文时,会向主树提交新的内容。要使用git签出,请克隆存储库:

此命令在您的主目录下创建一个nixpkgs/目录。如果您的用户名是vakelo,那么如果您使用的是GNU/Linux或MacOS,那么可以在/home/vakelo/nixpkgs/或/users/vakelo/nixpkgs/中找到存储库的克隆。

要使用git签出安装软件包,比如emem(Markdown到HTML的转换器),请运行:

这将下载EMEM及其所有依赖项,然后它将使程序可供您使用。要确保EMEM已成功安装,请运行:

如果您的shell没有呕吐并抱怨您正在寻找不存在的东西,而您看到的是一个版本号,这意味着您已经成功安装了EMEM。

通过通道安装包更好,因为使用它安装包的命令更方便。权衡的是,这些包将在几天内过期。如果你不介意的话,那就使用通道而不是git结账。

频道被标记为稳定、不稳定或带有特定版本号,例如18.09或20.03。对于本文,我们使用不稳定通道-它不像稳定通道那样过时,也不像git结账那么近。要订阅不稳定的频道,请运行:

这将从nixos.org获取标记为nixpkgs-unstant的频道,然后将其安装到您的用户配置文件中。

使用上面的示例,要安装EMEM,请分别为NixOS和其他系统运行以下命令:

随着时间的推移,/nix/store/accument中的树会累积,并且可能会有不再被任何包引用的路径。要清理它,请运行以下命令:

文件~/.nixpkgs/config.nix是由NIX命令读取的NIX表达式。在它中,我们可以指定包覆盖-取代默认设置的配置,以及其他旋钮,包括但不限于浏览器插件、GUI配置、SSL等。

{pkgs}:{PackageOverrides=pkgs:{emacs=pkgs.emacs.override{with GTK2=false;with GTK3=false;with Xwidgets=false;};};Firefox={jre=true;enableGoogleTalkPlugin=

..