惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
V
V2EX
博客园 - 【当耐特】
Vercel News
Vercel News
雷峰网
雷峰网
爱范儿
爱范儿
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Azure Blog
Microsoft Azure Blog
F
Full Disclosure
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog
NISL@THU
NISL@THU
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Microsoft Security Blog
Microsoft Security Blog
腾讯CDC
P
Proofpoint News Feed
B
Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
I
InfoQ
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 最新话题
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
V
Visual Studio Blog
AI
AI
Schneier on Security
Schneier on Security
B
Blog RSS Feed
T
Tor Project blog
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Cyber Attacks, Cyber Crime and Cyber Security
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
V2EX - 技术
V2EX - 技术
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
Arctic Wolf
Webroot Blog
Webroot Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main

f2h2h1's blog

使用yii3实现一个微框架 claw养殖技术 计算机网络基础知识 定时任务 ACME的使用经验 magento2加上varnish缓存 开发Magento2的模块 在magento2中使用persisted-query socket编程 一段CSDN文章主要内容的油猴脚本 电子邮件的不完整总结 git的笔记 在Windows下配置PHP服务器 终端,控制台和外壳 PHP各种运行方式的不完整总结 把网页导出成PDF 和颜色相关的笔记 HTTP认证方式的不完整总结 SEO的经验 密码学入门简明指南 文件的上传和下载 用纯CSS3实现的滑动按钮 在VSCode里调试PHP Linux的GUI 关于字符编码的一些坑 nc的使用和原理 在Windows下安装Magento2 对JS原型链的理解 使用docker-compose部署magento2 浏览器和服务器通讯方式的不完整总结 观察网站性能 一些关于Linux的笔记 telnet的不完整总结 在Windows下安装pear MySQL的时间类型和时间相关的函数 Windows下通过PEB读取进程的环境变量 关于 在VSCode里使用Xdebug远程调试PHP 在Windows下搭建git服务 关于环境变量的不完整总结 使用shell实现的kv数据库 如何完成以xx管理系统为选题的毕业设计 数字号码资源 各种标记语言 使用PowerShell实现的http服务器 kind相关经验 DNSSEC简介 nginx+ffmpeg+websocket实现的直播例子 使用Tesseract识别字符验证码 使用docker部署nuxt FirstData后台的设置 paypal,firtdata,支付宝的不完整接入指南 微信支付的不完整接入指南 用docker-compose部署lnmp环境 mongodb分片 练习
一些开发笔记
2025-09-09 · via f2h2h1's blog

一些开发笔记

在 VSCode 里调试 JAVA

  1. 安装 JDK
  2. 把 JDK 添加进环境变量
  3. 安装 VSCode 的 JAVA extension pack 拓展
  4. 使用 VSCode 打开文件夹
  5. 新增一个 debug 的类型 JAVA ,点击右下角的添加配置,选择 JAVA Lanuch Program
  6. 写一份测试的代码,并在 mian 函数加个断点
  7. 然后在终端里运行编译命令和运行命令,如果编译命令不复杂,可以配合 code runner 拓展,直接点击右上角的运行图标,或设置一个任务

在 VSCode 里调试 Python

  1. 安装 Python
  2. 把 Python 的安装目录和安装目录下的 script 添加进环境变量里
  3. 在 VSCode 安装 Python 拓展( microsoft 出品的)
  4. 新增一个 debug 的类型 Python ,点击右下角的添加配置,选择 Python: Terminal (integrated)
  5. 写一份测试的代码,并加个断点
  6. 然后在终端里运行,可以配合 code runner 插件,直接点击右上角的运行图标,或设置一个任务

在 Windows10 里修改 administrator 帐号的帐号名的三种方式

  1. 通过管理里的系统工具修改
选中桌面中的计算机图标,右键,点击管理
系统工具
    本地用户和组
        用户
            选中Administrator,然后重命名
重启电脑
  1. 通过策略租修改
win+r 打开运行
输入 gpedit.msc 打开策略组
计算机配置
    windows设置
        安全设置
            本地策略
                安全选项
                    拉到最下面
                    双击这个选选项 重命名系统管理员帐户
重启电脑
  1. 通过 netplwiz (Network Places Wizard) 网上邻居向导 修改
win+r 打开运行
输入 netplwiz
选中Administrator,然后双击,然后修改
重启电脑

Windows10 无密码远程连接

  1. 允许任何远程着桌面的连接
  2. 修改 Windows 的安全策略,允许远程桌面连接使用空密码
win+r 打开运行
输入 gpedit.msc 打开策略组
计算机配置
    windows 设置
        安全设置
            本地策略
                安全选项
                    拉到最下面
                    禁用 使用空密码的本地账户只允许控制台登录
  • 电脑提示“账户名与安全标识间无任何映射完成” 一般是修改玩用户名后没有重启导致的。

  • 因为公司的电脑不能设密码,同时我又想远程桌面公司的电脑,考虑到安全问题,不能用默认用户名裸奔,所以修改用户名和设置无密码的远程桌面

ss 的使用

需要一个在 wall 以外的服务器

ss 的各个版本 https://github.com/shadowsocks/shadowsocks/wiki/Feature-Comparison-across-Different-Versions

