bsd 开机启动

说明

启动顺序

1
2
3
4
5
# 自己的启动脚本,需要 chmod a+x
/usr/local/etc/rc.d/

# 最后加载
/etc/rc.local

分类

rc 格式

需要符合 rc 格式

1
chmod a+x /usr/local/etc/rc.d/*

格式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh
#
# PROVIDE: myapp
# REQUIRE: DAEMON
# KEYWORD: shutdown
#

# 加载预定义变量和函数
. /etc/rc.subr

name="myapp"
rcvar="myapp_enable"
command="/usr/local/bin/myapp"

# 用于重启
procname="/usr/local/bin/myapp"
desc="my custom service"

load_rc_config $name
# 如果变量未定义,则为 NO
: ${myapp_enable:="NO"}

# $1 为启动参数
run_rc_command "$1"

自定义脚本

1
chmod a+x /etc/rc.local

标准 shell 格式

1
2
3
4
5
6
7
8
9
10
#!/bin/sh -e

. /etc/profile

# nohup xxx > /dev/null 2>&1 &
# screen -dm xxx

/opt/demo/_build/prod/rel/demo/bin/demo daemon

exit 0

buildroot 基础用法

准备

1
https://buildroot.org/downloads/buildroot-2025.02.3.tar.gz

配置

defconfig

1
make O=output qemu_arm_vexpress_defconfig
1
make O=output menuconfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Bootloaders  --->
[*] U-Boot
(vexpress_ca9x4) Board defconfig
U-Boot binary format --->
[*] u-boot

Target packages --->
# 这个要选中,不然 vim 和 bash 都看不到
[*] Show packages that are also provided by busybox
Development tools --->
# gnu sed,自带的不标准,elixir 会报错
[*] sed

# sh 对标准的支持太差
Shell and utilities --->
[*] bash
Text editors and viewers --->
[*] vim

编译

1
make O=output -j$(nproc)

编译结果在 output/images

erlang 的 heart 用法

说明

beam 被杀死以后,会重新启动,用于对于稳定性要求很高的场合。

或者先 kill 掉 heart 进程,在 kill 掉 beam 进程

激活

rebar3 项目

vm.args

1
2
3
4
5
6
7
# 这个必须设置
-heart

# 不设置 HEART_COMMAND,使用默认重启命令
# 一定要用绝对路径,不然找不到
-env HEART_COMMAND "/xxx/rel/demo_release/bin/demo_release daemon"
-env HEART_BEAT_TIMEOUT 10

或者设置环境变量

1
2
export HEART_COMMAND="/xxx/rel/demo_release/bin/demo_release daemon"
export HEART_BEAT_TIMEOUT=10

原始命令行

方法 1

节点 1

1
2
3
erl -heart -sname aaa@manjaro -setcookie 123456 \
-env HEART_COMMAND "erl -heart -sname aaa@manjaro -setcookie 123456" \
-env HEART_BEAT_TIMEOUT 10

节点 2

1
2
erl -sname bbb@manjaro -setcookie 123456
net_kernel:connect_node('aaa@manjaro').

方法 2

节点 1

1
2
3
export HEART_COMMAND="erl -heart -sname aaa@manjaro -setcookie 123456"
export HEART_BEAT_TIMEOUT=10
erl -heart -sname aaa@manjaro -setcookie 123456

节点 2

1
2
erl -sname bbb@manjaro -setcookie 123456
net_kernel:connect_node('aaa@manjaro').

切换到远程节点

1
2
3
4
Ctrl + G
User switch command
--> r 'aaa@manjaro'
--> c

动态修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
os:getenv("HEART_COMMAND").
os:getenv("HEART_BEAT_TIMEOUT").

% 获取 HEART_COMMAND 命令
heart:get_cmd().
{ok,"erl -heart"}

% 设置临时 HEART_COMMAND 命令
heart:set_cmd("heart -shutdown").
ok

% 获取 HEART_COMMAND 命令,当临时 HEART_COMMAND 命令设置时取了 临时命令的值
heart:get_cmd().
{ok,"heart -shutdown"}

% 清除临时 HEART_COMMAND 命令
heart:clear_cmd().
ok

% 获取 HEART_COMMAND 命令
heart:get_cmd().
{ok,"erl -heart"}

关闭

正常关闭

如果要关闭,则 remsh 到目标节点,执行

1
:init.stop()

杀掉

不可以用 pkill, 因为 heart 也是 erl 体系内的

1
2
ps aux | grep erl
kill -9 xxx

angular 下 signal用法

说明

1
2
computed() 的值随 signal 变化自动保持最新
effect() 在 signal 被改变时触发

例子

component 例子

1
2
3
4
5
6
7
<div>
<p>计数值 {{ count() }}</p>
<p>平方值 {{ squared() }}</p>
<button (click)="inc()">inc</button>
<button (click)="dec()">dev</button>
<button (click)="reset()">set</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export class Demo1 {
count = signal(0);
squared = computed(() => {
return this.count() * this.count();
});

inc() {
this.count.update((current) => {
return current + 1;
});
}

dec() {
this.count.update((current) => {
return current - 1;
});
}

reset() {
this.count.set(0);
}

constructor() {
effect(() => {
console.log(`on effect, count = ${this.count()}`);
});
}
}

service 例子

demo-service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { computed, effect, Injectable, signal } from "@angular/core";

@Injectable({
providedIn: "root",
})
export class DemoService {
count = signal(0);
squared = computed(() => {
return this.count() * this.count();
});

constructor() {
effect(() => {
console.log(`on effect count = ${this.count()}, squared = ${this.squared()}`);
});
}

set(value: number) {
this.count.set(value);
}

inc() {
this.count.update((current) => {
return current + 1;
});
}
}

demo2.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Component, OnInit, Signal, WritableSignal } from "@angular/core";
import { DemoService } from "../demo-service";

@Component({
selector: "app-demo2",
templateUrl: "./demo2.html",
styleUrls: ["./demo2.css"],
})
export class Demo2 implements OnInit {
count: any;
squared: any;

constructor(private demoService: DemoService) {}

ngOnInit(): void {
// 这里注意写法
this.count = this.demoService.count;
this.squared = this.demoService.squared;
}

inc() {
this.demoService.inc();
}

reset() {
this.demoService.set(0);
}
}

demo2.html

1
2
3
4
5
6
<div>
<p>计数值 {{ count() }}</p>
<p>平方值 {{ squared() }}</p>
<button (click)="inc()">inc</button>
<button (click)="reset()">set</button>
</div>

erlang 热更新

说明

依赖于 rebar3 的 appup 插件

步骤

代码

创建项目

1
rebar3 new release demo

rebar.config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
% 编译时配置
{erl_opts, [debug_info]}.
{deps, []}.

% 热更新的插件
{plugins, [rebar3_appup_plugin]}.

{provider_hooks, [
{pre, [{tar, {appup, tar}}]},
{post, [
{compile, {appup, compile}},
{clean, {appup, clean}}]}
]}.

% 发布时配置
% 默认模式的配置
{relx, [
{release, {demo, "1.0.0"}, [
demo,
sasl
]},

{mode, dev},
{sys_config, "./config/sys.config"},
{vm_args, "./config/vm.args"}
]}.

% prod 模式下的特殊配置,其它的配置,从上面的默认模式里面拿
{profiles, [
{prod, [
{relx, [
{mode, prod}
]}
]}
]}.

初始版本

1
2
rebar3 as prod release
rebar3 as prod tar

修改版本号

1
2
rebar.config 里面的 release 版本号
多个 .app.src 里面的 vsn 版本号

构建新版本

原来 build 过的老的 release 不能删除,这个升级插件至少需要两个版本的 release

1
2
3
4
5
6
rebar3 as prod release

# 生成 appup 文件
rebar3 as prod appup generate
rebar3 as prod relup -n demo -v 2.0.0
rebar3 as prod tar

测试

1.0.0 的压缩包先解压,然后运行

1
bin/demo console

一定要注意,新版本的存放目录

1
2
3
4
5
cp ../demo-2.0.0.tar.gz ./releases/
bin/demo upgrade 2.0.0
bin/demo versions
bin/demo attach
application:which_applications().

降级

1
2
bin/demo downgrade 1.0.0
bin/demo uninstall 2.0.0

erlang 多环境发布

参考链接

步骤

创建项目

1
2
3
4
rebar3 new release demo
cd demo/apps/
rebar3 new app demo1
rebar3 new app demo2

rebar 配置

demo1, demo2 等为自动启动的 application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
% 编译时配置
{erl_opts, [debug_info]}.
{deps, []}.

% 发布时配置
% 默认配置
% release 需要在这里统一配置
{relx, [
{release, {demo, "0.1.0"}, [
sasl,
demo1,
demo2
]},

{mode, dev}
]}.

% 特定模式的配置,缺乏的从默认发布时配置里面拿
{profiles, [
{dev,
[
{erl_opts, [native, {hipe, [verbose]}, debug_info]},
{relx, [
{mode, dev},
{vm_args_src, "config/dev/vm.args.src"},
{sys_config_src, "config/dev/sys.config.src"}]
}
]
},
{prod,
[
% 编译时就需要 key 文件
% {erl_opts, [encrypt_debug_info]},
% 反编译时才需要 key 文件
% {erl_opts, [debug_info,{debug_info_key,{des3_cbc,"12345"}}]},
{erl_opts, [no_debug_info]},
{relx, [
% 这个参数自动设置 include_erts
{mode, prod},
{vm_args_src, "config/prod/vm.args.src"},
{sys_config_src, "config/prod/sys.config.src"}]
}
]
},
{test,
[
{erl_opts, [{native, o3}]},
{relx, [
{dev_mode, false},
{include_erts, false},
{include_src, false},
{vm_args_src, "config/test/vm.args.src"},
{sys_config_src, "config/test/sys.config.src"}]
}
]
}
]}.

发布

生成 release 目录

1
2
REBAR_PROFILE=prod rebar3 release -o ~/downloads
rebar3 as prod release -o ~/downloads

打包

1
2
REBAR_PROFILE=prod rebar3 tar -o ~/downloads
rebar3 as prod tar -o ~/downloads

angular 动态修改 title description

根据路由修改 title, description

用于 seo

步骤

创建

1
2
3
4
ng new demo

ng g c aaa
ng g c bbb

代码

app.routes.ts

1
2
3
4
5
6
7
8
9
10
import { Routes } from "@angular/router";
import { Home } from "./home/home";
import { Aaa } from "./aaa/aaa";
import { Bbb } from "./bbb/bbb";

export const routes: Routes = [
{ path: "", component: Home },
{ path: "aaa", component: Aaa },
{ path: "bbb", component: Bbb },
];

app.html

1
2
3
4
5
6
7
8
9
<div>
<a [routerLink]="['/aaa']">aaa</a>
</div>
<div>
<a [routerLink]="['/bbb']">bbb</a>
</div>

<input type="button" value="js 到 aaa" (click)="toAaa()" />
<router-outlet></router-outlet>

app.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Component } from "@angular/core";
import { Router, RouterModule, RouterOutlet } from "@angular/router";

@Component({
selector: "app-root",
imports: [RouterOutlet, RouterModule],
templateUrl: "./app.html",
styleUrl: "./app.css",
})
export class App {
protected title = "demo";

constructor(private router: Router) {}

toAaa() {
this.router.navigate(["/aaa"]);
}
}

aaa.ts

1
2
3
4
5
6
7
8
9
10
11
export class Aaa implements OnInit {
constructor(private title: Title, private meta: Meta) {}

ngOnInit() {
this.title.setTitle("aaa 页的 title");
this.meta.updateTag({
name: "description",
content: "aaa 页的 description",
});
}
}

bbb.ts

1
2
3
4
5
6
7
8
9
10
11
export class Bbb implements OnInit {
constructor(private title: Title, private meta: Meta) {}

ngOnInit() {
this.title.setTitle("bbb 页的 title");
this.meta.updateTag({
name: "description",
content: "bbb 页的 description",
});
}
}

python 实现图片转换为 pdf

直接贴代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import os
import re
from PIL import Image
from PyPDF2 import PdfWriter, PdfReader

def natural_sort_key(s):
return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)]

img_folder = './xx图/'
pdf_path = 'output.pdf'

imgs = [f for f in os.listdir(img_folder) if f.lower().endswith('.jpg')]
imgs.sort(key=natural_sort_key)

image_list = []
for img_name in imgs:
img_path = os.path.join(img_folder, img_name)
img = Image.open(img_path).convert('RGB')
image_list.append(img)

temp_pdf = os.path.join(img_folder, 'temp.pdf')
image_list[0].save(temp_pdf, save_all=True, append_images=image_list[1:])

reader = PdfReader(temp_pdf)
writer = PdfWriter()

for i, page in enumerate(reader.pages):
writer.add_page(page)
title = os.path.splitext(imgs[i])[0]
writer.add_outline_item(title=title, page_number=i)

with open(pdf_path, 'wb') as f:
writer.write(f)

print("PDF 已生成:", pdf_path)

docker

基本用法

1
2
3
docker run -it xxx /bin/bash
docker run --rm xxx
docker rmi xxx --force

自动重启

1
docker run --restart=always xxxx

环境变量

1
docker run xxxx -e TZ=Asia/Shanghai

以某个用户名启动

1
docker run -u root

指定用户进入 shell

1
docker exec -it -u root xxx bash

默认是 root, 有时候希望不是

1
docker run -it --user $(id -u ${USER}):$(id -g ${USER}) xxx

默认 root 有些权限用不了

1
docker run -it --privileged xxx

清理

1
truncate -s 0 /var/lib/docker/containers/*/*.log

