跳到主要内容

Docker Image

· 阅读需 1 分钟

watchtower

自动检测并更新 docker image

version: "3"
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 3600 # 每小时检查一次更新

nvm

· 阅读需 1 分钟

nvm 设置默认 node 版本

nvm alias default 18

.npmrc 文件的作用

配置说明文档: https://docs.npmjs.com/cli/v9/using-npm/config

npm 命令的预设配置文件,在执行 npm 命令时,会加载并根据这些配置来执行命令。

audit=false
loglevel=error
registry=http://mirrors.tencent.com/npm/
engine-strict=true

Git 常用命令

· 阅读需 2 分钟

git log

--since 语法

  1. 绝对日期格式
    • YYYY-MM-DD
    • Month Day, Year
    • YYYY-MM-DD HH:mm:ss
  2. 相对时间格式
    • N year/months/weeks/days/hours/minutes/seconds ago
    • yesterday/today
  3. --until --before 结合
    • --since>=
    • --until, 即 <

从今天开始,过去 1 个月的提交

git log --since="1 month ago"

一行显示

git log --since="2025-06-01" --until="2025-07-01" --oneline

自定义输出

# %h commit hash 的缩写
# %an 作者
# %ar 提交的相对时间
# %s 提交信息
git log --since="1 month ago" --pretty=format:"%h - %an, %ar : %s"

图形化显示

git log --since="1 month ago" --graph --oneline

--pretty=format

--pretty:format"- %s" 格式化输出

  • %h commit hash
  • %an author name
  • %ad author date(format respects --date=option) 搭配 --date 使用--date=format:"%Y-%m-%d %H:%M:%S"
  • %ar author date, relative (相对时间)
  • %at author date, UNIX timestamp
  • %ai author date, ISO 8601
  • %ae author email
  • %s subject

查询指定日期之后提交记录并格式化显示

git log --since="2025-11-31" --author="wangzhy" --pretty=format:" - %s %ad" --date=format:"%Y-%m-%d %H:%M:%S"

git config

pull.rebase

git pull 命令的行为方式。

  • git config pull.rebase true 表示使用 rebase 方式来合并远程代码 (要求有干净的工作区,即没有未提交的代码)
  • git config pull.rebase false 表示使用 merge 方式来合并远程代码

问题记录

· 阅读需 1 分钟

2025.6.27

  1. 序列化与反序列
    • 兼容多种日志格式
    • jackson、gson、fastjson 的写法
  2. Dockerfile 构建优化技巧

自解释代码

· 阅读需 2 分钟

summary

  1. 使用命名常量而非晦涩的错误代码 | Using named constants instead of cryptic error codes
  2. 将复杂逻辑抽离成独立函数 | Extracting complex logic and putting it in its own function
  3. 运用短路求值使代码流程线性化 | Using short-circuit evaluation to make the code flow linear
  4. 引入类型注解辅助静态检查与实时编码反馈 | Introducing type annotations to help with static type checking and real-time coding feedback

original code

async function createUser(user) {
if (!validateUserInput(user)) {
throw new Error('u105');
}

const rules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/];
if (user.password.length >= 8 && rules.every((rule) => rule.test(user.password))) {
if (await userService.getUserByEmail(user.email)) {
throw new Error('u212');
}
} else {
throw new Error('u201');
}

user.password = await hashPassword(user.password);
return userService.create(user);
}

命名常量与单一职责原则

const err = {
userValidationFailed: 'u105',
userExists: 'u212',
invalidPassword: 'u201',
};

function isPasswordValid(password) {
const rules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/];
return password.length >= 8 && rules.every((rule) => rule.test(password));
}

async function createUser(user) {
if (!validateUserInput(user)) {
throw new Error(err.userValidationFailed);
}

if (isPasswordValid(user.password)) {
if (await userService.getUserByEmail(user.email)) {
throw new Error(err.userExists);
}
} else {
throw new Error(err.invalidPassword);
}

user.password = await hashPassword(user.password);
return userService.create(user);
}

短路求值

function throwError(error) {
throw new Error(error);
}

async function createUser(user) {
validateUserInput(user) || throwError(err.userValidationFailed);
isPasswordValid(user.password) || throwError(err.invalidPassword);
!(await userService.getUserByEmail(user.email)) || throwError(err.userExists);

user.password = await hashPassword(user.password);
return userService.create(user);
}

