查看 VPS 日志的时候发现 /var/log/
下存在一个 100M+ 大小的 btmp 文件,我知道 /var/log
目录下一般都是 Linux 系统的日志存放路径,通常 auth.log 会记录登录相关的日志,其他的 Nginx,PHP,dpkg,syslog 等等都比较熟悉,唯一没见过的就是 btmp 文件。所以简单记录一下。
btmp 文件也是日志文件,不过仅仅记录失败的登录。这也就意味着有人尝试暴力登录我的服务器。
和 btmp 相关的日志文件还有两个:
以上三个文件都是二进制数据。
btmp 文件不是一个纯文本的文件,所以不能直接使用 less, cat 查看,需要借助 last
命令:
last -f /var/log/btmp
last -f /var/log/wtmp
last -f /var/log/utmp
如果直接执行 last
不带任何参数,则会展现用户什么时候登录,什么时候登出的。
也可以使用 lastb
命令,默认会读取 /var/log/btmp
文件。
另外也可以使用 utmpdump
命令将二进制文件转成文本文件查看。
utmpdump /var/log/btmp
上文提到过 btmp 只会记录失败的登录,如果这个文件增长很快那么也就意味着有人在不断尝试登录,可能就是在暴力尝试,在上面的日志文件中也能看到很多 root 尝试登录,也有很多默认的用户在不停尝试。这个时候就需要特别注意一下系统的安全。
通常情况下,在刚初始化系统的时候这些安全措施就需要补全。
通常在配置了 [[fail2ban]] 之后就会好很多了。
have i been pwned? 是一个提供了查询电子邮件、电话号码、密码等是否被泄漏的在线网站。该网站提供了过去几十年中的各大数据泄漏事件。用户注册账户之后,可以在数据被泄漏的第一时间收到提醒。
这一次 Twitter 的 2 亿多条数据泄漏,包括邮箱,用户名,手机号等,就是 have i been pwned 第一事件发送了提醒。
Firefox Monitor 是由 Mozilla 开发的,在 Firefox 上提供的一项服务,可以帮助用户检查电子邮件或者密码有没有被泄漏。
在网站上输入电子邮箱地址,Firefox Monitor 会在自己的数据库中查询历史上曾经发生的数据泄露事件中有没有泄漏查询的数据,对于保护用户个人的网络安全非常有用。
前两天在 Twitter 上发了一个贴子,说如果 [[Obsidian]] 中能继承 [[ChatGPT]] 和 [[GitHub Copilot]] 就好了,我想来虽然也可以在 IntelliJ 和 VSCode 中打开本地的笔记仓库,但是一个礼拜前我尝试了一下用 VSCode 打开,竟然比 Obsidian 还卡,不知道哪里除了问题,所以只能再切换回 Obsidian。好在发帖没多久就发现了 Text Generator 这个插件,用 GTP-3 的 API 驱动的文字生成,并且在调研的过程中又还发现了 Obisidain 下不少能提高输入效率的工具,这里就一切总结一下。
使用 Text Generator 生成文本
View API keys
,或者直接访问 OpenAI API keycmd+j
快捷键cmd+j
,让 Text Generator 续写后文内容。Text Expander 类的工具是将用户的输入自动扩展成一个更长的内容,通常用来快速输入某些内容。因为我已经有了很多方法来实现 Text Expander(文本扩展),就没有使用 Obsidian 中的插件。
我个人使用的文本扩展方法主要有两个:
这两个工具一直都秉持我的理念,开源,跨平台,并且可以通过文本的方式配置。
RIME 是中州韻輸入法引擎 (Rime Input Method Engine) 的缩写,由 RIME 扩展出了不同平台上的输入法,小狼毫,中州韵,鼠须管等等,在 RIME 中可以配置自定义词库,然后在输入法中,通过自定义的映射来实现快速输入。
比如在自定义短语中配置:
MySQL mysql 100
我看下 wkx 30
就可以实现在输入 mysql 的时候自动修正为 MySQL
,在输入 wkx
的时候扩展成 我看下
。通过进阶的 lua 脚本也可以实现输入 date
自动扩展成当前日期等等。
Espanso 是一款是 Rust 编写的跨平台的 Text Expander。Espanso 可以通过纯文本配置 来设定文本的扩展。
比如演示中输入 :date
自动扩展成日期。为了防止误扩展,一般都会在缩略词前面加上冒号。
早之前在使用 [[Obsidian]] 的时候就有了解过 [[Logseq]] 但一直没有找到机会去尝试一下,毕竟我从 [[WizNote]] 迁移到 Obsidian 之后使用一直没有遇到任何问题,毕竟 Obsidian 够简单,也足够扩展性,并且基于 Markdown 文件的笔记是我过去一直在使用的方式。但现在让我有尝试使用一下 Logseq 的契机是因为在以前我都是使用 Vault 存放所有的笔记,然后使用其中一个 Blog 目录存放我想发布的内容,这样每一次我想发布一个具体的文章的时候就可以直接将文件移动到 Blog/_posts
目录之下,然后 Git 提交即可,但这个发布存在的问题便是其中的特殊双向链接 [[]]
会在页面上有些违和,而我看到 Logseq 可以将页面直接以 HTML 方式发布,并且也可以非常好的处理双向链接的问题,于是想来试一试。
Logseq 官方的文档 便是一个非常好的例子,展示界面几乎和用户的编辑页面是一致的,在线浏览也没有任何卡顿的情况。
上手体验的第一个非常大的区别就在于 Logseq 有 block 的概念,这个概念可能是从 Roam Research 中借用而来,不像 Obsidian 整个文档就是一个 markdown 文件,所以每一行文字其实还是段落,但是 Logseq 中每一次 “Enter” 都创建了一个 block。Logseq 中使用 ((uuid))
的语法来引用 block。有了 block 的概念之后就会发现其实文档内容的最小单元变成了 block,而在 Obsidian 中我能使用的最小单元也无非是通过 #
来划分出来的页面的段落。这可能是 Logseq 更加灵活的地方,但因为目前我还没有想好怎么使用这个 block 所以之后有了具体的使用场景再来分享。
Obsidian 中的 Command 和 Logseq 中的 Command 在使用起来还有一些区别,我设定了 Ctrl + P 来调用 Obsidian 中的 Command Palette,而这其中的命令大部分是对整个文档,或者部分内容进行的调整。而在 Logseq 中,在任何页面中使用 /
都可以进行响应的插入,/
更像是 Notion 的方式,通过 /
来快捷调用复杂的输入操作,比如插入页面引用、块引用、标题、图片等等。
但我个人觉得在 /
中提示输入标题有些累赘,我个人的习惯一般不会在一篇文章中使用三级标题以上的子标题,那这也就意味着我只会使用 1~3 个 #
,那么在纯 markdown 文档中,输入 #
要远比先输入 /
然后搜索对应的标题,选中要来的快,即使是输入三个 #
也会比 /
快。
/
让我觉得最重要的就应该是页面引用和块引用了,通过模糊搜索,在笔记和笔记之间建立关系使用 /
就大大简化的。而在 Obsidian 中我就只能使用 [[
来进行页面的关联。
Obsidian 让我直接上手的一个非常重要的原因就是开箱支持的 Vim 模式,我几乎没有想就启用了,并且一直使用到现在,这无非只是让我在终端的笔记转移到了 Obsidian,并且我熟悉而这个快捷键,命令都可以直接使用,这也让我在终端,IntelliJ IDEA,浏览器 Vimium 达成了统一。而 Logseq 开箱就是所见即所得的界面,当然普通使用起来是没有问题的,但用起来就慢慢地发现有些别扭,比如在上一行插入,或者快速跳转到页尾,以前非常熟悉的 O
或者 G
突然没有了就有些陌生。
然后再看到 Logseq 的 Feature Request ,大家对 Vim-mode 的讨论,要求支持还是挺多,不过截止目前还用不了。
就像上面提到的一样,让我试用 Logseq 最重要的一个原因就是 Logseq 生成在线文档的能力,因为我之前使用 Jekyll 的文档分享部分 Obsidian Vault 中的内容就没有购买 Obsidian Publish 服务,因为一来我觉得 Jekyll 够用,然后 Obsidian Publish 服务的页面最初的时候访问优点慢,并且早鸟价 8$ 一个月的价格也有些高。但后来在使用的过程中还是会有一些些的不便,毕竟双向链接如果没有特殊的处理会显得有些奇怪。
在 Logseq 中使用 #tag
新建 tag,当点击 tag 的时候会创建新的页面,而在 Obsidian 中 #tag
就只是页面的一个元数据,标签。在 Logseq 中可以点击 tag
页面来查看所有该标签下的页面和块,而在 Obsidian 中我通常是使用搜索。
Logseq 中使用 key:: value
格式来对页面或块进行属性设置,页面的属性在页面的第一个块定义,作为 frontmatter,块属性可以在任何块中定义。
Logseq 中的 Properties 的两大作用:
在上面的使用过程中,我一直使用 Git 来追踪 Logseq 界面中修改了文档之后在原始文档中的体现,大体来看如果没有使用 Logseq 自身的特殊语法,大致还是兼容 markdown 语法的。
所以我想到了如果将 Logseq 仓库存放在 Obsidian Vault 中,那么其实和我之前使用 Jekyll 发布内容的方式就是一致的了,我只需要把需要发布的内容移动到 Logseq 文件夹下即可。比如说我的 Logseq 仓库叫做 notes,那么他下面会有一个 pages 文件夹,存放的都是 Logseq 中的笔记原始文件。那么我将整个 notes 作为我 Obsidian Vault 的一部分,那么我既可以在 Obsidian 中编辑这些笔记,也可以用 Logseq 打开这个子文件夹来编辑。我只需要使用 Logseq 兼容的语法,那么每一次提交,然后推送到 GitHub 之后,就可以利用 GitHub Actions 自动发布。
Obsidian Vault 是一个独立的仓库,然后使用 git submodule 将 Logseq 仓库添加到其中,还可以利用版本控制来管理。
这里使用 pengx17 的 Logseq-publish 的 GitHub Action,在个人仓库下创建 .github/workflows/main.yml
文件,根据自己的情况填入配置:
name: CI
# Controls when the workflow will run
on:
push:
branches: [master]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Logseq Publish
uses: pengx17/logseq-publish@main
- name: add a nojekyll file
run: touch $GITHUB_WORKSPACE/www/.nojekyll
- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages # The branch the action should deploy to.
folder: www # The folder the action should deploy.
clean: true
single-commit: true
然后每一次 push 之后会自动触发 build,将静态 HTML 文件发布到 gh-pages
分支中。然后在 GitHub 仓库设置界面 Pages 中设置域名即可。
我发布的内容 https://notes.einverne.info
根据豆瓣的记录,今年读了 48 本书,没去年读哲学、金融那么入迷,所以大部分的读书记录还是集中在了前两个季度。
之间几年的读书笔记:
因为去年政治学的著作,所以想要开始读一些西方哲学史相关的内容,但柏拉图、苏格拉底等著作目前又无法直接读懂,需要借助大量的二手著作,二次解读,所以经过了去年大量[[洛克]]的作品之后,今年第一季度就读了很多[[卢梭]]的作品:
[[算法帝国]] 当时了解到盈透证券,偶然间获知了此书,书中的第一章关于[[Interactive Brokers | 盈透证券]] 创始人的故事非常精彩,读完了这部分后面就略读过去了 |
Parameter Expansion 是一个用来描述命令行中部分参数被展开(内容被替换)的术语。在大部分的场景中,被展开的参数通常会带有 $
符号,在一些特定的场景中,额外的花括号(curly braces)也是必须的。
比如:
echo "'$USER', '$USERs', '${USER}s'"
'testuser', '', 'testusers'
上面的例子展示了基本的 parameter expansions(PE) 是什么,第二个 PE 结果是一个空字符串,那是因为参数 USERs
是空的。其实 s
不是参数的一部分,但是因为 bash 无法分辨这一点,所以我们需要使用 {}
来限定参数的边界(前后)。
Parameter Expansion 也可以让我们去修改会被展开的字符串,这个操作会使得一些修改变得非常方便:
for file in *.JPG *.jepg
do mv -- "$file" "${file%.*}.jpg"
done
上述的代码会重命名所有扩展名为 .JPG
和 .jepg
的 JPEG 文件到 .jpg
扩展名。然后 ${file%.*}
表达式则会截取 file 文件从开头到最后一个 .
的内容。
其他的一些 PE tricks
${parameter:-word}
, Use Default Value 如果当 parameter 未设置或为null时,使用默认值 word
,否则直接使用 parameter 的值${parameter:=word}
,Assign Default Value 赋值,当 parameter
未设置或为 null 时,word
会被赋值给 parameter,然后 parameter 的值会被展开${parameter:+word}
,Use Alternate Value 如果 parameter 是 null 或者 未设置,那么结果没有任何被替换,如果 parameter 有值,则会被替换成 word${parameter:offset:length}
Substring Expansion,使用 offset 和 length 限定的字串展开,序号从0开始。${#parameter}
使用 parameter 的长度展开,如果 parameter 是一个数组名,则展开其中元素个数${parameter#pattern}
pattern
会从 parameter 值开始匹配,最短的 match 会从 parameter 中被删除然后剩余的被展开${parameter##pattern}
, ##
后面的 pattern 会把最长的 match 删掉${parameter%pattern}
,pattern 从 parameter 后往前匹配,最短的匹配被删除,剩余部分展开${parameter%%pattern}
,从后往前,最长的 match 会被删除${parameter/pat/string}
,parameter 值中的第一个出现的 pat
会被替换为 string
${parameter//pat/string}
, 每一个出现的 pat
都会被替换${parameter/#pat/string}
,${parameter/%pat/string}
HandBrake 是一款开源的全平台的视频转码压制工具,基于 [[FFmpeg]],可以算作一个 GUI 版本的 FFmpeg。
macOS:
brew install --cask handbrake
分辨率,也称为解析度,指的是视频中像素点的个数。
帧率指的是每秒在屏幕上刷新的画面个数。
需要显示器硬件支持,大多数屏幕的刷新率在 60Hz 左右。
通常情况下 30FPS 已经能够保证流畅。
码率指的是单位时间内文件包含的数据量。
相同分辨率,码率越高约清晰。但是超过一定范围之后,清晰度变不会显著提高。
文件体积 = 总码率 * 时长。
通常见到的媒体封装格式包括:
视频、音频数据的压缩方式。
常见的有:
通常可以通过分辨率大小、码率、帧率来预估一个视频文件的大小:
当了解了视频编解码的基本信息之后再去使用 HandBrake 就简单很多了。
使用 HandBrake 的使用场景通常有:
软件界面:
之前一次不消息把 Google Play Store 中的自动更新启用了,之后一个夜里把所有应用都更新了,不过有些应用本来就不想升级的,比如网易云音乐(有一些低版本没有广告,没有乱七八糟的直播什么的),微信。所以想着能不能在不丢失数据的情况下降级应用。简单的搜索了一下果然可以。
这里需要使用到 adb 命令,不同的系统直接安装即可,我现在在 Linux 下之前就已经安装过。
macOS 下:
brew cask install android-platform-tools
执行:
adb devices
查看是否连接,如果出现了设备 ID,则表明连接成功了。
然后准备好特定版本的 apk。
adb push wechat_7.0.0.apk /sdcard/Download/wechat_7.0.0.apk
然后进入下一步:
adb shell
进入系统的 shell 环境。
pm install -d -r /sdcard/Download/wechat_7.0.0.apk
说明:
-d
表示运行降级安装-r
表示保存数据重新安装现有应用如果无法安装报错:
255|OnePlus7Pro:/ $ pm install -d -r /sdcard/Download/wechat_7.0.0.apk
avc: denied { read } for scontext=u:r:system_server:s0 tcontext=u:object_r:fuse:s0 tclass=file permissive=0
System server has no access to read file context u:object_r:fuse:s0 (from path /sdcard/Download/wechat_7.0.0.apk, context u:r:system_server:s0)
Error: Unable to open file: /sdcard/Download/wechat_7.0.0.apk
Consider using a file under /data/local/tmp/
Error: Can't open file: /sdcard/Download/wechat_7.0.0.apk
Exception occurred while executing 'install':
java.lang.IllegalArgumentException: Error: Can't open file: /sdcard/Download/wechat_7.0.0.apk
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:520)
at com.android.server.pm.PackageManagerShellCommand.doRunInstall(PackageManagerShellCommand.java:1283)
at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:1249)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:185)
at android.os.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:98)
at android.os.ShellCommand.exec(ShellCommand.java:44)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:22322)
at android.os.Binder.shellCommand(Binder.java:940)
at android.os.Binder.onTransact(Binder.java:824)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:4644)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:4515)
at android.os.Binder.execTransactInternal(Binder.java:1170)
at android.os.Binder.execTransact(Binder.java:1134)
那么需要将 apk 移动到 /data/local/tmp/
:
mv /sdcard/Download/wechat_7.0.0.apk /data/local/tmp/
然后再安装:
pm install -d -r /data/local/tmp/wechat_7.0.0.apk
如果还不行,那么可以保留数据卸载应用然后重新安装:
pm uninstall -k com.tencent.mm
pm install -d /data/local/tmp/wechat_7.0.0.apk
[[Swizzin]] 是一个轻量级、模块化的 [[Seedbox]] 解决方案,可以安装在 Debian 9/10 或 Ubuntu 16.04/18.04/20.04 上。QuickBox 的软件包已经被移植过来,包括 Webmin 控制面板。
Swizzin 可以安装如下的组件:
后台界面:
项目地址 GitHub: https://github.com/swizzin/swizzin
wget
bash <(wget -qO - git.io/swizzin) && . ~/.bashrc
curl
bash <(curl -sL git.io/swizzin) && . ~/.bashrc
如果在 Ubuntu 下,以 sudo 来执行上面的安装脚本,需要在命令中包含 -H
参数,以确保你的主目录在 sudo 启动时被修改为 /root 。安装程序会自动处理这个问题:
sudo -H su -c 'bash <(wget -O- -q https://git.io/swizzin-setup)'
更进阶的设置可以参考 这里 。
box 有如下子命令:
help
install
remove
update
upgrade
adduser
deluser
chpasswd
panel fix-disk
list
test
Swizzin 的密码是在执行初始化脚本的时候设定的,可以使用如下命令重置密码:
sudo box chpasswd your_name
修改 vi ~/.rtorrent.rc
:
directory.default.set = /mnt/storage/torrents/rtorrent
重启 rtorrent 服务
sudo systemctl restart rtorrent@<yourusername>
sudo systemctl status rtorrent@<yourusername>
sudo systemctl restart [email protected]
sudo systemctl restart plexmediaserver
ruTorrent 的安装地址在
/srv/rutorrent/plugins/theme/themes
找到合适的主题克隆到该目录下即可。
[[Transdroid]] 是一款 Android 上用来管理 BT 客户端的软件,Swizzin 安装的 ruTorrent 需要按照如下方式配置才能连接上。
Name: rtorrent (or whatever you like)
Server type: rtorrent
IP or host name: <the hostname of your server>
Username: <your username>
Password: <your password>
Advanced Settings:
SCGI mount point: /rutorrent/plugins/httprpc/action.php OR /<username>
Use SSL: ON
ARP( Address Resolution Protocol) 地址解析解析,用来将 IP 地址解析为 MAC 地址的协议。
主机会维护一张 ARP 表,存储 IP 地址和 MAC 地址映射关系。
直接执行 arp
会列出系统当前的 arp 记录:
arp
Address HWtype HWaddress Flags Mask Iface
10.236.151.50 ether 50:9a:4c:2d:--:-- C eth0
172.17.0.2 ether 02:42:ac:11:00:02 C docker0
10.236.151.83 ether ac:87:a3:17:72:26 C eth0
172.25.0.2 ether 02:42:ac:19:00:02 C br-8ff872eb50bd
如果有多张网卡,可以使用 -i
指定网卡:
arp -i eth0
使用 -a
参数使用 BSD 风格输出:
$ arp -a
? (10.236.151.19) at 50:9a:4c:2d:--:-- [ether] on eth0
? (10.236.151.200) at e4:54:e8:cd:--:-- [ether] on eth0
删除 ARP 记录,可以使用 -d
参数:
sudo arp -d 192.168.0.1
删除之后 ARP 记录就会显示为 incomplete。
添加 ARP 记录:
sudo arp -s 192.168.0.1 12:34:56:78:90:ab
如果要批量添加,可以从文件中读取添加,可以先准备好一个文件,然后其中添加 IP 和 MAC 地址:
192.168.0.201 11:22:33:44:55:66
192.168.0.202 12:23:34:45:56:67
最后使用 -f
参数:
sudo arp -f arp_list.txt