查看资源

查看 cpu,内存等等

1
docker stats

构建

1
docker build -t xxx ./ --no-cache

构建时使用变量

多阶段构建,需要在每个阶段都写一遍

1
2
ARG MIX_ENV
ENV MIX_ENV=${MIX_ENV}

例子

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 构建阶段
FROM debian:12.5 as builder

ARG MIX_ENV
ENV MIX_ENV=${MIX_ENV}

WORKDIR /build
COPY . .

ADD ./deploy/ustc.list /etc/apt/sources.list


# 运行阶段
FROM debian:12.5 as runner

WORKDIR /app

ARG MIX_ENV
ENV MIX_ENV=${MIX_ENV}

构建

1
docker build --build-arg MIX_ENV=test -t demo ../ --no-cache

同步时区

构建时

DockerFile

1
2
3
RUN rm -f /etc/localtime \
&& ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone

运行时

1
docker run xxx -v /etc/localtime:/etc/localtime -e TZ=Asia/Shanghai

或者

1
docker run xxx -v /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime -e TZ=Asia/Shanghai

自定义存储路径

ntfs 分区不支持

1
/etc/docker/daemon.json
1
2
3
{
"data-root": "/mnt/vdb/docker"
}

导入导出

容器保存为 tar,比 save 保存的文件小很多