Keeping a Mistake Journal

· 阅读需 1 分钟

Entry Format

Name:名称,简明扼要的标题。
Tags:标签,分类
Context:背景,错误发生的具体环境或情境。
Problem:问题,对错误本身的描述。
Impacts:影响,可能导致的后果。
Lessons learned:经验总结,从这次错误中学到了什么。
Correction plan: 改进方案,今后如何避免重蹈覆辙。
Latest occurrence:最近发送,上次犯这个错误的时间。
Repetition: 重复次数,这个错误发送的概率。

Linux Shell 常用命令

· 阅读需 6 分钟

du

1、查看指定文件夹的大小

-h 以K,M,G为单位,提高信息的可读性。
-s 仅显示总计。

du -sh dir_path

2. 查询当前文件夹总大小

du -sh .

3. 查看当前文件夹所有子项的大小

du -sh *

df

df (disk filesystem)用于显示文件系统的磁盘空间使用情况.

查看 block 使用情况

df -h

查看 inode 使用情况

df -i

alias

alias gg='gg'
alias cls='clear'
alias ls='ls -alFh --ignore=. --ignore=.. --color=auto --group-directories-first'
alias acme.sh=~/.acme.sh/acme.sh

ln

ln -s source_file target_file

nginx

ln -s /wangzhy/cloud-conf/nginx/etc/nginx/conf /etc/nginx/conf
ln -s /wangzhy/.config/nginx_ip_conf /etc/nginx/ip-conf

把程序添加到 /usr/bin 下面

ln -s /source_path/source_name /usr/bin/target_name 

find

find 语法

find [path] [expression]

path:是要查找的目录路径,可以是一个目录或文件名,也可以是多个路径,多个路径之间用空格分隔,如果未指定路径,则默认为当前目录。

expression:是可选参数,用于指定查找的条件,可以是文件名、文件类型、文件大小等等。

-name pattern:按文件名查找,支持使用通配符 * 和 ?。

pattern 用引号括起来,例如

# 查找所有的 pdf 文件
find / -name '*.pdf*'

-type type:按文件类型查找,可以是 f(普通文件)、d(目录)、l(符号链接)等。

查看大于 100M 的文件

find / -type f -size +100M

-size [+-]size[cwbkMG]:按文件大小查找,支持使用 + 或 - 表示大于或小于指定大小,单位可以是 c(字节)、w(字数)、b(块数)、k(KB)、M(MB)或 G(GB)。

-mtime days:按修改时间查找,支持使用 + 或 - 表示在指定天数前或后,days 是一个整数表示天数。

-user username:按文件所有者查找。

-group groupname:按文件所属组查找。

find 命令中用于时间的参数如下:

-amin n:查找在 n 分钟内被访问过的文件。

-atime n:查找在 n*24 小时内被访问过的文件。

-cmin n:查找在 n 分钟内状态发生变化的文件(例如权限)。

-ctime n:查找在 n*24 小时内状态发生变化的文件(例如权限)。

-mmin n:查找在 n 分钟内被修改过的文件。

-mtime n:查找在 n*24 小时内被修改过的文件。

在这些参数中,n 可以是一个正数、负数或零。正数表示在指定的时间内修改或访问过的文件,负数表示在指定的时间之前修改或访问过的文件,零表示在当前时间点上修改或访问过的文件。

+n : n 天之前修改的文件 -n : n 天内修改的文件 n : n 天前修改的文件(第 n 天前的当天)

find 使用案例

  1. 在脚本中使用

find /opt/oracle/oradata/ORCLCDB/oracle_exp_dir -mtime +1 -name "WIKI_USER*" - rm -f exec {} \;

  1. 在 shell 使用

find /opt/oracle/oradata/ORCLCDB/oracle_exp_dir -mtime +1 -name "WIKI_USER*" - rm -f exec {} +

  1. 查找多种后缀的文件
find ~ \( -name "*.zip" -o -name "*.log" -o -name "*.txt" -o -name "*.jar" -o -name "*.json" -o -name "*.xls" -o -name "*.xlsx" -o -name "*.doc" -o -name "*.docx" \)
  1. 查找大于 10M 的 zip 文件
find /path/to/search -type f -name "*.zip" -size +10M -exec du -h {} + > ~/Desktop/a.txt

watch

