分类目录归档:技术

在 Dokuwiki 中配置安全规则,来保护配置和数据文件

在 Dokuwiki 中配置安全规则,来保护配置和数据文件

由于 dokuwiki 的文件路径默认会放在 data/conf/bin 等几个目录当中,如果你不对相应的文件进行保护,如果某个人是了解 dokuwiki 的情况下,它可以越过你的 dokuwiki ,直接读取 wiki 的内容。

而你没有进行保护的时候,Dokuwiki也会在配置当中展示你的配置不安全。

想要开启 Nginx 的保护,你需要在 Nginx 的配置文件当中,添加如下代码。

 location ~ /(data|conf|bin|inc|vendor)/ {
      deny all;
 }

添加完成后,执行如下命令来重启 Nginx,使命令生效。

nginx -t 
nginx -s reload

生效成功后,再次刷新,就可以看到提示已经消失了。同时如果你直接访问 data 目录下的数据文件,也会直接报错。

Dokuwiki 配置 Rewrite 优化路径显示

Dokuwiki 配置 Rewrite 优化路径显示

Dokuwiki 在生成 URL 的时候,支持生成三种不同的 URL:

  • Rewrite 版: /wiki:welcome?do=admin&page=config
  • dokuwiki 控制版: /doku.php/start?do=admin&page=config
  • 默认版:/doku.php?id=start&do=admin&page=config

配置 Rewrite 可以让你的 wiki 的路径更加简单的和明确,屏蔽语言信息,因此,一般而言,都建议大家配置上对应的 rewrite 规则。

在 Nginx 你的 Host 配置下加入如下规则,来实现 Rewrite 的转发


    location / { try_files $uri $uri/ @dokuwiki; }
 
    location @dokuwiki {
        rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
        rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
        rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
        rewrite ^/(.*) /doku.php?id=$1&$args last;
    }

添加完成后可以执行如下命令来重启 Nginx

nginx -t 
nginx -s reload

再回到 Dokuwiki 后台的配置管理器,找到 userewrite 这一项配置,将其配置为使用.htaccess,并保存,即可将 dokuwiki 默认生成的 URL 变成一个更加干净的 URL。

Dokuwiki 忘记密码了怎么办?

Dokuwiki 忘记密码了怎么办?

我的个人 Wiki 系统是 Dokuwiki 。相比于别的 Wiki ,Dokuwiki 轻量的同时,功能齐全,帮助我在 TiddlyWiki 和 MediaWiki 之间找到了一个平衡。

我在使用 Tiddly Wiki 的时候,会遇到忘记密码的情况,这种情况下,就需要对 Dokuwiki 进行密码重置的操作。

如果你和我一样,关闭了 Dokuwiki 的任意人可上传,则需要通过修改具体的 auth 文件来完成。

Dokuwiki 的用户授权信息放置在 conf/users.auth.php 文件当中,你需要在服务器上打开这个文件,并在其中添加如下信息

deleteme:$1$4fd0ad31$.cId7p1uxI4a.RcrH81On0:-:-:admin,user 

添加完成后,你将会获得一个名为 deleteme,密码为admin 的用户,接下来你只需要使用这个用户登录到你的 Dokuwiki 当中,并在管理当中的「用户管理器」中修改之前的用户的账号密码。

在用户管理器当中可以修改对应的用户的密码。

修改完成后,使用之前的用户登录,并删除之前的用户即可。

使用 Sheetjs 将 JSON Array 转化为 Excel

使用 Sheetjs 将 JSON Array 转化为 Excel

使用 node-excel-stream 来按行处理 Excel 数据 中,我提到,如果你希望简单的完成 Excel 的读取和处理,那么 node-excel-stream 是个不错的选择。而反过来,如果你希望将 JSON Array 导出为 Excel,那么 Sheetjs 是个不错的选择。

注意

Sheetjs 和 exceljs 不同,区分了商业版和社区版。我们这里使用的是社区版 Sheetjs CE

用法

使用 Sheetjs 对数据进行导出时,你只需要调用 XLSX 方法当中的 json_to_sheet ,就可以将你的 JSON Array 变为一个 worksheet,接下来只需要将其放入一个新的 workbook 当中,并导出为文件,就可以完成 JS 数据导出为 Excel。

