使用 mdBook 生成在线文档

很早以前就用过 [[GitBook]] 来将 Markdown 生成网页1,但是后来 GitBook 命令行工具不再持续的更新,开发团队转向了维护商业版本的 GitBook 之后就用的少了。

但随后就发现了使用 [[Rust]] 编写的 [[mdBook]],体验和 GitBook 一致,基本上可以完美的代替 GitBook。有趣的是官方的介绍也是对标 GitBook 的:

Create book from markdown files. Like Gitbook but implemented in Rust.

官方网站:

安装

因为 mdBook 依赖与 Rust 所以需要安装 Rust 环境。

然后执行如下命令即可:

cargo install mdbook

用例

初始化:

mdbook init

构建:

mdbook build

监控更改:

mdbook watch

启动一个本地服务:

mdbook serve

清理:

mdbook clean

插件

如何生成 TOC

要生成页内目录可以使用 toc

cargo install mdbook-toc

相关

Python 的文档工具 [[mkdocs]] :

tag: #GitBook #wiki #Book #Markdown #note #writing #个人知识管理


2020-12-03 gitbook , online-document , rust , wiki , markdown , notes

我的 IntelliJ IDEA Vim 插件配置

IdeaVim 是 IntelliJ IDEA 编辑器下一款模拟 Vim 快捷键的开源插件。

鉴于大部分的时间都在 IntelliJ IDEA 下工作,所以总结一下在 IDEA 下使用 Vim 的动作。

为什么要用 IdeaVim

  • 既充分利用了 IntelliJ 提供的代码补全,重构,代码浏览等等功能,又可以充分利用 Vim 的多模式,以及 Vim 在编辑器中的高效操作
  • 利用 ~/.ideavimrc 来复用 Vim 的工作方式,即使将工作环境切换到 Terminal 下也可以沿用,并且充分利用 Idea 提供的 Action
  • 多平台下沿用一套工作流程,不需要解决因为操作系统不同而产生的快捷键冲突

必须安装的插件

  • IdeaVim
  • AceJump
  • IdeaVim-EasyMotion

利用 .ideavimrc 配置 IdeaVim

重新加载 .ideavimrc

如果安装了插件之后,IntelliJ IDEA 在启动时会自动加载 ~/.ideavimrc 这个配置文件,改动该文件后可以使用如下方式手动重新加载:

:source ~/.ideavimrc

或者直接在编辑器中 :e ~/.ideavimrc 然后在右上角的地方会出现重新 Load 的图表,点击即可。

使用案例

打开最近使用的项目

我映射了 leader + o 打开最近项目列表,用来快速的打开项目。

首先创建一个 keymap(用过 Vim 的都知道,可以自定义一个 modifier key,通过这个修饰键可以形成一套新的快捷键组合):

let mapleader = ","
nnoremap <Leader>o :<C-u>action RecentProjectListGroup<CR>

然后使用配置的 leader 快捷键 , + o 就可以快速弹出最近打开项目,使用模糊搜索就可以快速打开新的项目。

在不知道这个方法以前,我都是在 [[Alfred]] 中配置了一个 Workflow 来打开新的项目的。在发现上面这个方法后,发现在 IDE 内通过这个方式打开别的项目,远比 Alfred 中要快。熟悉一段时间之后,甚至可以不用看搜索结果,直接使用逗号加 o 然后快速输入项目的模糊查询的关键字,然后回车。

Go to Declaration

IDEA 自身就提供了非常多的快捷来在代码之间跳转,比如:

  • 跳转到变量,方法,类的定义
  • 跳转到 Super 类
  • 跳转到方法被调用的地方
  • 跳转到实现的地方
  • 跳转到文件
  • 跳转到测试类

等等,在我的工作流里面,为了方便记忆,统一使用 g 作为简记符。比如 gd 表示 go to definition

.ideavimrc 文件中,定义 map xxx :action yyy 表示自定义一个 keymap 调用 IntelliJ 的 action。

nnoremap gd :action GotoDeclaration

这里的 GotoDeclaration 是 IntelliJ 的一个 action,一个 IntelliJ 的 Action 对应着 IntelliJ 的一个功能。上面的定义就表示在 Normal 模式下定义新的 keymap gd,表示的是在 Normal 模式下,按下 gd 就会执行 IDEA 的 action GotoDeclaration

IntelliJ 提供了一系列的 Action 可以使用。

比如我定义了如下的跳转:

" go to somewhere (g in normal mode for goto somewhere)
nnoremap ga :<C-u>action GotoAction<CR>
nnoremap gb :<C-u>action JumpToLastChange<CR>
nnoremap gc :<C-u>action GotoClass<CR>
nnoremap gd :<C-u>action GotoDeclaration<CR>
nnoremap gs :<C-u>action GotoSuperMethod<CR>
nnoremap gi :<C-u>action GotoImplementation<CR>
nnoremap gf :<C-u>action GotoFile<CR>
nnoremap gm :<C-u>action GotoSymbol<CR>
nnoremap gu :<C-u>action ShowUsages<CR>
nnoremap gt :<C-u>action GotoTest<CR>
nnoremap gp :<C-u>action FindInPath<CR>
nnoremap gr :<C-u>action RecentFiles<CR>
nnoremap gh :<C-u>action Back<CR>
nnoremap gl :<C-u>action Forward<CR>

Toggle Actions

我使用 t 加上一个字母作为 Toggle 动作的开始方便记忆。

比如下面的第一条的 ta,表示的就是 Toggle Annotate,在 IDEA 主编辑区域经常看这行代码是谁提交的,那么会使用右击序号空白处,然后选择 Annotate,这个操作可以简化成直接在 Vim 模式的阅读模式下按下 ta

一些其他的定义可以参考:

nnoremap ta :action Annotate<cr>  
nnoremap tb :action ToggleLineBreakpoint<cr>  
nnoremap tm :action ToggleBookmark<cr>  
nnoremap tp :action ActivateProjectToolWindow<CR>

查看 IDEA 支持的 Action

在安装 IdeaVim 之后,可以在 normal 模式下使用如下命令查看 IDE 支持的 action:

:actionlist [pattern]

如果要搜索对应的 action 可以直接加上模糊词来搜索,比如 :actionlist declaration 来搜索相关的内容。

执行 action

:action {name}

比如执行 :action Debug

重新命名 Action

~/.ideavimrc 文件中可以给 Action 其名字,比如

command! Reformat action ReformatCode

在 action 后面的 ReformatCode 是一个合法的 ActionName,通过上面的语句就重新起了一个新的名字叫做 Reformat。这样就可以通过 :Reformat 来调用。

切换标签页

使用空格加 hl 来切换标签页

nnoremap <space>h gT
nnoremap <space>l gt

编辑器分屏

Vim 的命令 :e, :sp, :vsp 是支持的。

也可以直接使用快捷键 <C-W>s, <C-W>v, <C-w>c 来实现对编辑器的分屏。

<C-W>w 可以快速在不同的 Panel 之间切换。

我想要达到的效果是和我在终端中使用 Tmux+Vim 类似,使用 Ctrl+h/j/k/l 来进行分屏。

