2018年5月

前一段时间,我们发布了一个使用 pip 管理 Python 包的指南。今天,我们将讨论如何使用 npm 管理 NodeJS 包。npm 是最大的软件注册中心,包含 600,000 多个包。每天,世界各地的开发人员通过 npm 共享和下载软件包。在本指南中,我将解释使用 npm 基础知识,例如安装包(本地和全局)、安装特定版本的包、更新、删除和管理 NodeJS 包等等。

安装 npm

用于 npm 是用 NodeJS 编写的,我们需要安装 NodeJS 才能使用 npm。要在不同的 Linux 发行版上安装 NodeJS,请参考下面的链接。

检查 node 安装的位置:

$ which node
/home/sk/.nvm/versions/node/v9.4.0/bin/node

检查它的版本:

$ node -v
v9.4.0

进入 Node 交互式解释器:

$ node
> .help
.break Sometimes you get stuck, this gets you out
.clear Alias for .break
.editor Enter editor mode
.exit Exit the repl
.help Print this help message
.load Load JS from a file into the REPL session
.save Save all evaluated commands in this REPL session to a file
> .exit

检查 npm 安装的位置:

$ which npm
/home/sk/.nvm/versions/node/v9.4.0/bin/npm

还有版本:

$ npm -v
5.6.0

棒极了!Node 和 npm 已安装好!正如你可能已经注意到,我已经在我的 $HOME 目录中安装了 NodeJS 和 NPM,这样是为了避免在全局模块时出现权限问题。这是 NodeJS 团队推荐的方法。

那么,让我们继续看看如何使用 npm 管理 NodeJS 模块(或包)。

安装 NodeJS 模块

NodeJS 模块可以安装在本地或全局(系统范围)。现在我将演示如何在本地安装包(LCTT 译注:即将包安装到一个 NodeJS 项目当中,所以下面会先创建一个空项目做演示)。

在本地安装包

为了在本地管理包,我们通常使用 package.json 文件来管理。

首先,让我们创建我们的项目目录。

$ mkdir demo
$ cd demo

在项目目录中创建一个 package.json 文件。为此,运行:

$ npm init

输入你的包的详细信息,例如名称、版本、作者、GitHub 页面等等,或者按下回车键接受默认值并键入 yes 确认。

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (demo)
version: (1.0.0)
description: demo nodejs app
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /home/sk/demo/package.json:

{
 "name": "demo",
 "version": "1.0.0",
 "description": "demo nodejs app",
 "main": "index.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC"
}

Is this ok? (yes) yes

上面的命令初始化你的项目并创建了 package.json 文件。

你也可以使用命令以非交互式方式执行此操作:

npm init --y

现在让我们安装名为 commander 的包。

$ npm install commander

示例输出:

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN [email protected] No repository field.

+ [email protected]
added 1 package in 2.519s

这将在项目的根目录中创建一个名为 node_modules 的目录(如果它不存在的话),并在其中下载包。

让我们检查 pachage.json 文件。

$ cat package.json 
{
 "name": "demo",
 "version": "1.0.0",
 "description": "demo nodejs app",
 "main": "index.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC",
 "dependencies": {
 "commander": "^2.13.0"
 }
}

你会看到添加了依赖文件,版本号前面的插入符号 ( ^ ) 表示在安装时,npm 将取出它可以找到的最高版本的包。

$ ls node_modules/
commander

package.json 文件的优点是,如果你的项目目录中有 package.json 文件,只需键入 npm install,那么 npm 将查看文件中列出的依赖关系并下载它们。你甚至可以与其他开发人员共享它或将其推送到你的 GitHub 仓库。因此,当他们键入 npm install 时,他们将获得你拥有的所有相同的包。

你也可能会注意到另一个名为 package-lock.json 的文件,该文件确保在项目安装的所有系统上都保持相同的依赖关系。

要在你的程序中使用已安装的包,使用实际代码在项目目录中创建一个 index.js(或者其他任何名称)文件,然后使用以下命令运行它:

$ node index.js

在全局安装包

如果你想使用一个包作为命令行工具,那么最好在全局安装它。这样,无论你的当前目录是哪个目录,它都能正常工作。

$ npm install async -g
+ [email protected]
added 2 packages in 4.695s

或者

$ npm install async --global

要安装特定版本的包,我们可以:

$ npm install [email protected] --global

更新 NodeJS 模块

要更新本地包,转到 package.json 所在的项目目录并运行:

$ npm update

然后,运行以下命令确保所有包都更新了。

$ npm outdated

如果没有需要更新的,那么它返回空。

要找出哪一个全局包需要更新,运行:

$ npm outdated -g --depth=0

如果没有输出,意味着所有包都已更新。

更新单个全局包,运行:

$ npm update -g <package-name>

更新所有的全局包,运行:

$ npm update -g

列出 NodeJS 模块

列出本地包,转到项目目录并运行:

$ npm list
[email protected] /home/sk/demo
└── [email protected]

如你所见,我在本地安装了 commander 这个包。

要列出全局包,从任何位置都可以运行以下命令:

$ npm list -g

示例输出:

/home/sk/.nvm/versions/node/v9.4.0/lib
├─┬ [email protected]
│ └── [email protected]
└─┬ [email protected]
 ├── [email protected]
 ├── [email protected]
 ├── [email protected]
 ├── [email protected]
 ├── [email protected]
 ├── [email protected]
[...]

该命令将列出所有模块及其依赖关系。

要仅仅列出顶级模块,使用 -depth=0 选项:

$ npm list -g --depth=0
/home/sk/.nvm/versions/node/v9.4.0/lib
├── [email protected]
└── [email protected]

寻找 NodeJS 模块

要搜索一个模块,使用 npm search 命令:

npm search <search-string>

例如:

$ npm search request

该命令将显示包含搜索字符串 request 的所有模块。

移除 NodeJS 模块

要删除本地包,转到项目目录并运行以下命令,这会从 node_modules 目录中删除包:

$ npm uninstall <package-name>

要从 package.json 文件中的依赖关系中删除它,使用如下所示的 save 选项:

$ npm uninstall --save <package-name>

要删除已安装的全局包,运行:

$ npm uninstall -g <package>

清除 npm 缓存

默认情况下,npm 在安装包时,会将其副本保存在 $HOME 目录中名为 .npm 的缓存文件夹中。所以,你可以在下次安装时不必再次下载。

查看缓存模块:

$ ls ~/.npm

随着时间的推移,缓存文件夹会充斥着大量旧的包。所以不时清理缓存会好一些。

从 npm@5 开始,npm 缓存可以从 corruption 问题中自行修复,并且保证从缓存中提取的数据有效。如果你想确保一切都一致,运行:

$ npm cache verify

清除整个缓存,运行:

$ npm cache clean --force

查看 npm 配置

要查看 npm 配置,键入:

$ npm config list

或者:

$ npm config ls

示例输出:

; cli configs
metrics-registry = "https://registry.npmjs.org/"
scope = ""
user-agent = "npm/5.6.0 node/v9.4.0 linux x64"

; node bin location = /home/sk/.nvm/versions/node/v9.4.0/bin/node
; cwd = /home/sk
; HOME = /home/sk
; "npm config ls -l" to show all defaults.

要显示当前的全局位置:

$ npm config get prefix
/home/sk/.nvm/versions/node/v9.4.0

好吧,这就是全部了。我们刚才介绍的只是基础知识,npm 是一个广泛话题。有关更多详细信息,参阅 NPM Getting Started 指南。

希望这对你有帮助。更多好东西即将来临,敬请关注!

干杯!


via: https://www.ostechnix.com/manage-nodejs-packages-using-npm/

作者:SK 译者:MjSeven 校对:wxy

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

保持对你的工作小时数的追踪将让你知晓在一个特定时间区间内你所完成的工作总量。在网络上有大量的基于 GUI 的生产力工具可以用来追踪工作小时数。但我却不能找到一个基于 CLI 的工具。今天我偶然发现了一个简单而奏效的叫做 Moro 的追踪工作时间数的工具。Moro 是一个芬兰词汇,意为“Hello”。通过使用 Moro,你可以找到你在完成某项特定任务时花费了多少时间。这个工具是自由开源软件,它是通过 NodeJS 编写的。

Moro - 一个追踪工作时间的命令行生产力工具

由于 Moro 是使用 NodeJS 编写的,保证你的系统上已经安装了 NodeJS。如果你没有安装好 NodeJS,跟随下面的链接在你的 Linux 中安装 NodeJS 和 NPM。

NodeJS 和NPM一旦装好,运行下面的命令来安装 Moro。

$ npm install -g moro

用法

Moro 的工作概念非常简单。它记录了你的工作开始时间,结束时间和在你的系统上的休息时间。在每天结束时,它将会告知你已经工作了多少时间。

当你到达办公室时,只需键入:

$ moro

示例输出:

 Moro \o/
 You clocked in at: 9:20

Moro 将会把这个时间注册为你的开始时间。

当你离开办公室时,再次键入:

$ moro

示例输出:

 Moro \o/
 You clocked out at: 19:22
 Today looks like this so far:
┌──────────────────┬─────────────────────────┐
│ Today you worked │ 9 Hours and 72 Minutes │
├──────────────────┼─────────────────────────┤
│ Clock in │ 9:20 │
├──────────────────┼─────────────────────────┤
│ Clock out │ 19:22 │
├──────────────────┼─────────────────────────┤
│ Break duration │ 30 minutes │
├──────────────────┼─────────────────────────┤
│ Date │ 2018-03-19 │
└──────────────────┴─────────────────────────┘
 Run moro --help to learn how to edit your clock in, clock out or break duration for today