const XLSX = require("xlsx");

const data = [
  {
    ...
  },
  ...
]

const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "sheetNameIsFirst");
XLSX.writeFile(workbook, "output.xlsx");
使用 node-excel-stream 来按行处理 Excel 数据

使用 node-excel-stream 来按行处理 Excel 数据

数据分析是一个非常常见的需求,而在实际的落地场景当中, Python 是使用最多的。不过我因为写了很久的前端,其实对于Python已经生疏了。当我开始启动项目时,就会选择执行 npm init 来初始化一个项目。既然如此,就试着使用 Node.js 来做数据分析。

在 Node.js 当中操作 Excel ,最好的便是 Exceljs。不过 ExcelJs 封装了大量的函数,对于绝大多数的数据分析场景来说,可能并不适用(也不一定,只是我比较喜欢用代码来描述逻辑,Excel 更多是一个导入导出)。

当明确了我只是需要一个简单的导入导出后,那么 node-excel-stream 就进入了我的视野。

读取 Excel 内容

和 Exceljs 不同,node-excel-stream 的封装相对简单,就是一个 Reader 和 Writer ,提供的方法也十分简单:读取文件、定义格式,按行处理内容;

需要注意的是,node-excel-stream 只支持 xlsx ,而不支持 xls,所以如果你用的是旧版,则需要重新保存成 xlsx 来进行处理。

let dataStream = fs.createReadStream('data.xlsx');
let reader = new ExcelReader(dataStream, {
    sheets: [{
        name: 'Users',
        rows: {
            headerRow: 1,
            allowedHeaders: [{
                name: 'User Name',
                key: 'userName'
            }, {
                name: 'Value',
                key: 'value',
                type: Number
            }]
        }
    }]
})
console.log('starting parse');
reader.eachRow((rowData, rowNum, sheetSchema) => {
    console.log(rowData);
})
.then(() => {
    console.log('done parsing');
});

写入 Excel 内容

写入时和读取时相比,稍微复杂一点,需要将所有的输入使用 Promise.all包裹起来

let writer = new ExcelWriter({
    sheets: [{
        name: 'Test Sheet',
        key: 'tests',
        headers: [{
            name: 'Test Name',
            key: 'name'
        }, {
            name: 'Test Coverage',
            key: 'testValue',
            default: 0
        }]
    }]
});
let dataPromises = inputs.map((input) => {
    // 'tests' is the key of the sheet. That is used
    // to add data to only the Test Sheet
    writer.addData('tests', input);
});
Promise.all(dataPromises)
.then(() => {
    return writer.save();
})
.then((stream) => {
    stream.pipe(fs.createWriteStream('data.xlsx'));
});

总结

如果你需要将 Excel 导入到 Js 当中进行处理,那么 node-excel-stream 是一个不错的选择。

warehouse — 一个简单易用的 JSON 数据库

warehouse — 一个简单易用的 JSON 数据库

在 Hexo 的 Github 组织下,有一个不明显,但却很有用的仓库 —— warehouse。

warehouse 是一个 JSON 数据库,基于 JSON 实现了各种类似于 SQL 的查询,可以帮助我们基于一个 JSON 文件来进行查询。在 Hexo 的静态生成过程中,warehouse 帮了大忙。

用官方的话来说,warehouse 就是 A JSON database with Models, Schemas, and a flexible querying interface.

在实际使用上,warehouse 确实如他所说的那边方便(虽然某些方法没有,但依然不影响他的使用很方便)。

Example

比如,如下代码就定义了 一个 Post 模型和对应的表。并实现了在这个表中插入一个新的数据。

var Database = require('warehouse');
var db = new Database();

var Post = db.model('posts', {
  title: String,
  created: {type: Date, default: Date.now}
});

Post.insert({
  title: 'Hello world'
}).then(function(post){
  console.log(post);
});

如果你需要将这些数据保存为一个单独的文件,只需要修改初始化的参数,并执行 save 方法,就可以将 JSON 导出到指定的文件中

var db = new Database({
  path: "./test.json", // 将数据存储在 test.json 当中
});
db.save();

类似的,如果数据已经构建好了,也只需要执行 load 方法,就可以加载数据。

