标签 配置文件 下的文章

使用 Lua 配置持久化应用设置。

不是所有的应用都需要配置文件;对很多应用来说,在启动时变得焕然一新对它们更有利。例如,简单的工具就极少需要偏好项和设置在使用过程中保持稳定不变。然而,当你编写一个复杂的应用程序时,如果能让用户设置与应用的交互方式,以及应用与系统交互的方式会很不错。这就是配置文件用来做的事情。本文将讨论一些利用 Lua 进行持久化配置的方法。

选择一种格式

关于配置文件很重要的两点是一致性和可预见性。你不会希望为了保存用户偏好项,将信息转储到文件中,然后再花几天去编码实现“逆向工程”,处理最后出现在文件里的随机信息。

这里用一些常用的 配置文件格式。Lua 有一些库可以处理大多数常用的配置格式;在本文中,我会采用 INI 格式。

安装库

Lua 库的核心仓库是 Luarocks.org。你可以在这个网站搜索库,或者你可以安装并使用 luarocks 终端命令。

Linux 环境中,你可以从发行版的软件仓库中下载它,例如:

$ sudo dnf install luarocks

在 macOS 上,请使用 MacPorts 或者 Homebrew。在 Windows 上,请使用 Chocolatey

luarocks 安装后,你可以使用 search 子命令来搜索一个恰当的库。如果你不知道库的名字,可以通过关键词来搜索这个库,例如 ini、xml或者json,这取决于你想要用这个库做什么。打个比方,你可以搜索inifile`, 这个库被我用来解析 INI 格式的文本文件。

$ luarocks search inifile
Search results:
inifile
 1.0-2 (rockspec) - https://luarocks.org
 1.0-2 (src) - https://luarocks.org
 1.0-1 (rockspec) - https://luarocks.org
 [...]

一个开发者容易犯的错误是在系统上安装了这个库却忘了把它和应用打包。这会给没有安装这个库的用户带来麻烦。为了防止这个问题发生,可以使用 --tree 选项将它安装在项目的本地文件夹中。如果你没有这个项目文件夹,那就先创建这个文件夹再安装库:

$ mkdir demo
$ cd demo
$ luarocks install --tree=local inifile

--tree 选项指示 luarocks 创建一个新文件夹并在其中安装你的库,例如这个例子中的 local 文件夹。 使用这个简单的技巧,你可以将所有你项目要使用的依赖项直接安装到项目文件夹中。

配置代码

首先,在一个名 myconfig.ini 的文件中创建一些 INI 数据。

[example]
name=Tux
species=penguin
enabled=false

[demo]
name=Beastie
species=demon
enabled=false

将这个文件保存到你的主目录下,命名为 myconfig.ini, 不要 存到项目文件夹下。你通常会希望配置文件独立于你的文件存在,这样当用户卸载你的应用时,使用应用时产生的数据可以保存在系统中。有些用户会删除不重要的配置文件,但大多数不会。最终,如果他们要重装这个应用,还会保留着所有的用户偏好项。

配置文件的位置以技术来说并不重要,但每一个操作系统都有存储它们的特定或者默认的路径。在 Linux 中,这个路径由 Freedesktop 规范 指定。它规定配置文件被保存在一个名为 ~/.config 的隐藏文件夹中。为了操作时更加清晰明确,可以在主目录下存储配置文件,以便于使用和寻找。

创建第二个文件,命名为 main.lua,并在你喜欢的文本编辑器中打开它。

首先,你必须告诉 Lua 你将想要使用的附加库放置在哪里。package.path 变量决定了 Lua 到哪里去寻找这些库。你可以从终端中查看 Lua 默认的包地址:

$ Lua
> print(package.path)
./?.lua;/usr/share/lua/5.3/?.lua;/usr/share/lua/5.3/?/init.lua;/usr/lib64/lua/5.3/?.lua;/usr/lib64/lua/5.3/?/init.lua

在你的 Lua 代码中,将你本地库的路径添加到 package.path 中:

package.path = package.path .. ';local/share/lua/5.3/?.lua

使用 Lua 解析 INI 文件

当包的位置确定以后,下一件事就是引入 inifile 库并处理一些操作系统逻辑。即使这是一个很简单的应用,代码也需要从操作系统获取到用户主目录的路径,并建立在必要时将文件系统路径返回给操作系统的通信方式。

package.path = package.path .. ';local/share/lua/5.3/?.lua
inifile = require('inifile')

-- find home directory
home = os.getenv('HOME')

-- detect path separator
-- returns '/' for Linux and Mac
-- and '\' for Windows
d = package.config:sub(1,1)

现在你可使用 inifile 来从配置文件解析数据到 Lua 表中。一旦这些数据被导入进表中,你可以像查询其他的 Lua 表一样查询它。

-- parse the INI file and
-- put values into a table called conf
conf = inifile.parse(home .. d .. 'myconfig.ini')

-- print the data for review
print(conf['example']['name'])
print(conf['example']['species'])
print(conf['example']['enabled'])

在终端中运行代码可以看见结果:

$ lua ./main.lua
Tux
penguin
false

这看起来是正确的。试试在 demo 块中执行同样的操作。

使用 INI 格式存储数据

不是所有用来解析的库都会读写数据(通常被称为 \_编码 和 解码),但是 inifile 会这样做。这意味着你可以使用它对配置文件进行修改。

为了改变配置文件中的值,你可以对被解析的表中的变量进行设置,然后把表重写回配置文件中。

-- set enabled to true
conf['example']['enabled'] = true
conf['demo']['enabled'] = true

-- save the change
inifile.save(home .. d .. 'myconfig.ini', conf)

现在再来看看配置文件:

$ cat ~/myconfig.ini
[example]
name=Tux
species=penguin
enabled=true

[demo]
name=Beastie
species=demon
enabled=true

配置文件

按照用户的设想来存储数据对程序来说是至关重要的。幸运的是,这对工程师来说是一个很常规的任务,大多数工作可能早已被完成了。只要找到一个好用的库完成开放格式下编码和解码,你就能为用户提供一致且持续的体验。

以下是完整的演示代码,可供参考。

package.path = package.path .. ';local/share/lua/5.3/?.lua'
inifile = require('inifile')

-- find home directory
home = os.getenv('HOME')

-- detect path separator
-- returns '/' for Linux and Mac
-- and '\' for Windows
d = package.config:sub(1,1)

-- parse the INI file and
-- put values into a table called conf
conf = inifile.parse(home .. d .. 'myconfig.ini')

-- print the data for review
print(conf['example']['name'])
print(conf['example']['species'])
print(conf['example']['enabled'])

-- enable Tux
conf['example']['enabled'] = true

-- save the change
inifile.save(home .. d .. 'myconfig.ini', conf)

via: https://opensource.com/article/21/6/parsing-config-files-lua

作者:Seth Kenlon 选题:lujun9972 译者:hadisi1993 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

流行的配置文件格式有若干种,每种都有其自身优势。从中找到最适合你的格式吧!

 title=

计算机上有数以千计的配置文件。你可能永远不会直接与其中的大部分文件打交道,但它们确实散落在你的 /etc 以及 ~/.config~/.local/usr 文件夹中。还有一些可能在 /var,甚至 /opt 文件夹中。如果无意中打开过或更改过它们,你就可能会有疑问:为什么有些配置文件看起来是某一种格式,而另一些则是看起来完全不同的格式?

存储配置是一项很灵活的任务,因为只要开发人员知道他们的代码是如何将数据存入文件的,他们就可以轻松编写代码来根据需要提取数据。然而,科技行业非常青睐有详细文档的标准化事物,因此多年来出现了几种比较普遍的格式用来简化配置任务。

为什么我们需要配置文件

配置文件对于现代计算来说很重要。它们使你能够自定义与应用程序交互的方式,或自定义应用程序与系统内其他程序的交互方式。有了配置文件,每当你启动某个应用程序时,它都会有“记忆”,记录了你喜欢如何去使用该程序。

配置文件的结构可以很简单,而且通常确实也很简单。例如,如果你要编写一个应用程序,程序唯一需要知道的是其用户的偏好名字,那么它的唯一配置文件就可以只包含一个词:用户名。就像下面这样:

Tux

但通常应用程序需要追踪的不仅仅是一条信息,因此配置文件通常会有一个键和一个值:

NAME='Tux'
SPECIES='Penguin'

即使没有编程经验,你也可以想象出代码如何解析这些数据。这里有两个简单的例子,一个使用 awk 命令,另一个使用 grep 命令。两个例子都是只关注包含 NAME “键”的行,并返回出现在等号 (=) 之后的“值”:

$ awk -F'=' '/NAME/ { print $2; }' myconfig.ini
'Tux'
$ grep NAME fake.txt | cut -d'=' -f2
'Tux'

同样的原则适用于任何编程语言和任何配置文件。只要你有统一的数据结构,就可以在需要的时候编写简单的代码来提取和解析它。

选择格式

为了保证普遍有效性,配置文件最重要的一点是它们是一致的和可预测的。你绝对不会想做这样的事:以保存用户首选项的名义,将信息随意存储到文件中,然后花好几天时间逆向工程,来找到最终出现在文件中的随机信息。

流行的配置文件格式有若干种,每种格式都有自己的优势。

INI

INI 文件采用了键值对的格式:

[example]
name=Tux
style=widgety,fidgety
enabled=1

这种简单的配置风格很直观,只要你别选择使用糟糕的键名(比如用 unampref 这样的神秘键名来代替 name)就好。这些键值对很容易解析和编辑。

除了键和值之外,INI 格式还可以分 section 。在下列示例代码中,[example][demo] 就是配置文件中的两个节:

[example]
name=Tux
style=widgety,fidgety
enabled=1

[demo]
name=Beastie
fullscreen=1

这几个配置语句解析起来有点复杂,因为有两个 name 键。想象一下,一个粗心的程序员在这个配置文件中查询 name,结果总是返回 Beastie,因为这是文件中对 name 的最后一个定义值。在解析这样的文件时,开发人员必须加倍小心地在各节中搜索键,这可能会很棘手,具体取决于用来解析该文件的语言。然而,它仍然是一种很流行的格式,大多数语言都会有一个现成的库来帮助程序员解析 INI 文件。

YAML

YAML 文件 是结构化列表,可以包含值或者键值对:

---
Example:
  Name: 'Tux'
  Style:
   - 'widgety'
    - 'fidgety'
  Enabled: 1

YAML 格式很流行,部分原因是它看起来很整洁。数据要放置到相对其上层数据的特定位置,除此之外没有太多其他语法。然而,对于某些人来说的这种特色,在其他人眼中可能就是一个问题。许多开发人员不愿使用 YAML,正是因为它很看重本质上 不存在 的东西。如果你在 YAML 中缩进错误,YAML 解析器可能会将你的文件视为无效文件,即使不视为无效,返回的数据也可能是错误的。

大多数语言都有 YAML 解析器,并且有很好的开源 YAML linters(验证语法的应用程序)来帮你确保 YAML 文件的完整性。

JSON

JSON 文件在技术上来说是 YAML 的子集,因此其数据结构是相同的,尽管其语法完全不同:

{
  "Example": {
    "Name": [
      "Tux"
    ],
    "Style": [
      "widgety",
      "fidgety"
    ],
    "Enabled": 1
  }
}

JSON 在 JavaScript 程序员中很流行,这并不奇怪,因为 JSON 全称为 JavaScript 对象符号 JavaScript Object Notation 。由于与 Web 开发密切相关,JSON 是 Web API 的常见输出格式。大多数编程语言都有解析 JSON 的库。

XML

XML 使用标签作为键,将配置值围绕起来:

<example>
  <name>Tux</name>
  <style priority="user">widgety</style>
  <style priority="fallback">fidgety</style>
  <enabled>1</enabled>
</example>

XML 经常被 Java 程序员使用,Java 有一套丰富的 XML 解析器。虽然 XML 以非常严格而著称,但同时也非常灵活。与有一系列特定标签的 HTML 不同,XML 中可以随意发明自己的标签。只要始终坚持相同的构建规则,并有一个良好的库来解析它,你就可以准确而轻松地提取数据。

有一些很好的开源 linter 可以帮你验证 XML 文件,并且大多数编程语言都提供用于解析 XML 的库。

二进制格式

Linux 以纯文本配置为傲。这样做的优点是可以使用 cat 等基本工具查看配置数据,甚至可以使用你 最喜欢的文本编辑器 来编辑配置。

但是,某些应用程序使用二进制格式配置,就意味着数据以某种非自然语言的格式进行编码。这些文件通常需要一个特殊的应用程序(通常是它们要配置的应用程序)来解释它们的数据。你无法查看这些文件,至少无法以任何有意义的方式查看,并且无法在其宿主应用程序之外编辑它们。选用二进制格式的一些原因如下:

  • 速度: 程序员可以使用自定义符号在二进制配置文件中的某些点注册特定的信息位。提取数据时不涉及搜索,因为所有内容都已标注了索引。
  • 大小: 文本文件可能会变大,如果选择压缩文本文件,实际上是在将其转换为二进制格式。二进制文件可以通过编码技巧变得更小(文本文件也是如此,但在某些时候,你的优化会使数据变得晦涩,以至于文件也成了二进制文件)。
  • 晦涩: 一些程序员甚至不希望人们查看他们的配置文件,因此将它们编码为二进制数据。这通常只会让用户感到沮丧,并不是使用二进制格式的好理由。

如果必须使用二进制格式进行配置,请使用已作为开放标准存在的格式,例如 NetCDF

找到有效的配置格式

配置格式帮助开发人员存储应用程序所需的数据,并帮助用户存储他们希望应用程序如何操作的偏好项。对于应该使用什么格式的问题,可能没有错误的答案,只要你觉得所使用的语言能很好地支持就可以。在开发应用程序时,查看可用格式,用一些样例数据建模,查看和评估你的编程语言提供的库和实用程序,然后选择你觉得最合适的一种格式吧。


via: https://opensource.com/article/21/6/what-config-files

作者:Seth Kenlon 选题:lujun9972 译者:unigeorge 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

第一步是选择配置文件的格式:INI、JSON、YAML 或 TOML。

 title=

有时,程序需要足够的参数,将它们全部作为命令行参数或环境变量既不让人愉快也不可行。 在这些情况下,你将需要使用配置文件。

有几种流行的配置文件格式。其中包括古老的(虽然有时定义不明确)INI 格式,虽然流行但有时难以手写的 JSON 格式,使用广泛但有时在细节方面令人意外的 YAML 格式,以及很多人还没有听说过的最新出现的 TOML。

你的首要任务是选择一种格式,然后记录该选择。解决了这个简单的部分之后就是时候解析配置了。

有时,在配置中拥有一个与“抽象“数据相对应的类是一个不错的想法。因为这段代码不会对配置做任何事情,所以这是展示解析逻辑最简单的方式。

想象一下文件处理器的配置:它包括一个输入目录、一个输出目录和要提取的文件。

配置类的抽象定义可能类似于:

from __future__ import annotations
import attr

@attr.frozen
class Configuration:
    @attr.frozen
    class Files:
        input_dir: str
        output_dir: str
    files: Files
    @attr.frozen
    class Parameters:
        patterns: List[str]
    parameters: Parameters

为了使特定于格式的代码更简单,你还需要编写一个函数来从字典中解析此类。请注意,这假设配置将使用破折号,而不是下划线。 这种差异并不少见。

def configuration_from_dict(details):
    files = Configuration.Files(
        input_dir=details["files"]["input-dir"],
        output_dir=details["files"]["output-dir"],
    )
    parameters = Configuration.Paraneters(
        patterns=details["parameters"]["patterns"]
    )
    return Configuration(
        files=files,
        parameters=parameters,
    )

JSON

JSON(JavaScript Object Notation)是一种类似于 JavaScript 的格式。

以下是 JSON 格式的示例配置:

json_config = """
{
    "files": {
        "input-dir": "inputs",
        "output-dir": "outputs"
    },
    "parameters": {
        "patterns": [
            "*.txt",
            "*.md"
        ]
    }
}
"""

解析逻辑使用 json 模块将 JSON 解析为 Python 的内置数据结构(字典、列表、字符串),然后从字典中创建类:

import json
def configuration_from_json(data):
    parsed = json.loads(data)
    return configuration_from_dict(parsed)

INI

INI 格式,最初只在 Windows 上流行,之后成为配置标准格式。

这是与 INI 相同的配置:

ini_config="""
[files]
input-dir = inputs
output-dir = outputs

[parameters]
patterns = ['*.txt', '*.md']
"""

Python 可以使用内置的 configparser 模块解析它。解析器充当类似 dict 的对象,因此可以直接传递给 configuration_from_dict

import configparser

def configuration_from_ini(data):
    parser = configparser.ConfigParser()
    parser.read_string(data)
    return configuration_from_dict(parser)

YAML

YAML(Yet Another Markup Language)是 JSON 的扩展,旨在更易于手动编写。为了实现了这一点,部分原因是有一个很长的规范。

以下是 YAML 中的相同配置:

yaml_config = """
files:
  input-dir: inputs
  output-dir: outputs
parameters:
  patterns:
  - '*.txt'
  - '*.md'
"""

要让 Python 解析它,你需要安装第三方模块。最受欢迎的是PyYAMLpip install pyyaml)。 YAML 解析器还返回可以传递给 configuration_from_dict 的内置 Python 数据类型。但是,YAML 解析器需要一个字节流,因此你需要将字符串转换为字节流。

import io
import yaml
def configuration_from_yaml(data):
    fp = io.StringIO(data)
    parsed = yaml.safe_load(fp)
    return configuration_from_dict(parsed)

TOML

TOML(Tom's Own Markup Language)旨在成为 YAML 的轻量级替代品。其规范比较短,已经在一些地方流行了(比如 Rust 的包管理器 Cargo 就用它来进行包配置)。

这是与 TOML 相同的配置:

toml_config = """
[files]
input-dir = "inputs"
output-dir = "outputs"

[parameters]
patterns = [ "*.txt", "*.md",]
"""

为了解析 TOML,你需要安装第三方包。最流行的一种被简单地称为 toml。 与 YAML 和 JSON 一样,它返回基本的 Python 数据类型。

import toml
def configuration_from_toml(data):
    parsed = toml.loads(data)
    return configuration_from_dict(parsed)

总结

选择配置格式是一种微妙的权衡。但是,一旦你做出决定,Python 就可以使用少量代码来解析大多数流行的格式。


via: https://opensource.com/article/21/6/parse-configuration-files-python

作者:Moshe Zadka 选题:lujun9972 译者:zepoch 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

许多 GNU/Linux 程序的一个特点是有个易于编辑的配置文件。几乎所有常见的自由软件都将配置设置保存在纯文本文件中,通常采用结构化格式,如 JSON、YAML 或“类似 ini” 的文件中。这些配置文件经常隐藏在用户的主目录中。但是,基本的 ls 不会显示它们。UNIX 标准要求以点开头的任何文件或目录名称都被视为“隐藏”,除非用户特意要求,否则不会列在目录列表中。例如,要使用 ls 列出所有文件,要传递 -a 选项。

随着时间的推移,这些配置文件会有很多定制配置,管理它们变得越来越具有挑战性。不仅如此,在多台计算机之间保持同步是大型组织所面临的共同挑战。最后,许多用户也对其独特的配置感到自豪,并希望以简单的方式与朋友分享。这就是用到 rcm 介入的地方。

rcm 是一个 “rc” 文件管理套件(“rc” 是命名配置文件的另一种约定,它已被某些 GNU/Linux 程序采用,如 screenbash)。 rcm 提供了一套命令来管理和列出它跟踪的文件。使用 dnf 安装 rcm。

开始使用

默认情况下,rcm 使用 ~/.dotfiles 来存储它管理的所有隐藏文件。一个被管理的隐藏文件实际保存在 ~/.dotfiles 目录中,而它的符号链接会放在文件原本的位置。例如,如果 ~/.bashrc 由 rcm 所管理,那么详细列表将如下所示。

[link@localhost ~]$ ls -l ~/.bashrc
lrwxrwxrwx. 1 link link 27 Dec 16 05:19 .bashrc -> /home/link/.dotfiles/bashrc
[link@localhost ~]$

rcm 包含 4 个命令:

  • mkrc – 将文件转换为由 rcm 管理的隐藏文件
  • lsrc – 列出由 rcm 管理的文件
  • rcup – 同步由 rcm 管理的隐藏文件
  • rcdn – 删除 rcm 管理的所有符号链接

在两台计算机上共享 bashrc

如今用户在多台计算机上拥有 shell 帐户并不罕见。在这些计算机之间同步隐藏文件可能是一个挑战。这里将提供一种可能的解决方案,仅使用 rcm 和 git。

首先使用 mkrc 将文件转换成由 rcm 管理的文件。

[link@localhost ~]$ mkrc -v ~/.bashrc
Moving...
'/home/link/.bashrc' -> '/home/link/.dotfiles/bashrc'
Linking...
'/home/link/.dotfiles/bashrc' -> '/home/link/.bashrc'
[link@localhost ~]$

接下来使用 lsrc 验证列表是否正确。

[link@localhost ~]$ lsrc
/home/link/.bashrc:/home/link/.dotfiles/bashrc
[link@localhost ~]$

现在在 ~/.dotfiles 中创建一个 git 仓库,并使用你选择的 git 仓库托管设置一个远程仓库。提交 bashrc 文件并推送一个新分支。

[link@localhost ~]$ cd ~/.dotfiles
[link@localhost .dotfiles]$ git init
Initialized empty Git repository in /home/link/.dotfiles/.git/
[link@localhost .dotfiles]$ git remote add origin [email protected]:linkdupont/dotfiles.git
[link@localhost .dotfiles]$ git add bashrc
[link@localhost .dotfiles]$ git commit -m "initial commit"
[master (root-commit) b54406b] initial commit
1 file changed, 15 insertions(+)
create mode 100644 bashrc
[link@localhost .dotfiles]$ git push -u origin master
...
[link@localhost .dotfiles]$

在第二台机器上,克隆这个仓库到 ~/.dotfiles 中。

[link@remotehost ~]$ git clone [email protected]:linkdupont/dotfiles.git ~/.dotfiles
...
[link@remotehost ~]$

现在使用 rcup 更新受 rcm 管理的符号链接。

[link@remotehost ~]$ rcup -v
replacing identical but unlinked /home/link/.bashrc
removed '/home/link/.bashrc'
'/home/link/.dotfiles/bashrc' -> '/home/link/.bashrc'
[link@remotehost ~]$

覆盖现有的 ~/.bashrc(如果存在)并重启 shell。

就是这些了!指定主机选项 (-o) 是对上面这种情况的有用补充。如往常一样,请阅读手册页。它们包含了很多示例命令。


via: https://fedoramagazine.org/managing-dotfiles-rcm/

作者:Link Dupont 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出