1
docker export ${CONTAINER_ID} > xxx.tar

从容器 tar 还原为 image

1
docker import xxx.tar image_name:tag

当前容器直接保存为 image

1
docker commit ${CONTAINER_ID} image_name:tag

保存 image 为 tar 包,使用 tag 保存,不然 load 的时候,会显示 <none>

1
docker save -o xxx.tar redis:5.0.0

tar 包还原为 image,不可以重命名,这里的 tar 是指docker images保存出的 tar

1
docker load -i xxx.tar

linux 的用户相关的东西

查看信息

1
getent passwd postgres

用户组

查看

1
cat /etc/group

添加组

1
groupadd $GROUP

删除组

1
groupdel $GROUP

添加用户到用户组

1
gpasswd -a $USER $GROUP

用户组里面删除用户

1
gpasswd -d $USER $GROUP

目录权限

1
2
chown -R $USER $DIR
chgrp -R $GROUP $DIR

用户组赋予读写执行权限

1
2
3
chmod -R ug+w $DIR
chmod -R g=rwx $DIR
chmod -R ugo=rwx $DIR

修改 id

1
2
usermod -u $UID $USER
groupmod -g $GID $GROUP

修改完以后, 尤其是改完 uid

一定要 修改 被修改 id 的用户 目录下的文件权限,否则无法登录