var db = new Database({
  path: "./test.json", // 将数据存储在 test.json 当中
});
db.load();

场景

如果你希望在内存当中对于 JSON 有一个更好的操作方式,那么 warehouse 是个不错的选择,不需要另外单独安装数据库,就可以实现类似于数据库的查询方式,体验还是非常好的。

如果你想了解更多,可以查看

如何判断一个颜色是什么颜色?

如何判断一个颜色是什么颜色?

当你看到这个题目的时候,你可能会有点迷惑:“一个颜色是什么颜色”,这个问题好无厘头。但如果我换个用法 ,你可能就能明白 — 人类是如何辨别出一个颜色是红色而不是绿色?

回想小时候,大家应该都听说过 —— 三原色。不同的人可能记得不同,有的人记得是红绿蓝(色光三原色),也有人记得是红青黄(美术三原色)。结果不一样,但并不影响。两种三原色都告诉你了 —— 一个颜色是可以由另外三种颜色组合而成。

这意味着,每一个颜色都对应着三个坐标。他们是 RGB 也好,还是其他也好,都是通过三个颜色的色码来确认是哪个具体的颜色的。我们的显示器也是如此制造的。

但三原色有个问题 —— 变量太多。假设我们在每个维度分 3 个不同的结果,三原色可以拼出 27 种不同的组合。如果我们在每个维度分成 10 阶,这个结果就是 1000 个不同的维度。按照 RGB的 0 ~ 255 ,一共 256 阶,则一共可以形成 16,777,216 个组合。如果你想要构建出一组用于判断的规则,就十分的困难。

因此,换一个坐标系会是更好的办法 —— 试试 HSL 描述方法。

HSL 是将 RGB 三维坐标转换为色相、饱和度、亮度(英语:Hue, Saturation, Lightness)组成的新维度坐标。

当我们变为新的坐标系后,我们定义颜色的坐标就从过去的三个维度,变为了只需要色相(Hue)这一个坐标就可以定位的坐标。毕竟,如果一个蓝色不饱和饱和,都不影响他是个蓝色;同样的,一个蓝色的亮度不高,但依然是蓝色。

通过从 RGB 坐标系转变为 HSL 坐标系,我们就将过去的 16,777,216 个组合简化为 360 阶。从过去的三维坐标系,变为了全新的一维坐标系。

这样我们就可以十分简单的来细分我们的不同的颜色,更好的来编写规则。比如Hue 在 15 ~ 45 时为橙色;在45 ~ 75 时为黄色;在 75 ~105 时为绿色等等。

在我们写代码的时候也会更加简单,我们只需要计算出具体的 Hue 的值,就可以得出对应的颜色的名字。

RGB to HSL 算法

如下算法来自 Wikipedia

https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4

设 (rgb)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于rgb中的最大者。设min等于这些值中的最小者。要找到在HSL空间中的 (hsl)值,这里的h ∈ [0, 360)是角度的色相角,而sl ∈ [0,1]是饱和度和亮度,计算为:h={\begin{cases}0^{\circ }&{\mbox{if }}max=min\\60^{\circ }\times {\frac  {g-b}{max-min}}+0^{\circ },&{\mbox{if }}max=r{\mbox{ and }}g\geq b\\60^{\circ }\times {\frac  {g-b}{max-min}}+360^{\circ },&{\mbox{if }}max=r{\mbox{ and }}g<b\\60^{\circ }\times {\frac  {b-r}{max-min}}+120^{\circ },&{\mbox{if }}max=g\\60^{\circ }\times {\frac  {r-g}{max-min}}+240^{\circ },&{\mbox{if }}max=b\end{cases}}

s={\begin{cases}0&{\mbox{if }}l=0{\mbox{ or }}max=min\\{\frac  {max-min}{max+min}}={\frac  {max-min}{2l}},&{\mbox{if }}0<l\leq {\frac  {1}{2}}\\{\frac  {max-min}{2-(max+min)}}={\frac  {max-min}{2-2l}},&{\mbox{if }}l>{\frac  {1}{2}}\end{cases}}”></p>



<p><img decoding=

h的值通常规范化到位于0到360°之间。而h = 0用于max = min的(定义为灰色)时候而不是留下h未定义。