Moro 将会把这个时间注册为你的结束时间。

现在,Moro 将会从结束时间减去开始时间,然后从总的时间减去另外的 30 分钟作为休息时间,并给你在那天总的工作时间。抱歉,我的数学计算过程解释实在糟糕。假设你在早上 10:00 来工作并在晚上 17:30 离开。所以,你总共在办公室呆了 7:30 小时(例如 17:30-10)。然后在总的时间减去休息时间(默认是 30 分钟)。因此,你的总工作时间是 7 小时。明白了?很好!

注意:不要像我在写这个手册的时候一样把 “moro” 和 “more” 弄混了。

查看你注册的所有小时数,运行:

$ moro report --all

以防万一,如果你忘记注册开始时间或者结束时间,你一样可以在之后指定这些值。

例如,将上午 10 点注册为开始时间,运行:

$ moro hi 10:00
 Moro \o/
 You clocked in at: 10:00
 Working until 18:00 will make it a full (7.5 hours) day

注册 17:30 作为结束时间:

$ moro bye 17:30
 Moro \o/
 You clocked out at: 17:30
 Today looks like this so far:

┌──────────────────┬───────────────────────┐
│ Today you worked │ 7 Hours and 0 Minutes │
├──────────────────┼───────────────────────┤
│ Clock in │ 10:00 │
├──────────────────┼───────────────────────┤
│ Clock out │ 17:30 │
├──────────────────┼───────────────────────┤
│ Break duration │ 30 minutes │
├──────────────────┼───────────────────────┤
│ Date │ 2018-03-19 │
└──────────────────┴───────────────────────┘
 Run moro --help to learn how to edit your clock in, clock out or break duration for today

你已经知道 Moro 默认将会减去 30 分钟的休息时间。如果你需要设置一个自定义的休息时间,你可以简单使用以下命令:

$ moro break 45

现在,休息时间是 45 分钟了。

若要清除所有的数据:

$ moro clear --yes
 Moro \o/
 Database file deleted successfully

添加笔记

有时候,你想要在工作时添加笔记。不必去寻找一个独立的作笔记的应用。Moro 将会帮助你添加笔记。要添加笔记,只需运行:

$ moro note mynotes

要在之后搜索所有已经注册的笔记,只需做:

$ moro search mynotes

修改默认设置

默认的完整工作时间是 7.5 小时。这是因为开发者来自芬兰,这是官方的工作小时数。但是你也可以修改这个设置为你的国家的工作小时数。

举个例子,要将其设置为 7 小时,运行:

$ moro config --day 7

同样地,默认的休息时间也可以像下面这样从 30 分钟修改:

$ moro config --break 45

备份你的数据

正如我已经说了的,Moro 将时间追踪信息存储在你的家目录,文件名是 .moro-data.db

但是,你可以保存备份数据库到不同的位置。要这样做的话,像下面这样将 .moro-data.db 文件移到你选择的一个不同的位置并告知 Moro 使用那个数据库文件。

$ moro config --database-path /home/sk/personal/moro-data.db

在上面的每一个命令,我都已经把默认的数据库文件分配到了 /home/sk/personal 目录。

需要帮助的话,运行:

$ moro --help

正如你所见,Moro 是非常简单而又能用于追踪你完成你的工作使用了多少时间的。对于自由职业者和任何想要在一定时间范围内完成事情的人,它将会是有用的。

并且,这些只是今天的内容。希望这些内容能够有所帮助。更多的好东西将会出现。请保持关注!

干杯!


via: https://www.ostechnix.com/moro-a-command-line-productivity-tool-for-tracking-work-hours/

作者:SK 译者:leemeans 校对:wxy

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

你已经知道 top 命令,对么?是的,它提供类 Unix 操作系统中运行中的进程的动态实时信息。一些开发人员为 top 命令构建了图形前端,因此用户可以在图形窗口中轻松找到他们系统的活动。其中之一是 Gotop。顾名思义,Gotop 是一个 TUI 图形活动监视器,使用 Go 语言编写。它是完全免费、开源的,受到了 gtopvtop 的启发。

在此简要的指南中,我们将讨论如何安装和使用 Gotop 来监视 Linux 系统的活动。

安装 Gotop

Gotop 是用 Go 编写的,所以我们需要先安装它。要在 Linux 中安装 Go 语言,请参阅以下指南。

安装 Go 之后,使用以下命令下载最新的 Gotop 二进制文件。

$ sh -c "$(curl https://raw.githubusercontent.com/cjbassi/gotop/master/download.sh)"

然后,将下载的二进制文件移动到您的 $PATH 中,例如 /usr/local/bin/

$ cp gotop /usr/local/bin

最后,用下面的命令使其可执行:

$ chmod +x /usr/local/bin/gotop

如果你使用的是基于 Arch 的系统,Gotop 存在于 AUR 中,所以你可以使用任何 AUR 助手程序进行安装。