python

  1. 下载 python
  2. 安装 python
  3. 把 python 添加到环境变量
  4. 刷新环境变量
  5. 下载 ss
    python -m pip install shadowsocks
    # 如果上面那句下载失败,可以尝试用这句安装
    python -m pip install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U
    # 如果上面那句还是下载失败,可以尝试先把 master.zip 文件下载到本地,再安装
    
  6. 在服务器的策略组放行相应的端口
  7. 在防火墙放行相应的端口
  8. 运行以下命令
    ssserver -p 61813 -k windows@163.qq -m aes-256-cfb
        -p 是端口号
        -k 是密码
        -m 是加密方式
    上面那条命令需要在 服务器的策略组 和 防火墙 放行 61813 端口
    
  9. 下载客户端
  10. 正确填写 ip 端口 密码
  11. 一些加密的算法会依赖这个库 libsodium ,最好把这个库也装上
    https://download.libsodium.org/libsodium/releases/
    
  12. 更新 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
    
  13. 失效时,可以尝试,更改端口,更改密码,更改加密方式,换一个ip
  14. 参考
    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
      
  • 作为服务端运行

    ss-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-server.asciidoc

  • 作为客户端运行

    ss-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"
    }
    

    对应的文档 https://github.com/shadowsocks/shadowsocks-libev/blob/master/doc/ss-local.asciidoc

    在命令行里加入这个环境变量

    export ALL_PROXY=socks5h://127.0.0.1:1080
    

    检测代理是否有效

    curl -v -L www.baidu.com
    curl -v -L www.cip.cc
    curl -v -L ip-api.com
    curl -i -v -L --proxy socks5://127.0.0.1:1080 ip-api.com
    curl -i -v -L --proxy http://127.0.0.1:1081 ip-api.com
    curl -i -v -L --proxy http://127.0.0.1:1081 https://registry-1.docker.io/v2/
    

shadowsocks-rust

通过 cargo 安装

  1. 下载 rust 的二进制文件
    curl -L -O https://static.rust-lang.org/dist/rust-1.91.1-x86_64-unknown-linux-gnu.tar.xz
    
  2. 下载完后运行安装命令
    tar -xJf rust-1.91.1-x86_64-unknown-linux-gnu.tar.xz
    cd rust-1.91.1-x86_64-unknown-linux-gnu.tar.xz
    bash install.sh
    
  3. 再用 cargo 安装 shadowsocks-rust
    cargo install shadowsocks-rust
    

直接下二进制文件

  1. 下载
    curl -L -O https://github.com/shadowsocks/shadowsocks-rust/releases/download/v1.24.0/shadowsocks-v1.24.0.x86_64-unknown-linux-gnu.tar.xz
    
  2. 解压
    tar -xJf shadowsocks-v1.24.0.x86_64-unknown-linux-gnu.tar.xz
    
  3. 解压完后当前目录就会多出这几个文件
    • ssserver
    • ssurl
    • ssservice
    • ssmanager
    • sslocal

安装完后,通过配置文件启动客户端

  • 新建 ss-local-config.json
    vi ss-local-config.json
    
    {
        // Server configuration
        // listen on :: for dual stack support, no need add [] around.
        "server": " ",
        // Change to use your custom port number
        "server_port": 61813,
        "method": " ",
        "password": "windows@163.qq",
        // Server: TCP socket timeout in seconds.
        // Client: TCP connection timeout in seconds.
        // Omit this field if you don't have specific needs.
        "timeout": 7200,
    
        // Extended multiple local configuration
        "locals": [
            {
                // SOCKS5, SOCKS4/4a local server
                "protocol": "socks",
                // Basic configuration, a SOCKS5 local server
                "local_address": "0.0.0.0",
                "local_port": 1080,
                // OPTIONAL. Setting the `mode` for this specific local server instance.
                // If not set, it will derive from the outer `mode`
                "mode": "tcp_only"
            },
            {
                // HTTP local server (feature = "local-http")
                "protocol": "http",
                // Listen address
                "local_address": "0.0.0.0",
                "local_port": 1081
            }
        ]
    }
    
  • 启动命令
    ./sslocal -c ss-local-config.json --worker-threads=4 -d
    

通过 docker 运行

docker run --name sslocal-rust \
  --restart always \
  -p 1080:1080/tcp \
  -p 1081:1081/tcp \
  -v ~/ss-local-config.json:/etc/shadowsocks-rust/config.json:ro \
  ghcr.io/shadowsocks/sslocal-rust:latest \
  sslocal -c /etc/shadowsocks-rust/config.json

使用 privoxy 将 socks5 转换为 http 代理

安装

sudo apt install privoxy

配置 在这个文件 /etc/privoxy/config 的最后追加内容

# 转发设置为本地socks代理
forward-socks5  /  127.0.0.1:1080 .
# 本地http代理监听端口
listen-address  127.0.0.1:1081

重启

systemctl restart privoxy
systemctl enable privoxy

在命令行里加入环境变量

export http_proxy=http://127.0.0.1:1081; \
export HTTPS_PROXY=http://127.0.0.1:1081; \
export ALL_PROXY=socks5h://127.0.0.1:1080

export http_proxy= ; \
export HTTPS_PROXY= ; \
export ALL_PROXY=

export -n http_proxy ; \
export -n HTTPS_PROXY ; \
export -n ALL_PROXY

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

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盘 。