每隔 1s 查询一下本目录下的,以 sql 结尾的文件的大小,单位是 MB。

watch -n 1 -d ls -lh --block-size=M *.sql

vim

1、vim 显示隐藏行号

# 显示行号
set number
set nu
# 隐藏行号
set nonumber
set nonu

2、vim 删除以 XX 开头的行

# 按 esc 进入命令输入模式
# 删除以 XX 开头的行
:g/^XX/d

tee

tee 的主要功能是从标准输入读取数据,然后将数据写入标准输出(即输出到终端上面)和文件。

echo 'hello world' | tee -a xxx.txt

tee>> 的区别?

tee 可以一次写入多个文件,并且可以在终端显示写入的内容。 >> 是静默的写入。

tar 压缩/解压文件

# 压缩
tar -zcvf test.tar.gz test
# 解压
tar -zxvf file.tar.gz

五选一

-c 压缩 -x 解压 -t 查看内容 -r 追加文件 -u 更新原压缩包的文件

可选

-z gzip -j bz2 -Z compress -v 显示过程 -O 标准输出

必选

-f 使用档案名字,这个参数是最后一个参数,后面只能接档案名。

systemd

Ubuntu 16.04 不再支持 rc.local 了。开机自启只能通过 systemd 来实现。

# 查看systemd 版本
systemctl --version
# 查看服务是否启用
systemctl is-enabled SERVICE_NAME

scp

[root@VM-16-10-centos ~]# scp
usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user@]host1:]file1 ... [[user@]host2:]file2

scp 命令传输文件

# 复制远程机器上的 xxx.zip 到本地
scp cloud:/wangzhy/xxx.zip ~/backups/
# 复制远程机器上的文件夹到本地
# -r 表示递归复制整个目录
scp -r cloud:/wangzhy ~/backup/
  • -C:开启压缩传输

passwd

1、设置简单的密码

# 方法一
echo '123456' | passwd --stdin username
# 方法二
echo username:password | chpasswd

2、删除用户的登录密码

passwd -d root

3、清除 last、lastb 的信息

# 清除登录记录
echo > /var/log/wtmp # last
echo > /var/log/btmp # lastb

查看 linux 版本信息

# 查看架构信息
uname -a
# 查看发行版本信息
cat /etc/os-release
# 输出机器的体系结构
arch

ntp

同步操作系统的时间

# 安装 ntp
yum -y install ntp
# 同步时间,这个只是修改操作系统的时间,重启之后就会失效
ntpdate -u cn.pool.ntp.org
# 查看当前硬件时间
clock -r
# 把当前操作系统的时间写入硬件中
clock -w

curl

curl -L 参数的作用是让 curl 在遇到 HTTP 3xx 状态码(重定向)时,自动跟随新的位置(URL)

curl -L www.sina.com

使用 -k--insecure 参数让 curl 忽略 SSL 证书验证

curl -k https://wangzhy.com 

下载文件

-o 需要指定文件名

-O 使用 URL 中的文件名

brew

安装 xcode 才能升级 brew

xcode-select --install

brew 升级

brew update
brew upgrade

brew 查看安装的软件

brew list

brew 卸载软件

brew uninstall software_name

查看磁盘读写总量

brew install smartmontools

smartctl -a /dev/disk2

安装 svn

brew install subversion

svn help

grep

egrep 是 grep 的增强版本,支持更多的正则表达式

-v 告诉 egrep ,不现实匹配的行,即反向匹配。

egrep -v "^*#|^$" filebeat.yml

wget

-i 下载本地或外部文件中的 URL,如: wget -i file.txt

-O 将输出写入指定文件。 sh -c "$(wget https://xxx.sh -O -)", 末尾的 - 表示标准输出(stdout) 这句命令的作用是,通过 sh 执行 https://xxx.sh 脚本。

-b 后台下载。

-d 调试模式。

awk 命令基本使用

· 阅读需 3 分钟

awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。

默认分隔符是:空白键或者 tab 建,以 \n 为一行

$0:所有域
$n:第 n 个域

1、显示出最近登录成功的用户

使用默认分隔符

last -n 5 | awk '{print $1}'

2、查看用户

通过 -F ':' 指定分隔符

# 下面两个命令等效
cat /etc/passwd | awk -F ':' '{print $1}'
cat /etc/passwd | awk -F: '{print $1}'