RGB to HSL Sample Code

function rgbToHsl(r, g, b) {
  r /= 255, g /= 255, b /= 255;

  var max = Math.max(r, g, b),
    min = Math.min(r, g, b);
  var h, s, l = (max + min) / 2;

  if (max == min) {
    h = s = 0; // achromatic
  } else {
    var d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

    switch (max) {
      case r:
        h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }

    h /= 6;
  }

  return [h, s, l];
}

延展阅读

https://www.december.com/html/spec/colorterms.html

使用小程序的 Canvas 2D 提取特定点的颜色

使用小程序的 Canvas 2D 提取特定点的颜色

在小程序当中,我们可以通过在 Canvas 画布上绑定 Tap 事件,来获取到用户点击行为,当我们获取到点击行为对应的坐标时,就可以读取到对应位置的颜色。并根据我们的需求,将颜色转换成我们想要的 RBG 色值。

这就是我们常见的各种取色软件的最常见的实现方式。当然,落实到实际开发过程中,大家或多或少会有所不同(涉及到调色。我们拍照的颜色和我们所看到的颜色并不完全一样。不同的相机调色会有所不同。类似的,我们需要加入相应的逆向算法排除对应的相机自身调教的影响)。

在小程序的场景中具体实现方式可参考如下代码:

页面布局

<view>
  <view><button bindtap="chooseImage">1. 选择图片</button></view>
  <view><canvas id="myCanvas" bindtap="onCanvasTap" type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>

对应的 JS

和在 在小程序中使用的 Canvas 2D API 绘制本地图片 一样,你需要提取到 Canvas 的 Ctx ,用于执行操作。当你通过 bindTap 拿到你的点击点相对于 Canvas 的 左上角的坐标后,就可以使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData">getImageData</a> 来提取具体范围的数据。因为我的目的是提取某个点的颜色,因此我的第三、第四参数都是 1 (取一个像素点的宽度和高度)。

提取出图片数据后,你可以在 data 当中提取一个 4 个元素的数组,按照顺序,分别是 RGBA 的四个值,你可以根据需要使用这个数据(比如放大展示在界面上)。

Page({
  data: {
    color: ''
  },
  onCanvasTap(e) {
    const query = wx.createSelectorQuery()
    query.select('#myCanvas')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')
        const data = ctx.getImageData(e.detail.x, e.detail.y, 1, 1).data;
        const [red,gree,blue,alpha] = data;
      })
  },
});
在小程序中使用的 Canvas 2D API 绘制本地图片

在小程序中使用的 Canvas 2D API 绘制本地图片

小程序自 2.9.0 起,不再推荐使用其自己封装的 Canvas Context,而是更加推荐大家使用标准的 Canvas 2D API 来完成相关操作。因此,对于开发者来说,需要将过去的 Canvas API 调整为新的标准的 API 。

刚好我最近也实现了类似的功能,将这部分逻辑分享给大家。

页面布局

<view>
  <view><button bindtap="chooseImage">选择图片</button></view>
  <view><canvas id="myCanvas"  type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>

页面逻辑

Page({
   chooseImage(){
    // 创建一个 Query
    const query = wx.createSelectorQuery()
    // 选中 Canvas 对象
    query.select('#myCanvas')
      .fields({
        node: true,
        size: true // 提取 Node 信息
      })
      .exec((res) => {

        // 获取到 Canvas Node
        const canvas = res[0].node

        // 获取到 Context
        const ctx = canvas.getContext('2d')
        wx.chooseMedia({
          count: 1,
          success: function (res) {
            // 提取图片的基本信息
            wx.getImageInfo({
              src: res.tempFiles[0].tempFilePath,
              success: imgInfo => {
                // 使用 canvas.createImage 来创建一个图片
                const img = canvas.createImage()
                img.src = imgInfo.path
                img.onload = () => {
                  // 将图片画在画布上
                  ctx.drawImage(img, 0, 0, imgInfo.width, imgInfo.height)
                }
              }
            })
          }
        })
      })
   }
})

参考文档

WordPress 出现 RedisException: OOM command not allowed when used memory > ‘maxmemory’ 的报错怎么处理?