使用 Cower

$ cower -S gotop

使用 Pacaur

$ pacaur -S gotop

使用 Packer

$ packer -S gotop

使用 Trizen

$ trizen -S gotop

使用 Yay

$ yay -S gotop

使用 yaourt

$ yaourt -S gotop

用法

Gotop 的使用非常简单!你所要做的就是从终端运行以下命令。

$ gotop

这样就行了!你将在简单的 TUI 窗口中看到系统 CPU、磁盘、内存、网络、CPU温度和进程列表的使用情况。

要仅显示CPU、内存和进程组件,请使用下面的 -m 标志:

$ gotop -m

你可以使用以下键盘快捷键对进程表进行排序。

  • c – CPU
  • m – 内存
  • p – PID

对于进程浏览,请使用以下键。

  • 上/下 箭头或者 j/k 键用于上移下移。
  • Ctrl-dCtrl-u – 上移和下移半页。
  • Ctrl-fCtrl-b – 上移和下移整页。
  • ggG – 跳转顶部和底部。

按下 TAB 切换进程分组。要杀死选定的进程或进程组,请输入 dd。要选择一个进程,只需点击它。要向下/向上滚动,请使用鼠标滚动按钮。要放大和缩小 CPU 和内存的图形,请使用 hl。要显示帮助菜单,只需按 ?

就是这些了。希望这有帮助。还有更多好东西。敬请关注!

资源


via: https://www.ostechnix.com/gotop-yet-another-tui-graphical-activity-monitor-written-in-go/

作者:SK 选题:lujun9972 译者:geekpi 校对:wxy

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

用这些 ImageMagick 命令行图像编辑应用的技巧更好的管理你的数码照片集。

在我先前的ImageMagick 入门:使用命令行来编辑图片 文章中,我展示了如何使用 ImageMagick 的菜单栏进行图片的编辑和变换风格。在这篇续文里,我将向你展示使用这个开源的图像编辑器来查看图片的另外方法。

别样的风格

在深入 ImageMagick 的高级图片查看技巧之前,我想先分享另一个使用 convert 达到的有趣但简单的效果,在上一篇文章中我已经详细地介绍了 convert 命令,这个技巧涉及这个命令的 edgenegate 选项:

convert DSC_0027.JPG -edge 3 -negate edge3+negate.jpg

 title=

使用edgenegate 选项前后的图片对比

这些使我更喜爱编辑后的图片:海的外观,作为前景和背景的植被,特别是太阳及其在海上的反射,最后是天空。

使用 display 来查看一系列图片

假如你跟我一样是个命令行用户,你就知道 shell 为复杂任务提供了更多的灵活性和快捷方法。下面我将展示一个例子来佐证这个观点。ImageMagick 的 display 命令可以克服我在 GNOME 桌面上使用 Shotwell 图像管理器导入图片时遇到的问题。

Shotwell 会根据每张导入图片的 Exif 数据,创建以图片被生成或者拍摄时的日期为名称的目录结构。最终的效果是最上层的目录以年命名,接着的子目录是以月命名 (01、 02、 03 等等),然后是以每月的日期命名的子目录。我喜欢这种结构,因为当我想根据图片被创建或者拍摄时的日期来查找它们时将会非常方便。

但这种结构也并不是非常完美的,当我想查看最近几个月或者最近一年的所有图片时就会很麻烦。使用常规的图片查看器,我将不停地在不同层级的目录间跳转,但 ImageMagick 的 display 命令可以使得查看更加简单。例如,假如我想查看最近一年的图片,我便可以在命令行中键入下面的 display 命令:

display -resize 35% 2017/*/*/*.JPG

我可以一个月又一个月,一天又一天地遍历这一年。

现在假如我想查看某张图片,但我不确定我是在 2016 年的上半年还是在 2017 的上半年拍摄的,那么我便可以使用下面的命令来找到它:

display -resize 35% 201[6-7]/0[1-6]/*/*.JPG

这限制查看的图片拍摄于 2016 和 2017 年的一月到六月

使用 montage 来查看图片的缩略图

假如现在我要查找一张我想要编辑的图片,使用 display 的一个问题是它只会显示每张图片的文件名,而不显示其在目录结构中的位置,所以想要找到那张图片并不容易。另外,假如我很偶然地在从相机下载图片的过程中将这些图片从相机的内存里面清除了它们,结果使得下次拍摄照片的名称又从 DSC_0001.jpg 开始命名,那么当使用 display 来展示一整年的图片时,将会在这 12 个月的图片中花费很长的时间来查找它们。

这时 montage 命令便可以派上用场了。它可以将一系列的图片缩略图放在一张图片中,这样就会非常有用。例如可以使用下面的命令来完成上面的任务:

montage -label %d/%f -title 2017 -tile 5x -resize 10% -geometry +4+4 2017/0[1-4]/*/*.JPG 2017JanApr.jpg