这是各种方法的总结

  1. 最简单的,在系统托盘右键,弹出,或在此电脑的界面,选中 U盘,右键,弹出
  2. 关掉所有文件夹再试一次弹出 U盘
  3. explorer.exe 重启,再试一次弹出 U盘
  4. 注销当前的登录,再次登录,再试一次弹出 U盘
  5. 通过任务管理器找到占用 U盘 的进程
    1. 打开任务管理器
    2. 切换到性能页面
    3. 打开资源监视器
    4. 切换到 cpu 页面
    5. 在 关联的句柄 栏目的搜索框内输入你的 U盘 盘符(如G:\)
    6. 即可看到当前占用 U盘 的进程
  6. 关闭 U盘 的写入缓存
    1. 控制面板
    2. 管理工具
    3. 计算机管理
    4. 设备管理
    5. 磁盘驱动
    6. 选择当前的 U盘
    7. 右键 属性 策略
    8. 关闭写缓存
    9. 关闭写缓存可能会造成 U盘 数据的丢失,关闭了写缓存后,要确保 U盘 里的文件已经保存好再弹出 U盘
  7. 在磁盘管理里使 U盘 脱机
    1. 控制面板
    2. 管理工具
    3. 磁盘管理
    4. 选择当前的 U盘
    5. 右键 脱机
    6. U盘 脱机后下次插入要重新挂载,脱机之后可以马上联机,再安全弹出,这样 U盘 下次插入时就不用联机了
    7. 脱机会直接关掉所有的文件句柄,要确保 U盘 里的文件已经保存好再脱机
    8. 脱机选项不可选,可能是有虚拟内存分配到 U盘 ,这时取消在 U盘 上的虚拟内存即可
      • 进入电脑属性->高级系统设置->高级->性能设置->高级->更改,关闭自动管理分页文件大小,取消在该磁盘上设置的虚拟内存
    9. 有时脱机还会失败,这时就继续参考下面的步骤
  8. 通过 日志 查找占用 U盘 的进程
    1. 打开事件查看器
    2. windows日志
    3. 系统
    4. 找到最近的一条 来源 Kernel-PnP 的记录
    5. 记录的常规里有占用 U盘 的 进程id 的
    6. 再通过任务管理器的 pid 找到对应的进程
    7. 有时,即使找到占用 U盘 的进程,但却无法结束进程,或 进程是系统的核心进程
      • 这时可以尝试以这样的关键词(进程名 或 进程id + Kernel-PnP )在网上搜索解决的方法
  9. 如果是 Windows Defender 占用 U盘 可以试试这样操作
    1. 打开 Windows Defender 安全中心
    2. 病毒和威胁防护
    3. 病毒和威胁防护 设置
    4. 将在使用的 U盘 盘符添加为排除项
  10. 最后的方法,关机,关机后就肯定可以安全地拔 U盘

笔者通常在直接弹出 U盘 失败后,会多试几次,然后通过 日志 找到对应的进程,然后通过 任务管理器 结束对应的进程,如果结束进程失败,还是会再次在网上搜索解决方法。 脱机 和 关闭写缓存都有一点副作用; 关闭文件夹,重启 explorer.exe ,注销,关机,这类都有点麻烦。

windows 下的 linux 环境

  • DJGPP
  • Cygwin
  • mingw
  • mingw-w64
  • MSYS
  • MSYS2
  • Git for Windows
  • TDM-GCC
  • gnuwin32/gnuwin64/UnxUtils
  • 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. 需求

  1. 一部安卓手机
  2. AcFun 安卓版客户端
  3. ffmpeg

1. 使用手机客户端缓存视频

2. 在手机的文件管理器里找到缓存文件

  1. 用文件管理器打开手机的根目录
  2. 找到 acfun 文件夹
  3. 点进这个目录里 acfun→core→local
  4. 然后点进一个以数字名命的文件夹
  5. 这个文件夹里就是缓存的视频,文件名都没有后缀,可以通过创建时间大致判断出哪个是最新缓存的视频

3. 在电脑里下载 ffmpeg

  1. 打开 ffmepg 的下载网址,并选择系统对应的版本下载
http://www.ffmpeg.org/download.html
  1. 把下载后的文件解压
  2. 解压后的文件里会有个一个名为 ffmpeg 的文件,这个就是 ffmpeg 的主程序,可以用来转换视频格式

4. 把缓存的视频文件复制进电脑,并使用 ffmpeg 转换为 MP4 格式

  1. 把缓存文件从手机复制进 ffempeg 的解压目录
  2. cd 进 ffempeg 的解压目录
  3. 运行以下命令,转换格式后的文件名必须带有 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 环境变量

  1. 新建一个环境变量 JAVA_HOME ,值为 JDK 的安装目录,例子
C:\Program Files\Java\jdk1.8.0_161
  1. 把以下值加入到环境变量 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

  1. 解压下载的文件
  2. 把解压后的文件夹复制到 C 盘的根目录下(这里可以是任意目录)
  3. 把 Tomcat 目录下的 bin 文件夹的路劲加入的环境变量
  4. 把网站程序复制进 Tomcat 的 webapps 文件夹
  5. 启动 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 下载拓展

