一些开发笔记
在 VSCode 里调试 JAVA
- 安装 JDK
- 把 JDK 添加进环境变量
- 安装 VSCode 的 JAVA extension pack 拓展
- 使用 VSCode 打开文件夹
- 新增一个 debug 的类型 JAVA ,点击右下角的添加配置,选择 JAVA Lanuch Program
- 写一份测试的代码,并在 mian 函数加个断点
- 然后在终端里运行编译命令和运行命令,如果编译命令不复杂,可以配合 code runner 拓展,直接点击右上角的运行图标,或设置一个任务
在 VSCode 里调试 Python
- 安装 Python
- 把 Python 的安装目录和安装目录下的 script 添加进环境变量里
- 在 VSCode 安装 Python 拓展( microsoft 出品的)
- 新增一个 debug 的类型 Python ,点击右下角的添加配置,选择 Python: Terminal (integrated)
- 写一份测试的代码,并加个断点
- 然后在终端里运行,可以配合 code runner 插件,直接点击右上角的运行图标,或设置一个任务
在 Windows10 里修改 administrator 帐号的帐号名的三种方式
- 通过管理里的系统工具修改
选中桌面中的计算机图标,右键,点击管理
系统工具
本地用户和组
用户
选中Administrator,然后重命名
重启电脑
- 通过策略租修改
win+r 打开运行
输入 gpedit.msc 打开策略组
计算机配置
windows设置
安全设置
本地策略
安全选项
拉到最下面
双击这个选选项 重命名系统管理员帐户
重启电脑
- 通过 netplwiz (Network Places Wizard) 网上邻居向导 修改
win+r 打开运行
输入 netplwiz
选中Administrator,然后双击,然后修改
重启电脑
Windows10 无密码远程连接
- 允许任何远程着桌面的连接
- 修改 Windows 的安全策略,允许远程桌面连接使用空密码
win+r 打开运行
输入 gpedit.msc 打开策略组
计算机配置
windows 设置
安全设置
本地策略
安全选项
拉到最下面
禁用 使用空密码的本地账户只允许控制台登录
电脑提示“账户名与安全标识间无任何映射完成” 一般是修改玩用户名后没有重启导致的。
因为公司的电脑不能设密码,同时我又想远程桌面公司的电脑,考虑到安全问题,不能用默认用户名裸奔,所以修改用户名和设置无密码的远程桌面
ss 的使用
需要一个在 wall 以外的服务器
ss 的各个版本 https://github.com/shadowsocks/shadowsocks/wiki/Feature-Comparison-across-Different-Versions
python
- 下载 python
- 安装 python
- 把 python 添加到环境变量
- 刷新环境变量
- 下载 ss
python -m pip install shadowsocks # 如果上面那句下载失败,可以尝试用这句安装 python -m pip install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U # 如果上面那句还是下载失败,可以尝试先把 master.zip 文件下载到本地,再安装
- 在服务器的策略组放行相应的端口
- 在防火墙放行相应的端口
- 运行以下命令
ssserver -p 61813 -k windows@163.qq -m aes-256-cfb -p 是端口号 -k 是密码 -m 是加密方式 上面那条命令需要在 服务器的策略组 和 防火墙 放行 61813 端口
- 下载客户端
- 正确填写 ip 端口 密码
- 一些加密的算法会依赖这个库 libsodium ,最好把这个库也装上
https://download.libsodium.org/libsodium/releases/
- 更新 ss
python -m pip install --upgrade shadowsocks # 如果上面那句更新失败,可以尝试这样更新,先卸载再重新安装,卸载前记得先备份当前的版本 python -m pip show shadowsocks python -m pip uninstall shadowsocks python -m pip install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U
- 失效时,可以尝试,更改端口,更改密码,更改加密方式,换一个ip
- 参考
https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
libev
- 安装
- debian
apt update apt install shadowsocks-libev
- debian
- 作为服务端运行
对应的文档 https://github.com/shadowsocks/shadowsocks-libev/blob/master/doc/ss-server.asciidocss-server -s 0.0.0.0 -p 61813 -k windows@163.qq -m aes-256-cfb 或者 ss-server -c config.json { "server": "0.0.0.0", "server_port": 61813, "password": "windows@163.qq", "method": "aes-256-gcm" }
- 作为客户端运行
对应的文档 https://github.com/shadowsocks/shadowsocks-libev/blob/master/doc/ss-local.asciidocss-local -s host -p 61813 -k windows@163.qq -m aes-256-cfb -l 1080 或者 ss-local -c config.json { "server": "host", "server_port": 61813, "local_address": "0.0.0.0", "local_port": 1080, "password": "windows@163.qq", "timeout": 600, "method": "aes-256-gcm" }
VS 和 VC 库 的对应关系
Visual Studio 6.0 | VC6 |
Visual Studio .NET 2002 | VC7.0 |
Visual Studio .NET 2003 | VC7.1 |
Visual Studio 2005 | VC8 |
Visual Studio 2008 | VC9 |
Visual Studio 2010 | VC10 |
Visual Studio 2012 | VC11 |
Visual Studio 2013 | VC12 |
Visual Studio 2015 | VC14 |
Visual Studio 2017 | VC15 |
Visual Studio 2019 | VS16 |
Visual Studio 2022 | VS17 |
- Visual C++ 6.0 是 Visual Studio 6.0 的一部分,但可以独立安装,类似于 word 和 office 之间的关系
- 从 VC10 开始才有 64位
- vc 库的下载地址 https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist
php 版本和 VC 库的对应关系
5.2 | vc6 |
5.3 | vc9 |
5.4 | vc9 |
5.5 | vc11 |
5.6 | vc11 |
7.0 | vc14 |
7.1 | vc14 |
7.2 | vc15 |
7.3 | vc15 |
7.4 | vc15 |
8.0 | vs16 |
8.1 | vs16 |
- 其实下载下来的压缩包名有写着对应的 vc 库,系统位数, ts 和 nts
Windows 安全地删除U盘
U盘 弹出失败的原因是进程占用了 U盘 ,只要占用 U盘 的进程都不在占用 U盘 或 都结束了, U盘 就可以安全地弹出了。
所以,让 U盘 安全地弹出的关键是找到占用 U盘 的进程。
当占用 U盘 的进程结束后,有时立即弹出 U盘 还是会失败的,这时再等待五六秒,再试一次弹出 U盘 就可以了。
可以安全退出 U盘 时也不要马上拔 U盘 ,最好等个五六秒再拔 U盘 。
这是各种方法的总结
- 最简单的,在系统托盘右键,弹出,或在此电脑的界面,选中 U盘,右键,弹出
- 关掉所有文件夹再试一次弹出 U盘
- explorer.exe 重启,再试一次弹出 U盘
- 注销当前的登录,再次登录,再试一次弹出 U盘
- 通过任务管理器找到占用 U盘 的进程
- 打开任务管理器
- 切换到性能页面
- 打开资源监视器
- 切换到 cpu 页面
- 在 关联的句柄 栏目的搜索框内输入你的 U盘 盘符(如G:\)
- 即可看到当前占用 U盘 的进程
- 关闭 U盘 的写入缓存
- 控制面板
- 管理工具
- 计算机管理
- 设备管理
- 磁盘驱动
- 选择当前的 U盘
- 右键 属性 策略
- 关闭写缓存
- 关闭写缓存可能会造成 U盘 数据的丢失,关闭了写缓存后,要确保 U盘 里的文件已经保存好再弹出 U盘
- 在磁盘管理里使 U盘 脱机
- 控制面板
- 管理工具
- 磁盘管理
- 选择当前的 U盘
- 右键 脱机
- U盘 脱机后下次插入要重新挂载,脱机之后可以马上联机,再安全弹出,这样 U盘 下次插入时就不用联机了
- 脱机会直接关掉所有的文件句柄,要确保 U盘 里的文件已经保存好再脱机
- 脱机选项不可选,可能是有虚拟内存分配到 U盘 ,这时取消在 U盘 上的虚拟内存即可
- 进入电脑属性->高级系统设置->高级->性能设置->高级->更改,关闭自动管理分页文件大小,取消在该磁盘上设置的虚拟内存
- 有时脱机还会失败,这时就继续参考下面的步骤
- 通过 日志 查找占用 U盘 的进程
- 打开事件查看器
- windows日志
- 系统
- 找到最近的一条 来源 Kernel-PnP 的记录
- 记录的常规里有占用 U盘 的 进程id 的
- 再通过任务管理器的 pid 找到对应的进程
- 有时,即使找到占用 U盘 的进程,但却无法结束进程,或 进程是系统的核心进程
- 这时可以尝试以这样的关键词(进程名 或 进程id + Kernel-PnP )在网上搜索解决的方法
- 如果是 Windows Defender 占用 U盘 可以试试这样操作
- 打开 Windows Defender 安全中心
- 病毒和威胁防护
- 病毒和威胁防护 设置
- 将在使用的 U盘 盘符添加为排除项
- 最后的方法,关机,关机后就肯定可以安全地拔 U盘
笔者通常在直接弹出 U盘 失败后,会多试几次,然后通过 日志 找到对应的进程,然后通过 任务管理器 结束对应的进程,如果结束进程失败,还是会再次在网上搜索解决方法。 脱机 和 关闭写缓存都有一点副作用; 关闭文件夹,重启 explorer.exe ,注销,关机,这类都有点麻烦。
windows 下的 linux 环境
- Git for Windows
- mingw
- mingw-w64
- TDM-GCC
- Cygwin
- MSYS
- MSYS2
- gnuwin
- windows10 子系统
- wsl1
- wsl2
- 虚拟机
- VMware
- VirtualBox
- Hyper-V
- qemu
其实就三种套路
- 子系统
- 虚拟机
- 运行在 win32 上的兼容层
wsl1 和 wsl1 之前的 sfu/sua 是子系统, wsl2 其实算是虚拟机,其它的都是运行在 win32 上的兼容层。 nt 内核其实是有三个子系统 win32 ,os/2 和 posix ,只是除了 win32 其它两个都没有什么存在感。
所谓的 linux 环境,除了需要 bash 之外,还需要各种 linux 的工具,还需要处理管道,还需要处理文件名和路径的问题。 还有各种 linux api 的问题(但不写需要编译的代码,只使用环境中的工具,其实这个问题可以忽略的)。
下载 AcFun 视频
0. 需求
- 一部安卓手机
- AcFun 安卓版客户端
- ffmpeg
1. 使用手机客户端缓存视频
2. 在手机的文件管理器里找到缓存文件
- 用文件管理器打开手机的根目录
- 找到 acfun 文件夹
- 点进这个目录里 acfun→core→local
- 然后点进一个以数字名命的文件夹
- 这个文件夹里就是缓存的视频,文件名都没有后缀,可以通过创建时间大致判断出哪个是最新缓存的视频
3. 在电脑里下载 ffmpeg
- 打开 ffmepg 的下载网址,并选择系统对应的版本下载
http://www.ffmpeg.org/download.html
- 把下载后的文件解压
- 解压后的文件里会有个一个名为 ffmpeg 的文件,这个就是 ffmpeg 的主程序,可以用来转换视频格式
4. 把缓存的视频文件复制进电脑,并使用 ffmpeg 转换为 MP4 格式
- 把缓存文件从手机复制进 ffempeg 的解压目录
- cd 进 ffempeg 的解压目录
- 运行以下命令,转换格式后的文件名必须带有 MP4 的后缀
ffmpeg -i "缓存视频文件的文件名" "转换格式后的文件名"
在Windows下配置Tomcat服务器
0. 目标
- 在 Windows 下配置 Tomcat 服务器
- Windows 10 (x64)
- Tomcat 9
- JAVA 8
1. 下载 JAVA
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
选择,Windows 64 位
2. 安装 JAVA
打开下载的 exe 文件,一路点 next 直到安装完毕
3. 配置 JAVA 环境变量
- 新建一个环境变量 JAVA_HOME ,值为 JDK 的安装目录,例子
C:\Program Files\Java\jdk1.8.0_161
- 把以下值加入到环境变量 Path
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
%JAVA_HOME%\lib
3. 下载 Tomcat
https://tomcat.apache.org/download-90.cgi
选择,core,Windows 64 位
4. 配置 Tomcat
- 解压下载的文件
- 把解压后的文件夹复制到 C 盘的根目录下(这里可以是任意目录)
- 把 Tomcat 目录下的 bin 文件夹的路劲加入的环境变量
- 把网站程序复制进 Tomcat 的 webapps 文件夹
- 启动 Tomcat ,启动 Tomcat 的脚本在 bin\startup.bat
DOS 的启动过程
- 从启动盘中读取这两个系统文件
- IO.SYS
- MSDOS.SYS
- 启动盘的根目录下寻找并执行这三个文件
- CONFIG.SYS
- COMMAND.COM
- AUTOEXEC.BAT
- IO.SYS、MSDOS.SYS 和 COMMAND.COM 是系统的核心
- CONFIG.SYS 用来配置系统运行环境
- AUTOEXEC.BAT 用来自动执行一些批处理命令
- IO.SYS、MSDOS.SYS 和 COMMAND.COM 其中一个缺失了,系统会无法启动
- CONFIG.SYS 或 AUTOEXEC.BAT 缺失了,系统依然能启动,但一些软件或驱动可能无法正常运行
安装 PHP7 的 GUI 扩展
1 下载拓展
- https://pecl.php.net/package/ui
- 按照本地的 PHP 版本下载对应的拓展文件
2 安装拓展
- 把下载下来的压缩包解压
- 把 php_ui.dll 复制到 PHP 的 ext 目录下
- 把 libui.dll 和 pthreadVC2.dll 放到 PHP 的根目录下
3 运行 demo
- 解压的压缩包里有四个 demo
- gallery.php histogram.php snake.php starfield.php
- 可以使用命令行运行
- 例如 php snake.php
- 如果是 windows 系统,可以使用 php-win 运行,这样就没有黑框了
注意
如果出现这种错误
无法启动此程序,因为计算机中丢失 libui.dll,尝试重新安装该程序以解决此问题。
这个提示出现说明你没有放入 libui 和 pthreadVC2 文件到 php 的根目录下
linux 实现后台不间断运行
- supervisor
- supervisor 是守护进程
- supervisor 会根据配置运行对应的 start/stop/restart/reload 命令
- supervisor 是子进程的父进程,子进程退出时,父进程能收到相关的信号 SIGCHLD , supervisor 收到信号后就能执行相应的操作,例如重启进程
- supervisor 不能监控守护进程是因为守护进程的父进程 id 不是 supervisor 的进程id,所以当守护进程退出时 supervisor 无法收到 SIGCHLD 信号
- screen/tmux 这类终端复用的软件
- tmux 分成两部分 server 和 client ,其中 server 是守护进程
- 通过 ssh 连接到服务器后 ssh server 会 fork 一个 shell ,远程终端就是显示这个 shell 的输入和输出
- ssh 的 shell 能通过 tmux 的 clinet 让 tmux 的 server 再新建一个 shell
- 这个新建的 shell 的父进程是 tmux 的 server ,所以当前终端关闭不会关闭这个 shell ,从而达到了类似于后台运行的效果
- 大致的示意图
remote terminal <----> ssh server <----> shell <----> tmux client <----> tmux server <----> shell
- 把代码改写成支持以 守护进程 的形式运行
- 步骤
- fork子进程,父进程退出(必须)
- 子进程创建新会话(必须,setsid())
- 改变当前工作目录 chdir(非必须,建议做 chdir("/var");)
- 重设子进程掩码(非必须 umask(0))
- 关闭文件描述符(非必须)
close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 另一种关闭文件描述符的方法,但 getdtablesize() 来自 unistd.h // for(i = 0; i < getdtablesize(); i++) // { // close(i); // }
- 执行核心工作(必须)
- C 的例子
- 新建源码文件
vi deamon.c
- 代码
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <time.h> int main(int argc, char *argv[]) { pid_t pid = fork(); if (pid > 0) { exit(0); // 1.父进程退出 } else if (pid == 0) { // 2.变为会话组长,脱离了控制终端 setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离 setsid(); // 3.改变进程的工作目录 chdir("/var"); // 4. 重置文件掩码 umask(0); // 5. 关闭文件描述符 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 6. 执行核心操作 // 每三秒一条记录到日志文件里,执行100次 int fd = open("/var/temp-deamon-log.txt", O_CREAT | O_WRONLY | O_APPEND, 0664); int i; for (i = 0; i < 100; i++) { sleep(3); time_t curtime; time(&curtime); char* pt = ctime(&curtime); write(fd, pt, strlen(pt) + 1); } close(fd); } return EXIT_SUCCESS; }
- 编译运行命令
gcc deamon.c && \ ./a.out && \ sleep 30 && \ cat -n /var/temp-deamon-log.txt && \ kill `ps -elf | grep a.out | awk '{print $4}'` && \ rm /var/temp-deamon-log.txt
- 新建源码文件
- 这种是 sysvinit 体系下的 守护进程 ,虽然 systemd 也兼容,但 systemd 似乎会有不一样的更高效的实现。
- 步骤
- nohup 配合 &
- nohup 的作用,nohup 英文全称 no hang up(不挂起)
- 将 stdin 重定向到 /dev/null
- 将 stdout 和 stderr 重定向到 nohup.out 或者用户通过参数指定的文件
- 屏蔽掉 SIGHUP 信号,因为 SIGHUP 被屏蔽了,所以会话关闭后程序能继续运行
- 调用 exec 启动指定的命令
>> out.log
把输出追加到out.log
文件里2>&1
把 stderr 重定向到 stdout- & 的作用是把程序放到后台运行,但不会改变 stdin stdout stderr
- 例子
nohup php queue.php >> out.log 2>&1 &
- nohup 的作用,nohup 英文全称 no hang up(不挂起)
- systemctl
- 创建一个 service
- 类型为 service 的 unit
- 在
/etc/systemd/system
目录新建一个文件 myDaemon.service[Unit] Description=myDaemon service [Service] # service 的类型 Type=simple # 退出后马上重启 Restart=always # 这里的 bash 路径和脚本路径都必须是绝对路径 ExecStart=/bin/bash /root/myDaemon.sh [Install] # 开机启动时的依赖项,大多数情况下都是填这个 WantedBy=multi-user.target
- 启动 service 并加入到开机启动中
# 重新加载 systemctl 配置 systemctl daemon-reload # 启动 myDaemon.service systemctl start myDaemon.service # myDaemon.service 加入到开机启动中 systemctl enable myDaemon.service
- 创建一个 service
- cron *****
- 例子
* * * * * php queue.php >> out.log 2>&1
- 例子
- docker run 同时加上这两个参数 --restart always 和 -d ,也能达到类似的效果(docker 要设置好开机启动和服务)
gcc 编译流程
编译的四个步骤
- 预处理 由 .c 文件到 .i 文件。
- 对各种预处理命令进行处理,包括头文件包含、宏定义的扩展、条件编译的选择等
- 编译 由 .i 文件到 .s 文件。
- 将预处理得到的源代码文件,进行“翻译转换”,产生出机器语言的目标程序,得到机器语言的汇编文件
- 汇编 由 .s 文件到 .o 文件。
- 将汇编代码翻译成了机器码,但是还不可以运行
- 链接 由 .o 文件到可执行文件。
- 处理可重定位文件,把各种符号引用和符号定义转换成为可执行文件中的合适信息,通常是虚拟地址
编译的四个步骤对应的 gcc 命令
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test
实际上 gcc 这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预处理器cpp、预编译程序cc1、汇编器as、链接器ld
cpp test.c -o test.i
cc1 test.i -o test.s
as test.s -o test.o
ld test.o -o test
- 头文件应该是在预处理阶段加上去的
- 各种库应该是在链接阶段加上去的
GNU 的命令
GNU 工具链
- glibc
- gcc
- gdb
- gnu make
- gnu bison
- autotools GNU构建系统(GNU Build System)又名Autotools
- autoscan
- aclocal
- autoconf
- autoheader
- automake
- binutils 二进制工具组
- as 汇编
- ld 连接
- nm 显示目标文件内的符号
- ar 静态库归档
- objdump 反汇编
- readelf elf 结构分析工具
- coreutils GNU核心工具组
- fileutils 文件工具
- chgrp chown chmod cp ls mkdir rm touch
- textutils 文本工具
- cat head tail wc
- shellutils shell 工具
- echo printf nohup pwd sleep
- fileutils 文件工具
- findutils GNU查找工具组
- find
- xargs
- bash
- gzip
- gnu grep
- gnu awk
- gnu sed
- grub
- ...
其它
- flex(快速词法分析产生器,英语:fast lexical analyzer generator)是一种词法分析程序。
- 它是lex的开放源代码版本,以BSD许可证发布。
- 通常与 GNU bison 一同运作,但是它本身不是 GNU 计划的一部分。
- pkg-config 是一个在源代码编译时查询已安装的库的使用接口的计算机工具软件。 pkg-config 托管在 freedesktop.org 。 php 编译需要这个
- 使用的效果大概像这样
# 没使用 pkg-config gcc test.c -o test -I/usr/local/Cellar/opencv3/3.1.0_4/include/opencv -I/usr/local/Cellar/opencv3/3.1.0_4/include # 使用了 pkg-config gcc test.c -o test $(pkg-config opencv --cflags)
- BusyBox 是 GNU Core Utilities 的另一个开源替代,通常用在嵌入式系统里
- util-linux, GNU 核心工具组中未包含的一组大约100个基本 Linux 系统实用程序,例如mount,fdisk,more和kill
- IEEE Std 1003.1-2008 utilities
- 这个列表中的UNIX实用程序由IEEE Std 1003.1-2008定义,是单一UNIX规范(SUS)的一部分。列表中的实用程序可以在UNIX操作系统和绝大多数类UNIX操作系统中找到。
参考
- https://zh.wikipedia.org/wiki/GNU%E8%BD%AF%E4%BB%B6%E5%8C%85%E5%88%97%E8%A1%A8
- https://zh.wikipedia.org/wiki/GNU%E6%A0%B8%E5%BF%83%E5%B7%A5%E5%85%B7%E7%BB%84
感觉 GNU 真的除了内核之外,什么都有了。
让网站支持 ipv6
- 拥有一个 ipv6 地址
- 域名解释 aaaa 记录指向 ipv6 的地址
- 让网站程序支持 ipv6 地址
- 通过网关翻译
- 代码里用 ipv4 地址,域名解释 aaaa 记录到 网关,网关把 ipv6 的流量翻译成 ipv4 的流量再传递给网站
- 代码里用 ipv6 地址,域名解释 a 记录到 网关,网关把 ipv4 的流量翻译成 ipv6 的流量再传递给网站
- 让 web 服务器同时监听两个地址,代码里和 ip 相关的部分,都改成能兼容 ipv4 和 ipv6
- 通过网关翻译
用来测试网站对 ipv6 的支持,也可以用来查看 DNSSEC 的支持 https://ipv6.ustc.edu.cn/onlinechecklog.php
在命令行里格式化 json
python 的 json.tool ,好像 python2 和 3 都可以这样用
echo '{ "name": "xiaohong", "age": 18 }' | python -m json.tool
- 会转义中文,如果想不转义中文
- 需要修改标准库里的文件,这样不是很好,在 python 的标准库 json 文件夹下有个 tool.py 文件,更改其中调用的 json.dump 函数,传一个ensure_ascii = False 参数即可
json_pp , windwos 的 git bash 和 大多数 linux 发行版都有这个工具
echo '{ "name": "xiaohong", "age": 18 }' | json_pp
jq , 虽然大多数 linux 发行版都没有这个工具,但中文互联网环境下有好多文章都推荐这个
php 的命令行,标准输入中一定要有数据,不然会一直等待;同样地 python 或 node 也可以实现类似的
echo '{ "name": "xiaohong", "age": 18, "chinese character":"汉字" }' | \ php -r 'print(json_encode(json_decode(file_get_contents("php://stdin")),JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));';
PowerShell 的 ConvertFrom-Json 和 ConvertTo-Json ,这两个要组合来使用
echo '{"type":"image","offset":0,"count":20}' | ConvertFrom-Json | ConvertTo-Json
配合 curl 使用
curl -s -k https://localhost/dev.json | json_pp
curl -s -k https://localhost/dev.json | python -m json.tool
- -s 只输出 body
- -k 忽略 https
配合 PowerShell 的 Invoke-WebRequest 使用
Invoke-WebRequest https://localhost/dev.json -UseBasicParsing -SkipCertificateCheck | Select -ExpandProperty Content | ConvertFrom-Json | ConvertTo-Json
Invoke-WebRequest https://localhost/dev.json -UseBasicParsing -SkipCertificateCheck | Select -ExpandProperty Content | python -m json.tool
- -UseBasicParsing 把结果输出到命令行
- -SkipCertificateCheck 忽略 https ,这个参数要在 PowerShell6 之后才有效
- Select -ExpandProperty Content 选取 body 的输出
如果 Invoke-WebRequest 出现了这种错误。
因为 Internet Explorer 引擎不可用,或者 Internet Explorer 的首次启动配置不完整
可以尝试以下步骤来解决
- 打开 IE 的 internet 选项
- 点击安全选项卡,选中本地 intranet ,并点击站点按钮
- 新的窗口中点击高级
- 添加 about:security_powershell.exe 到输入框,点击添加
- 把所有 IE 窗口一个个关闭就好了,再次在 powershell 下运行 Invoke-WebRequest
Windows 里的 Java 环境配置
- 下载并安装 JDK
- 这些是免费的 JDK
- 这是 oracle 的 JDK
- 如果是 JDK 1.8 以上的版本,笔者比较倾向于用 microsoft 的 openjdk
- 通常只有 oracleJDK 需要安装,其他的 JDK 都是压缩包,直接解压就好了
- 把 JDK 加入环境变量
- 新建一个新的环境变量 JAVA_HOME ,值是 JDK 的根目录
- 在 PATH 里加入 %JAVA_HOME%\bin 和 %JAVA_HOME%\lib 和 %JAVA_HOME%\jre\bin (如果没有这个目录就忽略)
- 如果是 JDK 1.5 及之前的版本还需要一个 CLASSPATH 的环境变量
- CLASSPATH 的值是 .;%Java_Home%\bin;%JAVA_HOME%\lib;%Java_Home%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
- 下载并安装 maven
- 把 maven 加入环境变量
- 新建一个新的环境变量 MAVEN_HOME ,值是 maven 的根目录
- 在 PATH 里加入 %MAVEN_HOME%\bin
windows10 之前的系统在修改 path 时要注意分号 ;
在 JDK1.5 以后,classpath 并不是必须配置了,在 JDK1.5 之前,是没有办法在当前目录下加载类的(找不到 JDK 目录下 lib 文件夹中的 .jar 文件),所以我们需要通过配置 classpath ,但 JDK1.5 之后, JRE 能自动搜索目录下类文件,并且加载 dt.jar 和 tool.jar 的类。 dt.jar 是关于运行环境的类库,主要是用于 swing 的包,如果不使用可以不配置。 tools.jar 是工具类库。
编译和运行时可以通过参数 -classpath 指定 classpath 的路径,例如这样
javac -encoding UTF-8 -classpath .;./junit4.jar;./org.hamcrest.core_1.3.0.jar AaaTest.java JunitRunner.java
java -Dfile.encoding=UTF-8 -classpath .;./junit4.jar;./org.hamcrest.core_1.3.0.jar JunitRunner
无法执行 powershell 脚本
通常是执行策略的原因导致的。
设置脚本执行策略,通常把 策略 设为 RemoteSigned 就可以了
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
查看 powershell 脚本执行策略
Get-ExecutionPolicy
Get-ExecutionPolicy -List
python 的 http 服务和 cgi
python 一行命令启动 http 服务
# 最简单的 http 服务
python -m http.server
# 有 cgi 的 http 服务
python -m http.server --cgi
# 有 cgi 的和指定端口号的 http 服务
python -m http.server --cgi 8081
# 有 cgi 的,指定端口号的和指定ip地址的 http 服务
python -m http.server --cgi 8081 --bind 127.0.0.1
# 有 cgi 的,指定端口号的,指定ip地址的和指定站点根目录的 http 服务
python -m http.server --cgi 8081 --bind 127.0.0.1 --directory _book
- 所有 cgi脚本或程序 都必须有执行权限;在 linux 环境下是 755 ,整个路径都必须是可读可执行的。
- 默认情况下 cgi脚本或程序 要在这个目录下 /cgi-bin 。
- windows 环境下 py 脚本开头那句 #! 是没有效果的,其实在 windows 环境下没有开头那句也没问题的。
- windows 环境下只要不是 py 后缀的都会被当成 可执行文件, linux 可以执行其他脚本。
- 端口号要在 --cgi 参数后面。
- 无法设置 响应行 和 响应码,响应码都是 200 。 (
起码 3.10 还是这样)
请求 cgi 脚本的例子
curl 127.0.0.1:8000/cgi-bin/test.py
cgi 脚本的例子
#!/usr/bin/python3
print ("Content-type:text/html")
print () # 空行,告诉服务器结束头部
print ('<html>')
print ('<head>')
print ('<meta charset="utf-8">')
print ('<title>Hello Word</title>')
print ('</head>')
print ('<body>')
print ('<h2>Hello Word!</h2>')
print ('</body>')
print ('</html>')
#!/usr/bin/python3
print ("Content-type: application/json")
print () # 空行,告诉服务器结束头部
print ('{"result": "this is a test"}')
这个例子大致相当于这个命令 python -m http.server 8888
from http.server import HTTPServer, SimpleHTTPRequestHandler
if __name__ == '__main__':
host = ('0.0.0.0', 8888)
server = HTTPServer(host, SimpleHTTPRequestHandler)
print('Serving HTTP on {host} port {port} (http://{host}:{port}/) ...'.format(host=host[0], port=host[1]))
server.serve_forever()
这个例子大致相当于这个命令 python -m http.server --cgi 8888
from http.server import HTTPServer, CGIHTTPRequestHandler
if __name__ == '__main__':
host = ('0.0.0.0', 8888)
server = HTTPServer(host, CGIHTTPRequestHandler)
print('Serving HTTP on {host} port {port} (http://{host}:{port}/) ...'.format(host=host[0], port=host[1]))
server.serve_forever()
不使用命令行的 http 服务例子,这是单线程的
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'result': 'this is a test'}).encode())
if __name__ == '__main__':
host = ('0.0.0.0', 8888)
server = HTTPServer(host, myHandler)
print('Serving HTTP on {host} port {port} (http://{host}:{port}/) ...'.format(host=host[0], port=host[1]))
server.serve_forever()
不使用命令行的 http 服务例子,这是多线程的
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
import json
class ThreadingHttpServer(ThreadingMixIn, HTTPServer):
pass
class myHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(json.dumps({'result': 'this is a test'}).encode())
if __name__ == '__main__':
host = ('0.0.0.0', 8888)
server = ThreadingHttpServer(host, myHandler)
print('Serving HTTP on {host} port {port} (http://{host}:{port}/) ...'.format(host=host[0], port=host[1]))
server.serve_forever()
不使用命令行的 http 服务例子,这是多线程的,再加上 cgi 的支持
from http.server import HTTPServer, CGIHTTPRequestHandler
from socketserver import ThreadingMixIn
class ThreadingHttpServer(ThreadingMixIn, HTTPServer):
pass
if __name__ == '__main__':
host = ('0.0.0.0', 8888)
server = ThreadingHttpServer(host, CGIHTTPRequestHandler)
print('Serving HTTP on {host} port {port} (http://{host}:{port}/) ...'.format(host=host[0], port=host[1]))
server.serve_forever()
HTTPServer 和 BaseHTTPRequestHandler 是两个关键的类,一个用于接收 http 请求,一个用于处理请求,其它类基本是派生自这两个类
BaseHTTPRequestHandler -> SimpleHTTPRequestHandler -> CGIHTTPRequestHandler
HTTPServer -> ThreadingHttpServer
参考文档
- https://www.runoob.com/python3/python3-cgi-programming.html
- https://docs.python.org/zh-cn/3/library/cgi.html
- https://docs.python.org/zh-cn/3/library/http.server.html
debian 一句命令安装 docker
因为要经常部署和重装系统,所以就整理了这样一句命令,可能会因为 docker 的更新而失效,要注意修命令里的版本号
具体环境
- debian 11
- Docker version 20.10.12, build e91ed57
- docker-compose version 1.29.2, build 5becea4c
这是文档 https://docs.docker.com/engine/install/debian/
sudo apt-get remove -y docker docker-engine docker.io containerd runc ; \
sudo apt-get update && \
sudo apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release && \
if [ -e /usr/share/keyrings/docker-archive-keyring.gpg ] ; \
then rm /usr/share/keyrings/docker-archive-keyring.gpg; \
else curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg ;fi && \
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null && \
sudo apt-get update && \
sudo apt-get install -y docker-ce docker-ce-cli containerd.io && \
sudo docker run --rm hello-world && \
sudo curl -L --retry 100 --retry-delay 2 "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose && \
docker-compose --version
如果 docker-compose 总是下载失败,可以尝试使用这样的脚本下载
COMMAND="curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose"
for ((i=0;i<100;i++))
do
$COMMAND
if [ $? -eq 0 ]; then
exit 0;
fi
done
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
生成用于测试的容器的命令
具体环境
- debian 11
- Docker version 20.10.12, build e91ed57
docker pull debian:11
docker run \
-it \
--rm \
-p 443:443 \
debian:11 /bin/bash
cp /etc/apt/sources.list /etc/apt/sources.list_bak && \
sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list && \
apt update && \
apt install -y vim && \
apt install -y curl && \
apt install -y net-tools && \
apt install -y netcat && \
apt install -y procps
apt install -y bsdmainutils
apt install -y python3
apt install -y python3-pip
curl 断点续传
例子
curl -C - -o php-7.3.11.tar.gz https://www.php.net/distributions/php-7.3.11.tar.gz
解释
-L 允许重定向
-C 开启断点续传
- 这个表示开始和结束位置,一般就这样就可以了
--retry 100 超时重试 100 次,也可以是断点续传时的重试
--retry-delay 5 每次超时后等待 5 秒再重试
--connect-timeout 5 连接超过 5 秒算超时
一次连接超时时间。如果出错, 提示形如:curl: (28) connect() timed out!
--max-time 10 单次请求的最大时间
一次连接过程最大的允许时间。出错提示如:curl: (28) Operation timed out after 2000 milliseconds with 0 bytes received
--max-time 要大于 --connect-timeouts
--retry-max-time 30 整体请求的最大时间
-o 参数将服务器的回应保存成文件
-O 参数将服务器回应保存成文件,并将 URL 的最后部分当作文件名。
如果服务器主动返回 失败 例如 reset 这类的,就会直接退出的了,不论有没有设置 --retry
github 好像不支持断点下载
在 debian 新建命令的别名
- 只对当前终端有效
- 直接运行这个命令
alias ll="ls -l "
- 直接运行这个命令
- 终端退出后仍然有效
- 在 ./bashrc 里加上一行
alias ll="ls -l "
- 然后运行这个命令 source ~/.bashrc
- 在 ./bashrc 里加上一行
用于 git pull 和 git push 的脚本
因为 github 的 pull 和 push 总是超时,所以写了两段脚本用于失败后的自动重试
#!/bin/bash
# ./git_help.sh pull
# ./git_help.sh push
GIT_COMMAND="git "
case $1 in
"push"|"pull")
GIT_COMMAND=$GIT_COMMAND$1
;;
*)
echo "only input push or pull";
exit 1;
;;
esac
for ((i=0;i<100;i++))
do
$GIT_COMMAND
if [ $? -eq 0 ]; then
exit 0;
fi
done
# ./git_help.ps1 pull
# ./git_help.ps1 push
param($a)
$GIT_COMMAND = "git "
if ($a -eq "push") {
$GIT_COMMAND = $GIT_COMMAND + $a
} elseif ($a -eq "pull") {
$GIT_COMMAND = $GIT_COMMAND + $a
} else {
echo "only input push or pull"
exit 1
}
for ($i = 1; $i -lt 100; $i++) {
echo $i" "$GIT_COMMAND
PowerShell -command $GIT_COMMAND
# Invoke-Expression $GIT_COMMAND
# Invoke-Command -ScriptBlock {Write-Host $GIT_COMMAND}
# Invoke-Command -ScriptBlock {$GIT_COMMAND}
if ($? -eq $true) {
break
}
}
href 和 src
href 是 Hypertext Reference (超文本引用) 的缩写。 href 目的是为了建立联系,让当前标签能够链接到目标地址。
src 是 source (来源) 的缩写。 指向的内容将会应用到文档中当前标签所在位置。 例如 img 和 script 的 src 属性。
href 和 src 的值通常是 url 但也可以不是 url 。 其中一个例子就是 img 标签的 src 可以直接放 base64 的值。
可以简单但不严谨地理解为 href 不会加载, src 会加载。 这样解释好象很有道理。 但 link 标签是用 href 的。 link 标签能加载 css 和 页面图标 还有 manifest 还有 预加载的 js 。
URI URL URN
它们的全称
- URI: Uniform Resource Identifier (统一资源标识符)
- URN: Uniform Resource Name (统一资源名称)
- URL: Uniform Resource Locator (统一资源定位符)
- URC: Uniform Resource Characteristic (统一资源特征)
URL,URN,URC 都属于 URI 。 语法都是
URI = scheme ":" scheme-specific-part
URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment]
authority = [userinfo "@"] host [":" port]
一些例子
userinfo host port
┌──┴───┐ ┌──────┴──────┐ ┌┴┐
https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top
└─┬─┘ └───────────┬──────────────┘└───────┬───────┘ └───────────┬─────────────┘ └┬┘
scheme authority path query fragment
ldap://[2001:db8::7]/c=GB?objectClass?one
└┬─┘ └─────┬─────┘└─┬─┘ └──────┬──────┘
scheme authority path query
mailto:John.Doe@example.com
└─┬──┘ └────┬─────────────┘
scheme path
news:comp.infosystems.www.servers.unix
└┬─┘ └─────────────┬─────────────────┘
scheme path
tel:+1-816-555-1212
└┬┘ └──────┬──────┘
scheme path
telnet://192.0.2.16:80/
└─┬──┘ └─────┬─────┘│
scheme authority path
urn:oasis:names:specification:docbook:dtd:xml:4.1.2
└┬┘ └──────────────────────┬──────────────────────┘
scheme path
URN
URN 的作用是描述资源的身份,例如 一个人的名字。 URN 的其中一个应用例子是 图书的 ISBN 号码 。
URN 的语法
urn:NID:NSS
NID 是 namespace identifier (命名空间标识符) 的缩写。
NSS 是 namespace specific string (命名空间特定字符串) 的缩写。
URN 的 NID 和 NSS 部分相当于 URI 里的 path 部分。
URL
URL 的作用是描述资源的访问路径,例如 一个人的住址。
URL 的语法
hierarchical part
┌───────────────────┴─────────────────────┐
authority path
┌───────────────┴───────────────┐┌───┴────┐
abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
└┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘
scheme user information host port query fragment
其它
URC (统一资源特征),在九十年代的时候, URL URI URC 被期望能组成一个互联网信息架构。 但 URC 一直停留在理论阶段,随之更晚出现的其他技术(例如 资源描述框架)取代了它们。
URI 的 scheme 和 URN 的 NID 都需要在 IANA 注册。
完整的 url 或 urn 会被称为绝对 uri , 只有一部分的 url 或 urn 会被称为相对 uri 。
还有一个 Data URI 或者叫做 Data URL 。 这是具体的语法,也属于 URI 。
dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
mediatype := [ type "/" subtype ] *( ";" parameter )
data := *urlchar
parameter := attribute "=" value
比较好区分的一个特征是,以 urn 开头的 uri 就一定是 urn , 以 data 开头的 uri 就一定是 data url 。 除此之外的都是 url 。
url 两个斜杆 // 其实是没什么作用的
- 联网创始人蒂姆·伯纳斯-李承认,互联网地址中http:后面的两条斜线并无必要。伯纳斯-李解释说,虽然当时的编程惯例是双斜线,但结果并非真正必要。
- 虽然是这样,但 // 开头的 url 可以兼容 http 和 https 的链接
为什么文件 URL 以3斜杠开头?
- file:/// 之所以是三个斜杆,是因为忽略了主机名,更完整的 url 应该是这样的 file://host/
- 这两句 curl 的命令效果是一样的
curl -i -v file:///C:/Users/example.txt curl -i -v file://localhost/C:/Users/example.txt
参考
- uri https://datatracker.ietf.org/doc/html/rfc3986
- uri url run 之间的关系 https://datatracker.ietf.org/doc/html/rfc3305
- data url https://datatracker.ietf.org/doc/html/rfc2397
- https://en.wikipedia.org/wiki/List_of_URI_schemes
linux 系统的启动流程
- 通电
- 加电自检 (POST, Power-On Self-Test)
- 硬件初始化
- legacy BIOS
- MBR
- UEFI
- GPT
- legacy BIOS
- 执行引导程序 (bootloader)
- LILO (LInux LOader)
- SYSLINUX
- GRUB (GNU Grand Unified Bootloader)
- 加载和启动 linux 内核镜像
- initrd
- initramfs
- 执行 init
- init (SystemV init)
- UpStart
- systemd (system deamon)
- 执行守护进程
现在主流的启动过程是
MBR -> GRUB -> initramfs -> systemd
简单但又不严谨地理解, init 是第一个运行在用户态的进程。 这里描述的 init 可能是 SystemV init 也可能是 UpStart 也可能是 systemd 。
笔者认为作为一个写上层应用的程序员,了解到 加载和启动 linux 内核镜像 这一层就已经足够深入的了。
获取本机公网 IP 的几个方法
# 获取本机公网 IP
curl ifconfig.me
curl icanhazip.com
curl ipinfo.io/ip
curl ipecho.net/plain
curl www.trackip.net/i
curl ip.sb
curl whatismyip.akamai.com
curl ifconfig.co
curl ident.me
curl inet-ip.info
curl 'https://api.ipify.org?format=json' #ipv4
curl 'https://api64.ipify.org?format=json' #ipv6
# 获取本机公网 IP ,这几个有返回位置信息
curl myip.ipip.net
curl ip.cn
curl cip.cc
curl ip-api.com
curl ip-api.com/json
# 查询某个ip的信息
curl http://ip-api.com/json/58.62.220.66
在命令行中直接运行代码
大概就两种套路,从标准输入读取代码,从命令行参数读取代码,从命令行参数读取代码时要留意转义字符
php
代码开头都不需要 <? 或 <?php
phpcode=$(cat <<- 'EOF'
echo '123';
echo "asd";
$a="qwe";
echo $a;
echo "\"\$a\"\\";
var_dump($argv);
EOF
);
echo $phpcode | php -a -- a=1;
php -r "$phpcode" -- a=1;
php -a <<- 'EOF'
echo '123';
echo "asd";
$a="qwe";
echo $a;
echo "\"\$a\"\\";
var_dump($argv);
EOF
node
nodecode=$(cat <<- 'EOF'
console.log('hello');
EOF
);
echo $nodecode | node
node -e "$nodecode"
node <<- 'EOF'
console.log('hello');
EOF
python
执行标准输入里的代码,和普通文件里的代码一样,必须要有换行符
pythoncode=$(cat <<- 'EOF'
print('hi1')
print('hi2')
EOF
);
echo $pythoncode | python
python << 'EOF'
print('hi1')
print('hi2')
EOF
在一行里,换行用分号;替代
python -c "import os;print(os.environ['PATH'])"
分页公式
相关变量
- 总行数 totalRecord
- 当前页 pageNo
- 每页大小 pageSize
- 总页数 totalPage
- 当前页大小 currentPageSize
计算总页数
伪代码
totalPage = ceil((totalRecord + pageSize - 1) / pageSize);
php
$totalPage = ceil(($totalRecord + $pageSize - 1) / $pageSize);
sql
SELECT @totalRecord := 123, @pageSize := 6;
SELECT ceil((@totalRecord + @pageSize + 1) / @pageSize);
ceil 是 向上取整 的函数
sql
关于分页的语法
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
三种写法
SELECT * FROM table LIMIT rows
SELECT * FROM table LIMIT offset, rows
SELECT * FROM table LIMIT rows OFFSET offset
具体的例子
-- 每页 10 行,第 1 页
SELECT * FROM table LIMIT 10
SELECT * FROM table LIMIT 0, 10
SELECT * FROM table LIMIT 10 OFFSET 0
-- 每页 10 行,第 2 页
SELECT * FROM table LIMIT 10, 10
SELECT * FROM table LIMIT 10 OFFSET 10
-- 每页 10 行,第 3 页
SELECT * FROM table LIMIT 20, 10
SELECT * FROM table LIMIT 10 OFFSET 20
-- 每页 10 行,第 4 页
SELECT * FROM table LIMIT 30, 10
SELECT * FROM table LIMIT 10 OFFSET 30
这是获取总行数的 sql
SELECT count(*) FROM table LIMIT 1;
- 虽然还有其它方式,但使用 count 是最保险的,多数框架都是用 count 实现的
这是获取总页数的 sql
SELECT @pageSize := 6;
SELECT ceil((count(*) + @pageSize + 1) / @pageSize) FROM table LIMIT 1;
SELECT ceil((count(*) + 7) / 6) from table LIMIT 1;
- 多数情况下都不会用 sql 来计算
这是获取某一页的 sql
-- 每页 6 行,第 2 页
SELECT @pageSize := 6;
SELECT @pageNo := 2;
SELECT @rows := @pageSize;
SELECT @offset := (@pageNo-1)*@pageSize;
SELECT * FROM table LIMIT @offset, @rows;
SELECT * FROM table LIMIT @rows offset @offset;
计算当前页的大小
计算当前页的行数,这是伪代码
function getCurrentPageSize(pageSize, pageNo, totalRecord, totalPage)
{
if (pageNo < totalPage) {
return pageSize;
} else if (pageNo == totalPage) {
return pageSize + (totalRecord - totalPage*pageSize);
} else {
// Throw an exception
}
}
安装 busybox
- 通过包管理
apt install -y busybox
- 通过 docker
docker run -it --rm --name my-busybox busybox:1.36-glibc sh
- 通过 type 命令判断当前系统有没有安装 busybox
type busybox
- 查看 busybox 的帮助和版本
busybox --help
- 可以在浏览器体验 busybox ,但版本好像有一点旧
https://busybox.net/live_bbox/live_bbox.html
- windows 版的 busybox
https://frippery.org/busybox/
https://github.com/rmyorston/busybox-w32
建议下载 busybox64u.exe 这个版本,64位且支持 unicode ,虽然这个版本只支持 win10和win11
编译安装
直接用 apt 安装的 busybox 版本太旧了, busybox 常常要用到宿主机的文件,容器里的 busybox 用起来不怎么方便。
安装前置的依赖
apt install -y bzip2 && \
apt install -y make && \
apt install -y gcc
下载源码
curl -O "https://busybox.net/downloads/busybox-1.36.1.tar.bz2"
解压源码
tar -xjf ./busybox-1.36.1.tar.bz2
切换进源码目录并开始安装
cd busybox-1.36.1
make defconfig
make install
编译后的 可执行二进制文件 可能在当前编译的目录,可能在源码的目录,反正要找一下
find / -name busybox
找到后就复制或剪切到 /bin 目录
cp /root/busybox-1.36.1/busybox /bin/busybox
一句命令完成下载和安装,但需要是 root 用户,其它用户需要修改对应的目录
apt install -y bzip2 && \
apt install -y make && \
apt install -y gcc && \
cd && \
curl -O "https://busybox.net/downloads/busybox-1.36.1.tar.bz2" && \
tar -xjf ./busybox-1.36.1.tar.bz2 && \
cd busybox-1.36.1 && \
make defconfig && \
make install && \
cp /root/busybox-1.36.1/busybox /bin/busybox && \
cd
如何自建一个 DNS 服务
使用这个仓库的代码来搭建 DNS , 使用这个仓库是因为笔者平时主要使用php做开发, https://github.com/yswery/PHP-DNS-SERVER
使用的是 v1.4.1 版本
git checkout v1.4.1
- 下载源码
https://github.com/yswery/PHP-DNS-SERVER
- 安装依赖,加上 --ignore-platform-reqs 参数,是因为这个仓库声明依赖的php版本比较旧,没这个参数可能无法下载composer里的依赖
composer install --no-suggest --no-dev --ignore-platform-reqs
- 参考 example/example.php 文件自己新建一个启动用的php文件 startup.php
require_once __DIR__.'/vendor/autoload.php';
$json = <<< EOF
{
"test.localhost.com": {
"A": "127.0.0.1"
}
}
EOF;
class JsonTextResolver extends \yswery\DNS\Resolver\JsonResolver
{
public function addZoneText(string $zone)
{
$zone = json_decode($zone, true);
$this->addZoneArr($zone);
}
public function addZoneArr(array $zone)
{
$resourceRecords = $this->isLegacyFormat($zone) ? $this->processLegacyZone($zone) : $this->processZone($zone);
$this->addZone($resourceRecords);
}
}
// JsonResolver created and provided with json dns records
$jsonResolver = new JsonTextResolver([]);
$jsonResolver->addZoneText($json);
// ipconfig /flushdns
// php localhost2.php
// System resolver acting as a fallback to the JsonResolver
$systemResolver = new yswery\DNS\Resolver\SystemResolver();
// StackableResolver will try each resolver in order and return the first match
$stackableResolver = new yswery\DNS\Resolver\StackableResolver([$jsonResolver, $systemResolver]);
// Create the eventDispatcher and add the event subscribers
$eventDispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
$eventDispatcher->addSubscriber(new \yswery\DNS\Event\Subscriber\EchoLogger());
$eventDispatcher->addSubscriber(new \yswery\DNS\Event\Subscriber\ServerTerminator());
// Create a new instance of Server class
$config = null;
$storageDirectory = null;
$useFilesystem = false;
$ip = '127.0.0.1';
$port = 53;
$server = new yswery\DNS\Server($stackableResolver, $eventDispatcher, $config, $storageDirectory, $useFilesystem, $ip, $port);
// Start DNS server
$server->start();
- 这是启动的命令,可以把这个命令保存在一个用于启动的脚本文件
php startup.php
修改网卡的 dns 配置
可以用这样的命令来测试是否生效,如果没有生效可以尝试刷洗 dns 缓存
nslookup -debug -querytype=A -port=53 test.localhost.com 127.0.0.1
如何写配置文件可以参考仓库里的文档或参考 example 里的文件
如果不把设为服务,则每次开机后都要手工启动一次
在本地使用 unbound 搭建一个 DNS 服务
笔者原本是想用php的库来建一个本地的dns服务,但找了很久都没找到合适的,最后还是选择用 unbound , 和 bind 相比, unbound 提供了免编译免安装的版本。
使用的是 v1.19.0 版本
- unbound 的仓库
https://github.com/NLnetLabs/unbound
- unbound 的文档
https://unbound.docs.nlnetlabs.nl/en/latest/index.html
- 下载 unbound
https://nlnetlabs.nl/projects/unbound/download/
- 下载完后,解压,然后复制一份配置文件
cp example.conf unbound.conf
- 修改配置文件,整个配置文件非常大,在特定位置改好这几项就好了
interface: 127.0.0.1
local-data: "test.localhost.com A 127.0.0.1"
forward-zone:
name: "."
forward-addr: 系统原本的主dns地址
forward-addr: 系统原本的备用dns地址
- 检测配置文件
unbound-checkconf unbound.conf
./unbound-checkconf.exe unbound.conf
- 运行
# 在前台运行
unbound -vv -c unbound.conf
./unbound.exe -vv -c unbound.conf
# 在后台运行
unbound -d -vv -c unbound.conf
./unbound.exe -d -vv -c unbound.conf
那些能作为工具的网站
- 能查看到编译的汇编代码 https://gcc.godbolt.org/
- 能在线运行 php 代码,能一次运行多个版本
- 一个能生成 curl 命令的网站,在开发的时候十分有用,特别是测试第三方接口的时候 https://reqbin.com/
- 在线的 svg 编辑器 https://c.runoob.com/more/svgeditor/
- 可视化的正则表达式
- ASCII 画图 https://asciiflow.com/
- 百度翻译能直接复制图片,并识别出文字
- 临时上传图片用的
- 文本压缩,去除空行和多余的空格 http://www.wuqianling.top/software/notepad/compress.html
- url 编码
- 因此当你需要编码整个 URL,就用 encodeURI。
- 如果只需要编码 URL 中的参数时,就使用 encodeURIComponent。
- https://www.iamwawa.cn/urldecode.html
- graphql 格式化 https://verytoolz.com/graphql-formatter.html
- 在线的剪切板
- 工具网站的集合
- 各种 jdk 的下载渠道 https://www.injdk.cn/
- 英文缩略词查询 https://www.abbreviationfinder.org/cn/
- 在线 ps
- 拯救词穷的字典。由清华大学 NLP 实验室开源,可以根据你的意思返回相关词汇,有效解决词穷、话到嘴边说不出来的窘境。
- 用于查看 winapi 里的各种结构体,以及各个 Windows 版本的差异 http://terminus.rewolf.pl/terminus/
- 微软的数学求解器 https://mathsolver.microsoft.com/
- 由 google 提供的在线调试工具 https://toolbox.googleapps.com/apps/main/
- 网页快照