2021年7月

使用 qpdf 和 poppler-utils 来分割、修改和合并 PDF 文件。

 title=

你收到的许多文件都是 PDF 格式的。有时这些 PDF 需要进行处理。例如,可能需要删除或添加页面,或者你可能需要签署或修改一个特定的页面。

不管是好是坏,这就是我们所处的现实。

有一些花哨的图形用户界面工具可以让你编辑 PDF,但我一直对命令行感到最舒服。在这个任务的许多命令行工具中,当我想修改一个 PDF 时,我使用的是 qpdfpoppler-utils

安装

在 Linux 上,你可以用你的包管理器(如 aptdnf)来安装 qpdfpoppler-utils。比如在 Fedora 上:

$ sudo dnf install qpdf poppler-utils

在 macOS 上,使用 MacPortsHomebrew。在 Windows 上,使用 Chocolatey

qpdf

qpdf 命令可以做很多事情,但我主要用它来:

  1. 将一个 PDF 分割成不同的页面
  2. 将多个 PDF 文件合并成一个文件

要将一个 PDF 分割成不同的页面:

qpdf --split-pages original.pdf split.pdf

这就会生成像 split-01.pdfsplit-02.pdf 这样的文件。每个文件都是一个单页的 PDF 文件。

合并文件比较微妙:

qpdf --empty concatenated.pdf --pages split-*.pdf --

这就是 qpdf 默认的做法。--empty 选项告诉 qpdf 从一个空文件开始。结尾处的两个破折号(--)表示没有更多的文件需要处理。这是一个参数反映内部模型的例子,而不是人们使用它的目的,但至少它能运行并产生有效的 PDF!

poppler-utils

这个软件包包含几个工具,但我用得最多的是 pdftoppm,它把 PDF 文件转换为可移植的像素图(ppm)文件。我通常在用 qpdf 分割页面后使用它,并需要将特定页面转换为我可以修改的图像。ppm 格式并不为人所知,但重要的是大多数图像处理方法,包括 ImageMagickPillow 等,都可以使用它。这些工具中的大多数也可以将文件保存为 PDF。

工作流程

我通常的工作流程是:

  • 使用 qpdf 将 PDF 分割成若干页。
  • 使用 poppler-utils 将需要修改的页面转换为图像。
  • 根据需要修改图像,并将其保存为 PDF。
  • 使用 qpdf 将各页合并成一个 PDF。

其他工具

有许多很好的开源命令来处理 PDF,无论你是 缩小它们从文本文件创建它们转换文档,还是尽量 完全避免它们。你最喜欢的开源 PDF 工具是什么?请在评论中分享它们。


via: https://opensource.com/article/21/7/qpdf-command-line

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

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

FCC 补偿 19 亿美元替换美国网络中的华为和中兴设备

2020 年,美国联邦通信委员会(FCC)将华为和中兴通讯列为对美国通信网络的国家安全威胁,此举将禁止美国公司利用政府资金从这两家公司购买设备。周二 FCC 一致投票决定敲定一项涉及 19 亿美元补偿资金的计划,以帮助美国农村运营商替换网络中使用的华为和中兴等中国公司的电信网络设备。

以后,互联网也许也会变成国家网。

Android 12 将默认允许边下载边玩

谷歌宣布了 Android 12 和 Play 商店的一项新功能:默认允许边下载边玩。如果玩家下载一个大型游戏,在下载完成前就可以试玩。部分游戏商店很早就提供了边下载边玩的功能,这是将游戏分成必要和次要部分,先下载必要部分然后在游戏过程中下载次要部分。

主要原因还是游戏越来越大了。

加密货币交易量锐减

周一公布的研究显示,6 月主要加密货币交易所的交易量下降逾 40%,现货交易量下降 42.7% 至 2.7 万亿美元,衍生品交易量则下降 40.7% 至 3.2 万亿美元。

挺好的,热钱其实带来的是不理智。

用 GPG 和 Python 的 getpass 模块给你的密码多一层安全保障。

 title=