2 安装拓展

  1. 把下载下来的压缩包解压
  2. 把 php_ui.dll 复制到 PHP 的 ext 目录下
  3. 把 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
      
  • 把代码改写成支持以 守护进程 的形式运行
    1. 步骤
      1. fork子进程,父进程退出(必须)
      2. 子进程创建新会话(必须,setsid())
      3. 改变当前工作目录 chdir(非必须,建议做 chdir("/var");)
      4. 重设子进程掩码(非必须 umask(0))
      5. 关闭文件描述符(非必须)
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        // 另一种关闭文件描述符的方法,但 getdtablesize() 来自 unistd.h
        // for(i = 0; i < getdtablesize(); i++) 
        // {
        //     close(i);
        // }
        
      6. 执行核心工作(必须)
    2. 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
      
    3. 这种是 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 &
  • systemctl
    1. 创建一个 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
        
    2. 启动 service 并加入到开机启动中
      # 重新加载 systemctl 配置
      systemctl daemon-reload
      # 启动 myDaemon.service
      systemctl start myDaemon.service
      # myDaemon.service 加入到开机启动中
      systemctl enable myDaemon.service
      
  • cron *****
    • 例子 * * * * * php queue.php >> out.log 2>&1
  • docker run 同时加上这两个参数 --restart always 和 -d ,也能达到类似的效果(docker 要设置好开机启动和服务)

gcc 编译流程

编译的四个步骤

  1. 预处理 由 .c 文件到 .i 文件。
    • 对各种预处理命令进行处理,包括头文件包含、宏定义的扩展、条件编译的选择等
  2. 编译 由 .i 文件到 .s 文件。
    • 将预处理得到的源代码文件,进行“翻译转换”,产生出机器语言的目标程序,得到机器语言的汇编文件
  3. 汇编 由 .s 文件到 .o 文件。
    • 将汇编代码翻译成了机器码,但是还不可以运行
  4. 链接 由 .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
  • findutils GNU查找工具组
    • find
    • xargs
  • inetutils 网络工具组
    • inetd
    • telnet
    • ftp
    • ping
    • traceroute
    • ifconfig
  • 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操作系统中找到。

参考

感觉 GNU 真的除了内核之外,什么都有了。

让网站支持 ipv6

  1. 拥有一个 ipv6 地址
  2. 域名解释 aaaa 记录指向 ipv6 的地址
  3. 让网站程序支持 ipv6 地址
    • 通过网关翻译
      • 代码里用 ipv4 地址,域名解释 aaaa 记录到 网关,网关把 ipv6 的流量翻译成 ipv4 的流量再传递给网站
      • 代码里用 ipv6 地址,域名解释 a 记录到 网关,网关把 ipv4 的流量翻译成 ipv6 的流量再传递给网站
    • 让 web 服务器同时监听两个地址,代码里和 ip 相关的部分,都改成能兼容 ipv4 和 ipv6

用来测试网站对 ipv6 的支持,也可以用来查看 DNSSEC 的支持 https://ipv6.ustc.edu.cn/onlinechecklog.php

在命令行里格式化 json

  1. python 的 json.tool ,好像 python2 和 3 都可以这样用

    echo '{ "name": "xiaohong", "age": 18 }' | python -m json.tool
    
    • 会转义中文,如果想不转义中文
    • 需要修改标准库里的文件,这样不是很好,在 python 的标准库 json 文件夹下有个 tool.py 文件,更改其中调用的 json.dump 函数,传一个ensure_ascii = False 参数即可
  2. json_pp , windwos 的 git bash 和 大多数 linux 发行版都有这个工具

    echo '{ "name": "xiaohong", "age": 18 }' | json_pp
    
  3. jq , 虽然大多数 linux 发行版都没有这个工具,但中文互联网环境下有好多文章都推荐这个

  4. 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));';
    
  5. 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 的首次启动配置不完整

可以尝试以下步骤来解决

  1. 打开 IE 的 internet 选项
  2. 点击安全选项卡,选中本地 intranet ,并点击站点按钮
  3. 新的窗口中点击高级
  4. 添加 about:security_powershell.exe 到输入框,点击添加
  5. 把所有 IE 窗口一个个关闭就好了,再次在 powershell 下运行 Invoke-WebRequest

Windows 里的 Java 环境配置

  1. 下载并安装 JDK
  2. 把 JDK 加入环境变量
    1. 新建一个新的环境变量 JAVA_HOME ,值是 JDK 的根目录
    2. 在 PATH 里加入 %JAVA_HOME%\bin 和 %JAVA_HOME%\lib 和 %JAVA_HOME%\jre\bin (如果没有这个目录就忽略)
    3. 如果是 JDK 1.5 及之前的版本还需要一个 CLASSPATH 的环境变量
      • CLASSPATH 的值是 .;%Java_Home%\bin;%JAVA_HOME%\lib;%Java_Home%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
  3. 下载并安装 maven
  4. 把 maven 加入环境变量
    1. 新建一个新的环境变量 MAVEN_HOME ,值是 maven 的根目录
    2. 在 PATH 里加入 %MAVEN_HOME%\bin
  5. 下载并安装 ant
  6. 把 ant 加入环境变量
    1. 新建一个新的环境变量 ANT_HOME ,值是 ant 的根目录
    2. 在 PATH 里加入 %ANT_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

python 的 http 服务和 cgi

python 的 cgi 模块在 3.15 被移除了 https://docs.python.org/zh-cn/3.12/library/cgi.html https://peps.python.org/pep-0594/#cgi

3.14 是唯一一个既有 cgi 也有 tls 的版本

python 一行命令启动 http 服务

# 最简单的 http 服务
python -m http.server
# 指定端口号的 http 服务
python -m http.server 8081
# 有 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

参考文档

embed 版的 python 如何安装 pip

  1. cd 到 python 的根目录
  2. 然后运行这句 curl https://bootstrap.pypa.io/get-pip.py | ./python
  3. 然后修改这个文件 python312._pth , 312 是版本号,在这个文件里加上
    ./Lib/site-packages
    