WordPress 出现 RedisException: OOM command not allowed when used memory > ‘maxmemory’ 的报错怎么处理?

早晨起来,想登录博客,记录下自己的灵感,突然发现死活登录不上 WordPress 后台。

登录到 VPS 后台,发现没有出现我之前常出的问题 — 硬盘满了。于是再次回到网页端登录,仔细研究后发现,我的登录应该是成功的,但登录完成后,又重新跳转回来,根据这个情况,我猜测可能是登录态出现了问题。

于是尝试切换到 Safari 、Chrome 的无痕模式登录,依然没有解决问题。因此可以排除掉客户端的问题导致的。

找到问题

接下来就是查看服务端问题。登录到服务器上,找到 WordPress 的日志,查看最近的几条日志,突然在众多 Notice 当中,看到了一个 Exception:

RedisException: OOM command not allowed when used memory > 'maxmemory'

看到这个报错,突然明白了问题在哪了。

我的 WordPress 使用 Redis 作为缓存,而我过去一直配置的缓存空间是 128M,看报错,显然是因为 Redis 使用的内存空间大于 128M,而我过去没有配置逐出机制(默认是 noeviction),导致直接 OOM 爆掉了。

而我的登录态也使用了 Redis,没办法在缓存当中塞入新的 Key,自然登录也就失败了。

解决问题

找到问题之后,下一步便是解决问题。

解决问题并不复杂,为 Redis 调整内存空间大小,并配置逐出机制,就可以解决这个问题。

maxmemory 221000000
maxmemory-policy allkeys-lru

将 Redis 逐出机制设置为 allkeys-lru ,并将内存设置为 200M 后,重启 Redis ,果然我的 WordPress 可以正常登录了。

参考

allkeys-lru 表示对移除最近使用最少的 (least recently used)Key。

更多算法可以参考 Key Evicution

为你的 WordPress 站点配置 Telegram Instant View

为你的 WordPress 站点配置 Telegram Instant View

Telegram 内置了一个非常好用的阅读功能 —— Instant View。Instant View 可以实现在 Telegram 内部重新对网站内容进行排版,从而为读者提供更好的阅读体验。

作为一个网站主,如果你希望对你的网站提供相应的支持,则可以自行在 Instant View 的网站添加你自己的网站进行适配。

为 WordPress 添加 Instant View 适配

访问 Instant View 官网,并使用你自己的 Telegram 账号登录

Instant View 官网

登录成功后,点击右侧的 My Templates,进入到 Templates 管理页面。

并在 Templates 管理页面中间的输入框中输入你网站任一文章的地址,并回车,你会自动进入到规则的适配页面。

规则适配页面

随后,在页面中输入你的站点的规则,这里我们可以使用其他开发者写好的规则。将规则粘贴在页面中

# Use Instant View version 2.0
~version: "2.0"

# Use this template only blog article pages
?exists: /html/head/meta[@property="article:published_time"]

# Get article text in <article> 
body:     //article

# Get title from <h1>

title:    $body//h1[1]
subtitle: $title/next-sibling::h2
author_url: //span[has-class("author")]//@href

# Get article cover image
cover: //img[has-class("wp-post-image")]

# Convert all iframe elements to inline element
@inline: $body//iframe[starts-with(@src, "/media/")]

# Remote header and footer
@remove: //article/header
@remove: //article/footer

# Replace p to figure
@replace_tag(<figure>): $body//p[.//img]

# Youtube Embedded Fix
@replace_tag(<figure>): $body//p[.//iframe]

粘贴并保存后,会自动在最右侧页面生成预览的效果。

加入规则适配后的效果

当你的规则适配效果无误后,接下来只需要点击右上叫的 Mark as Checked ,来标记该页面已经检查完车。

标记为检查成功

测试完成第一个后, 接下来你只需要在你的博客当中挑选出 10 篇文章,进行文章的验证即可。

验证至少 10 个页面才行

根据 Telegram Instant View 的规则,你需要验证满 10 篇文章,才能提交你的模板给官方人员审核。

提交后,你会见到这样的提示,接下来只需要等待你的模板通过测试即可。

提交成功

预览效果