密码对程序员来说尤其重要。你不应该在不加密的情况下存储它们,而且你也不应该在用户输入密码的时候显示出输入的内容。当我决定要提高我的笔记本电脑的安全性时,这对我来说变得特别重要。我对我的家目录进行了加密,但当我登录后,任何以纯文本形式存储在配置文件中的密码都有可能暴露在偷窥者面前。

具体来说,我使用一个名为 Mutt 的应用作为我的电子邮件客户端。它可以让我在我的 Linux 终端中阅读和撰写电子邮件,但通常它希望在其配置文件中有一个密码。我限制了我的 Mutt 配置文件的权限,以便只有我可以看到它,我是我的笔记本电脑的唯一用户,所以我并不真的担心经过认证的用户会无意中看到我的配置文件。相反,我想保护自己,无论是为了吹嘘还是为了版本控制,不至于心不在焉地把我的配置发布到网上,把我的密码暴露了。此外,虽然我不希望我的系统上有不受欢迎的客人,但我确实想确保入侵者不能通过对我的配置上运行 cat 就获得我的密码。

Python GnuPG

Python 模块 python-gnupggpg 应用的一个 Python 封装。该模块的名字是 python-gnupg,你不要把它和一个叫做 gnupg 的模块混淆。

GnuPG(GPG) 是 Linux 的默认加密系统,我从 2009 年左右开始使用它。我对它很熟悉,对它的安全性有很高的信任。

我决定将我的密码输入 Mutt 的最好方法是将我的密码存储在一个加密的 GPG 文件中,创建一个提示我的 GPG 密码来解锁这个加密文件,然后将密码交给 Mutt(实际上是交给 offlineimap 命令,我用它来同步我的笔记本和电子邮件服务器)。

用 Python 获取用户输入 是非常容易的。对 input 进行调用,无论用户输入什么,都会被存储为一个变量:

print("Enter password: ")
myinput = input()

print("You entered: ", myinput)

我的问题是,当我根据密码提示在终端上输入密码时,我所输入的所有内容对任何从我肩膀上看过去或滚动我的终端历史的人来说都是可见的:

$ ./test.py
Enter password: my-Complex-Passphrase

用 getpass 输入不可见密码

正如通常的情况一样,有一个 Python 模块已经解决了我的问题。这个模块是 getpass4,从用户的角度来看,它的行为和 input 完全一样,只是不显示用户输入的内容。

你可以用 pip 安装这两个模块:

$ python -m pip install --user python-gnupg getpass4

下面是我的 Python 脚本,用于创建密码提示:

#!/usr/bin/env python
# by Seth Kenlon
# GPLv3

# install deps:
# python3 -m pip install --user python-gnupg getpass4

import gnupg
import getpass
from pathlib import Path

def get_api_pass():
  homedir = str(Path.home())
  gpg = gnupg.GPG(gnupghome=os.path.join(homedir,".gnupg"), use_agent=True)
  passwd = getpass.getpass(prompt="Enter your GnuPG password: ", stream=None)

  with open(os.path.join(homedir,'.mutt','pass.gpg'), 'rb') as f:
    apipass = (gpg.decrypt_file(f, passphrase=passwd))

  f.close()

  return str(apipass)
 
if __name__ == "__main__":
  apipass = get_api_pass()
  print(apipass)

如果你想试试,把文件保存为 password_prompt.py。如果你使用 offlineimap 并想在你自己的密码输入中使用这个方案,那么把它保存到某个你可以在 .offlineimaprc 文件中指向 offlineimap 的位置(我使用 ~/.mutt/password_prompt.py)。

测试密码提示

要查看脚本的运行情况,你首先必须创建一个加密文件(我假设你已经设置了 GPG):

$ echo "hello world" > pass
$ gpg --encrypt pass
$ mv pass.gpg ~/.mutt/pass.gpg
$ rm pass

现在运行 Python 脚本:

$ python ~/.mutt/password_prompt.py
Enter your GPG password:
hello world

当你输入时没有任何显示,但只要你正确输入 GPG 口令,你就会看到该测试信息。

将密码提示符与 offlineimap 整合起来

我需要将我的新提示与 offlineimap 命令结合起来。我为这个脚本选择了 Python,因为我知道 offlineimap 可以对 Python 程序进行调用。如果你是一个 offlineimap 用户,你会明白唯一需要的“整合”是在你的 .offlineimaprc 文件中改变两行。

首先,添加一行引用 Python 文件的内容:

pythonfile = ~/.mutt/password_prompt.py

然后将 .offlineimaprc中的 remotepasseval 行改为调用 password_prompt.py中的 get_api_pass() 函数:

remotepasseval = get_api_pass()

配置文件中不再有密码!

安全问题

在你的个人电脑上考虑安全问题有时会让人觉得很偏执。你的 SSH 配置是否真的需要限制为 600?隐藏在名为 .mutt 的无关紧要的电子邮件密码真的重要吗?也许不重要。

然而,知道我没有把敏感数据悄悄地藏在我的配置文件里,使我更容易把文件提交到公共 Git 仓库,把片段复制和粘贴到支持论坛,并以真实好用的配置文件的形式分享我的知识。仅就这一点而言,安全性的提高使我的生活更加轻松。而且有这么多好的 Python 模块可以提供帮助,这很容易实现。


via: https://opensource.com/article/21/7/invisible-passwords-python

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

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

使用数据库查询操作轻松获取系统信息。

 title=

Linux 提供了很多帮助用户收集主机操作系统信息的命令:列出文件或者目录的属性信息;查询安装的软件包、正在执行的命令、开机时启动的服务;或者了解系统的硬件。

每个命令使用自己的输出格式列出系统的信息。你需要使用 grepsedawk 这样的工具过滤命令输出的结果,以便找到特定的信息。此外,很多这样的信息会频繁变动,导致系统状态的改变。

将所有的信息格式化为一个数据库的 SQL 查询的输出进行查看将会十分有益。想象一下,你能够像查询具有类似名称的 SQL 数据库表一样查询 psrpm 命令的输出。

幸运的是,有一个工具刚好实现了这个功能,而且功能更多:Osquery 是一个 开源的 “由 SQL 驱动的操作系统仪表、监控和分析框架”。

许多处理安全、DevOps、合规性的应用,以及仓储管理管理(仅举几例)在内部依赖 Osquery 提供的核心功能。

安装 Osquery

Osquery 适用于 Linux、macOS、Windows、FreeBSD。请按照 指南 为你的操作系统安装最新版本。(我会在下面的例子中使用 4.7.0 版本。)

安装完成后,确保 Osquery 可以工作:

$ rpm -qa | grep osquery
osquery-4.7.0-1.linux.x86_64
$
$ osqueryi --version
osqueryi version 4.7.0
$

Osquery 组件

Osquery 有两个主要组件:

  • osqueri 是一个交互式的 SQL 查询控制台,可以独立运行,不需要超级用户权限(除非要查询的表格需要访问权限)。
  • osqueryd 像一个安装在主机的监控守护进程,可以定期调度查询操作执行,从底层架构收集信息。

可以在不运行 osqueryd 的情况下执行 osqueri。另一个工具,osqueryctl,控制守护进程的启动、停止,并检查其状态。

$ rpm -ql osquery-4.8.0-1.linux.x86_64 | grep bin
/usr/bin/osqueryctl
/usr/bin/osqueryd
/usr/bin/osqueryi
$

使用 osqueryi 交互式命令提示符

你和 Osquery 的交互与使用 SQL 数据库十分相似。事实上,osqueryi 是 SQList shell 的一个修改版。执行 osqueryi 命令进入交互式命令提示符 ,就可以执行 Osquery 的命令,通常以 . 开始:

$ osqueryi
Using a virtual database. Need help, type '.help'
osquery>