参考这个文档 https://pip.pypa.io/en/stable/installation/

python 的环境变量,除了安装目录之外,还有一个 Script 目录 但其实一般影响都不大,根目录重要一点

如果没法把 python 加入环境变量,那么每次调用最好都用绝对路径

列出 pip 的所有模块

python -m pip list

列出 python 所有可用的模块

python -c 'import pydoc;pydoc.help("modules")'

这句也有类似的效果,但好像只有 sys 下的模块

python -c 'import sys;print(sys.modules.keys())'

embed 版的 python 如何安装 tkiner

安装命令

python -m pip install tkinter-embed

安装完成后还需要设置这两个环境变量 TCL_LIBRARY TK_LIBRARY

export TCL_LIBRARY="/d/python-3.12.3-embed-amd64/Lib/site-packages/tcl/tcl8.6"
export TK_LIBRARY="/d/python-3.12.3-embed-amd64/Lib/site-packages/tcl/tk8.6"

embed 版的 python 如何导入工作目录下的模块

embed 版的 Python 的模块并不会将工作目录添加到搜索路径中

这会导致一般的引用方式报错

ImportError: attempted relative import with no known parent package

需要用动态导入的方式加上 工作目录,在调用的文件里的开头加上这一句

import sys, os
sys.path.append(os.getcwd())

或者在安装目录下的 python312._pth 文件里加上 工作目录的路径,要用绝对路径

C:/Users/username/project

如何退出 python 的交互式解释器

  • 用 sys.exit 退出
    • 先输入 import sys ,如果之前已引入了 sys 模块,就忽略这一句
    • 再输入 sys.exit()
    • 一行代码
      import sys;sys._exit()
      
  • 用 os._exit 退出
    • 先输入 import os ,如果之前已引入了 os 模块,就忽略这一句
    • 再输入 os._exit(0)
    • 一行代码
      import os;os._exit(0)
      
  • 用信号退出
    import os
    import signal
    current_pid = os.getpid()
    print(f"当前进程的 PID 是: {current_pid}")
    os.kill(current_pid, signal.SIGINT)
    
  • 快捷键退出: 在交互式解释器中,你可以使用快捷键退出:
    • 在 Windows 和 Linux 上,按 Ctrl + Z 然后按 Enter。
    • 在 macOS 上,按 Ctrl + D。

python 常用的库和工具

python 常用的库

  • 核心数据分析库
    • Pandas :提供高效的数据结构(如 Series 和 DataFrame),支持数据清洗、转换、聚合和时间序列分析,是数据分析的基础工具
    • NumPy :专注于多维数组和矩阵运算,为其他库(如 Pandas 和 SciPy)提供底层支持,适合数值计算
    • Matplotlib :基础的可视化库,支持绘制折线图、柱状图、散点图等,灵活性高但语法较复杂
  • 可视化工具
    • Seaborn :基于 Matplotlib 的高级库,简化了统计图表的生成,如热力图、分类图等,适合快速生成美观的可视化结果
    • Plotly :交互式可视化库,支持动态图表和仪表盘开发,常用于 Web 应用
    • pyecharts :是一个基于 ECharts 的 Python 数据可视化库
  • 科学计算与统计分析
    • SciPy :提供科学计算函数(如积分、优化、信号处理),结合 NumPy 使用,适合工程和数学领域
    • Statsmodels :专注于统计模型(如回归分析、时间序列),适合学术研究和复杂统计分析
  • 大数据处理
    • PySpark :PySpark是Apache Spark的Python接口,用于大规模数据处理和分析。
  • 数据获取与处理
    • Requests:用于网络数据抓取
    • BeautifulSoup:从网页中提取结构化数据
  • 机器学习
    • Torch
    • TensorFlow
    • Keras
    • sklearn
  • web框架
    • Django
    • Flask
    • FastAPI
  • 其它
    • pillow 图像处理
    • openpyxl 操作 excel
    • Playwright 操作浏览器
    • Pyinstaller 打包成 exe
    • auto-py-to-exe 基于 pyinstaller 的,有图形界面的打包工具
    • pywin32 操作windows窗口,类似于 autoit
    • pywinauto 类似于 pywin32

python 常用的工具

  • 包的管理
    • easy_install
    • pip
  • 环境管理
    • venv 自 3.3 版本之后添加的官方库,自 3.6 版本之后,成为官方推荐的多环境管理工具,3.3版本之后自带的模块,只支持3.3版本之后的 但只能有一个 python 解释器,只能有一个 python 版本
    • virtualenv 可以有多个 python 解释器,多个 python 版本可以共存,特别是 python2 和 python3
    • virtualenvwrapper 用于管理 virtualenv
    • pyenv 侧重于多个 python 解释器
    • pipenv pipenv 是一款比较新的包管理工具,其借鉴了 javascript 的 npm 和 PHP 的 composer 等理念,通过一个依赖描述文件 Pipfile 来安装和管理依赖 pipenv 其实整合了 pip 和 virtualenv 等库
    • Anaconda Anaconda通过管理工具包、开发环境、Python版本,大大简化了你的工作流程。 不仅可以方便地安装、更新、卸载工具包,而且安装时能自动安装相应的依赖包, 同时还能使用不同的虚拟环境隔离不同要求的项目。 安装Acaconda默认的Python是2.7;安装Acaconda3默认的Python是3.6
    • Miniconda Miniconda 是一个 Anaconda 的轻量级替代,默认只包含了 python 和 conda,但是可以通过 pip 和 conda 来安装所需要的包。 Miniconda 相比 Anaconda ,去掉了科学计算的相关包,更加轻量级,没有那么臃肿。可以认为是Anaconda的最小化版本,用法与Anaconda完全一致。
  • ide
    • PyCharm
    • Jupyter
    • Spyder
      • (前身为Pydee)是一个使用Python语言的开放源代码跨平台科学运算集成开发环境(IDE)。Spyder集成了NumPy,SciPy,Matplotlib与IPython,以及其他开源软件。
    • idle
  • Anaconda 系列
    • Anaconda Distribution
    • Miniconda
    • Anaconda.org
    • conda-forge
    • miniforge
    • mamba
    • Micromamba