首先要到设置中把可能的快捷键冲突解决,比如 Ctrl+H 原来被我映射成 Find in Path,现在我使用 gp (go to path) 作为快捷键触发。

然后将 作为 Leader 快捷键。

" screen management
" Vertical split screen
nnoremap <Leader>\ <C-W>v
nnoremap <Leader>- <C-W>s
nnoremap <C-h> <C-W>h
nnoremap <C-l> <C-W>l
nnoremap <C-j> <C-W>j
nnoremap <C-k> <C-W>k

这样就实现了 IDEA 内部的快速分屏。

记得去设置中将 Ctrl + h/j/k/l 的默认快捷键移除,否则可能会有冲突。

更多的配置可以参考我的 dotfile 配置。

两个比较有用的快捷键

  • ⌘+F12 ActivateTerminalToolWindow 在 Linux 下设置成 Alt+F12 可以快速调用或隐藏 IDEA 内置的终端
  • ⌘+⇧+F12 HideAllWindows 最大化编辑器,隐藏所有其他窗口

Cmd 组合

  • Cmd+a/c/v/x/z
  • Cmd+w 关闭当前的文件
  • Cmd+e 最近的文件
  • Cmd+t Refactor this 重构
  • Cmd+n Generate 快速生成模板代码
  • Cmd+o File Structure 当前文件的文件结构
  • Cmd+b 跳转到定义
  • Cmd+[/]

surround

启用 surround 插件来模拟 surround

set surround

reference

[[idea-plugins]]

category: [[IntelliJ IDEA]] [[编程工具]] [[学习笔记]]


2020-12-02 idea , vim , vimrc , config , vim-config , jetbrain , intellij-idea , ide

图片管理工具 Eagle

远在移动互联网还没有那么发达的今天,Google 曾经收购过一家图片管理与分享的网站叫做 Picasa,Picasa 同时提供了一个跨平台的照片管理工具 Picasa Desktop,用这个工具不仅可以非常方便的管理本地的图片文件,也可以非常方便的分享到 Picasa Web 上,然而随着移动互联网的到来,以及 Google 的转型,Picasa 的服务在 Google 变得没有那么重要,随即在 2016 年停止了服务,我的图片管理也被迫迁移到了 Google Photos。然而一切都开始变得不方便,Google Photos 内自动备份的照片,相册开始无法管理,并且 Google 停止了桌面版的开发,同样使得在桌面上管理图片变得困难,这些年来尝试了 Lightroom,TagSpaces 等等工具,都没有找到特别舒心的。

我的需求其实也并不复杂:

  • 管理本地大量的单反照片,添加标签以及其他 meta 信息,可以方便我快速定位、查找,这一点 Lightroom 做的不错,但 Lightroom 有些笨重
  • 集中管理我所有的照片,图片,让我可以快速预览照片
  • 我希望我的图片库可以随时随地可以访问

我带着这一些需求一直在寻求一款合适的工具,直到有一天在 Twitter 上有人有相同的需求时,一则评论吸引了我的注意,打开 Eagle 官网 的同时我就被吸引了,一张软件界面的截图直接了当的说明了这个软件的功能,并且几个关键字,「收集」,「整理」,「搜索』,似乎让我一下子想到了 Picasa。更加了解之后才发现,原来 Eagle 不止图片管理这么简单,音频,字体,GIF 管理等等让我迫不及待地下载进行尝试。 安装后 Eagle 会引导安装浏览器扩展,这个扩展可以用来收藏图片,包括拖拽,Alt+ 右击,右键上下文菜单,甚至可以直接批量一键导入网页图片,或者将整个网页保存为图片,或者剪切部分网页。这部分内容扩展的页面 已经给出了非常详细的说明,这里就略过了。

picasa screenshot

我个人的习惯是每次旅行或外出回来时,会新建一个以日期开头加上地点的文件夹,并导入当前拍摄的照片,然后保证无误后格式化 SD 卡。然后使用 Lightroom 对这个新导入的文件进行一轮的筛选,使用 1~5 快捷键给照片打分,然后对打分的照片进行简单的快速处理。如果有 4 或 5 分的照片,会更进一步微调。然后对有打分的照片进行导出分享。

关于收藏图片

在遇到 Eagle 之前,我接触的大部分图片都是在浏览器中,所以在浏览器中安装了一个叫做 Imagus 的工具,当鼠标悬浮在图片上后会放大缩略图,并且使用 Ctrl+s 就可以直接下载该图片。但这样的问题便是所有的图片都在 Downloads 文件夹下和其他格式的文件混在一起。等到真正需要使用的时候,搜索变得非常不方便。

而有了 Eagle 之后这个工作流程更加简单了,鼠标拖拽图片即可。

字体管理

虽然我不是学设计的,但是大学的时候曾经做过一段时间和字体相关的事情,所以也对字体有一些要求,所以如果阅读器支持更换字体,肯定会换成自己喜欢的方正北魏楷书或方正宋三。曾经下载过很多的字体进行对比,如果当时有 Eagle 就方便很多,直接将字体导入软件就能预览文本,再不用现在文本编辑器中编辑一段对话,然后再挨个更换字体了。

eagle font manage

常用的快捷键

  • ⌘+⇧(Shift)+n 创建文件夹
  • ⌘+t 增加标签
  • ⌘+Alt+x
  • ⌘+j 在不同的文件夹之间切换
  • ⌘+⇧(Shift)+j 将指定图片移动到指定文件夹
  • F 弹出分类对话框,可以快速进行分类

几个独特有有用的功能

  • 按颜色过滤素材

缺点

不管是在 Picasa,还是在 Lightroom 个人的使用习惯是管理工具管理的内容,在磁盘上实际的位置还是原来的位置,不管是编辑了图片或者是移动了图片都是在本地文件中的修改,然而虽然 Eagle 是一个离线可用的图片管理工具,但一旦图片文件夹导入到 Eagle ,再去磁盘中查看时文件的结构就变成了 Eagle 处理过的扁平的样子,并且会冗余两份图片,一份在原来的位置,一份在 Eagle 中,即使之前存在目录结构也被打散了。一旦文件导入到 Eagle 再想要使用文件管理器来管理这些图片变的不可能。当然这个只是个人的习惯问题。

eagle folders

要再想恢复到原来的文件结构可以右击文件夹,选择导出。

另外一点令我顾虑的就是,一旦使用 Eagle 来管理我的大量图片,这就使我锁定在了 Eagle,让我有一点介意的就是当我试用期过了之后,我竟然无法导出我的所有图片,虽然这些图片都在本地,但是因为已经被 Eagle 重新命名过所以几乎无法直接使用,只能让我用虚拟机再导出一遍。这也加重了我的顾虑。

再以缺点就是在我使用的过程中,有两次无法退出,即使使用强制退出,也无法退出,甚至让我在重启电脑的时候都卡在去停止 Eagle 的地方,我从没在 Mac 上遇到软件强制退出都不行的情况。

同类型的其他产品

总结

综上所有的优点,缺点,我仔细思考了一下,Eagle 确实在某些方面做的不错,对于设计师,或者对于刚刚开始整理素材的人来说是一个非常不错的选择,而一旦已经有了自己的素材库的人来说,不可避免的需要将素材导入到 Eagle,而这是一个不算愉快的选择,并且一旦想要放弃 Eagle,那么将数据导出便又是一段不愉快的旅程。所以最后再试用期满后,我迫不得已按了一台虚拟机导出整个库后删除了 Eagle。