要退出交互式命令提示符,执行 .quit 命令回到操作系统的命令提示符:

osquery>
osquery> .quit
$

找出可用的表

如前所述,Osquery 像 SQL 查询一样输出数据,数据库中的信息通常保存在表中。但是如何在不知道表名的情况下查询这些表呢?你可以运行 .tables 命令列出所有可以查询的表。如果你是一个 Linux 长期用户或者一个系统管理员 ,就会对表名十分熟悉,因为你一直在使用操作系统命令获取同样的信息:

osquery> .tables
  => acpi_tables
  => apparmor_events
  => apparmor_profiles
  => apt_sources

<<裁剪>>

  => arp_cache
  => user_ssh_keys
  => users
  => yara
  => yara_events
  => ycloud_instance_metadata
  => yum_sources
osquery>

检查各个表的模式

知道表名后,可以查看每个表提供的信息。既然 ps 命令经常用于获取进程信息,就以 processes 为例。执行 .schema 命令加上表名查看表中保存的信息。如果要验证命令返回的结果,可以快速执行 ps -efps aux,对比命令的输出和表中的内容:

osquery> .schema processes
CREATE TABLE processes(`pid` BIGINT, `name` TEXT, `path` TEXT, `cmdline` TEXT, `state` TEXT, `cwd` TEXT, `root` TEXT, `uid` BIGINT, `gid` BIGINT, `euid` BIGINT, `egid` BIGINT, `suid` BIGINT, `sgid` BIGINT, `on_disk` INTEGER, `wired_size` BIGINT, `resident_size` BIGINT, `total_size` BIGINT, `user_time` BIGINT, `system_time` BIGINT, `disk_bytes_read` BIGINT, `disk_bytes_written` BIGINT, `start_time` BIGINT, `parent` BIGINT, `pgroup` BIGINT, `threads` INTEGER, `nice` INTEGER, `is_elevated_token` INTEGER HIDDEN, `elapsed_time` BIGINT HIDDEN, `handle_count` BIGINT HIDDEN, `percent_processor_time` BIGINT HIDDEN, `upid` BIGINT HIDDEN, `uppid` BIGINT HIDDEN, `cpu_type` INTEGER HIDDEN, `cpu_subtype` INTEGER HIDDEN, `phys_footprint` BIGINT HIDDEN, PRIMARY KEY (`pid`)) WITHOUT ROWID;
osquery>

要进一步确认,可以使用下面的命令查看 RPM 包的结构信息,然后与操作系统命令 rpm -qarpm -qi 的输出比较:

osquery>
osquery> .schema rpm_packages
CREATE TABLE rpm_packages(`name` TEXT, `version` TEXT, `release` TEXT, `source` TEXT, `size` BIGINT, `sha1` TEXT, `arch` TEXT, `epoch` INTEGER, `install_time` INTEGER, `vendor` TEXT, `package_group` TEXT, `pid_with_namespace` INTEGER HIDDEN, `mount_namespace_id` TEXT HIDDEN, PRIMARY KEY (`name`, `version`, `release`, `arch`, `epoch`, `pid_with_namespace`)) WITHOUT ROWID;
osquery>

从 Osquery 的 表格文档 获取更多信息。

使用 PRAGMA 命令

或许模式信息对你来说太难看懂,还有另一种途径能够以详细的表格格式打印表中的信息:PRAGMA 命令。例如,我想通过 PRAGMA 用一种易于理解的格式查看 rpm_packages 表的信息:

osquery> PRAGMA table_info(rpm_packages);

这种表格式信息的一个好处是你可以关注想要查询的字段,查看命令提供的类型信息:

osquery> PRAGMA table_info(users);
+-----+-------------+--------+---------+------------+----+
| cid | name        | type   | notnull | dflt_value | pk |
+-----+-------------+--------+---------+------------+----+
| 0   | uid         | BIGINT | 1       |            | 1  |
| 1   | gid         | BIGINT | 0       |            | 0  |
| 2   | uid_signed  | BIGINT | 0       |            | 0  |
| 3   | gid_signed  | BIGINT | 0       |            | 0  |
| 4   | username    | TEXT   | 1       |            | 2  |
| 5   | description | TEXT   | 0       |            | 0  |
| 6   | directory   | TEXT   | 0       |            | 0  |
| 7   | shell       | TEXT   | 0       |            | 0  |
| 8   | uuid        | TEXT   | 1       |            | 3  |
+-----+-------------+--------+---------+------------+----+
osquery>

进行你的第一次查询

在你从表、模式、条目中获取到所有进行查询所需要的信息后,进行你的第一次 SQL 查询查看其中的信息。下面的查询返回系统中的用户和每个用户的用户 ID、组 ID、主目录和默认的命令行解释器。Linux 用户通过查看 /etc/passwd 文件的内容并执行 grepsedawk 命令获取同样的信息。

osquery>
osquery> select uid,gid,directory,shell,uuid FROM users LIMIT 7;
+-----+-----+----------------+----------------+------+
| uid | gid | directory      | shell          | uuid |
+-----+-----+----------------+----------------+------+
| 0   | 0   | /root          | /bin/bash      |      |
| 1   | 1   | /bin           | /sbin/nologin  |      |
| 2   | 2   | /sbin          | /sbin/nologin  |      |
| 3   | 4   | /var/adm       | /sbin/nologin  |      |
| 4   | 7   | /var/spool/lpd | /sbin/nologin  |      |
| 5   | 0   | /sbin          | /bin/sync      |      |
| 6   | 0   | /sbin          | /sbin/shutdown |      |
+-----+-----+----------------+----------------+------+
osquery>

不进入交互模式的查询

如果你想要在不进入 osqueri 交互模式的情况下进行查询,该怎么办?要用查询操作写命令行解释器脚本,这种方式可能十分有用。这种情况下,可以直接从 Bash 解释器 echo SQL 查询,通过管道输出到 osqueri

$ echo "select uid,gid,directory,shell,uuid FROM users LIMIT 7;" | osqueryi
+-----+-----+----------------+----------------+------+
| uid | gid | directory      | shell          | uuid |
+-----+-----+----------------+----------------+------+
| 0   | 0   | /root          | /bin/bash      |      |
| 1   | 1   | /bin           | /sbin/nologin  |      |
| 2   | 2   | /sbin          | /sbin/nologin  |      |
| 3   | 4   | /var/adm       | /sbin/nologin  |      |
| 4   | 7   | /var/spool/lpd | /sbin/nologin  |      |
| 5   | 0   | /sbin          | /bin/sync      |      |
| 6   | 0   | /sbin          | /sbin/shutdown |      |
+-----+-----+----------------+----------------+------+
$

获悉系统启动时开始的服务

Osquery 还可以列出系统启动时开始的所有服务。例如,可以查询 startup_items 表获取启动时开始的前五项服务的名称、状态和路径:

osquery> SELECT name,type,status,path FROM startup_items LIMIT 5;
  name = README
  type = Startup Item
status = enabled
  path = /etc/rc.d/init.d/README

  name = anamon
  type = Startup Item
status = enabled
  path = /etc/rc.d/init.d/anamon

  name = functions
  type = Startup Item
status = enabled
  path = /etc/rc.d/init.d/functions

  name = osqueryd
  type = Startup Item
status = enabled
  path = /etc/rc.d/init.d/osqueryd

  name = AT-SPI D-Bus Bus
  type = Startup Item
status = enabled
  path = /usr/libexec/at-spi-bus-launcher --launch-immediately
osquery>

查阅二进制文件的 ELF 信息

假如你想要弄清 ls 二进制文件的更多细节,通常会通过 readelf -h 命令,加上 ls 命令的路径。查询 Osquery 的 elf_info 表你可以得到同样的信息:

osquery> SELECT * FROM elf_info WHERE path="/bin/ls";
      class = 64
        abi = sysv