debian 一句命令安装 docker

因为要经常部署和重装系统,所以就整理了这样一句命令,可能会因为 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

docker设置代理

拉取镜像时的代理

需要改的不是环境变量,而且是启动时的环境变量 https://docs.docker.com/engine/daemon/proxy/#environment-variables

在这个文件里 /etc/systemd/system/docker.service.d/http-proxy.conf 追加以下内容

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:1081"
Environment="HTTPS_PROXY=http://127.0.0.1:1081"
Environment="NO_PROXY=localhost,127.0.0.1,.corp"

如果没有 http-proxy.conf 就自己新建一个

sudo mkdir -p /etc/systemd/system/docker.service.d
vim /etc/systemd/system/docker.service.d/http-proxy.conf

修改完后的重启

sudo systemctl daemon-reload
sudo systemctl restart docker

查看环境变量有没有设置成功

sudo systemctl show --property=Environment docker
docker info | grep -i proxy

查看 docker 的状态和日志,这个是用于观察 docker 是否有正确启动,配置文件有没有写错,如果启动失败有什么错误输出

systemctl status docker
journalctl -u docker.service

如果拉取镜像时遇到这种错误 TLS handshake timeout , 可以尝试修改 daemon.json 里的 mut , daemon.json 的路径可能是 /etc/docker/daemon.json 也可能是 ~/docker/daemon.json

{
  ...
  "mtu": 1300
  ...
}

构建镜像时的代理

运行容器时的代理

生成用于测试的容器的命令

具体环境

  • 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

curl 提供了一个脚本用于方便下载文件 https://github.com/curl/wcurl

github 好像不支持断点下载

curl 遇到这种错误会直接退出,--retry 这时是没有效果的 Connection reset by peer

在 debian 新建命令的别名

  • 只对当前终端有效
    • 直接运行这个命令 alias ll="ls -l "
  • 终端退出后仍然有效
    • 在 ./bashrc 里加上一行 alias ll="ls -l "
    • 然后运行这个命令 source ~/.bashrc
  • 列出当前环境所有的别名
    alias
    alias -p
    
  • 删除别名
    删除一个别名
    unalias ll
    删除两个别名
    unalias ll ll2
    删除全部别名
    unalias -a
    

在 bash 里判断一个变量是不是环境变量

判断 PS1 是不是环境变量

printenv|awk -F '=' ' {print $1}'|grep "^PS1
    quot;
printenv|awk -F '=' ' {print $1}'|grep "^PS1
    quot;;if [ "$?" -eq 1 ]; then echo "1"; else echo "0"; fi;
printenv|awk -F '=' ' {print $1}'|grep "^PS1
    quot;;if [ "$?" -eq 1 ]; then echo "This variable is not an environment variable"; else echo "This variable is an environment variable"; fi;
printenv|awk -F '=' ' {print $1}'|grep "^PS1
    quot; 1>/dev/null ;if [ "$?" -eq 1 ]; then echo "This variable is not an environment variable"; else echo "This variable is an environment variable"; fi;

用于 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 两个斜杆 // 其实是没什么作用的

为什么文件 URL 以3斜杠开头?

  • file:/// 之所以是三个斜杆,是因为忽略了主机名,更完整的 url 应该是这样的 file://host/
  • 这两句 curl 的命令效果是一样的
    curl -i -v file:///C:/Users/example.txt
    curl -i -v file://localhost/C:/Users/example.txt
    

参考

linux 系统的启动流程

  1. 通电
  2. 加电自检 (POST, Power-On Self-Test)
  3. 硬件初始化
    • legacy BIOS
      • MBR
    • UEFI
      • GPT
  4. 执行引导程序 (bootloader)
    • LILO (LInux LOader)
    • SYSLINUX
    • GRUB (GNU Grand Unified Bootloader)
  5. 加载和启动 linux 内核镜像
    • initrd
    • initramfs
  6. 执行 init
    • init (SystemV init)
    • UpStart
    • systemd (system deamon)
  7. 执行守护进程

现在主流的启动过程是

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' -- a=1;
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.37.0.tar.bz2"

解压源码

tar -xjf ./busybox-1.37.0.tar.bz2

切换进源码目录并开始安装

cd busybox-1.37.0
make defconfig
make install

编译后的 可执行二进制文件 可能在当前编译的目录,可能在源码的目录,反正要找一下

find / -name busybox

找到后就复制或剪切到 /bin 目录

cp /root/busybox-1.37.0/busybox /bin/busybox

一句命令完成下载和安装,但需要是 root 用户,其它用户需要修改对应的目录