2020-12-02 software , photo , photo-management , picasa , picture

提升 mysqldump 导入导出 MySQL 的速度

在前端时间网上泄漏出来一个巨大包括了近 8 亿 QQ 账号的绑定电话号码数据库,于是想着导入到本地的 MySQL 看看,提升一下查询的速度,因为这个巨大的绑定关系,即使用 grep 查询也需要花费非常多的时间。

于是我新建了表

CREATE TABLE `qq_bind` (
  `phone` bigint NOT NULL,
  `qq` bigint DEFAULT NULL,
  KEY `ix_qq_bind` (`phone`,`qq`),
  KEY `ix_qq` (`qq`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

于是我想当然的想利用 mysqldump 命令来导入文件,但是发现导入的速度非常的慢,并且看到磁盘占用的速度飞速上升。以这样的导入速度,我大致计算了一下得一个多星期才能导入完成。

后来我就想办法怎么才可以提升这个导入的速度,发现如果表上有索引,或者 Primary Key 会大大的影响导入速度,所以:

  1. 移除所有的索引,包括唯一索引,除非能确保导入的数据是唯一的,否则也不建议留着,Primary Key 也是索引。

但是我移除了所有的索引之后,再执行 mysqldump 速度虽然有提升,但依然非常慢。所以不得不找其他办法。

使用 LAOD DATA INFILE 导入文件到 MySQL

再搜寻了一番之后发现 MySQL 可以使用 LOAD DATA INFILE 这样的语句来批量导入数据。

登录 MySQL cli 后可以执行:

LOAD DATA INFILE '/Users/einverne/Downloads/demo.csv' 
IGNORE INTO TABLE demo_table 
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"' 
LINES TERMINATED BY '\n' 
IGNORE 1 ROWS;

或者使用命令行:

mycli -h host -u root -p -D database_name -e "LOAD DATA INFILE '/path/to/file.csv' INTO TABLE demo_table FIELDS TERMINATED BY ','"

说明:

  • Terminated 字段分隔符(列分隔符)。一般是空格或者 \t
  • Enclosed 字段括起字符。没有为空字符即可
  • Escaped 转义字符。没有为空字符即可
  • Terminated 记录分隔符(行结束符)

插入语句:

  • Into Table 代表插入,记录已存在(唯一键约束)则失败不再往下执行。
  • Replace Into Table 代表覆盖,记录已存在则覆盖(是整条记录覆盖,没有列出的字段给默认值)。
  • Ignore Into Table 遇到已存在直接跳过。

LOAD DATA INFILE 原理

LOAD DATA INFILE 比单纯的 INSERT 要快。

  • insert 每次运行,都会更新一次索引,而 load 语句全部执行完才会更新索引。

需要注意的是,当时用 LOCAL 或者 LOAD DATA 时,文件的拷贝会保存到服务器的 temp 目录,这个目录不是由 tmpdir or slave_load_tmpdir 配置决定的,而是操作系统的临时目录 (temporary 目录)。

所以如果 CSV 文件比较大,操作系统临时目录无法放下,可以将文件分割成多份,分批次进行操作。

$ split -l (numbersofrowsinfile / ((filesize/tmpsize) + 1)) /path/to/your/<file>.csv

reference


2020-11-25 mysql , linux , mysqldump , sql , methodology

Mac 应用篇:Hammerspoon 自动化工具使用

这是一篇耽搁了很久,一直躺在我的 Obsidian 笔记中的一篇文章,一直就想好好介绍一下 Hammerspoon,但是因为过去虽然也在用 macOS,但是使用最多的还是 Ubuntu,Hammerspoon 只能在 macOS 上使用,就没有那么大的兴致再花时间学习它的使用。但最近更新了一下系统,发现 Hammerspoon 出了一点问题,没有了 Hammerspoon 之后我才发现很多不适应的地方,那就在花一点时间再梳理一下我的配置。

Hammerspoon 是什么?

Hammerspoon 是一个 macOS 上开源的自动化工具,什么叫做自动化工具呢?通过 Hammerspoon ,可以使用一些脚本来实现原来只能通过界面操作,或快捷键才能达到的效果,并且实现系统自动化。最简单的例子,比如当我连上家里的 WiFi 的时候,就自动将音量调成 3 档;再比如当我切换窗口的时候,自动切换输入法,比如在 IDEA IntelliJ 中自动使用英文输入法,当我打开 Obsidian 则自动切换成中文输入法。

Hammerspoon 使用 Lua 脚本语言与操作系统通信。通过编写 Lua 脚本实现与 macOS API 的交互,Hammerspoon 提供的 API,包括应用的、窗口的、鼠标指针、文件系统、声音设备、电池、屏幕、键盘/鼠标事件、粘贴板、地理位置服务、WiFi 等等。Hammerspoon 是操作系统和 Lua 执行引擎的桥梁,通过 Hammerspoon 可以让 macOS 实现非常强大的自动化。

官网: https://www.hammerspoon.org/

Hammerspoon 能做什么

Hammerspoon 实际上是将 macOS 的系统接口实现了一层转发,让用户可以通过简单的 Lua 脚本进行配置,从而实现一定的 UI 自动化,一旦能够直接从 API 层面对接操作系统,那么 Hammerspoon 能够做的事情就非常多了:

  • 创建并管理全局快捷键,这样就可以创建一组属于自己的快捷键工作流
  • 管理窗口 move windows,结合自定义快捷键可以非常轻松地对窗口进行管理
  • 发送通知 display notifications
  • 和其他应用交互 talk to other applications
  • 添加自定义内容到状态栏 add things to your menu bar,有了这个功能就可以在状态栏上添加任何自定义的内容,比如有人利用公开的天气 API 在状态栏显示最近和未来的天气,可以显示系统的网速等等
  • 监听文件内容变化,可以利用这个自动加载配置,或者自动提交任务等等
  • 监听 WiFi 变化,可以利用不同的网络环境配置不同的网络配置,自动切换不同的工作环境等等
  • 监听应用的启动和停止
  • 在屏幕上绘制 draw on the screen
  • 监听电池变化 watch for changes to your battery
  • 当电脑接口对接硬件时触发动作 carry out actions when you plug things into your computer

Installation

通过 Homebrew 安装:

brew install --cask hammerspoon

上手配置

Hammerspoon 的默认配置在 ~/.hammerspoon/init.lua,我个人通过将配置文件放在 dotfiles 中软链接到目的位置来同步配置。

做一个最简单的例子,在 init.lua 文件中写入:

hs.alert.show("Config reload!")

然后重新加载 Hammerspoon 配置,就会看到在屏幕中央出现 “Config reload!” 的弹出提示。

使用 Hammerspoon 作为管理窗口工具

虽然 [[Mac 上的窗口管理工具]] 有很多,免费的,收费的,Moom, Rectangle 等等,但是自由度都没有 Hammerspoon 多。

下面是我使用的一些窗口管理快捷键。

按下 Option+r 进入窗口的管理模式,在该模式下按下快捷键可以实现非常多的操作:

  • HL/JK 可以用来将窗口按照左、右、下、上分屏
  • ASDW 可以用来移动窗口的位置,对应上下左右
  • Y/O/U/I 可以将窗口变成四分之一屏幕大小,分别是左上,右上,左下,右下
  • Shift+HL/JK 可以收缩窗口大小,比如 Shift+H 就是让窗口右侧往左侧缩小
  • =/- 用来扩展和收缩窗口
  • Left/Right/Up/down 将窗口移动到左边、右边、上边、下边
  • F 全屏
  • C 中央
  • ESC/Q 退出该模式
  • Tab 显示 Cheatsheet

这一套窗口管理方法来自 ashfinal/awesome-hammerspoon

不过我个人最常使用的快捷键还是 Hyper + h/l/j/k 可以将当前的窗口以左/右/下/上方式进行分屏。这里需要结合 [[Mac 应用 Karabiner Elements 键盘自定义工具]]

[[Mac 下的自定义快捷键]]

使用 Hammerspoon 实现 Hyper + h/l/j/k 管理窗口的相关配置:

hyper = {"ctrl", "alt", "cmd", "shift"}
function move_window(direction)
    return function()
        local win      = hs.window.focusedWindow()
        local app      = win:application()
        local app_name = app:name()
        local f        = win:frame()
        local screen   = win:screen()
        local max      = screen:frame()
        if direction == "left" then
            f.x = max.x + 6
            f.w = (max.w / 2) - 9
        elseif direction == "right" then
            f.x = (max.x + (max.w / 2)) + 3
            f.w = (max.w / 2) - 9
        elseif direction == "up" then
            f.x = max.x + 6
            f.w = max.w - 12
        elseif direction == "down" then
            f.x = (max.x + (max.w / 8)) + 6
            f.w = (max.w * 3 / 4) - 12
        end
        f.y = max.y + 6
        f.h = max.h - 12
        win:setFrame(f, 0.0)
    end
end
hs.hotkey.bind(hyper, "Left", move_window("left"))
hs.hotkey.bind(hyper, "Right", move_window("right"))
hs.hotkey.bind(hyper, "Up", move_window("up"))
hs.hotkey.bind(hyper, "Down", move_window("down"))
hs.hotkey.bind(hyper, "H", move_window("left"))
hs.hotkey.bind(hyper, "L", move_window("right"))
hs.hotkey.bind(hyper, "K", move_window("up"))
hs.hotkey.bind(hyper, "J", move_window("down"))

使用 Hammerspoon 在切换 WiFi 时自动切换对应设置

当连接的 WiFi 发生变化的时候触发一个监听事件,更加详细的配置可以看我的 dotfiles

function ssidChangedCallback()
    newSSID = hs.wifi.currentNetwork()

    local devices = hs.usb.attachedDevices()

    if newSSID == homeSSID and lastSSID ~= homeSSID then
        -- We just joined our home WiFi network
        hs.alert.show("Welcome home!")
        hs.audiodevice.defaultOutputDevice():setVolume(25)
        -- result = hs.network.configuration:setLocation("Home")
        -- hs.alert.show(result)
    elseif newSSID ~= homeSSID and lastSSID == homeSSID then
        -- We just departed our home WiFi network
        hs.alert.show("left home!")
        hs.audiodevice.defaultOutputDevice():setVolume(0)
        -- result = hs.network.configuration:setLocation("Automatic")
        -- hs.alert.show(result)
    end

    if newSSID == workSSID then
        hs.alert.show("work karabiner setup")
        selectKarabinerProfile("goku")
    else
        hs.alert.show("built-in karabiner setup")
        selectKarabinerProfile("goku")
    end

    lastSSID = newSSID
end

wifiWatcher = hs.wifi.watcher.new(ssidChangedCallback)
wifiWatcher:start()

使用 Hammerspoon 一键布局桌面窗口

我抢了同事一个显示器使用,所以外接了三个显示器,在每一个显示器中都有默认的布局。我一般左边竖置的显示器常驻一个 Terminal,中间横置的一个显示器为主要工作的区域,一般放 IntelliJ IDEA,DataGrip,SmartGit 等等其他工具,右侧竖置的显示器上面为即时通信窗口,下面是浏览器。

使用 Hammerspoon 可以很快速的恢复所有窗口的布局,不过我自己用的并不多。

输入法自动切换

比如在特定应用中自动切换成 Rime 输入法 或者切换成 ABC 英文。比如在 IntelliJ IDEA 中不会输入中文的,直接切换成 ABC 输入英文即可,而当切换到浏览器的时候切换到 Rime。

结合 Rime 输入法的自动设置输入法的自动切换 就非常舒服了。

完美的代替了 kawa 这款切换输入法的工具。

定时自动执行脚本

比如我使用 Obsidian 来作笔记,同时使用 git 来做版本管理,写一个脚本,每 30 分钟提交一次。

log = hs.logger.new('autoscript', 'debug')
local cmdArr = {
    "cd /Users/einverne/Sync/wiki/ && /bin/bash auto-push.sh",
}

function shell(cmd)
    hs.alert.show("execute")
    log.i('execute')
    result = hs.osascript.applescript(string.format('do shell script "%s"', cmd))
    hs.execute(cmd)
end

function runAutoScripts()
    for key, cmd in ipairs(cmdArr) do
        shell(cmd)
    end
end

myTimer = hs.timer.doEvery(10, runAutoScripts)
myTimer:start()

比如定时提交 git commit,定时 git push 等等。当然直接使用 Crontab 来实现也是可以的。

Mute on sleep

在笔记本合上时静音

function muteOnWake(eventType)
  if (eventType == hs.caffeinate.watcher.systemDidWake) then
    local output = hs.audiodevice.defaultOutputDevice()
    output:setMuted(true)
  end
end
caffeinateWatcher = hs.caffeinate.watcher.new(muteOnWake)
caffeinateWatcher:start()

Locking the screen

定义锁屏的快捷键。

-- lock screen shortcut
hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'L', function() hs.caffeinate.startScreensaver() end)

监听 USB 事件并做相应的设置

对我而言最常见的就是当我接入外接键盘的时候,自动切换 karabiner-Elements 的键盘 profile。

这样当我使用 macOS 自带的键盘和外置键盘的时候就可以保持一致的使用习惯。

绑定快捷键快速打开应用

快速打开终端:

hs.hotkey.bind({'ctrl', 'alt', 'cmd'}, 'K', function () hs.application.launchOrFocus("iTerm") end)

更多的例子可以参考我的 dotfiles

防止长时间不用进入休眠

参考 https://github.com/einverne/dotfiles/hammerspoon/ 关键字 Caffeine。

将当前窗口移动到其他屏幕中

定义了快捷键 Hyper + 1/2/3 将当前窗口快速移动到其他显示器:

function moveWindowToDisplay(d)
  return function()
    local displays = hs.screen.allScreens()
    local win = hs.window.focusedWindow()
    win:moveToScreen(displays[d], false, true)
  end
end

hs.hotkey.bind({"ctrl", "alt", "cmd"}, "1", moveWindowToDisplay(1))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "2", moveWindowToDisplay(2))
hs.hotkey.bind({"ctrl", "alt", "cmd"}, "3", moveWindowToDisplay(3))