abi_version = 0
       type = dyn
    machine = 62
    version = 1
      entry = 24064
      flags = 0
       path = /bin/ls
osquery>

现在你应该初步了解如何使用 osqueri 查询自己想要的信息。然而,这些信息保存在数量巨大的表中;我查询过的一个系统中,有 156 个不同的表,这个数字可能是十分惊人的:

$ echo ".tables" | osqueryi | wc -l
156
$

要让事情变得更容易,可以从这些表开始获取你的 Linux 系统的信息:

系统信息表:

osquery> select * from system_info;

系统限制信息:

osquery> select * from ulimit_info;

由各种进程打开的文件:

osquery> select * from process_open_files;

系统上开放的端口:

osquery> select * from listening_ports;

运行中的进程信息:

osquery> select * from processes;

已安装的包信息:

osquery> select * from rpm_packages;

用户登录信息:

osquery> select * from last;

系统日志信息:

osquery> select * from syslog_events;

了解更多

Osquery 是一个强大的工具,提供了许多可以用于解决各种使用案例的主机信息。你可以阅读 文档 了解更多 Osquery 的信息。


via: https://opensource.com/article/21/6/osquery-linux

作者:Gaurav Kamathe 选题:lujun9972 译者:YungeG 校对:wxy

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

阿里巴巴使用量子计算来保护支付宝的金融交易

阿里巴巴拥有自己的 11 量子比特的量子系统和模拟器,这包括一个 32 量子比特的服务,并通过与中国科学院的合作演示了一个 64 量子比特的模拟机。

据他们发表的一篇论文,利用量子模拟平台,阿里巴巴使用自己的云计算在后处理阶段提供了四种不同类型生成器的随机数生成。这项量子安全工作已经在生产环境运行了一年多。这听起来可能是一个简单的功能,但它是安全金融交易的关键。与过去的方法相比,量子随机数生成已被证明是最安全的加密方法。

没想到阿里巴巴已经不声不响地将量子计算用在了保障金融安全上了。

微软的内部 Linux 发行版 CBL-Mariner 发布 1.0 稳定版

微软创建的 Linux 发行版除了 Azure Cloud Switch 以外,还有一个 CBL-Mariner(CBL 是 “Common Base Linux”的缩写),这是一个内部发行版,但也是公开和开源的。CBL-Mariner 除了一些预编译的 RPM,没有提供官方的构建版本。它的目标不是成为一个通用的 Linux 发行版,而是让微软的各个工程团队可以用于他们不同的使用情况。它被用于 WSL、Azure Sphere OS、SONiC 以及微软其他基于 Linux 的工作。CBL-Mariner 以安全为重点,利用 RPM 与 DNF 进行软件包管理。

上周,经过几个月的迭代,CBL-Mariner 发布了 1.0 稳定版

我觉得微软不会发行一个正式的 Linux 发行版吧?不过也难说。

谷歌的证书授权服务正式可用

谷歌的证书授权服务(CAS)是一项可扩展的服务,用于通过自动化管理和部署私人证书,以及管理公钥基础设施(PKI)。谷歌云 CAS 提供了一个高度可扩展和可用的私有 CA。除了身份管理、加强数据传输的安全性和创建数字签名服务之外,谷歌还将 CAS 用在物联网中作为“随用随付”解决方案。

但是我还是喜欢 Let's Encrypt 的免费证书。

抛开关于是否使用 JSON 作为配置格式的争论,只需学习如何用 Groovy 来解析它。

 title=

应用程序通常包括某种类型的默认或“开箱即用”的状态或配置,以及某种让用户根据自己的需要定制配置的方式。

例如,LibreOffice Writer 通过其菜单栏上的工具 > 选项,可以访问诸如用户数据、字体、语言设置等(以及更多的)设置。一些应用程序(如 LibreOffice)提供了一个点选式的用户界面来管理这些设置。有些,像 Tracker(GNOME 的“任务”,用于索引文件)使用 XML 文件。还有一些,特别是基于 JavaScript 的应用,使用 JSON,尽管它有许多人抗议(例如,这位作者这位其他作者)。