apt install -y bzip2 && \
apt install -y make && \
apt install -y gcc && \
cd && \
curl -L -O "https://busybox.net/downloads/busybox-1.37.0.tar.bz2" && \
tar -xjf ./busybox-1.37.0.tar.bz2 && \
cd busybox-1.37.0 && \
make defconfig && \
make install && \
cp /root/busybox-1.37.0/busybox /bin/busybox && \
cd

寻找php的配置文件

  1. 使用内置函数
    php -r 'phpinfo();' | grep 'Loaded Configuration File'
    php -r 'echo php_ini_loaded_file();'
    
  2. 命令行
    php --ini
    php -i | grep 'Loaded Configuration File'
    
  3. 使用 strace 命令,这个命令会输出很多数据,要认真找一下才找到配置文件
    strace -f -e open,execve,openat php -v 2>&1 | grep "O_RDONLY) = 3"
    

cli fpm apache 的 php 配置文件可能会不一样。 fpm 还有一个单独的配置文件。 在 windows 环境下 strace 可能会有权限的问题,看不到输出。 cli 可以通过命令行比较准确地获得配置文件位置。 web 最保险的方式还是输出 php_ini_loaded_file() 函数。

如何自建一个 DNS 服务

使用这个仓库的代码来搭建 DNS , 使用这个仓库是因为笔者平时主要使用php做开发, https://github.com/yswery/PHP-DNS-SERVER

使用的是 v1.4.1 版本

git checkout v1.4.1
  1. 下载源码
https://github.com/yswery/PHP-DNS-SERVER
  1. 安装依赖,加上 --ignore-platform-reqs 参数,是因为这个仓库声明依赖的php版本比较旧,没这个参数可能无法下载composer里的依赖
composer install --no-suggest --no-dev --ignore-platform-reqs
  1. 参考 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();
  1. 这是启动的命令,可以把这个命令保存在一个用于启动的脚本文件
php startup.php
  1. 修改网卡的 dns 配置

  2. 可以用这样的命令来测试是否生效,如果没有生效可以尝试刷洗 dns 缓存

nslookup -debug -querytype=A -port=53 test.localhost.com 127.0.0.1
  1. 如何写配置文件可以参考仓库里的文档或参考 example 里的文件

  2. 如果不把设为服务,则每次开机后都要手工启动一次

在本地使用 unbound 搭建一个 DNS 服务

笔者原本是想用php的库来建一个本地的dns服务,但找了很久都没找到合适的,最后还是选择用 unbound , 和 bind 相比, unbound 提供了免编译免安装的版本。

使用的是 v1.19.0 版本

  1. unbound 的仓库
https://github.com/NLnetLabs/unbound
  1. unbound 的文档
https://unbound.docs.nlnetlabs.nl/en/latest/index.html
  1. 下载 unbound
https://nlnetlabs.nl/projects/unbound/download/
  1. 下载完后,解压,然后复制一份配置文件
cp example.conf unbound.conf
  1. 修改配置文件,整个配置文件非常大,在特定位置改好这几项就好了
# 设置日志文件的路径,默认情况下会使用 syslog
logfile: unbound.log

use-syslog: no

interface: 127.0.0.1

local-data: "test.localhost.com A 127.0.0.1"

forward-zone:
    name: "."
    forward-addr: 系统原本的主dns地址
    forward-addr: 系统原本的备用dns地址
  1. 检测配置文件
unbound-checkconf unbound.conf
./unbound-checkconf.exe unbound.conf
  1. 运行
# 在前台运行
unbound -vv -c unbound.conf
./unbound.exe -vv -c unbound.conf

# 在后台运行
unbound -d -vv -c unbound.conf
./unbound.exe -d -vv -c unbound.conf

powershell 的使用经验

无法执行 powershell 脚本

通常是执行策略的原因导致的。

设置脚本执行策略,通常把 策略 设为 RemoteSigned 就可以了

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine

查看 powershell 脚本执行策略

Get-ExecutionPolicy
Get-ExecutionPolicy -List

https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.2

套一层 bat 脚本就可以直接执行 powershell 脚本了

@echo off
powershell -ExecutionPolicy Bypass -File "1.ps1" arg1 arg2 arg3

接收命令行参数

# powershell -ExecutionPolicy Bypass -File "1.ps1" arg1 arg2 arg3
Write-Host args_Count: $($args.Count) # 输出命令行参数的数量
Write-Host "$args" # 输出全部命令行参数

# powershell -ExecutionPolicy Bypass -File "2.ps1" -Param1 "Param1" -Param4 123
param([string]$Param1, [int]$Param2 = 2, $Param3 = 3, $Param4)
Write-Output "1: $Param1"
Write-Output "2: $Param2"
Write-Output "3: $Param3"
Write-Output "4: $Param4"

# 两种形式只能选择其中一种

判断变量的类型

```
$variable.GetType()
```

获取变量的属性和方法

```
$variable | Get-Member
```

加载程序集

使用 win32api 或 c++的库

powershell 转换成 exe

powershell 的优缺点

优点

  • 从语法的角度看, powershell 比 bash 和 bat 都要好不少
  • 在 windows 环境下可以间接调用 windows api

缺点

  • 十分依赖 .NET
  • 管道传递的是对象,不像 bash 那样传递字节
  • 版本十分混乱
  • 虽然默认情况下 windows 都有安装 powershell ,但默认情况下是不能执行 powershell 脚本的