对虚拟桌面的支持

非官方支持

扩展

Hammerspoon 的配置文件是使用 Lua 书写,如果熟悉 Lua,可以更进一步使用 Lua 的 moonscript 来简化配置。

brew install lua@5.3
luarocks-5.3 install moonscript
luarocks-5.3 install lodash

参考 这里

这里

Hammerspoon 官网文档

Spoon 是什么?

Spoon 是预置在 Hammerspoon 内的插件系统,Spoon 是使用纯 Lua 实现的插件,可以方便用户集成集成到 Hammerspoon 的配置中。

可以从官方的页面获取 Spoon ,源码可以参考对应的 GitHub 页面,下载后解压得到 .spoon 文件,双击导入即可。文件会自动将自己拷贝到 ~/.hammerspoon/Spoons/NAME.spoon,然后在 init.luahs.loadSpoon("NAME") 即可。

更具体的 Spoon 的使用可以参考官网。

编写 Spoon

Spoon 文件有一定的格式,方便集成调用。

Spoon 文件中的常用方法:

  • NAME: init(),这个方法会被 hs.loadSpoon() 调用,会进行一些初始设置,这里面不应该执行任何动作
  • NAME: start(),如果有需要在后台进行的任务,可以由这个方法启动
  • NAME: stop(),关闭后台任务
  • NAME: bindHotkeys(mapping),定义功能快捷键,通常是 table 的形式:

reference


2020-11-24 mac , hammerspoon , automation , tool , window-manage , mac-app , mac-application

我的 Obsidian 笔记跨设备同步方案

自从半年前发现了 Obsidian 这款笔记软件,我就开始大量的使用该应用做笔记,有人说过:「工具是开发者方法论的固化」。这么多年了我一直有一种工具控的倾向,往往同一个需求会对比可能的所有方案,最后再决定一个,但是近些年来我越来越倾向于「简单就是好」,并且数据要由自己掌控的「工具选择逻辑」。

Do one thing and do it well

基于上面的选择逻辑,我的 Obsidian 跨平台同步工具,我选择了:

  • Syncthing,作为文件同步工具
  • Markor,作为 Android 上的 Markdown 编辑器,我提交了一段模板可以来创建 Zettelkasten 笔记
  • Git,版本同步(配合git subtree
  • Bash/Cron/Hammerspoon,定时脚本提交备份到 Git,然后自动推送到远端备份。

每一个工具都只专注做一件事情,搭配起来工作非常完美。

2021 年 9 月更新

这一年来 Obsidian 的生态发生了很多变化,Obsidian 已经发布了 Android/iOS 版本,虽然现在我依然使用 Markor 来查看笔记,但最近已经慢慢的转向 Obsidian 官方的应用,Obsidian 官方的应用在搜索方面做的要比 Markor 好。

而之前使用 Syncthing,现在也一如既往的使用 Syncthing,但是最近购入了 Obsidian Sync 一方面支持一下开发者,另一方面也想慢慢地将移动端使用官方的同步工具来同步笔记。

我的需求

我最基本的需求,就是当我有什么想法的时候,可以随时随地地记录到一个地方,这个地方以前是 wiznote,但是我迁移到了 Obsidian 之后,缺乏两个机制:

  • 一个是多平台的同步
  • 一个是版本管理

为了解决第一个问题,引入了 Syncthing, 这个是我已经使用很久了的文件同步工具,代替了 Dropbox,NextCloud,正好 Obisidian 本身管理的就是本地的纯文件,直接添加到同步目录即可。

为了解决第二个问题,纯文本自然地就想到了使用 Git 作为版本管理。

但是问题在于 Zettelkasten 的其中一条原则便是「最好在一个地方管理所有的笔记」,而我曾经有很多笔记已经记录到了 Jekyll 的博客里面,也正好是 Markdown 的文本文件。所以我在一个中心的 Git 仓库中存放了 Obisidian 中的所有笔记,然后使用 git subtree 将我的 Blog 作为其中的一个子目录 Blog 添加进来,这样我就可以在一个 Obsidian 的 Vault 中搜寻,连接我所有的笔记。

利用上面的 Syncthing,Git,我可以做到多地(本地,VPS,Android 手机)的备份,然后将仓库同步到 GitHub 再有一份备份。另外通过 Git 的提交历史可以看到所有的笔记修改记录。这时候我就想通过一个自动提交的脚本,每隔半小时提交一次,这样最多也就丢失半小时的笔记,对我个人而言我也能接受。所以当时直接用 Hammerspoon 的特性写了一个脚本自动同步。

#!/bin/bash

now=$(date +"%m.%d.%Y_%T")

cd /Users/einverne/Sync/wiki
git add --all && git commit -m "Auto commit at $now"
git push origin master

发布我部分的笔记

在知识管理的概念中,我了解到了 [[Digital Garden]] 概念1,Digital Garden 是一个开放分享的数字花园,这个概念中有两个重点:

  • 有选择地将一部分笔记和想法公开
  • 游客可以在笔记之间来回浏览

在这个概念上实现最好的就是 Andy Matuschak 的在线笔记。

我也想通过从我的 Obsidian 笔记中选择一部分整理好的内容分享出,但我又不想有额外的操作。所以正好通过上面提及的 git subtree,我日常的笔记会存放在其他的目录,当我准备发布的时候,将笔记移动到 Blog 中的 _posts 目录,在提交,之后将修改推送到 Blog 的远端时就自动完成了发布的过程。现在唯一不太满意的部分就是现在博客上进行页内跳转的时候还依赖于超链接,在编写文章的时候也需要跳出去选择相应的页面链接,而不能依赖与 Obisidian 提供的双向连接。

但总是上面一套流程是我如何实现在多平台上同步笔记的方案,虽然可能对非编程工作者会有一些障碍,但只要熟悉了这个流程之后,用起来真的非常方便。另外就是我身边并没有 iOS 的设备,在 iOS 上可能需要依赖其他的工具,比如可以使用 iCloud 或其他商业的同步工具来同步笔记,然后使用 1Writer 来作为编辑器。

总之这就是我实现跨设备的方案,具体我是如何做笔记,以及笔记的内容,如何对笔记内容进行连接,我会在另外一篇文章中再进行阐述。


2020-11-23 obsidian , markdown , git , syncthing , note , note-taking

配置 Rime 在 Vim 下退出编辑模式时自动切换成英文输入法

半年以前在 Obisidian 的文章下面有人曾经问过我一个问题,如何在 Vim 或者其他使用 Vim 模式的编辑器,比如 IntelliJ,或者 Obisidian 开启 Vim 模式后方便地切换中英文输入法,因为在编辑模式和普通模式下,需要经常切换输入法,使得体验变得非常槽糕。

rime vim mode auto switch

这个问题一直萦绕再我脑海里,直到今天再整理关于 Rime 相关的笔记的时候发现 macOS 下的 squirrel 支持了一个 vim_mode 选项,这使得我们可以在配置中定义在哪些应用中,当我们按下 Esc 的时候将 Rime 自动切换成英文状态。1

配置

squirrel.custom.yaml 配置文件中可以配置 vim_mode 这样的语句。

org.vim.MacVim:
  ascii_mode: true
  no_inline: true
  vim_mode: true

表示在 MacVim 应用中,当按下 Esc 的时候会自动将 Rime 切换成英文输入法。这样就可以节省 一次切换输入法的麻烦。

所以最后我的配置:

com.jetbrains.intellij:
  ascii_mode: true
  vim_mode: true
net.kovidgoyal.kitty:
  ascii_mode: true
  vim_mode: true
md.obsidian:
  vim_mode: true

配置不同的应用自动切换英文

上面的 vim_mode 只会对 Esc 按键进行响应,那么如果要针对不同的应用进行中英文的自动切换呢?答案上面的配置中也已经有了,通过 ascii_mode 来配置,比如我在 Alfred 中及几乎不会使用到中文的,基本上就是模糊打出应用的名字,然后直接回车就启动应用了;再比如在终端或者 IDE 中使用中文的场景也特别少,所以可以直接配置上 ascii_mode 当切换到该应用时立即切换成 Rime 下的英文模式。

在不同的应用间切换不同的输入法

上面的操作已经可以满足大部分人的需求了,但如果你还想更进一步,比如当我使用 Kakao talk 的时候想要切换成韩语输入法,那么就需要接触 Hammerspoon 了,配置可见我的 dotfiles


2020-11-21 rime , macos , vim , vim-mode , input-method , ime , obsidian

管理我的音乐库

自从2014年总结整理了当年所有流行的音乐网站 之后就一直使用网易云音乐到今天,然而这些年的发展过程中多多少少产生了一些变化,虾米没了,Google Play Music 也没有了。而如今网易云音乐也出现了各种各样的问题,虽然有些问题可以或多或少的被规避,但网易用起来就是没有那么舒服了。

我在原来的那篇文章中提到了互联网化,以及伴随着移动设备的发展,我个人偏好的几个产品特点:

  • WEB 化
  • 同步账号系统
  • 跨平台
  • UGC 用户贡献

这几点虽然现在网易云音乐依然做的非常不错,并且能推出 Linux 版本这件事情就是值得称赞的。而所有的音乐服务中,也只有网易能把「云村」这样的一个UGC社区运营得如此出彩。

但是在核心的音乐版权上却一而再的在退步,虽然偷偷摸摸地用音乐云盘的法律漏洞可以避免一些用户使用上的问题,但是我个人最无法忍受的事情,就是偷偷摸摸删除用户文件,这件事情让我再无法忍受。所以自从那件事情被暴露之后我就一直在思考有没有什么方案我可以迁移。相较于 Apple Music 或 Spotify 这样的音乐流媒体分发模式,我个人更偏向于购买或者能让我看到音乐文件的方式。所以我个人是不会去重度使用那些流媒体音乐服务的。

而由因为前段时间 Play Music 服务关闭,一下子下载了十几个GB的文件,造成了我对音乐文件的管理的困扰。所以一有时间我就会去想怎么离线管理起我的这些音乐文件。

我的音乐库管理方法

在寻找代替方案的过程中,我先整理了一下我的需求

  • 一个整体的音乐库(良好的组织方式),能够让我对所有的歌曲文件一目了然
    • 我个人偏向于按照音乐家,专辑,单曲这样的形式去管理
  • 可以让我对音乐文件的 metadata 进行编辑
  • 可以让我快速通过,名字,音乐家,或标签进行搜索

良好的文件组织形式

离线的音乐文件不像那些在线的音乐服务,可以直接将音乐加入到不同的列表进行管理,我个人更加倾向于在本地通过目录组织进行管理。

所以目前我在 Music 这样的目录中建立了一个顶层的文件夹用来管理所有的文件。

普通专辑

普通的专辑,我就以这样的层级进行管理:

Music > Artist > Album Name > Audio

Various Artist

对于合集,以这样的方式。

Music > Various Artists > Movies > Soundtracks
Music > Movies > Soundtracks > Hackers

找出重复文件

当需要管理成千上万个文件的时候,重复文件的查找就变得异常困难,幸好可以借助开源的命令行工具 jdupes 来快速找到重复文件并删除,节省了大量的空间。

管理音乐文件 metadata 和 ID3

一旦确定了音乐媒体库的文件结构,可以开始整理,但是整理的过程中,就会发现每个音乐文件都会带一个完整或不完整的 metadata 信息,这些信息会被一些音乐播放器获取在播放时用来显示封面或音乐家,专辑的信息。很多年前在使用 Play Music 的时候,为了让 Google 自动匹配上,使用 mp3tag 批量修改了一些文件,但是 mp3tag 只能在 Windows 上使用,所以又找到了一个叫做 MusicBrainz,这个软件是跨平台的,可以用来修改文件的 metadata。

ID3 是一个位于 mp3 文件开头或结尾若干字节的附加信息,包含了该 mp3 歌手,标题,专辑,年代,风格等等信息。

[[ID3 tags]] 通常会被播放器用来展示其基本信息,包括封面,歌手,专辑等等。

MusicBrainz Picard

MusicBrainz Picard 是一个 Python 编写的、开源的(GPL 2.0)、跨平台的音乐文件元数据管理工具。很早之前在 Windows 上用过 一款叫做 Mp3tag 的软件,MusicBrainz Picard 就是类似的工具。

关于该软件的使用就不再多说,可以直接参考图文教程

在使用之前可以做一些调整

screenshot-musicbrainz-options.png

利用 MusicBrainz 自动修改文件 metadata

使用 MusicBrainz 导入文件,然后点击菜单中的 Cluster,会自动根据专辑进行归类。

点击需要编辑的专辑,然后点击 Look Up,如果查找不到,可以点击 Scan,自动根据歌曲 metadata 匹配信息。匹配到的文件会自动到右边,在下方可以看到被编辑的内容,点击保存即可将内容保存到文件。

利用 MusicBrainz 自动修正目录

在菜单栏 Options 中默认只选中了 Save Tags,可以将 Move Files 勾选上,这样每一次保存的时候会自动修正目录。

reference


2020-11-17 music , netease , play-music , google-play , music-library , google-music , media-vault , media-server

Asus RT-AC86U 初始设置

前些天给家里买手机正好凑单了一个 Asus RT-AC86U,正好可以代替出了两次故障的小米 3G

提前工作

  • 登录管理后台(http://router.asus.com/Main_Login.asp)启用 SSH (系统管理 - 系统设置 - 服务 - 启用 SSH)
  • 开启 JFFS 分区,系统管理 - 系统设置 - Persistent JFFS2 partition - Enable JFFS custom scripts and configs
  • U 盘格式成 ext4 插到路由器
  • 一个已经刷成 Merlin 固件 的路由器

固件选择

刷机步骤

AC86U 的刷机步骤非常简单,通过网页「升级页面」,直接上传 .w 后缀的固件,然后等待刷机完成自动重启即可。

2020 年 11 月固件版本:3.0.0.4.386_40451_koolshare 2022 年 8 月升级到:3.0.0.4.386_41634_koolshare

启用 Clash 代理插件

禁用检测:

sed -i 's/\\tdetect\_package/\\t# detect\_package/g' /koolshare/scripts/ks_tar_install.sh

这里 是 shadowsocks 插件地址。

这里 是 Merlin Clash 插件。在这个 Telegram 频道中下载 merlinclash_hnd 开头的插件按照包。

在 Merlin firmware 下安装 Entware

Entware 是一个嵌入式设备的包管理工具,之前在 QNAP NAS 上也有安装过。

梅林内置了 entware 安装脚本,直接在终端执行:

entware-setup.sh

执行后 entware 会把软件安装在 /opt 目录下。

在安装 Entware 的时候记得一定保证网络环境畅通,否则下载下来的不完整的 opkg 二进制可能有各种问题,要不就是 Permission denied, 要不就是 Segmentation fault。

amtm terminal menu

在安装了 Entware 之后,就可以非常方便的进行常用的包安装,比如安装 rsync:

opkg update
opkg install rsync

或者安装更加复杂的应用,比如说在路由器上

等等很多特性,都可以在官方提供的页面 看到。

设置 Swap 分区

在之前的 Linux swap 分区 的文章里面提过,Swap 分区会在系统物理内存将满的时候被使用,虽然 AC86U 自身具备了 500 多兆的内存,但是如果跑多了应用可能会很快被用尽。所以如果看到内存将被用满,可以尝试创建 swap 分区。

如果是 Merlin 的固件,通过 SSH 登录后台之后直接运行 amtm,这是梅林固件自带的一个终端管理工具,在其中可以非常快速的通过交互命令创建 swap 分区。如果想手动创建也可以通过如下的方式纯手工进行设置。1

依次执行:

dd if=/dev/zero of=/tmp/mnt/sda1/swapfile bs=1024 count=512000
mkswap /tmp/mnt/sda1/swapfile
swapon /tmp/mnt/sda1/swapfile

然后,创建启动脚本:

echo '
#!/bin/sh

# Turn On Usage Of Swapfile
if [ -f "/tmp/mnt/sda1/swapfile" ];then
swapon /tmp/mnt/sda1/swapfile
echo "Turning Swapfile On"
fi
' >> /jffs/scripts/post-mount

增加执行权限:

chmod a+rx /jffs/scripts/*

这样系统每一次重启就会自动的使用该 swap 分区。

其他工具

可以通过 amtm 安装其他工具,比如:

dnscrypt-proxy, skynet, diversion, mini dns-server

2020-11-13 router , asus , 路由器 , linux , ssh , entware

扩展 Proxmox 系统分区以及 Proxmox 文件系统初识

昨天想要扩展一下之前安装的 Proxmox 容量,对系统进行了一次关机,然而关机之后就悲剧的发现在 U 盘中的系统启动不了了,将 U 盘拔下检测之后发现 U 盘可能挂了,一个全新的 U 盘,在连续 192 天运行之后挂掉了。无奈之下只能想办法先恢复一下 Proxmox 系统以及安装在系统之上的 OpenMediaVault 了。

恢复的过程倒也是很麻烦,只不过这一次想稳定一些,将系统还是安装在一块之前主力机上淘汰下的 SSD 上吧,所以用 Clonezilla 先备份 SSD 上的系统,然后将之前 U 盘上的 Proxmox 系统恢复到 SSD 上,做完之后发现 Clonezilla 实际上是将整块 U 盘上的分区表,分区一并搬到了 SSD 上,所以在磁盘里面能看到实际 Proxmox 系统只是占用了 32G 的大小。那这个时候就需要将现在的 Proxmox 分区扩展到整块磁盘了。

这个时候就需要复习之前整理过的两个命令了 fdiskparted。fdisk 用来查看磁盘的分区详情,然后使用 parted 对磁盘分区进行扩容。

不过在进入正题之前,先提前警告一下,对磁盘的操作请格外小心,请先备份好数据,或者找一块闲置的磁盘进行操作,否则可能丢失整块磁盘的数据!

前提知识

Proxmox 的文件系统

在正式进入之前先来回顾一下 Proxmox 的文件系统,在全新安装的 Proxmox 系统上可以看到一块硬盘被划分了三个分区。

root@pve:~# fdisk -l /dev/sdd
Disk /dev/sdd: 232.9 GiB, 250059350016 bytes, 488397168 sectors
Disk model: Samsung SSD 850
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: AC6AD606-ED82-475B-A813-7----------2

Device       Start       End   Sectors   Size Type
/dev/sdd1       34      2047      2014  1007K BIOS boot
/dev/sdd2     2048   1050623   1048576   512M EFI System
/dev/sdd3  1050624 488397134 487346511 232.4G Linux LVM

上面已经是我扩容后的结果,扩容之前 sdd3 这个分区只有不到 30G。

说明:

  • BIOS boot 分区是 GNU [[GRUB]] 来引导基于 Legacy BIOS 但是启动设备上有 GPT 格式分区表的操作系统时使用的分区。
  • EFI System 分区是一块 FAT32 格式的分区,存储 EFI 引导程序以及启动时固件使用的应用程序。
  • Linux LVM 分区则是系统真正可以使用的分区。LVM 是逻辑卷管理器,可以用来创建和管理逻辑卷,而不是直接管理磁盘,这就使得我们之后对分区大小进行调整变得可能。对 LVM 逻辑卷的扩大缩小并不会影响其中的已存储的数据。

接下来再来看看 LVM 分区下的逻辑卷。

Disk /dev/mapper/pve-swap: 3.5 GiB, 3758096384 bytes, 7340032 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/pve-root: 7 GiB, 7516192768 bytes, 14680064 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Disk /dev/mapper/pve-vm--100--disk--0: 32 GiB, 34359738368 bytes, 67108864 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 65536 bytes / 65536 bytes
Disklabel type: dos
Disk identifier: 0x4236f4d0

Device                                 Boot    Start      End  Sectors  Size Id Type
/dev/mapper/pve-vm--100--disk--0-part1 *        2048 56868863 56866816 27.1G 83 Linux
/dev/mapper/pve-vm--100--disk--0-part2      56870910 67106815 10235906  4.9G  5 Extended
/dev/mapper/pve-vm--100--disk--0-part5      56870912 67106815 10235904  4.9G 82 Linux swap / Solari

fdisk -l 的输出可以看到 Proxmox 创建了三个逻辑卷分区:

  • /dev/mapper/pve-swap 是 swap 分区
  • /dev/mapper/pve-root 是 Proxmox 的 root 分区 7 GB
  • /dev/mapper/pve-vm--100--disk--0 则是我在其中安装的 OpenMediaVault 划分给了它 32 GB 空间

使用 lvdisplay 可以看到逻辑卷的详细信息。可以看到 /dev/mapper/pve-root 就是 pve 卷组里面的逻辑卷。

root@pve:~# lvdisplay
  --- Logical volume ---
  LV Path                /dev/pve/swap
  LV Name                swap
  VG Name                pve
  LV UUID                cYatZ5-kif7-n8N2-v9c5-UOlb-wfLJ-qt35G7
  LV Write Access        read/write
  LV Creation host, time proxmox, 2020-11-10 18:42:21 +0800
  LV Status              available
  # open                 2
  LV Size                3.50 GiB
  Current LE             896
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0

  --- Logical volume ---
  LV Path                /dev/pve/root
  LV Name                root
  VG Name                pve
  LV UUID                dc0VlK-7DSo-lgzw-7Zxy-aK3s-jlTc-TPOmDA
  LV Write Access        read/write
  LV Creation host, time proxmox, 2020-11-10 18:42:22 +0800
  LV Status              available
  # open                 1
  LV Size                7.00 GiB
  Current LE             1792
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:1

  --- Logical volume ---
  LV Name                data
  VG Name                pve
  LV UUID                oNuSZd-JoDA-1jPW-Wdcs-q59D-vuDx-fDnUab
  LV Write Access        read/write
  LV Creation host, time proxmox, 2020-11-10 18:42:22 +0800
  LV Pool metadata       data_tmeta
  LV Pool data           data_tdata
  LV Status              available
  # open                 2
  LV Size                219.88 GiB
  Allocated pool data    1.72%
  Allocated metadata     1.84%
  Current LE             56290
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:4

到这里其实就能看到 Proxmox 安装的时候实际上创建了一个叫做 pve 的卷组 (Volume Group),然后在上面分别创建了 swap, root, data 三个逻辑卷 (Logical Volume)。

什么是 LVM

LVM 是 Logical Volume Manager 逻辑卷管理的简称。

LVM 对底层的物理磁盘进行封装,向上以逻辑卷的形式提供。当上层的应用要访问文件系统的时候,不是通过直接操作分区,而是通过 VLM 的逻辑卷,对底层的磁盘进行管理。

LVM 最大的特点是可以对磁盘进行动态管理,逻辑卷大小可以在变更文件内容情况下动态调整。

基本术语

存储介质,系统的存储设备,比如常见的硬盘等等。

Physical Volume 物理卷,物理卷在逻辑卷管理中的最底层,实际上是物理硬盘的分区,也可以是整个物理硬盘。

Volume Group 卷组,建立在物理卷之上,一个卷组至少要包括一个物理卷,卷组建立之后可以动态添加物理卷到卷组中。逻辑卷管理系统中可以只有一个卷组,也可以拥有多个卷组。

Logical Volume 逻辑卷,建立在卷组之上,卷组中未分配的空间可用于建立新的逻辑卷,逻辑卷建立之后可动态地扩展和缩小空间。

扩容过程

如果 Proxmox 没有安装 parted 先安装:

apt update && apt install -y parted

安装后再执行 parted -l 列出分区信息。

如果有弹出 Fix/Ignore 的提示,输入 Fix 快速修复。

扩容分区

使用 fdisk -l 来查看 Proxmox 在哪一块磁盘,以及磁盘上的分区信息,文章之前以及提过,可以看到 Proxmox 划分的三个分区没有完全利用 SSD 的全部空间。

对于我的系统,我的 Proxmox 安装在 sdd 这块硬盘上,使用 parted 对磁盘分区进行操作:

parted /dev/sdd

进入交互模式后,可以使用 print 来查看分区信息,可以看到 LVM 分区只用了很小一部分空间,扩容:

resizepart 3 100%

这里的 3 指的是分区编号,一定小心。

然后退出:

quit

这个时候已经将磁盘剩余的空间都划分给了 /dev/sdd3

可以使用 fdisk -lparted 的 print 来查看。

root@pve:~# parted /dev/sdd
GNU Parted 3.2
Using /dev/sdd
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: ATA Samsung SSD 850 (scsi)
Disk /dev/sdd: 250GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name  Flags
 1      17.4kB  1049kB  1031kB                     bios_grub
 2      1049kB  538MB   537MB   fat32              boot, esp
 3      538MB   250GB   250GB                      lvm

更新物理卷的大小:

pvresize /dev/sdd3

提示更新成功。

扩容逻辑卷

查看 Proxmox 的逻辑卷内容,cat /etc/pve/storage.cfg 可以看到:

root@pve:~# cat /etc/pve/storage.cfg
dir: local
        path /var/lib/vz
        content iso,backup,vztmpl

lvmthin: local-lvm
        thinpool data
        vgname pve
        content rootdir,images

local-lvm 是对应着 vgname 这个叫做 pve 的 Volume Group 中的 data 名字的逻辑卷。

使用 lvdisplay 可以看到:

  --- Logical volume ---
  LV Name                data
  VG Name                pve
  LV UUID                oNuSZd-JoDA-1jPW-Wdcs-q59D-vuDx-fDnUab
  LV Write Access        read/write
  LV Creation host, time proxmox, 2020-11-10 18:42:22 +0800
  LV Pool metadata       data_tmeta
  LV Pool data           data_tdata
  LV Status              available
  # open                 2
  LV Size                29.88 GiB
  Allocated pool data    1.72%
  Allocated metadata     1.84%
  Current LE             56290
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:4

这里的 LV Size 就是逻辑卷的大小。

然后使用 pvs 查看物理卷的使用情况,在 PFree 里面能看到有很大部分的空间是没有使用的。

然后利用 lvextend 命令进行逻辑卷的扩容:

lvextend -l +100%FREE /dev/pve/data

上面的命令会将 100% 空间划分给 data,如果只想要增加 10 GB,那么:

lvextend -L +10G /dev/pve/data

如果熟悉 lvresize 也可以:

lvresize --extents +100%FREE --resizefs /dev/pve/data

然后可以使用 pvslvdisplay 进行查看。

reference


2020-11-10 proxmox , linux , nas , openmediavault , system , fdisk , partition , gpt , clonezilla

电子书

最近文章

  • 在日本生活必备的应用收集整理 在上一篇梳理了一下如何 注册日区的 Apple ID ,其实主要是为了这一篇整理做准备,有很多的应用,如果不是日区就安装不了,并且如果要在日本生活有一些应用还是非常重要的,所以就提前梳理一下需要用到的应用。
  • 如何注册日区 Apple ID 以及初次登录日区 App Store 之前一直使用美区的账号,但突然发现有一些 App 在美区也没有,比如去日本经常需要用到的「乗換案内」就搜不到,美区里面尽是一些冒牌的,还穿插各种广告的应用,还有很多日本本地的一些应用也几乎都没有上架美区。
  • 《工作、消费主义和新穷人》读书笔记 怎么知道的这一本书 前些日子去线下逛书店的时候,在书店刚进门的架子上一眼就看到了这一本,或许是这本书的名字起得太吸引眼球了,但是拿起来之后看到作者才意识到这本书是之前刚刚读过的 [[现代性与大屠杀]] 的作者 [[齐格蒙 鲍曼]] 的著作,这更让我提起了兴趣。
  • 《活法》读书笔记 怎么知道的这一本书 这本书一直非常出名,[[稻盛和夫]] 的名字一直非常响亮,但我似乎对其没有任何了解,这一本书也是非常机缘巧合地在楼下的图书馆里面看到了。
  • Mastodon 站点管理:导入自定义表情包 [[Mastodon]] 实例可以允许站点管理员自定义整站上的表情包,管理的地址在 首选项-管理(Administration)-自定义表情(custom emojis) , 具体的页面地址是 https://instance.domain/admin/custom_emojis。