在审核期间,你可以在编辑器页面右上角点击「View in Telegram」来查看预览的效果。包括你也可以复制里面的 rhash 的值,使用这个值来生成 Instant View 的链接分享给别人。不过,最方便的当然还是等官方审核通过以后再用。

预览效果。

总结

Telegram Instant View 的开发不困难,掌握了一定的 HTML 、XPath 的基础,就可以开发完成,简单的几步,就可以让你的网站在 Telegram 当中拥有一个不错的预览效果,这个时间值得去花。

为你的服务器配上自定义的登录界面

为你的服务器配上自定义的登录界面

我自己在服务器上使用的是 Ubuntu 20.04,每次在登录时,Ubuntu 都会展示一系列的信息,冗余且在多个服务器上看不出差异,并不能非常友好的提醒我这个是哪个服务器。

于是,我决定对其进行自定义,改成我自己喜欢的样子。

1. 分析现有的 Message 格式

想要修改,就一下先分析一下组成结构。我截图所示的输出可以分为三个部分:header、motd 和 last login message

而我主要不满的是 header 和 motd 的部分,对于 last login message,我虽然觉得有些多余,但在很多场景下,确实可以用于提醒。所以我就不隐藏了。不过,如果你希望隐藏 last login 的话,可以在当前用户的根目录下创建一个 .hushlogin 文件来关闭 last login message

touch ~/.hushlogin

2. 修改 motd 和 header

修改 motd

motd 的全称是 Message of Today,他的内容被存放在 /etc/motd 当中,想要修改 Motd 的文字,只需要直接修改 /etc/motd 文件的内容即可。不过需要注意的是,motd 文件不支持做格式化,只支持展示默认的文本。因此,你可以通过在其中加入空行来引起注意。

修改 header

Header 部分存放在 /etc/update-motd.d/ 目录中,你可以通过修改这个文件夹下的文件来实现不同样式的 motd 风格。其中文件名格式为 优先级-文件名的方式进行组织,你可以根据自己的需要,创建适当优先级的文件。

比如,00-header 文件的源码如下

#!/bin/sh
#
#    00-header - create the header of the MOTD
#    Copyright (C) 2009-2010 Canonical Ltd.
#
#    Authors: Dustin Kirkland <kirkland@canonical.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

[ -r /etc/lsb-release ] && . /etc/lsb-release

if [ -z "$DISTRIB_DESCRIPTION" ] && [ -x /usr/bin/lsb_release ]; then
	# Fall back to using the very slow lsb_release utility
	DISTRIB_DESCRIPTION=$(lsb_release -s -d)
fi

printf "Welcome to %s (%s %s %s)\n" "$DISTRIB_DESCRIPTION" "$(uname -o)" "$(uname -r)" "$(uname -m)"

其中有价值的代码主要就是最后几行的输出。

类似的, 10-help-text 的代码则输出了文字。

#!/bin/sh
#
#    10-help-text - print the help text associated with the distro
#    Copyright (C) 2009-2010 Canonical Ltd.
#
#    Authors: Dustin Kirkland <kirkland@canonical.com>,
#             Brian Murray <brian@canonical.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

printf "\n"
printf " * Documentation:  https://help.ubuntu.com\n"
printf " * Management:     https://landscape.canonical.com\n"
printf " * Support:        https://ubuntu.com/advantage\n"

而我们自己如果想要控制,可以自己写一个文件,比如叫 01-custom ,在其中加入如下代码(这段代码可以生成一段黑底红字的输出,用于提醒我自己这是生产环境服务器),并执行 chmod +x /etc/update-motd.d/01-custom 来设置可执行权限。

#!/bin/bash
printf "\u1b[31mwelcome to ixiqin.com production server\n"

这里需要注意的是,/etc/update-motd.d 目录下的文件本质上是一个可执行文件,你可以使用 bash / dash 等任何 Shell 来实现,也可以考虑使用 Node.js、Python 之类的脚本来实现。这样就不用像我一样,使用 Termial Color Code 来实现颜色的变化。

执行完成后的结果如下

根据需要自定义

当你找到了具体的文件的位置和配置的方式,就可以根据自己的需要来调整 /etc/motd/etc/update-motd.d 当中文件的内容,来达到你想要的效果了。

这样,就完成了对于 Shell 的登录界面的改造工作了。