在这篇文章中,我将回避关于是否使用 JSON 作为配置文件格式的争论,并解释如何使用 Groovy 编程语言 来解析这类信息。Groovy 以 Java 为基础,但有一套不同的设计重点,使 Groovy 感觉更像 Python。

安装 Groovy

由于 Groovy 是基于 Java 的,它也需要安装 Java。你可能会在你的 Linux 发行版的软件库中找到最近的、合适的 Java 和 Groovy 版本。或者,你可以按照其网站上的 说明 安装 Groovy。 Linux 用户的一个不错的选择是 SDKMan,你可以使用它来获取 Java、Groovy 和许多其他相关工具的多个版本。 对于本文,我将使用我的发行版的 OpenJDK11 和 SDKMan 的 Groovy 3.0.7。

演示的 JSON 配置文件

在这个演示中,我从 Drupal 中截取了这个 JSON 文件,它是 Drupal CMS 使用的主要配置文件,并将其保存在文件 config.json 中:

{
 "vm": {
  "ip": "192.168.44.44",
  "memory": "1024",
  "synced_folders": [
   {
    "host_path": "data/",
    "guest_path": "/var/www",
    "type": "default"
   }
  ],
  "forwarded_ports": []
 },
 "vdd": {
  "sites": {
   "drupal8": {
    "account_name": "root",
    "account_pass": "root",
    "account_mail": "[email protected]",
    "site_name": "Drupal 8",
    "site_mail": "[email protected]",
    "vhost": {
     "document_root": "drupal8",
     "url": "drupal8.dev",
     "alias": ["www.drupal8.dev"]
    }
   },
   "drupal7": {
    "account_name": "root",
    "account_pass": "root",
    "account_mail": "[email protected]",
    "site_name": "Drupal 7",
    "site_mail": "[email protected]",
    "vhost": {
     "document_root": "drupal7",
     "url": "drupal7.dev",
     "alias": ["www.drupal7.dev"]
    }
   }
  }
 }
}

这是一个漂亮的、复杂的 JSON 文件,有几层结构,如:

<>.vdd.sites.drupal8.account_name

和一些列表,如:

<>.vm.synced_folders

这里,<> 代表未命名的顶层。让我们看看 Groovy 是如何处理的。

用 Groovy 解析 JSON

Groovy 自带的 groovy.json 包,里面有各种很酷的东西。其中最好的部分是 JsonSlurper 类,它包括几个 parse() 方法,可以将 JSON 转换为 Groovy 的 Map,一种根据键值存储的数据结构。

下面是一个简短的 Groovy 程序,名为 config1.groovy,它创建了一个 JsonSlurper 实例,然后调用其中的 parse() 方法来解析文件中的 JSON,并将其转换名为 configMap 实例,最后将该 map 输出:

import groovy.json.JsonSlurper

def jsonSlurper = new JsonSlurper()

def config = jsonSlurper.parse(new File('config.json'))

println "config = $config"

在终端的命令行上运行这个程序:

$ groovy config1.groovy
config = [vm:[ip:192.168.44.44, memory:1024, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 8, site_mail:[email protected], vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 7, site_mail:[email protected], vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
$

输出显示了一个有两个键的顶层映射:vmvdd。每个键都引用了它自己的值的映射。注意 forwarded_ports 键所引用的空列表。

这很容易,但它所做的只是把东西打印出来。你是如何获得各种组件的呢?下面是另一个程序,显示如何访问存储在 config.vm.ip 的值:

import groovy.json.JsonSlurper

def jsonSlurper = new JsonSlurper()

def config = jsonSlurper.parse(new File('config.json'))

println "config.vm.ip = ${config.vm.ip}"

运行它:

$ groovy config2.groovy
config.vm.ip = 192.168.44.44
$

是的,这也很容易。 这利用了 Groovy 速记,这意味着:

config.vm.ip

在 Groovy 中等同于:

config['vm']['ip']

configconfig.vm 都是 Map 的实例,并且都等同于在 Java 中的:

config.get("vm").get("ip")

仅仅是处理 JSON 就这么多了。如果你想有一个标准的配置并让用户覆盖它呢?在这种情况下,你可能想在程序中硬编码一个 JSON 配置,然后读取用户配置并覆盖任何标准配置的设置。

假设上面的配置是标准的,而用户只想覆盖其中的一点,只想覆盖 vm 结构中的 ipmemory 值,并把它放在 userConfig.json 文件中:

{
 "vm": {
  "ip": "201.201.201.201",
  "memory": "4096",
 }
}

你可以用这个程序来做:

import groovy.json.JsonSlurper

def jsonSlurper = new JsonSlurper()

// 使用 parseText() 来解析一个字符串,而不是从文件中读取。
// 这给了我们一个“标准配置”
def standardConfig = jsonSlurper.parseText("""
{
 "vm": {
  "ip": "192.168.44.44",
  "memory": "1024",
  "synced_folders": [
   {
    "host_path": "data/",
    "guest_path": "/var/www",
    "type": "default"
   }
  ],
  "forwarded_ports": []
 },
 "vdd": {
  "sites": {
   "drupal8": {
    "account_name": "root",
    "account_pass": "root",
    "account_mail": "[email protected]",
    "site_name": "Drupal 8",
    "site_mail": "[email protected]",
    "vhost": {
     "document_root": "drupal8",
     "url": "drupal8.dev",
     "alias": ["www.drupal8.dev"]
    }
   },
   "drupal7": {
    "account_name": "root",
    "account_pass": "root",
    "account_mail": "[email protected]",
    "site_name": "Drupal 7",
    "site_mail": "[email protected]",
    "vhost": {
     "document_root": "drupal7",
     "url": "drupal7.dev",
     "alias": ["www.drupal7.dev"]
    }
   }
  }
 }
}
""")

// 打印标准配置
println "standardConfig = $standardConfig"

//读入并解析用户配置信息
def userConfig = jsonSlurper.parse(new File('userConfig.json'))

// 打印出用户配置信息
println "userConfig = $userConfig"

// 一个将用户配置与标准配置合并的函数
def mergeMaps(Map input, Map merge) {
  merge.each { k, v -&gt;
    if (v instanceof Map)
      mergeMaps(input[k], v)
    else
      input[k] = v
  }
}

// 合并配置并打印出修改后的标准配置
mergeMaps(standardConfig, userConfig)

println "modified standardConfig $standardConfig"

以下列方式运行:

$ groovy config3.groovy
standardConfig = [vm:[ip:192.168.44.44, memory:1024, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 8, site_mail:[email protected], vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 7, site_mail:[email protected], vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
userConfig = [vm:[ip:201.201.201.201, memory:4096]]
modified standardConfig [vm:[ip:201.201.201.201, memory:4096, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 8, site_mail:[email protected], vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:[email protected], site_name:Drupal 7, site_mail:[email protected], vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
$

modified standardConfig 开头的一行显示,vm.ip and vm.memory 的值被覆盖了。

眼尖的读者会注意到,我没有检查畸形的 JSON,也没有仔细确保用户的配置是有意义的(不创建新字段,提供合理的值,等等)。所以用这个递归方法来合并两个映射在现实中可能并不那么实用。

好吧,我必须为家庭作业留下 一些 东西,不是吗?

Groovy 资源

Apache Groovy 网站有很多很棒的 文档。另一个很棒的 Groovy 资源是 Mr. Haki。学习 Groovy 的一个非常好的理由是继续学习 Grails,它是一个非常高效的全栈 Web 框架,建立在 Hibernate、Spring Boot 和 Micronaut 等优秀组件之上。


via: https://opensource.com/article/21/6/groovy-parse-json

作者:Chris Hermansen 选题:lujun9972 译者:geekpi 校对:wxy

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