从左到右,这个命令以标签开头,标签的形式是包含文件名(%f)和以 / 分割的目录(%d)结构,接着这个命令以目录的名称(2017)来作为标题,然后将图片排成 5 列,每个图片缩放为 10% (这个参数可以很好地匹配我的屏幕)。geometry 的设定将在每张图片的四周留白,最后指定那些图片要包括到这张合成图片中,以及一个合适的文件名称(2017JanApr.jpg)。现在图片 2017JanApr.jpg 便可以成为一个索引,使得我可以不时地使用它来查看这个时期的所有图片。

注意内存消耗

你可能会好奇为什么我在上面的合成图中只特别指定了为期 4 个月(从一月到四月)的图片。因为 montage 将会消耗大量内存,所以你需要多加注意。我的相机产生的图片每张大约有 2.5MB,我发现我的系统可以很轻松地处理 60 张图片。但一旦图片增加到 80 张,如果此时还有另外的程序(例如 Firefox 、Thunderbird)在后台工作,那么我的电脑将会死机,这似乎和内存使用相关,montage可能会占用可用 RAM 的 80% 乃至更多(你可以在此期间运行 top 命令来查看内存占用)。假如我关掉其他的程序,我便可以在我的系统死机前处理 80 张图片。

下面的命令可以让你知晓在你运行 montage 命令前你需要处理图片张数:

ls 2017/0[1-4/*/*.JPG > filelist; wc -l filelist

ls 命令生成我们搜索的文件的列表,然后通过重定向将这个列表保存在任意以名为 filelist 的文件中。接着带有 -l 选项的 wc 命令输出该列表文件共有多少行,换句话说,展示出了需要处理的文件个数。下面是我运行命令后的输出:

163 filelist

啊呀!从一月到四月我居然有 163 张图片,使用这些图片来创建一张合成图一定会使得我的系统死机的。我需要将这个列表减少点,可能只处理到 3 月份或者更早的图片。但如果我在 4 月 20 号到 30 号期间拍摄了很多照片,我想这便是问题的所在。下面的命令便可以帮助指出这个问题:

ls 2017/0[1-3]/*/*.JPG > filelist; ls 2017/04/0[1-9]/*.JPG >> filelist; ls 2017/04/1[0-9]/*.JPG >> filelist; wc -l filelist

上面一行中共有 4 个命令,它们以分号分隔。第一个命令特别指定从一月到三月期间拍摄的照片;第二个命令使用 >> 将拍摄于 4 月 1 日至 9 日的照片追加到这个列表文件中;第三个命令将拍摄于 4 月 10 日到 19 日的照片追加到列表中。最终它的显示结果为:

81 filelist

我知道假如我关掉其他的程序,处理 81 张图片是可行的。

使用 montage 来处理它们是很简单的,因为我们只需要将上面所做的处理添加到 montage 命令的后面即可:

montage -label %d/%f -title 2017 -tile 5x -resize 10% -geometry +4+4 2017/0[1-3]/*/*.JPG 2017/04/0[1-9]/*.JPG 2017/04/1[0-9]/*.JPG 2017Jan01Apr19.jpg

从左到右,montage 命令后面最后的那个文件名将会作为输出,在它之前的都是输入。这个命令将花费大约 3 分钟来运行,并生成一张大小约为 2.5MB 的图片,但我的系统只是有一点反应迟钝而已。

展示合成图片

当你第一次使用 display 查看一张巨大的合成图片时,你将看到合成图的宽度很合适,但图片的高度被压缩了,以便和屏幕相适应。不要慌,只需要左击图片,然后选择 View > Original Size 便会显示整个图片。再次点击图片便可以使菜单栏隐藏。

我希望这篇文章可以在你使用新方法查看图片时帮助你。在我的下一篇文章中,我将讨论更加复杂的图片操作技巧。

作者简介

Greg Pittman - Greg 肯塔基州路易斯维尔的一名退休的神经科医生,对计算机和程序设计有着长期的兴趣,最早可以追溯到 1960 年代的 Fortran IV 。当 Linux 和开源软件相继出现时,他开始学习更多的相关知识,并分享自己的心得。他是 Scribus 团队的成员。


via: https://opensource.com/article/17/9/imagemagick-viewing-images

作者:Greg Pittman 译者:FSSlc 校对:wxy

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

学习用简单的宏为你的课程论文添加脚注、引用、子标题及其它格式。

当我在 1993 年发现 Linux 时,我还是一名本科生。我很兴奋在我的宿舍里拥有 Unix 系统的强大功能,但是尽管它有很多功能,但 Linux 却缺乏应用程序。像 LibreOffice 和 OpenOffice 这样的文字处理程序还需要几年的时间才出现。如果你想使用文字处理器,你可能会将你的系统引导到 MS-DOS 中,并使用 WordPerfect、共享软件 GalaxyWrite 或类似的程序。

这就是我的方法,因为我需要为我的课程写论文,但我更喜欢呆在 Linux 中。我从我们的 “大 Unix” 校园计算机实验室得知,Unix 系统提供了一组文本格式化的程序 nrofftroff ,它们是同一系统的不同接口:nroff 生成纯文本输出,适用于屏幕或行式打印机,而 troff 产生非常优美的输出,通常用于在激光打印机上打印。

在 Linux 上,nrofftroff 被合并为 GNU troff,通常被称为 groff。 我很高兴看到早期的 Linux 发行版中包含了某个版本的 groff,因此我着手学习如何使用它来编写课程论文。 我学到的第一个宏集是 -me 宏包,一个简单易学的宏集。

关于 groff ,首先要了解的是它根据一组宏来处理和格式化文本。宏通常是个两个字符的命令,它自己设置在一行上,并带有一个引导点。宏可能包含一个或多个选项。当 groff 在处理文档时遇到这些宏中的一个时,它会自动对文本进行格式化。

下面,我将分享使用 groff -me 编写课程论文等简单文档的基础知识。 我不会深入细节进行讨论,比如如何创建嵌套列表,保存和显示,以及使用表格和数字。

段落

让我们从一个简单的例子开始,在几乎所有类型的文档中都可以看到:段落。段落可以格式化为首行缩进或不缩进(即,与左边齐平)。 包括学术论文,杂志,期刊和书籍在内的许多印刷文档都使用了这两种类型的组合,其中文档或章节中的第一个(主要)段落左侧对齐,而所有其他(常规)的段落缩进。 在 groff -me中,您可以使用两种段落类型:前导段落(.lp)和常规段落(.pp)。

.lp
This is the first paragraph.
.pp
This is a standard paragraph.

文本格式

用粗体格式化文本的宏是 .b,斜体格式是 .i 。 如果您将 .b.i 放在一行上,则后面的所有文本将以粗体或斜体显示。 但更有可能你只是想用粗体或斜体来表示一个或几个词。 要将一个词加粗或斜体,将该单词放在与 .b.i 相同的行上作为选项。 要用粗体或斜体格式化多个单词,请将文字用引号引起来。

.pp
You can do basic formatting such as
.i italics
or
.b "bold text."

在上面的例子中,粗体文本结尾的句点也是粗体。 在大多数情况下,这不是你想要的。 只要文字是粗体字,而不是后面的句点也是粗体字。 要获得您想要的效果,您可以向 .b.i 添加第二个参数,以指示以粗体或斜体显示的文本后面跟着的任意文本以正常类型显示。 您可以这样做,以确保尾随句点不会以粗体显示。

.pp
You can do basic formatting such as
.i italics
or
.b "bold text" .

列表

使用 groff -me,您可以创建两种类型的列表:无序列表(.bu)和有序列表(.np)。

.pp
Bullet lists are easy to make:
.bu
Apple
.bu
Banana
.bu
Pineapple
.pp
Numbered lists are as easy as:
.np
One
.np
Two
.np
Three
.pp
Note that numbered lists will reset at the next pp or lp.

副标题

如果你正在写一篇长论文,你可能想把你的内容分成几部分。使用 groff -me,您可以创建编号的标题(.sh) 和未编号的标题 (.uh)。在这两种方法中,将节标题作为参数括起来。对于编号的标题,您还需要提供标题级别 :1 将给出一个一级标题(例如,1)。同样,23 将给出第二和第三级标题,如 2.13.1.1

.uh Introduction
.pp
Provide one or two paragraphs to describe the work
and why it is important.
.sh 1 "Method and Tools"
.pp
Provide a few paragraphs to describe how you
did the research, including what equipment you used

智能引号和块引号

在任何学术论文中,引用他人的工作作为证据都是正常的。如果你引用一个简短的引用来突出一个关键信息,你可以在你的文本周围键入引号。但是 groff 不会自动将你的引用转换成现代文字处理系统所使用的“智能”或“卷曲”引用。要在 groff -me 中创建它们,插入一个内联宏来创建左引号(\*(lq)和右引号(\*(rq)。

.pp
Christine Peterson coined the phrase \*(lqopen source.\*(rq

groff -me 中还有一个快捷方式来创建这些引号(.q),我发现它更易于使用。

.pp
Christine Peterson coined the phrase
.q "open source."

如果引用的是跨越几行的较长的引用,则需要使用一个块引用。为此,在引用的开头和结尾插入块引用宏(.(q)。

.pp
Christine Peterson recently wrote about open source:
.(q
On April 7, 1998, Tim O'Reilly held a meeting of key
leaders in the field. Announced in advance as the first
.q "Freeware Summit,"
by April 14 it was referred to as the first
.q "Open Source Summit."
.)q

脚注

要插入脚注,请在脚注文本前后添加脚注宏(.(f),并使用内联宏(\**)添加脚注标记。脚注标记应出现在文本中和脚注中。

.pp
Christine Peterson recently wrote about open source:\**
.(f
\**Christine Peterson.
.q "How I coined the term open source."
.i "OpenSource.com."
1 Feb 2018.
.)f
.(q
On April 7, 1998, Tim O'Reilly held a meeting of key
leaders in the field. Announced in advance as the first
.q "Freeware Summit,"
by April 14 it was referred to as the first
.q "Open Source Summit."
.)q

封面

大多数课程论文都需要一个包含论文标题,姓名和日期的封面。 在 groff -me 中创建封面需要一些组件。 我发现最简单的方法是使用居中的文本块并在标题、名字和日期之间添加额外的行。 (我倾向于在每一行之间使用两个空行)。在文章顶部,从标题页(.tp)宏开始,插入五个空白行(.sp 5),然后添加居中文本(.(c) 和额外的空白行(.sp 2)。

.tp
.sp 5
.(c
.b "Writing Class Papers with groff -me"
.)c
.sp 2
.(c
Jim Hall
.)c
.sp 2
.(c
February XX, 2018
.)c
.bp

最后一个宏(.bp)告诉 groff 在标题页后添加一个分页符。

更多内容

这些是用 groff-me 写一份专业的论文非常基础的东西,包括前导和缩进段落,粗体和斜体,有序和无需列表,编号和不编号的章节标题,块引用以及脚注。

我已经包含一个示例 groff 文件来演示所有这些格式。 将 lorem-ipsum.me 文件保存到您的系统并通过 groff 运行。 -Tps 选项将输出类型设置为 PostScript ,以便您可以将文档发送到打印机或使用 ps2pdf 程序将其转换为 PDF 文件

groff -Tps -me lorem-ipsum.me > lorem-ipsum.me.ps
ps2pdf lorem-ipsum.me.ps lorem-ipsum.me.pdf

如果你想使用 groff -me 的更多高级功能,请参阅 Eric Allman 所著的 “使用 Groff -me 来写论文”,你可以在你系统的 groff 的 doc 目录下找到一个名叫 meintro.me 的文件。这份文档非常完美的说明了如何使用 groff-me 宏来格式化你的论文。


via: https://opensource.com/article/18/2/how-format-academic-papers-linux-groff-me

作者:Jim Hall 译者:amwps290 校对:wxy

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

“方其梦也,不知其梦也。梦之中又占其梦焉,觉而后知其梦也。”

—— 《庄子·齐物论》

递归是很神奇的,但是在大多数的编程类书藉中对递归讲解的并不好。它们只是给你展示一个递归阶乘的实现,然后警告你递归运行的很慢,并且还有可能因为栈缓冲区溢出而崩溃。“你可以将头伸进微波炉中去烘干你的头发,但是需要警惕颅内高压并让你的头发生爆炸,或者你可以使用毛巾来擦干头发。”难怪人们不愿意使用递归。但这种建议是很糟糕的,因为在算法中,递归是一个非常强大的思想。

我们来看一下这个经典的递归阶乘:

#include <stdio.h>

int factorial(int n)
{
    int previous = 0xdeadbeef;

    if (n == 0 || n == 1) {
        return 1;
    }

    previous = factorial(n-1);
    return n * previous;
}

int main(int argc)
{
    int answer = factorial(5);
    printf("%d\n", answer);
}

递归阶乘 - factorial.c

函数调用自身的这个观点在一开始是让人很难理解的。为了让这个过程更形象具体,下图展示的是当调用 factorial(5) 并且达到 n == 1这行代码 时,栈上 端点的情况:

每次调用 factorial 都生成一个新的 栈帧。这些栈帧的创建和 销毁 是使得递归版本的阶乘慢于其相应的迭代版本的原因。在调用返回之前,累积的这些栈帧可能会耗尽栈空间,进而使你的程序崩溃。

而这些担心经常是存在于理论上的。例如,对于每个 factorial 的栈帧占用 16 字节(这可能取决于栈排列以及其它因素)。如果在你的电脑上运行着现代的 x86 的 Linux 内核,一般情况下你拥有 8 GB 的栈空间,因此,factorial 程序中的 n 最多可以达到 512,000 左右。这是一个 巨大无比的结果,它将花费 8,971,833 比特来表示这个结果,因此,栈空间根本就不是什么问题:一个极小的整数 —— 甚至是一个 64 位的整数 —— 在我们的栈空间被耗尽之前就早已经溢出了成千上万次了。

过一会儿我们再去看 CPU 的使用,现在,我们先从比特和字节回退一步,把递归看作一种通用技术。我们的阶乘算法可归结为:将整数 N、N-1、 … 1 推入到一个栈,然后将它们按相反的顺序相乘。实际上我们使用了程序调用栈来实现这一点,这是它的细节:我们在堆上分配一个栈并使用它。虽然调用栈具有特殊的特性,但是它也只是又一种数据结构而已,你可以随意使用。我希望这个示意图可以让你明白这一点。

当你将栈调用视为一种数据结构,有些事情将变得更加清晰明了:将那些整数堆积起来,然后再将它们相乘,这并不是一个好的想法。那是一种有缺陷的实现:就像你拿螺丝刀去钉钉子一样。相对更合理的是使用一个迭代过程去计算阶乘。

但是,螺丝钉太多了,我们只能挑一个。有一个经典的面试题,在迷宫里有一只老鼠,你必须帮助这只老鼠找到一个奶酪。假设老鼠能够在迷宫中向左或者向右转弯。你该怎么去建模来解决这个问题?

就像现实生活中的很多问题一样,你可以将这个老鼠找奶酪的问题简化为一个图,一个二叉树的每个结点代表在迷宫中的一个位置。然后你可以让老鼠在任何可能的地方都左转,而当它进入一个死胡同时,再回溯回去,再右转。这是一个老鼠行走的 迷宫示例:

每到边缘(线)都让老鼠左转或者右转来到达一个新的位置。如果向哪边转都被拦住,说明相关的边缘不存在。现在,我们来讨论一下!这个过程无论你是调用栈还是其它数据结构,它都离不开一个递归的过程。而使用调用栈是非常容易的:

#include <stdio.h>
#include "maze.h"

int explore(maze_t *node)
{
    int found = 0;

    if (node == NULL)
    {
        return 0;
    }
    if (node->hasCheese){
        return 1;// found cheese
        }

    found = explore(node->left) || explore(node->right);
    return found;
    }

    int main(int argc)
    {
        int found = explore(&maze);
    }

递归迷宫求解 下载

当我们在 maze.c:13 中找到奶酪时,栈的情况如下图所示。你也可以在 GDB 输出 中看到更详细的数据,它是使用 命令 采集的数据。

它展示了递归的良好表现,因为这是一个适合使用递归的问题。而且这并不奇怪:当涉及到算法时,递归是规则,而不是例外。它出现在如下情景中——进行搜索时、进行遍历树和其它数据结构时、进行解析时、需要排序时——它无处不在。正如众所周知的 pi 或者 e,它们在数学中像“神”一样的存在,因为它们是宇宙万物的基础,而递归也和它们一样:只是它存在于计算结构中。

Steven Skienna 的优秀著作 算法设计指南 的精彩之处在于,他通过 “战争故事” 作为手段来诠释工作,以此来展示解决现实世界中的问题背后的算法。这是我所知道的拓展你的算法知识的最佳资源。另一个读物是 McCarthy 的 关于 LISP 实现的的原创论文。递归在语言中既是它的名字也是它的基本原理。这篇论文既可读又有趣,在工作中能看到大师的作品是件让人兴奋的事情。

回到迷宫问题上。虽然它在这里很难离开递归,但是并不意味着必须通过调用栈的方式来实现。你可以使用像 RRLL 这样的字符串去跟踪转向,然后,依据这个字符串去决定老鼠下一步的动作。或者你可以分配一些其它的东西来记录追寻奶酪的整个状态。你仍然是实现了一个递归的过程,只是需要你实现一个自己的数据结构。

那样似乎更复杂一些,因为栈调用更合适。每个栈帧记录的不仅是当前节点,也记录那个节点上的计算状态(在这个案例中,我们是否只让它走左边,或者已经尝试向右)。因此,代码已经变得不重要了。然而,有时候我们因为害怕溢出和期望中的性能而放弃这种优秀的算法。那是很愚蠢的!

正如我们所见,栈空间是非常大的,在耗尽栈空间之前往往会遇到其它的限制。一方面可以通过检查问题大小来确保它能够被安全地处理。而对 CPU 的担心是由两个广为流传的有问题的示例所导致的: 哑阶乘 dumb factorial 和可怕的无记忆的 O( 2 n ) Fibonacci 递归。它们并不是栈递归算法的正确代表。

事实上栈操作是非常快的。通常,栈对数据的偏移是非常准确的,它在 缓存 中是热数据,并且是由专门的指令来操作它的。同时,使用你自己定义的在堆上分配的数据结构的相关开销是很大的。经常能看到人们写的一些比栈调用递归更复杂、性能更差的实现方法。最后,现代的 CPU 的性能都是 非常好的 ,并且一般 CPU 不会是性能瓶颈所在。在考虑牺牲程序的简单性时要特别注意,就像经常考虑程序的性能及性能的测量那样。

下一篇文章将是探秘栈系列的最后一篇了,我们将了解尾调用、闭包、以及其它相关概念。然后,我们就该深入我们的老朋友—— Linux 内核了。感谢你的阅读!


via:https://manybutfinite.com/post/recursion/

作者:Gustavo Duarte 译者:qhwdw 校对:FSSlc

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