adminer 设置 sqlite 的密码访问

  1. 下载 adminer
    php -r "copy('https://github.com/vrana/adminer/releases/download/v5.4.1/adminer-5.4.1.php', 'adminer.php');"
    
  2. 新建一个 sqlite 库(如果已经有db文件,忽略这一步)
    php -r "print((new SQLite3('test.db'))->querySingle('SELECT SQLITE_VERSION()'));"
    
  3. 在 adminer.php 的同级目录下新建一个目录 adminer-plugins
    mkdir adminer-plugins
    
  4. 在 adminer-plugins 目录下新建一个文件 login-password-less.php ,并写入以下内容
    touch login-password-less.php
    vi login-password-less.php
    
    <?php
    
    /** Enable login without password
    * @link https://www.adminer.org/plugins/#use
    * @author Jakub Vrana, https://www.vrana.cz/
    * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
    * @license https://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2 (one or other)
    */
    class AdminerLoginPasswordLess extends Adminer\Plugin {
        protected $password_hash;
    
        /** Set allowed password
        * @param string $password_hash result of password_hash()
        */
        function __construct($password_hash) {
            $this->password_hash = $password_hash;
        }
    
        function credentials() {
            $password = Adminer\get_password();
            return array(Adminer\SERVER, $_GET["username"], (password_verify($password, $this->password_hash) ? "" : $password));
        }
    
        function login($login, $password) {
            // if ($login == 'admin' && $password == '') {
            // 	return true;
            // } else {
            // 	return false;
            // }
            if ($password != "") {
                return true;
            }
        }
    
        protected $translations = array(
            'cs' => array('' => 'Povolí přihlášení bez hesla'),
            'de' => array('' => 'Ermöglicht die Anmeldung ohne Passwort'),
            'pl' => array('' => 'Włącz logowanie bez hasła'),
            'ro' => array('' => 'Activați autentificarea fără parolă'),
            'ja' => array('' => 'パスワードなしのログインを許可'),
        );
    }
    
  5. 在 adminer.php 的同级目录下新建一个一个文件 adminer-plugins.php ,并写入以下内容
    touch adminer-plugins.php
    vi adminer-plugins.php
    
    <?php // adminer-plugins.php
    return array(
        // new AdminerLoginPasswordLess('$2y$07$Czp9G/aLi3AnaUqpvkF05OHO1LMizrAgMLvnaOdvQovHaRv28XDhG'),
        new AdminerLoginPasswordLess(password_hash("PASSWORD", PASSWORD_DEFAULT)),
        // You can specify all plugins here or just the ones needing configuration.
    );
    
  6. 参考

linux 的使用经验

git 的使用经验

symfony/rate-limiter 的使用经验

<?php

require 'vendor/autoload.php';

use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
use Symfony\Component\RateLimiter\RateLimiterFactory;

use Symfony\Component\RateLimiter\Storage\StorageInterface;
// use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\RateLimiter\LimiterStateInterface;


class FileStorage implements StorageInterface
{
    public function save(LimiterStateInterface $limiterState): void
    {
        file_put_contents($limiterState->getId(), serialize($limiterState));
    }

    public function fetch(string $limiterStateId): ?LimiterStateInterface
    {
        if (!file_exists($limiterStateId)) {
            return null;
        }

        $limiterState = unserialize(file_get_contents($limiterStateId));
        $expireAt = $this->getExpireAt($limiterState);
        if (null !== $expireAt && $expireAt <= microtime(true)) {
            $this->delete($limiterStateId);
            return null;
        } else if (null === $expireAt) {
            $this->delete($limiterStateId);
            return null;
        }

        return $limiterState;
    }

    public function delete(string $limiterStateId): void
    {
        if (!file_exists($limiterStateId)) {
            return;
        }

        unlink($limiterStateId);
    }

    private function getExpireAt(LimiterStateInterface $limiterState): ?float
    {
        if (null !== ($expireSeconds = $limiterState->getExpirationTime())) {
            return microtime(true) + $expireSeconds;
        }

        return null;
    }
}

$factory = new RateLimiterFactory([
    'id' => 'login',
    'policy' => 'fixed_window',
    'limit' => 20,
    // 'rate' => ['interval' => '15 minutes'],
    'interval' => '60 seconds',
], new FileStorage());

$limiter = $factory->create('id');

$rateLimit = $limiter->consume();
if (!$rateLimit->isAccepted()) {
    // header('');
    http_response_code(429);
    echo 'Too Many Requests';
} else {
    echo 'OK';
}
echo PHP_EOL;
try {

    // var_dump(file_exists('login-id'));
    // $limiterState = unserialize(file_get_contents('login-id'));
    // var_dump(get_class($limiterState));
    $limiterState = (new FileStorage())->fetch('login-id');
    echo $limiterState->getId();
    echo PHP_EOL;
    echo $limiterState->getExpirationTime();
    echo PHP_EOL;
} catch(\Throwable $e) {
    echo $e->getMessage();
    echo PHP_EOL;
}

exit(0);

/*

composer require symfony/rate-limiter:6.4

PHP_CLI_SERVER_WORKERS="10" php -S 0.0.0.0:8080

curl http://127.0.0.1:8080/ratelimiter.php

echo $(for i in $(seq 1 10); do echo curl -o /dev/null -s -w %{http_code}, http://127.0.0.1:8080/ratelimiter.php; echo ";";done ) | \
xargs -d ";" -n 1 -P 2 bash -c '$0' | \
sed 's/,/\n/g'

如何改用 redis 或其它存储?
这个组件在 Symfony 里是如何使用的?

*/

那些能作为工具的网站