3、查看用户的时候显示出 bash

通过双引号对 \t 转义

cat /etc/passwd | awk -F ':' '{print $1"\t"$7}'

4、BEGIN、END 使用

cat /etc/passwd | awk -F ':' 'BEGIN {"name,bash"} {print $1","$7} END {print "blue,/bin/nosh"}'

5、匹配含有关键字的行

'/pattern/action' 指定正则表达式和 action

awk -F ':' '/root/' /etc/passwd
awk -F ':' '/root/{print $7}' /etc/passwd

6、awk 内置变量

ARGC               命令行参数个数
ARGV 命令行参数排列
ENVIRON 支持队列中系统环境变量的使用
FILENAME awk浏览的文件名
FNR 浏览文件的记录数
FS 设置输入域分隔符,等价于命令行 -F选项
NF 浏览记录的域的个数
NR 已读的记录数
OFS 输出域分隔符
ORS 输出记录分隔符
RS 控制记录分隔符
awk -F ':' '{print $1 "\tfilename:" FILENAME "\t\tARGC:" ARGC "\tFNR:" FNR}' aa.txt 

7、print、printf

print函数的参数可以是变量、数值或者字符串。字符串必须用双引号引用,参数用逗号分隔。如果没有逗号,参数就串联在一起而无法区分。这里,逗号的作用与输出文件的分隔符的作用是一样的,只是后者是空格而已。

printf函数,其用法和c语言中printf基本相似,可以格式化字符串,输出复杂时,printf更加好用,代码更易懂。

8、自定义变量

awk -F ':' 'BEGIN {count=0; print "[start] user count is "count}{ count++; print $1"\t" count} END {print "user count is " count}' /etc/passwd

统计某个文件夹下的文件占用的字节数

ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'

如果以M为单位显示:

ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'

9、显示账户

awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd

查看拒绝访问的 IP

awk -F ':' '{print $2}' /etc/hosts.deny| sort | uniq -c | awk '{print $1"\t"$2}' | grep  '^[0-9]' 

MongoDB 常用查询语句/技巧

· 阅读需 2 分钟

转换为 UTC 时间字符串

// 转换为 UTC 时间字符串
db.collection.find({
timestamp: { $gte: new Date( new Date("2025-03-22T00:00:00+08:00").toISOString()) }
});

创建视图

标准视图使用底层集合的索引。

// ==============  AI800 A相进线温度  ==========
db.AI800.drop();
db.createView(
"AI800", // 视图名称
"equipment-1123", // 源集合
[
{ $match: { "code": "AI424" } }, // 过滤条件
]
)

db.AI800.find();

MongoDB 储存空间优化

数据库状态查询

db.status()

db.stats(1024*1024); // 输出单位为 MB

查询结果说明:

{
"db": "tmp_dev",
"collections": Long("275"),
"views": Long("0"),
"objects": Long("7891781"), // 文档总数
"avgObjSize": 234.529085766572, // 单个文档的平均大小,单位是:byte
"dataSize": 1765.11019039154, // 逻辑大小,不压缩的情况下,数据大小
"storageSize": 299.5, // 物理大小
"indexes": Long("281"), // 索引数量
"indexSize": 123.61328125, // 索引占用的存储空间
"totalSize": 423.11328125, // 总存储空间 = storageSize + indexSize
"scaleFactor": Long("1048576"), // 缩放因子, 1048576 = 1024 * 1024, 即 MB
"fsUsedSize": 126627.28515625, // 文件系统已使用的存储空间
"fsTotalSize": 181303.5546875, // 文件系统总存储空间
"ok": 1
}

空间优化

1. 删除 collection 是可以直接释放空间的

2. 删除 collection 里面的数据是不会直接释放空间,需要执行 db.runCommand({compact:"collectuinName"})

优化脚本

for (var collectionName of db.getCollectionNames()) {
// 清空数据
if (collectionName.startsWith("equipment")
|| collectionName.startsWith("transformer")
|| collectionName.startsWith("circuit-breaker")) {
// db.getCollection(collectionName).deleteMany({});
// db.runCommand({
// compact: collectionName
// })
db.getCollection(collectionName).drop();
}
}

字段排序

.sort(), 1 增序, -1 降序。

db.getCollection("transformer-1000").find({
"code": "AI801"
}).sort({
"dataTime": -1
}).limit(1000);