从 Debian 12 升级到 Debian 13

在进行重大版本升级之前,务必备份所有重要数据。尽管升级过程通常很稳定,但硬件故障或配置错误等意外情况可能会导致数据丢失。 以下是将Debian 12 (Bookworm) 升级到Debian 13 (Trixie) 的详细步骤。 备份 在开始升级之前,请务必创建一个完整的系统备份,包括: 所有重要数据和个人文件 (/home 目录)。 系统配置文件 (/etc 目录)。 已安装软件包的列表。您可以使用以下命令生成一个列表: Bashsudo dpkg --get-selections '*' > ~/installed-packages.list 第一步 升级现有系统 apt update apt upgrade apt autoremove apt clean 第二步 修改软件源列表 sed -i 's/bookworm/trixie/g' /etc/apt/sources.list 第三步 升级 #更新软件包索引 apt update #最小系统升级 apt upgrade --without-new-pkgs #完整系统升级 apt full-upgrade #清理和重启 apt --purge autoremove reboot #新的系统对于source.list的内容做了结构上的更新,使得格式更加易读了。可用如下的命令更新 source.list #此命令可以将原有的source.list做个备份--source.list.bak, 可以放心更新 sudo apt modernize-sources 确认系统版本 root@debian ~ # cat /etc/debian_version 13.0 root@debian ~ # lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 13 (trixie) Release: 13 Codename: trixie root@debian ~ # uname -a Linux debian 6.12.35+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.35-1 (2025-07-03) x86_64 GNU/Linu 如果您在升级过程中遇到任何问题,可以查阅Debian官方的升级指南获取更详细的信息和故障排除帮助 官方文档

2025/9/16
articleCard.readMore

在命令行 ssh 时按 <kbd>TAB</kbd> 自动补全主机名

Zsh + Oh My Zsh 必须是 zsh 或者 bash,并且补全脚本已经生效 echo $SHELL echo $0 输出 /bin/zsh 或 zsh → 正确 输出 bash → 需要用 bash 的补全方法(和 zsh 不同) 确认 oh-my-zsh 已安装。 在 ~/.zshrc 里启用插件: plugins=(git ssh) 确认 zsh 补全系统已经启用 zsh 的补全系统必须加载: autoload -Uz compinit compinit 在 ~/.zshrc 中通常 Oh My Zsh 已经自动加载,但如果手动配置过可能没生效。 重新加载 ~/.zshrc : source ~/.zshrc 然后输入测试: ssh db<TAB> 就会自动补全到 .ssh/config 里的 db1、db2。

2025/9/9
articleCard.readMore

访问世界上每一个经纬度为整数的交叉点

该项目的目标是访问世界上每一个经纬度为整数的交叉点,并在每个地点拍照记录。 目前中国的进度是 429/978,平原地区的交叉点基本上被记录完毕了,剩下的都是难啃的骨头。 the Degree Confluence Project: https://confluence.org

2025/3/28
articleCard.readMore

测试服务器能否正常收发邮件

需要收发邮件的话,要注意商家是否开放25端口 发邮件 使用 telnet smtp.qq.com 25 显示: telnet smtp.qq.com 25 Trying 43.129.255.54... 则无法使用。 显示: telnet smtp.qq.com 25 Trying 43.129.255.54... Connected to smtp.qq.com. Escape character is '^]'. 220 newxmesmtplogicsvrszc5-0.qq.com XMail Esmtp QQ Mail Server. 则可以正常使用。 使用gmail进行测试也是类似的: telnet smtp.gmail.com 25 Trying 172.253.117.109... Connected to smtp.gmail.com. Escape character is '^]'. 220 smtp.gmail.com ESMTP ca40-20020a056a0206a800b005897bfc2ed3sm7618602pgb.93 - gsmtp 当然,现在Gmail 推荐使用 TLS/SSL 连接,所以也要测试465: telnet smtp.gmail.com 465 显示: telnet smtp.gmail.com 465 Trying 173.194.174.108... Connected to smtp.gmail.com. Escape character is '^]'. 正常,超时则无法发送。 收邮件 telnet pop.gmail.com 995 显示: telnet pop.gmail.com 995 Trying 64.233.188.108... Connected to pop.gmail.com. Escape character is '^]'. 测试: telnet imap.gmail.com 993 显示: Trying 142.251.8.108... Connected to imap.gmail.com. Escape character is '^]'. 这两个都要测试,超时则无法收到邮件。

2025/2/24
articleCard.readMore

Nginx 出现 Too many open files 错误与修复

最近服务器遭遇间歇性流量攻击,服务器负载不高,却出现无法打开的情况,查看nginx错误日志,出现大量的“Too many open files”错误,大致意思就是说nginx无法打开更多的文件。 出现这个错误可能是由于系统的ulimit限制和nginx自身的配置有关系。 什么是ulimit? ulimit命令用来限制系统用户对shell资源的访问。 假设有这样一种情况,当一台 Linux 主机上同时登陆了 10 个人,在系统资源无限制的情况下,这 10 个用户同时打开了 500 个文档,而假设每个文档的大小有 10M,这时系统的内存资源就会受到巨大的挑战。 ulimit 用于限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。 简单来说,ulimit描述符可以对用户打开的文件数量进行限制(不止限制打开文件数量),让单个用户不至于打开较多的文件,导致系统奔溃或者资源不足的情况。 查看ulimit 既然知道了ulimit是做什么的,首先要先知道系统底层限制到底是多少,ulimit的参数如下: -a:显示目前资源限制的设定; -c <core文件上限>:设定core文件的最大值,单位为区块; -d <数据节区大小>:程序数据节区的最大值,单位为KB; -f <文件大小>:shell所能建立的最大文件,单位为区块; -H:设定资源的硬性限制,也就是管理员所设下的限制; -m <内存大小>:指定可使用内存的上限,单位为KB; -n <文件数目>:指定同一时间最多可开启的文件数; -p <缓冲区大小>:指定管道缓冲区的大小,单位512字节; -s <堆叠大小>:指定堆叠的上限,单位为KB; -S:设定资源的弹性限制; -t <CPU时间>:指定CPU使用时间的上限,单位为秒; -u <程序数目>:用户最多可开启的程序数目; -v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB。 由于上述nginx错误是无法打开过多的文件,那么我们直接使用ulimit -n查看同一时间最多可开启的文件数。 ulimit -n 可以看出限制的1024个文件,这就导致nginx尝试打开更多的文件(超出1024个)的时候出现错误“Too many open files” 修复问题 修改ulimit限制 直接执行命令ulimit -n 65535修改打开文件数,65535指的是需要同一时间最多打开多少个文件,请根据自身情况适当修改。 ulimit命令修改只对当前的shell有效,退出后失效,如果需要永久生效,需要修改/etc/security/limits.conf这个文件,在底部加入下面的配置: * soft nproc 65535 * hard nproc 65535 * soft nofile 65535 * hard nofile 65535 *:代表全局 soft:代表软件 hard:代表硬件 nproc:是代表最大进程数 nofile:是代表最大文件打开数 修改完毕后,再次执行命令:ulimit -n可以看到设置已经生效! 修改nginx打开文件限制 修改nginx.conf在 http 和 events 外层加入一行: worker_rlimit_nofile 65535; worker_rlimit_nofile这个参数的含义是:“为nginx工作进程改变打开最多文件描述符数目的限制。用来在不重启主进程的情况下增加限制。” Debian系统 看起来像这样的 user www-data; worker_processes auto; pid /run/nginx.pid; ############################################################################################################### # Must be less than LimitNOFILE for systemd # or /etc/security/limits.conf (non-systemd) # E.g. if LimitNOFILE is 65535, I set to 30000 (systemd) # E.g. if "nginx hard nofile 30000" in the /etc/security/limits.conf, I set to 30000 (non-systemd) ############################################################################################################### worker_rlimit_nofile 30000; #vg include /etc/nginx/modules-enabled/*.conf; events { worker_connections 65535; #vg multi_accept on; #vg } http { ## # Basic Settings ... ..... 重载nginx配置: nginx -s reload Nginx的其他优化 编辑 /etc/sysctl.conf 文件,添加或修改 fs.file-max = 70000 查看是否生效 sysctl -p

2025/2/23
articleCard.readMore

Debian12 系统添加多个IP

对于使用 传统的 ifconfig 配置的系统(例如老版本的Ubuntu、Debian等),直接修改 /etc/network/interfaces 文件。 编辑配置文件: sudo nano /etc/network/interfaces 添加多个IP地址: 在相应的网络接口下添加多个IP地址。例如: iface enp30 inet static address 192.168.0.250 netmask 255.255.255.0 gateway 192.168.0.1 iface enp30 inet static address 192.168.0.251 netmask 255.255.255.0 重启网络服务: 保存文件并退出后,重启网络服务: sudo systemctl restart networking 检查配置 通过以下命令验证多IP地址是否已成功添加: ip addr show enp30 这将显示 enp30 接口的所有IP地址。你应该能看到你配置的多个IP地址。 临时配置多个IP地址 为 enp30 接口添加一个新的IP 192.168.0.251 执行以下命令: ip addr add 192.168.0.251/24 dev enp30 这操作是临时的,重启后会丢失。

2025/2/21
articleCard.readMore

linux测试url的访问速度

使用curl命令来测试URL的访问速度。 以下是使用curl测试URL访问速度的步骤: 打开终端或命令行界面。 输入以下命令: curl -o /dev/null -s -w "time_namelookup: %{time_namelookup}\n time_connect: %{time_connect}\n time_pretransfer: %{time_pretransfer}\n time_starttransfer: %{time_starttransfer}\n time_total: %{time_total}\n" <URL> 请将<URL>替换为要测试的URL地址。 执行命令后,curl将会发送请求并返回一些关于请求的统计信息,包括以下内容: time_namelookup:DNS解析耗时,即将URL解析为IP地址的时间。 time_connect:建立TCP连接的耗时。 time_pretransfer:从开始到传输开始之前的耗时。 time_starttransfer:从开始到第一个字节接收完成的耗时。 time_total:总耗时,即从开始到请求完成的时间。 这些时间单位都以秒为单位。 通过这些统计信息,你可以了解到URL的访问速度和各个阶段的耗时情况。

2025/2/21
articleCard.readMore

Giffgaff 打开 wificall 的操作步骤及分流规则

打开wificall的好处:可以使用英国本地资费,免费接电话。 ios:需要运营商刷到58.0版本,或者ios17.4以上版本 关闭wifi和wificall开关 打开飞行模式 关闭你的国内运营商 打开你的wifi 全局连接英国节点,或走规则 打开giffgaff的wificall开关,正常就可以连接上了,成功为运营商名字变成giffgaff 可以解除飞行模式和正常打开你的国内运营商了 安卓:origin os系统 打开wificall开关 全局连接英国节点,或走规则 正常就可以直接连上wificall 分流规则 # 地区检测 DOMAIN-SUFFIX, gspe1-ssl.ls.apple.com # 沃达丰 DOMAIN-SUFFIX, epdg.epc.mnc015.mcc234.pub.3gppnetwork.org DOMAIN-SUFFIX, ss.epdg.epc.mnc015.mcc234.pub.3gppnetwork.org DOMAIN-SUFFIX, ss.epdg.epc.geo.mnc015.mcc234.pub.3gppnetwork.org DOMAIN-SUFFIX, entsrv-uk.vodafone.com DOMAIN-SUFFIX, vuk-gto.prod.ondemandconnectivity.com IP-CIDR,88.82.0.0/19 # CMLinkUK EE IP-CIDR,46.68.0.0/17 # Giffgaff IP-CIDR,87.194.0.0/16 或 # 地区检测 - 'DOMAIN-SUFFIX,gspe1-ssl.ls.apple.com,WifiCall' # 沃达丰 - 'DOMAIN-SUFFIX,epdg.epc.mnc015.mcc234.pub.3gppnetwork.org,WifiCall' - 'DOMAIN-SUFFIX,ss.epdg.epc.mnc015.mcc234.pub.3gppnetwork.org,WifiCall' - 'DOMAIN-SUFFIX,ss.epdg.epc.geo.mnc015.mcc234.pub.3gppnetwork.org,WifiCall' - 'DOMAIN-SUFFIX,entsrv-uk.vodafone.com,WifiCall' - 'DOMAIN-SUFFIX,vuk-gto.prod.ondemandconnectivity.com,WifiCall' - 'IP-CIDR,88.82.0.0/19,WifiCall,no-resolve' # CMLinkUK EE - 'IP-CIDR,46.68.0.0/17,WifiCall,no-resolve' # Giffgaff - 'IP-CIDR,87.194.0.0/16,WifiCall,no-resolve' 节点需要支持UDP!!! wificall走的UDP的500和4500端口

2025/1/28
articleCard.readMore

Docker本地镜像的导出、导入 (export,import,save,load)

对于镜像的导出和导入,Docker 提供了两种方案,下面分别进行介绍。 一、使用 export 和 import 1,查看本机的容器 使用 docker ps -a 命令查看本机所有的容器。 2,导出镜像 (1)使用 docker export 命令根据容器 ID 将镜像导出成一个文件。 docker export 镜像ID > server.tar (2)上面命令执行后,可以看到文件已经保存到当前的终端目录下。 3,导入镜像 (1)使用 docker import 命令则可将这个镜像文件导入进来。 docker import - new_server < server.tar (2)执行 docker images 命令可以看到镜像确实已经导入进来了。 二、使用 save 和 load 1,查看本机的容器 这两个命令是通过镜像来保存、加载镜像文件的。首先我们使用 docker images 命令查看本机所有的镜像。 2,保存镜像 (1)下面使用 docker save 命令根据 ID 将镜像保存成一个文件。 docker save 镜像ID > server.tar (2)我们还可以同时将多个 image 打包成一个文件,比如下面将镜像库中的 postgres 和 mongo 打包: docker save -o images.tar postgres mongo 3,载入镜像 使用 docker load 命令则可将这个镜像文件载入进来。 docker load < server.tar 附:两种方案的差别 特别注意:两种方法不可混用。 import 导入 save 产生的文件,虽然导入不提示错误,但是启动容器时会提示失败,会出现类似"docker: Error response from daemon: Container command not found or does not exist"的错误。 1,文件大小不同 export 导出的镜像文件体积小于 save 保存的镜像 2,是否可以对镜像重命名 docker import 可以为镜像指定新名称 docker load 不能对载入的镜像重命名 3,是否可以同时将多个镜像打包到一个文件中 docker export 不支持 docker save 支持 4,是否包含镜像历史 export 导出(import 导入)是根据容器拿到的镜像,再导入时会丢失镜像所有的历史记录和元数据信息(即仅保存容器当时的快照状态),所以无法进行回滚操作。 save 保存(load 加载)的镜像,没有丢失镜像的历史,可以回滚到之前的层(layer)。 5,应用场景不同 docker export 的应用场景:主要用来制作基础镜像,比如我们从一个 ubuntu 镜像启动一个容器,然后安装一些软件和进行一些设置后,使用 docker export 保存为一个基础镜像。然后,把这个镜像分发给其他人使用,比如作为基础的开发环境。 docker save 的应用场景:如果我们的应用是使用 docker-compose.yml 编排的多个镜像组合,但我们要部署的客户服务器并不能连外网。这时就可以使用 docker save 将用到的镜像打个包,然后拷贝到客户服务器上使用 docker load 载入。

2024/10/11
articleCard.readMore

Go语言进制以及进制转换

二进制 0B或者0b 表示 var bin1 = 0b1101 package main import "fmt" func main(){ var num01 int = 0b1100 fmt.Printf("%b的十进制为%d", num01,num01) } 八进制 0O或者0o 表示 var oct = 0o1234567 package main import "fmt" func main(){ var num01 int = 0o10 fmt.Printf("%o的十进制为%d", num01,num01) } 十六进制 0X或者0x 表示 var hex = 0x1234 package main import "fmt" func main(){ var num01 int = 0xf fmt.Printf("%x的十进制为%d", num01,num01) } 进制转换 1. 二进制转八进制 %b -> %o 2. 二进制转十进制 %b -> %d 3. 二进制转十六进制 %b -> %x 4. 八进制转二进制 %o -> %b 5. 八进制转十进制 %o -> %d 6. 八进制转十六进制 %o -> %x 7. 十进制转二进制 %d -> %b 8. 十进制转八进制 %d -> %o 9. 十进制转十六进制 %d -> %x 10. 十六进制转二进制 %x -> %b 11. 十六进制转八进制 %x -> %o 12. 十六进制转十进制 %x -> %d // 例 fmt.Printf("十进制%d转成八进制%o",num1,num2) %b 表示为二进制 %c 该值对应的unicode码值 %d 表示为十进制 %o 表示为八进制 %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 %x 表示为十六进制,使用a-f %X 表示为十六进制,使用A-F %U 表示为Unicode格式:U+1234,等价于"U+%04X" %E 用科学计数法表示 %f 用浮点数表示 快速测试 go.dev/play

2024/9/24
articleCard.readMore

Mac终端查看sqlite3数据库

用sqlite命令打开数据库文件 sqlite3 db.file 这样我们就进入了数据库操作,下面的命令都只能是sqlite下的命令,如果输入其他命令,都是无效的。 我们可以输入 .help 先查看下大概的命令: .help 常用的几种简单命令: 退出sqlite .quit 查看所有表 .tables 配置情况 .show 设置查询数据排列格式 .mode list .mode line .mode column 是否显示头 .headers on

2024/9/24
articleCard.readMore

解决 UFW 和 Docker 的问题

问题 UFW 是一个 iptables 前端,可以非常方便的管理防火墙的规则。但是当安装了 Docker,UFW 无法管理 Docker 发布出来的端口了。 具体现象是: 在一个对外提供服务的服务器上启用了 UFW,并且默认阻止所有未被允许的传入连接。 运行了一个 Docker 容器,并且使用 -p 选项来把该容器的某个端口发布到服务器的所有 IP 地址上。比如:docker run -d --name httpd -p 0.0.0.0:8080:80 httpd:alpine 将会运行一个 httpd 服务,并且将容器的 80 端口发布到服务器的 8080 端口上。 UFW 将不会阻止所有对 8080 端口访问的请求,用命令 ufw deny 8080 也无法阻止外部访问这个端口。 这个问题其实挺严重的,这意味着本来只是为了在内部提供服务的一个端口被暴露在公共网络上。 解决 UFW 和 Docker 的问题 撤销原先的修改 如果已经按照目前网络上搜索到解决方案修改过了,请先修改回来,包括: 启用 Docker 的 iptables 功能,删除所有类似 --iptables=false 的修改,包括 /etc/docker/daemon.json 配置文件。 UFW 的默认 FORWARD 规则改回默认的 DROP,而非 ACCEPT。 删除 UFW 配置文件 /etc/ufw/after.rules 中与 Docker 网络相关的规则。 如果修改了 Docker 相关的配置文件,重启 Docker。稍后还要修改 UFW 的配置,可以一并重启。 目前新的解决方案只需要修改一个 UFW 配置文件即可,Docker 的所有配置和选项都保持默认。 修改 UFW 的配置文件 /etc/ufw/after.rules,在最后添加上如下规则: # BEGIN UFW AND DOCKER *filter :ufw-user-forward - [0:0] :ufw-docker-logging-deny - [0:0] :DOCKER-USER - [0:0] -A DOCKER-USER -j ufw-user-forward -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN -A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] " -A ufw-docker-logging-deny -j DROP COMMIT # END UFW AND DOCKER 然后重启 UFW,sudo systemctl restart ufw。现在外部就已经无法访问 Docker 发布出来的任何端口了,但是容器内部以及私有网络地址上可以正常互相访问,而且容器也可以正常访问外部的网络。可能由于某些未知原因,重启 UFW 之后规则也无法生效,请重启服务器。 如果希望允许外部网络访问 Docker 容器提供的服务,比如有一个容器的服务端口是 80。那就可以用以下命令来允许外部网络访问这个服务: ufw route allow proto tcp from any to any port 80 这个命令会允许外部网络访问所有用 Docker 发布出来的并且内部服务端口为 80 的所有服务。 如果有多个容器的服务端口为 80,但只希望外部网络访问某个特定的容器。比如该容器的私有地址为 172.17.0.2,就用类似下面的命令: ufw route allow proto tcp from any to 172.17.0.2 port 80 如果一个容器的服务是 UDP 协议,假如是 DNS 服务,可以用下面的命令来允许外部网络访问所有发布出来的 DNS 服务: ufw route allow proto udp from any to any port 53 同样的,如果只针对一个特定的容器,比如 IP 地址为 172.17.0.2: ufw route allow proto udp from any to 172.17.0.2 port 53 解释 在新增的这段规则中,下面这段规则是为了让私有网络地址可以互相访问。通常情况下,私有网络是比公共网络更信任的。 -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 下面的规则是为了可以用 UFW 来管理外部网络是否允许访问 Docker 容器提供的服务,这样我们就可以在一个地方来管理防火墙的规则了。 -A DOCKER-USER -j ufw-user-forward 例如,我们要阻止一个 IP 地址为 172.17.0.9 的容器内的所有对外连接,也就是阻止该容器访问外部网络,使用下列命令 ufw route deny from 172.17.0.9 to any 下面的规则阻止了所有外部网络发起的连接请求,但是允许内部网络访问外部网络。对于 TCP 协议,是阻止了从外部网络主动建立 TCP 连接。对于 UDP,是阻止了所有小于端口 32767 的访问。为什么是这个端口的?由于 UDP 协议是无状态的,无法像 TCP 那样阻止发起建立连接请求的握手信号。在 GNU/Linux 上查看文件 /proc/sys/net/ipv4/ip_local_port_range 可以看到发出 TCP/UDP 数据后,本地源端口的范围,默认为 32768 60999。当从一个运行的容器对外访问一个 UDP 协议的服务时,本地端口将会从这个端口范围里面随机选择一个,服务器将会把数据返回到这个随机端口上。所以,我们可以假定所有容器内部的 UDP 协议的监听端口都小余 32768,不允许外部网络主动连接小余 32768 的 UDP 端口。 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN 如果一个容器在接受数据的时候,端口号没有遵循操作系统的设定,也就是说最小端口号要小余 32768。比如运行了一个 Dnsmasq 的容器,Dnsmasq 用于接受数据的最小端口号默认是 1024。那可以用下面的命令来允许 Dnsmasq 这个容器使用一个更大的端口范围来接受数据。 ufw route allow proto udp from any port 53 to any port 1024:65535 因为 DNS 是一个非常常见的服务,所以已经有一条规则用于允许使用一个更大的端口范围来接受 DNS 数据包 选择 ufw-user-forward 而不是 ufw-user-input 的原因 使用 ufw-user-input 优点: 使用的 UFW 命令比较简单,也比较容易理解 比如,允许公众网络访问一个已经发布出来的容器端口 8080,使用命令: ufw allow 8080 缺点: 不仅仅是暴露了已经发布的容器端口,也暴露了主机上的端口。 比如,如果在主机上运行了一个端口为 8080 的服务。命令 ufw allow 8080 允许了公共网络访问这个服务,也允许了访问所有已经发布的容器端口为 8080 的服务。但是我们可能只是希望保留主机上的这个服务,或者是运行在容器里面的服务,而不是两个同时暴露。 为了避免这个问题,我们可能需要使用类似下面的命令来管理已经发布的容器端口: ufw allow proto tcp from any to 172.16.0.3 port 8080 使用 ufw-user-forward 优点: 不会因为同一条命令而同时暴露主机和容器里面的服务。 比如,如果我们希望暴露所有容器端口为 8080 的服务,使用下面的命令: ufw route allow 8080 现在公共网络可以访问所有容器端口为 8080 的已经发布的服务,但是运行在主机上的 8080 服务仍然不会被公开。如果我们希望公开主机上的 8080 端口,可以执行下面的命令: ufw allow 8080 结论 如果我们正在使用老版本,我们可以使用 ufw-user-input。但是要小心避免把不该暴露的服务暴露出去。 如果正在使用支持 ufw route 命令的新版本,我们最好使用 ufw-user-forward,并且使用 ufw route 来管理与容器相关的防火墙规则。

2024/7/11
articleCard.readMore

docker 映射某个范围内的端口列表

在Dockerfile、命令行或docker-compose.yml中,都可以使用类似于8080-8090:8080-8090的格式,来映射多个端口 docker-compose.yml version: '3.6' services: web: image: nginx:1.18 ports: # 将会映射8080到8090这个范围内的端口 - 8080-8090:8080-8090 volumes: - ./www:/www 命令行 docker run -p 8080-8090:8080-8090 nginx Dockerfile Dockerfile EXPOSE 8080-8090

2024/3/9
articleCard.readMore

使用adb命令和电脑互传文件

电脑传文件到手机 把当前目录下的test.tex文件传到手机 /sdcard/test目录中 adb push test.txt /sdcard/test 手机文件传到电脑 把 /sdcard/test/ 目录下的test.txt文件传到本机当前目录test目录中 adb pull /sdcard/test/test.txt ./test 查看手机文件目录 #进入到根目录 adb shell #查看所有目录 ls #进入到sdcard目录,安卓手机的文件管理一般都这这里 cd sdcard #然后查看要传输哪些文件到哪个文件夹

2024/3/4
articleCard.readMore

查找子域名解析: 子域名探测方法大全

子域名探测 通过收集子域名信息来进行渗透是目前常见的一种手法。 子域名信息收集可以通过手工,也可以通过工具,还可以通过普通及漏洞搜索引擎来进行分析。 在挖SRC漏洞时,子域名信息的收集至关重要! 为什么要进行子域名探测? 子域名探测可以帮我们发现渗透测试中更多的服务,这将增加发现漏洞的可能性 子域名探测方法大全 在线接口 在线接口 https://crt.sh/ https://censys.io/ https://transparencyreport.google.com/https/certificates https://dnsdumpster.com/ https://hackertarget.com/find-dns-host-records/ https://x.threatbook.cn/ https://www.virustotal.com/gui/home/search https://site.ip138.com/baidu.com/domain.htm https://www.t1h2ua.cn/tools/ http://tool.chinaz.com/subdomain/ 暴力枚举 Layer子域名爆破机 Layer是windows下的一款子域名探测工具,其工作原理是利用子域名字典进行爆破,使用简单容易上手。 Amass 工具描述:爆破, google, VirusTotal, alt names go install github.com/OWASP/Amass/... amass -d target.com -o $outfile Knock 工具描述:AXFR, virustotal, 爆破 apt-get install python-dnspython git clone https://xxx.com/guelfoweb/knock.git cd knock nano knockpy/config.json # <- set your virustotal API_KEY python setup.py install 搜索引擎 Google Bing 百度 钟馗之眼 https://www.zoomeye.org/ site=target.com duckduckgo https://duckduckgo.com site:target.com Certificate Transparency (证书透明) SSL/TLS证书 证书透明度(Certificate Transparency)是证书授权机构的一个项目,证书授权机构会将每个SSL/TLS证书发布到公共日志中。 在线查询: https://crt.sh/ https://censys.io/ https://developers.facebook.com/tools/ct/ https://google.com/transparencyreport/https/ct/ https://transparencyreport.google.com/https/certificates CTFR 工具描述:滥用证书透明记录 git clone https://github.com/UnaPibaGeek/ctfr.git cd ctfr pip3 install -r requirements.txt python3 ctfr.py -d target.com -o $outfile Censys_subdomain_enum.py pip install censys git clone https://github.com/appsecco/the-art-of-subdomain-enumeration.git python censys_enumeration.py target.com Cloudflare_enum.py pip install censys git clone https://github.com/appsecco/the-art-of-subdomain-enumeration.git cloudflare_subdomain_enum.py your@cloudflare.email target.com Crt_enum_web.py pip install psycopg2 git clone https://github.com/appsecco/the-art-of-subdomain-enumeration.git python3 crtsh_enum_web.py target.com San_subdomain_enum.py git clone https://github.com/appsecco/the-art-of-subdomain-enumeration.git ./san_subdomain_enum.py target.com Subject Alternate Name (SAN)-主题备用名称 SAN(Subject Alternate Name)主题备用名称,主题备用名称证书简单来说,在需要多个域名,将其用于各项服务时,可使用SAN证书。 San_subdomain_enum.py Public datasets (公开数据集) 有些项目收集了全互联网范围内的扫描数据,并将其提供给研究人员和安全社区。 Rapid7 Forward DNS dataset (Project Sonar) 数据聚合网站 https://opendata.rapid7.com/ 信息泄漏 信息泄露 文件泄漏 Git仓库泄露 从流量中分析提取 内容解析(HTML,JavaScript,文件) BiLE-suite aptitude install httrack git clone https://github.com/sensepost/BiLE-suite.git perl BiLE.pl target.com Second Order go get xxx.com/mhmdiaa/second-order cp ~/go/src/xxx.com/mhmdiaa/second-order/config.json ~/go/src/xxx.com/mhmdiaa/second-order/config-subs-enum.json 编辑修改LogCrawledURLs为True second-order -base https://target.com -config config.json -output target.com DNS解析 在线查询: VirusTotal(https://www.virustotal.com/) ViewDNS(https://viewdns.info/) DNSdumpster(https://dnsdumpster.com/) BiLE-suite Massdns git clone https://github.com/blechschmidt/massdns.git cd massdns/ make 解析域名: /bin/massdns -r lists/resolvers.txt -t AAAA -w results.txt domains.txt -o S -w output.txt 爆破域名: ./scripts/subbrute.py wordlist.txt target.com | ./bin/massdns -r lists/resolvers.txt -t A -o S -w output.txt CT解析: ./scripts/ct.py target.com | ./bin/massdns -r lists/resolvers.txt -t A -o S -w output.txt 区域传送 域传送是一种DNS事务,DNS服务器将其全部或部分域文件的副本传递给另一个DNS服务器。 Windows: Linux: dig +multi AXFR target.com dig +multi AXFR $ns_server target.com DNS aggregators (DNS聚合器) Cloudflare_enum.py pip install censys git clone https://xxx.com/appsecco/the-art-of-subdomain-enumeration.git cloudflare_subdomain_enum.py your@cloudflare.email[4] target.com DNS Cache Snooping (域名缓存侦测) 域名缓存侦测(DNS Cache Snooping)技术 Alterations & permutations (换置 & 排序) AltDNS git clone https://xxx.com/infosec-au/altdns.git cd altdns pip install -r requirements.txt ./altdns.py -i subdomains.txt -o data_output -w words.txt -r -s results_output.txt DNSSEC(Domain Name System Security Extensions),DNS安全扩展,DNSSEC区域漫步 由于DNSSEC处理不存在域名的方式,您可以"遍历"DNSSEC域并枚举该域中的所有域名。 Ldns-walk aptitude install ldnsutils ldns-walk target.com ldns-walk @nsserver.com target.com 如果DNSSEC NSEC开启,可以获得全部域名。 CSP HTTP 首部 Domains-from-csp git clone https://github.com/yamakira/domains-from-csp.git pip install click python csp_parser.py $URL python csp_parser.py $URL -r SPF记录 SPF是通过域名的TXT记录来进行设置的,SPF记录列出了所有被授权代表域名发送电子邮件的主机 Assets-from-spf git clone https://github.com/yamakira/assets-from-spf.git pip install click ipwhois python assets_from_spf.py target.com 虚拟主机爆破 vhost-brute aptitude install php-curl git clone https://github.com/gwen001/vhost-brute.git Virtual-host-discovery git clone https://github.com/jobertabma/virtual-host-discovery.git ruby scan.rb --ip=1.1.1.1 --host=target.com --output output.txt ASN发现 通过域名查询到 ASN,再通过 ASN 查询到所属的所有 ip 范围 爬虫 Scraping(抓取) 泛解析问题 目前最好的解决方式是通过先获取一个绝对不存在域名的响应内容,再遍历获取每个字典对应的子域名的响应内容,通过和不存在域名的内容做相似度比对,来枚举子域名,但这样的实现是以牺牲速度为代价 Tools 工具也有很多厉害的,平时我一般使用 OneForALL + ESD + JSfinder 来进行搜集,(ESD 可以加载 layer 的字典,很好用) https://github.com/shmilylty/OneForAll 强大的快速子域枚举工具 https://github.com/aboul3la/Sublist3r Knock子域名获取,可用于查找子域名接管漏洞 https://github.com/guelfoweb/knock 一款便捷高效的子域名爆破工具 https://github.com/yanxiu0614/subdomain3 Go语言开发的子域名枚举工具 https://github.com/caffix/amass 继承于Sublist3r项目的模块化体系结构 https://github.com/Ice3man543/subfinder 带有网页截图功能的子可视化域名枚举工具 https://github.com/janniskirschner/horn3t Lijiejie开发的一款使用广泛的子域名爆破枚举工具 https://github.com/lijiejie/subDomainsBrute 猪猪侠开发的一款域名收集全面、精准的子域名枚举工具 https://github.com/ring04h/wydomain 子域名监控 https://github.com/LangziFun/LangSrcCurise https://www.freebuf.com/sectool/198396.html 参考 1 2 3 4

2024/2/5
articleCard.readMore

开启 Telegram 的邮箱登录

Telegram的邮箱登录是什么? 有什么用? 邮箱登录就是通过电子邮件获取验证码来登录Telegram账户, 此功能可以在把验证码发送到Telegram客户端的情况下同时向邮箱发送一份验证码, 而不是通过短信发送验证码. 极大减少了Telegram官方的短信费用支出. 对于使用接码的用户来说, 再也不怕没客户端也没手机号的情况下收不到验证码了! 如何开启Telegram邮箱登录? 注意 账号绑定的手机号需要可以接收短信验证码, 否则无法设置邮箱登录 根据官方文档来看, auth.sentCodeTypeSetUpEmailRequired: if the user logins often enough, Telegram will ask the user to verify an email that will be used to send the login code. 当你的登录频率足够高时, Telegram服务器会要求客户端设置邮箱登录 具体方法 使用Telegram官方移动客户端登录, 登录时选择 Tap to get a code via SMS 短信验证码和Telegram客户端验证码一致, 任选其一输入即可, 不需要输入2FA密码, 直接返回重新刷 如果过程中出现 Too many attemps, please try again later 则需要休息一段时间再刷 直到出现让你选择登录邮箱的提示界面 根据提示输入完成邮箱验证码后会向你的账号绑定手机号码发送短信验证码, 输入完即可开启邮箱登录 (2FA可以不输入) 如何判断有没有开启邮箱登录? 在设置-隐私与安全界面,可看到已设置的“登录邮箱” 参考: Telegram的邮箱登录 User Authorization

2023/12/26
articleCard.readMore

通过 ADB 卸载系统自带应用(无需Root),精简系统app

准备 ADB 使用条件 要使用 ADB(Android 调试桥),你需要按照以下步骤操作: 下载并安装 ADB 工具: 在计算机上安装 Android SDK 或者仅安装 ADB 工具。你可以从 Android 官方网站或者其他可信赖的来源获取它们。 如果你使用 macOS 或 Linux,你可以通过终端使用系统包管理器或 Homebrew 安装 ADB。 如果你使用 Windows,你可以下载 Android Studio,它包含了 ADB 工具。 连接 Android 设备: 使用 USB 线将 Android 设备连接到计算机上。 在 Android 设备上打开开发者选项。这通常需要在设备设置中多次点击“关于手机” -> “版本号”或者类似的选项。一旦开启,返回设置菜单并找到“开发者选项”。 在“开发者选项”中,启用“USB 调试”。 打开命令提示符(Windows)/终端(macOS、Linux): 运行以下命令以确保 ADB 正确识别设备: adb devices 这应该显示已连接设备的列表。 使用 ADB 列出已经安装的应用程序 打开命令提示符(Windows)/终端(macOS、Linux): 在命令提示符/终端中输入以下命令来列出已安装的应用程序: adb shell pm list packages 如果你想筛选出包含特定关键词的应用程序,可以使用以下命令: adb shell pm list packages | grep keyword 将 “keyword” 替换为你想要搜索的关键词。 比如魅族的手机 adb shell pm list packages | grep com.meizu 使用 ADB 在 Android 设备上卸载普通应用程序 打开命令提示符(Windows)/终端(macOS、Linux): 在命令提示符/终端中输入以下命令来卸载应用程序: adb uninstall package_name 将 package_name 替换为你想要卸载的应用程序的包名。你可以通过之前提到的 adb shell pm list packages 命令来获取应用程序的包名列表。 示例: 假设你想卸载名为 "com.example.app" 的应用程序,命令将如下所示: adb uninstall com.example.app 确认卸载: 执行命令后,ADB 会尝试卸载该应用程序。等待一段时间,命令提示符/终端会显示成功或失败的信息。 这个指令卸载系统自带应用可以能会遇到 Failure [DELETE_FAILED_INTERNAL_ERROR] 错误提示 使用 ADB 在 Android 设备上卸载系统自带应用程序 清除应用程序数据: 在卸载应用之前,尝试先清除该应用的数据。使用以下命令: adb shell pm clear package_name 将 package_name 替换为应用程序的包名。 强制停止应用程序: 如果应用程序正在运行,尝试通过 ADB 强制停止应用程序再进行卸载: adb shell am force-stop package_name 将 package_name 替换为应用程序的包名。 确保设备有足够的存储空间来完成卸载操作。有时设备存储空间不足可能会导致卸载失败。 使用 ADB shell 运行命令: adb shell pm uninstall -k --user 0 package_name 将 package_name 替换为要卸载的系统应用的包名。 列出魅族自带的部分应用程序 #列出包名前缀为 com.meizu 应用 $adb shell pm list packages | grep com.meizu package:com.meizu.documentsui package:com.meizu.flyme.hometools package:com.meizu.flyme.launcher package:com.meizu.desktopbackup package:com.meizu.account.pay package:com.meizu.powersave package:com.meizu.mzsimcontacts package:com.meizu.filemanager package:com.meizu.flyme.directservice package:com.meizu.compaign package:com.meizu.splitloccontroller package:com.meizu.backup package:com.meizu.flyme.update package:com.meizu.net.map package:com.meizu.flyme.wallet package:com.meizu.feedback package:com.meizu.media.camera package:com.meizu.flyme.providers.forcetouch package:com.meizu.mstore package:com.meizu.net.search package:com.meizu.perfui package:com.meizu.sceneinfo package:com.meizu.flyme.telecom package:com.meizu.notepaper package:com.meizu.flyme.calculator package:com.meizu.yellowpage package:com.meizu.safe package:com.meizu.media.reader package:com.meizu.flyme.toolbox package:com.meizu.connectivitysettings package:com.meizu.flyme.childrenlauncher package:com.meizu.gamecenter.service package:com.meizu.mznfcpay package:com.meizu.account package:com.meizu.media.ebook package:com.meizu.media.music package:com.meizu.media.video package:com.meizu.media.gallery package:com.meizu.alphame package:com.meizu.dataservice package:com.meizu.systemwallpaper package:com.meizu.flymecommunication package:com.meizu.flyme.mall package:com.meizu.voiceassistant package:com.meizu.cloud package:com.meizu.mcare package:com.meizu.setup package:com.meizu.share package:com.meizu.flyme.easylauncher package:com.meizu.battery package:com.meizu.mzsyncservice package:com.meizu.privacy package:com.meizu.datamigration package:com.meizu.location package:com.meizu.flyme.input package:com.meizu.flyme.service.find package:com.meizu.customizecenter package:com.meizu.flyme.gamecenter package:com.meizu.experiencedatasync package:com.meizu.callsetting package:com.meizu.netcontactservice package:com.meizu.media.life package:com.meizu.flyme.weather 卸载魅族自带的部分应用程序 $adb shell #语音助手 $pm uninstall -k --user 0 com.meizu.voiceassistant #天气预报 $pm uninstall -k --user 0 com.meizu.flyme.weather #备份与恢复 $pm uninstall -k --user 0 com.meizu.backup #手机云备份 $pm uninstall -k --user 0 com.meizu.desktopbackup #换机助手 $pm uninstall -k --user 0 com.meizu.datamigration #福利中心 $pm uninstall -k --user 0 com.meizu.compaign #钱包 $pm uninstall -k --user 0 com.meizu.flyme.wallet $pm uninstall -k --user 0 com.meizu.mznfcpay #全球虚拟流量 $pm uninstall -k --user 0 com.flyme.roamingpay #地图 $pm uninstall -k --user 0 com.meizu.net.map #快应用引擎 $pm uninstall -k --user 0 com.meizu.flyme.directservice #云号码 $pm uninstall -k --user 0 com.meizu.netcontactservice #生活服务 $pm uninstall -k --user 0 com.meizu.media.life #游戏中心 $pm uninstall -k --user 0 com.meizu.flyme.gamecenter $pm uninstall -k --user 0 com.meizu.gamecenter.service #音乐 $pm uninstall -k --user 0 com.meizu.media.music #视频 $pm uninstall -k --user 0 com.meizu.media.video #读书 $pm uninstall -k --user 0 com.meizu.media.ebook #新闻资讯 $pm uninstall -k --user 0 com.meizu.media.reader #便签 $pm uninstall -k --user 0 com.meizu.notepaper #应用商店 $pm uninstall -k --user 0 com.meizu.mstore $pm uninstall -k --user 0 com.flyme.meizu.store #系统更新 $pm uninstall -k --user 0 com.meizu.flyme.update #魅族服务 $pm uninstall -k --user 0 com.meizu.mcare $pm uninstall -k --user 0 com.meizu.flyme.mall #用户帮助 $pm uninstall -k --user 0 com.meizu.feedback #搜索 $pm uninstall -k --user 0 com.meizu.net.search #文件管理 $pm uninstall -k --user 0 com.meizu.filemanager #动态壁纸 $pm uninstall -k --user 0 com.vlife.mxlock.wallpaper #输入法 $pm uninstall -k --user 0 com.sohu.inputmethod.sogou #浏览器 $pm uninstall -k --user 0 com.android.browser # 家庭守护 $pm uninstall -k --user 0 com.meizu.sceneinfo #京东 $pm uninstall -k --user 0 com.jingdong.app.mall #天猫 $pm uninstall -k --user 0 com.tmall.wireless #微博 $pm uninstall -k --user 0 com.sina.weibo #高德地图 $pm uninstall -k --user 0 com.autonavi.minimap #今日头条 $pm uninstall -k --user 0 com.ss.android.article.news #去哪儿 $pm uninstall -k --user 0 com.Qunar #喜马拉雅 $pm uninstall -k --user 0 com.ximalaya.ting.android #趣视频 #拼多多 pm uninstall -k --user 0 com.xunmeng.pinduoduo #百度搜索 pm uninstall -k --user 0 com.baidu.searchbox #微博 pm uninstall -k --user 0 com.sina.weibolite #flyme实验室 pm uninstall -k --user 0 com.meizu.flymelab #好看 pm uninstall -k --user 0 com.baidu.haokan #淘宝 pm uninstall -k --user 0 com.taobao.taobao #搜狗输入法 pm uninstall -k --user 0 com.sohu.inputmethod.sogou.meizu #唯品会 pm uninstall -k --user 0 com.achievo.vipshop #部分系统应用 #计步器 pm uninstall -k --user 0 com.meizu.net.pedometer #支付中心 pm uninstall -k --user 0 com.meizu.account.pay #个人助理 pm uninstall -k --user 0 com.meizu.assistant #智能识屏 pm uninstall -k --user 0 com.meizu.picker #工具箱 pm uninstall -k --user 0 com.meizu.flyme.toolbox #系统壁纸 pm uninstall -k --user 0 com.meizu.systemwallpaper #动态主题服务 pm uninstall -k --user 0 com.ibimuyu.lockscreen #下载 pm uninstall -k --user 0 com.android.providers.downloads.ui #动态壁纸 pm uninstall -k --user 0 com.meizu.activeviewlivewallpaper #主题美化 pm uninstall -k --user 0 com.meizu.customizecenter #onemind pm uninstall -k --user 0 com.meizu.alphame #以下部分不建议删除应用 pm uninstall -k --user 0 com.meizu.flyme.hometools pm uninstall -k --user 0 com.android.providers.downloads pm uninstall -k --user 0 com.meizu.flyme.calculator pm uninstall -k --user 0 com.meizu.yellowpage pm uninstall -k --user 0 com.meizu.safe pm uninstall -k --user 0 com.android.printspooler pm uninstall -k --user 0 com.meizu.media.gallery pm uninstall -k --user 0 com.meizu.cloud pm uninstall -k --user 0 com.meizu.share pm uninstall -k --user 0 com.meizu.location pm uninstall -k --user 0 com.android.email pm uninstall -k --user 0 com.meizu.mzsyncservice pm uninstall -k --user 0 com.android.wallpaperbackup pm uninstall -k --user 0 com.meizu.flyme.mmfvideo pm uninstall -k --user 0 com.flyme.telecom.usagedata.service #谨慎删除 pm uninstall -k --user 0 com.meizu.account pm uninstall -k --user 0 com.meizu.flyme.service.find 附注: 安装 APK 文件: 将要安装的 APK 文件复制到计算机上,并记住它的路径。 在命令提示符/终端中,使用以下命令安装 APK 文件到设备: adb install /path/to/your/app.apk 请将 /path/to/your/app.apk 替换为实际 APK 文件的路径。 Flyme9 应用列表 全球流量 com.flyme.roamingpay 旅行助手com.android.cts.priv.ctsshim 应用沙盒com.meizu.pps 不建议删除 计步器 com.meizu.net.pedometer 电话和短信存储 com.android.providers.telephony 不建议删除 日历存储com.android.providers.calendar 不建议删除 媒体存储设备 com.android.providers.media 不建议删除 mbn测试 com.qualcomm.qit.modemtestmode 不建议删除 系统桌面 com.meizu.flyme.launcher 不建议删除 支付中心 com.meizu.account.pay 时钟 com.andriod.alarmclock html查看程序 com.android.htmlviewer 不建议删除 语音设置com.iflytek.speechsuite mms短信服务 com.android.mms.service 不建议删除 下载管理器com.android.providers.downloads 不建议删除 文件管理 com.meizu.filemanager 不建议删除 通讯录counnunity.fairphone.mycontacts 不建议删除 魅族浏览器 com.android.browser 快应用引擎 com.meizu.flyme.directservice 福利中心 com.meizu.compaign 录音机 com.android.soundrecorder 软件包权限帮助程序com.android.defcontainer 不建议删除 备份和恢复com.meizu.backup 下载com.android.providers.downloads.ui 不建议删除 系统更新com.meizu.flyme.update 无线网络助手com.meizu.wifiadmin 不建议删除 证书安装程序com.android.certinstaller 不建议删除 个人助理com.meizu.assistant 钱包com.meizu.flyme.wallet 用户帮助com.meizu.feedback 相机com.meizu.media.camera 不建议删除 设备信息com.qti.qualcomm.deviceinfo 不建议删除 信息com.android.mms 不建议删除 应用商店com.meizu.mstore 不建议删除 mtp主机com.android.mtp 不建议删除 usim卡应用com.android.stk 搜索com.meizu.net.search 性能监视器com.meizu.perfui 智能识屏 com.meizu.picker 家庭守护com.meizu.sceneinfo 便笺com.meizu.notepaper 计算器com.flyme.caculator 日历com.android.calendar 手机管家com.meizu.safe 不建议删除 新闻资讯com.meizu.media.reader 工具箱com.flyme.toolbox 网络设置com.connectivitysettings 不建议删除 设置存储com.android.providers.settings 不建议删除 打印处理服务com.android.printspooler 通话com.android.incallui 不建议删除 输入设备com.android.inputdevices · 不建议删除 系统公共组件com.meizu.flyme.sdkstage 不建议删除 系统界面助手com.flyme.systemuiex 不建议删除 默认打印服务com.android.bips 魅族游戏框架com.meizu.gamecenter.service 动态主题服务com.ibimuyu.lockscreen 不建议删除 xdivert设置com.qti.xdivert musicfx com.android.musicfx 通话控制com.android.server.telecom 不建议删除 魅族支付com.meizu.mznfcpay 密钥连com.android.keychain 电话com.android.dialer 不建议删除 系统界面工具com.flyme.systemuitools 不建议删除 flyme账号com.meizu.account 不建议删除 删除后进不了自带的系统设置 软件包安装程序com.android.packageinstaller 不建议删除 音乐com.meizu.media.music 视频com.meizu.media.video 运营商默认应用com.android.carrierdefaultapp 文本转语音组件com.svox.pico 图库com.meizu.media.gallery onemind com.meizu.alphame 工作资料设置com.android.managedprovisioning 不建议删除 platformfx com.meizu.dateservice 不建议删除 网络管理com.flyme.netadmin 不建议删除 远程协助com.meizu.remotecooperation 工程模式com.meizu.telephonyengineermode 系统壁纸com.meizu.systemwallpaper sms推送com.android.smspush 不建议删除 语音助手com.meizu.voiceassistant 推送服务com.meizu.cloud 不建议删除 魅族虚拟卡本地注册虚拟网络插件com.flyme.virtual.softsim 删除后无法使用全球流量功能 蓝牙分享com.meizu.share 不建议删除 魅族商城com.flyme.meizu.store 超大字体简易模式com.meizu.flyme.easylauncher 图像服务com.meizu.media.imageservice 不建议删除 存储空间管理器com.android.stooragemanager 不建议删除 人脸识别com.meizu.facerecognition com.google.andriod.onetimeinitialzer 设置com.android.settings 不建议删除 电量管理com.meizu.battery flyme云服务com.meizu.mzsyncservice 动态壁纸 com.meizu.activeviewlivewallpaper 换机助手com.meizu.datemigration 位置服务com.qualcomm.location 不建议删除 网络位置服务com.meizu.location 不建议删除 动态主题服务com.vlife.mxlock.wallpaper 桌面壁纸备份 com.android.wallpaperbackup 系统输入法com.meizu.flyme.input 不建议删除 查找手机服务com.meizu.flyme.service.find 电话服务com.android.phone 不建议删除 存储已屏蔽的号码com.android.providers.blockednumber 不建议删除 用户字典com.android.providers.userdictionary 急救信息com.android.emergency 不建议删除 主题美化com.meizu.customizecenter flyme实验室com.meizu.flymelab 一体化位置信息com.android.location.fused 不建议删除 游戏中心com.meizu.flyme.gamecenter 系统界面com.android.systemui 不建议删除 崩溃数据上传 com.meizu.experiencedatasync 电话设置com.meizu.callsetting 不建议删除 关机闹钟com.qualcomm.qti.poweroffalarm 不建议删除 号码云服务com.meizu.netcontactservice 生活助手com.meizu.media.life 联系人存储com.android.providers.contacts 不建议删除 天气com.meizu.flyme.weather 支付宝com.eg.android.AlipayGphone 电话服务com.flyme.telecom.usagedate.service 不建议删除 com.qualcomm.qti.perfdump 不建议删除 com.android.webview 不建议删除 com.android.backupconfirm 书签com.android.bookmarkprovider com.android.flyme.bridge.softsim com.meizu.privacy com.meizu.flyme.hometools 不建议删除 小区广播com.android.cellbroadcastreceiver 不建议删除 无线网络助手 com.meizu.wifiadmin 不建议删除 读书 com.meizu.media.ebook 邮件 com.android.email 趣视频 com.flyme.videoclips 新闻资讯 com.meizu.media.reader 换机助手 com.meizu.datamigration 语音设置 com.iflytek.speechsuite 魅族服务 com.meizu.mcare 开机引导 com.meizu.setup 极限模式 com.meizu.powersave 不建议删除 Aicy建议 com.meizu.suggestion 锁屏画报 com.meizu.net.nativelockscreen 伪基站拦截 com.meizu.mzbasestationsafe 打印服务处理 com.android.printspooler 不建议删除 html查看程序 com.android.htmlviewer 通话记录备份 com.android.calllogbackup 虚拟sim卡用的com.qualcomm.uimremoteclient 自带黑色主题com.android.systemui.theme.dark 无线电服务com.dsi.ant.server

2023/12/5
articleCard.readMore

Kubernetes 停止和移除 pod

停止和移除pod 按名称删除pod 按名称删除kubia-gpu pod: kubectl delete po kubia-gpu 在删除pod的过程中,实际上我们在指示Kubernetes终止该pod中的所有容器。Kubernetes向进程发送一个SIGTERM信号并等待一定的秒数(默认为30),使其正常关闭。如果它没有及时关闭,则通过SIGKILL终止该进程。因此,为了确保你的进程总是正常关闭,进程需要正确处理SIGTERM信号。 提示 还可以通过指定多个空格分隔的名称来删除多个pod(例如:kubectl delete po pod1 pod2)。 使用标签选择器删除pod 停止 kubia-manual 和 kubia-manual-v2 pod 。这两个pod都包含标签 creation_method=manual ,因此可以通过使用一个标签选择器来删除它们: kubectl delete po -l creation_method=manual 通过删除整个命名空间来删除pod 以下命令删除custom-namespace: kubectl delete ns custom-namespace 删除命名空间中的所有pod,但保留命名空间 查看 pods kubectl get pods 通过使用–all选项告诉Kubernetes删除当前命名空间中的所有pod: kubectl delete po --all 删除命名空间中的(几乎)所有资源 通过使用单个命令删除当前命名空间中的所有资源,可以删除ReplicationCcontroller和pod,以及我们创建的所有service: kubectl delete all --all 命令中的第一个all指定正在删除所有资源类型,而–all选项指定将删除所有资源实例,而不是按名称指定它们(我们在运行前一个删除命令时已经使用过此选项)。 注意 使用all关键字删除所有内容并不是真的完全删除所有内容。一些资源会被保留下来,并且需要被明确指定删除。 删除资源时,kubectl将打印它删除的每个资源的名称。 注意 kubectl delete all --all 命令也会删除名为kubernetes的Service,但它应该会在几分钟后自动重新创建。 kubectl 命令演示 kubectl 展示搜索出的pod列表(含pod所在的namespace) kubectl get pod -A |grep <podname> [root@node ~]# kubectl get pod -A |grep dashboard kubernetes-dashboard dashboard-metrics-scraper-5657497c4c-j5kr8 1/1 Running 0 113m kubernetes-dashboard kubernetes-dashboard-78f87ddfc-tlmjv 1/1 Running 0 113m kubectl 删除pod命令 kubectl delete pod <podname> -n <namespace> 在进行删除pod命令时,会发现pod并未被真正删除,原因是k8s误认为我们要删除的pod异常挂了,会启用容灾机制,导致重新在拉起一个新的pod。 故,我们想要正常且彻底的删除一个pod,必须要先破坏掉他的容灾机制,即删除deployment机制。 查看deployment信息 kubectl get deployment -n <namespace> [root@debian ~]# kubectl get deployment --all-namespaces NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE kube-system coredns 2/2 2 2 4h7m kubernetes-dashboard dashboard-metrics-scraper 1/1 1 1 117m kubernetes-dashboard kubernetes-dashboard 1/1 1 1 117m 删除deployment配置 kubectl delete deployment <deployment名> -n <namespace> [root@node ~]# kubectl delete deployment kubernetes-dashboard -n kubernetes-dashboard deployment.apps "kubernetes-dashboard" deleted [root@node ~]# kubectl delete deployment dashboard-metrics-scraper -n kubernetes-dashboard deployment.apps "dashboard-metrics-scraper" deleted 然后进行删除pod命令即可,我删除deployment后,再次查询pod发现,上面的pod已经开始自行删除了(这步可酌情处理) kubectl delete pod <podname> -n <namespace> [root@node ~]# delete pod dashboard-metrics-scraper-5657497c4c-j5kr8 -n kubernetes-dashboard pod "dashboard-metrics-scraper-5657497c4c-j5kr8" deleted # 附一个我这边删除deployment后pod自行删除的情况 [root@node ~]# kubectl get pod -A|grep dashboard

2023/11/20
articleCard.readMore

Docker的 privileged 选项解析(特权模式:赋予容器几乎与主机相同的权限)

Runtime privilege and Linux capabilities 参考官方文档:Docker run reference By default, Docker containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container. This is because by default a container is not allowed to access any devices, but a “privileged” container is given access to all devices (see the documentation on cgroups devices). The –privileged flag gives all capabilities to the container. When the operator executes docker run –privileged, Docker will enable access to all devices on the host as well as set some configuration in AppArmor or SELinux to allow the container nearly all the same access to the host as processes running outside containers on the host. Additional information about running with –privileged is available on the Docker Blog. Docker 容器的安全性 Linux Namespace 和 Capabilities Docker使用Linux namespace和capabilities来实现容器隔离和限制权限。 Linux Namespace:Docker利用namespace技术,使得每个容器都有其自己的进程、网络、挂载、用户ID等独立的空间。这保证了容器与容器之间以及容器与主机之间的隔离性。 Capabilities:Linux capabilities允许将传统的root权限分割成多个不同的能力,例如CAP_NET_ADMIN能力允许操作网络配置,CAP_CHOWN能力允许改变文件所有权。Docker默认情况下会赋予容器一些必要的capabilities,但不包括全部的能力,从而降低了被攻击的风险。 Docker 通过 –cap-add 和 –cap-drop 两个参数,可以灵活地添加或删除容器的 capabilities。 docker run --cap-add=SYS_PTRACE --rm -it alpine 上面 --cap-add=SYS_PTRACE 的意思就是:给容器添加 SYS_PTRACE 权限,允许容器内的进程可以 ptrace 和 debug 其他进程。 Docker限制和权限 在默认情况下,Docker对容器的权限进行了严格的限制,只提供了有限的capabilities。此外,许多系统级别的操作(例如挂载文件系统、修改内核参数等)都是被禁止的。这种安全模型使得Docker可以在不牺牲安全性的前提下,实现轻量级的虚拟化。 然而,在某些情况下,我们可能需要赋予容器更多的权限。例如,如果我们需要在容器中运行一些需要特权的服务(如网络设备管理、硬件设备接口等),那么默认的权限可能就不够用了。这时候,--privileged=true 选项就派上了用场。 Docker的–privileged=true选项 当使用–privileged=true选项运行容器时,Docker会赋予容器几乎与主机相同的权限。 具体来说,这个选项做了以下两件事情: 给容器添加了所有的capabilities 允许容器访问主机的所有设备 –privileged=true的风险 尽管 --privileged=true 选项为容器提供了强大的功能,但它也带来了一些严重的安全隐患。由于privileged容器具有几乎与主机相同的权限,所以如果容器被恶意代码控制,那么攻击者就可以轻易地突破容器的边界,对主机进行任意操作。 因此,我们需要谨慎地使用 --privileged=true 选项,只在真正需要的情况下才启用它。在可能的情况下,我们应该尽量使用其他更细粒度的权限控制手段,例如通过--cap-add或--device参数来分别添加必要的capabilities或设备访问权限。 细粒度的权限控制手段 # 添加单个capability docker run --rm --cap-add NET_ADMIN -it alpine sh # 添加设备访问权限 docker run --device=/dev/sda:/dev/xvdc -it alpine docker-compose 参考 version: "3" services: baicai_image: image: debian container_name: "baicai_image" restart: unless-stopped command: run -c /app/config.json volumes: - ./config.json:/app/config.json environment: TZ: Asia/Shanghai ports: - "80:80" privileged: false cap_add: - NET_ADMIN - SYS_MODULE - SYS_PTRACE - SYS_ADMIN - NET_RAW cap_drop: - ALL 附注: 在Docker Debian容器中安装ps、top等命令 debian镜像默认没有包括进程管理相关工具,在实际使用时可能有些麻烦,如果需要也可以自己安装,使用如下命令。 apt update && apt install -y procps

2023/11/17
articleCard.readMore

Debian 彻底卸载 LibreOffice

LibreOffice是一款非常优秀的开源且免费的办公软件。但我用不到,为了优化更新系统的速度,所以这就卸载了,这不就几条命令的事。 下面的指令对所有基于 Debian 发行版(Debian, Ubuntu, Kubuntu, Xubuntu, buntu, Sidux, Knoppix, Linux Mint, Damn Small Linux, Crunchbag 等)都适用。 彻底卸载 LibreOffice 终端中输入命令: #不要漏掉通配符“?”,否则无法清除/卸载全部 LibreOffice 软件包 sudo apt-get purge libreoffice? 或 #不要漏掉通配符“?”,否则无法清除/卸载全部 LibreOffice 软件包 sudo aptitude purge libreoffice? 或 #不要漏掉通配符“*”,否则无法清除/卸载全部 LibreOffice 软件包 sudo apt-get remove --purge libreoffice* 然后清理残留: sudo apt-get clean sudo apt-get autoremove 附注(不完全卸载): 卸载libreoffice表格 sudo apt remove libreoffice-calc 卸载libreoffice绘图 sudo apt remove libreoffice-draw 卸载libreoffice幻灯片 sudo apt remove libreoffice-impress 卸载libreoffice word文档工具 sudo apt remove libreoffice-writer 清理残留 sudo apt autoremove LibreOffice简介(来自官网): LibreOffice 是一款功能强大的办公软件,默认使用开放文档格式 (OpenDocument Format , ODF), 并支持 *.docx, *.xlsx, *.pptx 等其他格式。 它包含了 Writer, Calc, Impress, Draw, Base 以及 Math 等组件,可用于处理文本文档、电子表格、演示文稿、绘图以及公式编辑。 它可以运行于 Windows, GNU/Linux 以及 macOS 等操作系统上,并具有一致的用户体验。

2023/10/9
articleCard.readMore

通过 WireGuard 搭建 VPN 访问家里内网

家里网络没有公网 IP,因此需要一台具有公网 IP 的服务器作为 WireGuard 网络的“server”。家中需要有一台设备作为 WireGuard 网络中的节点。我们将使用手机,在 4G 网络下检查 VPN 是否搭建成功。 IP 段选择 WireGuard 组网需要使用一个不与你的任何设备的网络相冲突的 IP 地址段。像 192.0.2.0/24 、198.51.100.0/24 、203.0.113.0/24 这些分配为用于文档和示例中的“TEST-NET”,这些地址段通常不会被你需要连接的其他网络所使用。 在下面的配置中,我会分别将 192.0.2.1、192.0.2.2、192.0.2.3 分配给公网服务器、家中的 Mac 和 iPhone。 在服务器上配置 WireGuard 要使用 WireGuard,首先需要确保 Linux 内核支持。可使用 modinfo wireguard 命令检查是否内置了 WireGuard。也可用过 uname -r 检查内核版本是否为 5.6 以上。 安装 wireguard Debian apt install wireguard 其他系统参考:install 完成服务器端的配置 在正确安装 wireguard 后,你可以通过如下命令快速创建一组公钥和私钥。 $ wg genkey | tee peer_A.key | wg pubkey > peer_A.pub && cat peer_A.key && cat peer_A.pub 创建 /etc/wireguard/wg0.conf 并填配置 [Interface] PrivateKey = (your server private key here) Address = 192.0.2.1/24 ListenPort = 51820 #PreUp = echo WireGuard PreUp #PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE #PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE #PostUp = iptables -I INPUT -p udp --dport 51820 -j ACCEPT #PostDown = iptables -D INPUT -p udp --dport 51820 -j ACCEPT #ipv4 局域网够用配置 PostUp = iptables -A FORWARD -i wg0 -j ACCEPT PostDown = iptables -D FORWARD -i wg0 -j ACCEPT #ipv4防火墙放行转发和NAT(访问公共网络) #PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE #PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE #ipv4防火墙放行转发和NAT(访问公共网络) 扩展防火墙规则(节点之间双向互联) #PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE #PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE #PreDown = echo WireGuard PreDown [Peer] # Mac at home PublicKey = (Mac public key here) AllowedIPs = 192.0.2.2/32, 192.168.1.0/24 [Peer] # iPhone PublicKey = (iPhone public key here) AllowedIPs = 192.0.2.3/32 在 WireGuard 中,你需要手动给各个设备分配 IP,并确保每个设备都有唯一的 IP。Interface 包含了当前设备的设置,对于“服务端”来说,ListenPort 是必须的。下面的每一个 Peer 段代表了能连接到本设备的一个其他设备。 配置文件保存后,我们可以使用 wg-quick up wg0 来启用配置文件。wg-quick 会自动配置路由表,无需我们手动设置。 记得放行 51820 UDP 端口。 iptables 配置参考 家中Mac端的配置 创建 /etc/wireguard/wg0.conf 并填配置 [Interface] PrivateKey = (private key of Mac) Address = 192.0.2.2/24 DNS = 1.1.1.1 [Peer] PublicKey = (public key of server) AllowedIPs = 192.0.2.0/24 Endpoint = (server ip address):51820 PersistentKeepalive = 10 在这里,我们将公网服务器作为唯一的 Peer,通过设置 PersistentKeepalive 来进行连接的保活。这里 AllowedIPs 的作用是确保来自于我们 WireGuard 子网网段来的流量能被本机的 WireGuard 虚拟网卡进行处理。 iphone 配置 安装 WireGuard  Download from App Store 这个配置可以参考应用商店的截屏。 设置开机启动 如果你的系统使用systemd,如ubuntu,设置wireguard开机启动命令如下 systemctl enable wg-quick@wg0 启用服务器Peer端转发 打开 /etc/sysctl.conf 修改 net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 或 echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.conf sysctl -p 附注 保留地址段 保留IP地址 小插曲 当遇到错误提示: /usr/bin/wg-quick: line 31: resolvconf: command not found [WireGuard | Debian] 可以创建软链接解决 ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf 当遇到错误提示: [SELF-SOLVED] Unit dbus-org.freedesktop.resolve1.service not found 可以通过启动 systemd-resolved 服务解决 systemctl start systemd-resolved.service systemctl enable systemd-resolved.service 配置详解 WireGuard 使用 INI 语法作为其配置文件格式。配置文件可以放在任何路径下,但必须通过绝对路径引用。默认路径是 /etc/wireguard/wg0.conf。 配置文件的命名形式必须为 ${WireGuard 接口的名称}.conf。通常情况下 WireGuard 接口名称以 wg 为前缀,并从 0 开始编号,但你也可以使用其他名称,只要符合正则表达式 ^[a-zA-Z0-9_=+.-]{1,15}$ 就行。 你可以选择使用 wg 命令来手动配置 VPN,但一般建议使用 wg-quick,它提供了更强大和用户友好的配置体验,可以通过配置文件来管理配置。 [Interface] 定义本地 VPN 配置。例如: 1.本地节点是客户端,只路由自身的流量,只暴露一个 IP。 [Interface] # Name = phone.example-vpn.dev Address = 192.0.2.5/32 PrivateKey = <private key for phone.example-vpn.dev> 本地节点是中继服务器,它可以将流量转发到其他对等节点(peer),并公开整个 VPN 子网的路由。 [Interface] # Name = public-server1.example-vpn.tld Address = 192.0.2.1/24 ListenPort = 51820 PrivateKey = <private key for public-server1.example-vpn.tld> DNS = 1.1.1.1 # Name 这是 INI 语法中的标准注释,用于展示该配置部分属于哪个节点。这部分配置会被 WireGuard 完全忽略,对 VPN 的行为没有任何影响。 Address 定义本地节点应该对哪个地址范围进行路由。如果是常规的客户端,则将其设置为节点本身的单个 IP(使用 CIDR 指定,例如 192.0.2.3/32);如果是中继服务器,则将其设置为可路由的子网范围。 例如: 常规客户端,只路由自身的流量: Address = 192.0.2.3/32 中继服务器,可以将流量转发到其他对等节点(peer): Address = 192.0.2.1/24 也可以指定多个子网或 IPv6 子网: Address = 192.0.2.1/24,2001:DB8::/64 ListenPort 当本地节点是中继服务器时,需要通过该参数指定端口来监听传入 VPN 连接,默认端口号是 51820。常规客户端不需要此选项。 PrivateKey 本地节点的私钥,所有节点(包括中继服务器)都必须设置。不可与其他服务器共用。 私钥可通过命令 wg genkey > example.key 来生成。 DNS 通过 DHCP 向客户端宣告 DNS 服务器。客户端将会使用这里指定的 DNS 服务器来处理 VPN 子网中的 DNS 请求,但也可以在系统中覆盖此选项。例如: #如果不配置则使用系统默认 DNS #可以指定单个 DNS: DNS = 1.1.1.1 #也可以指定多个 DNS: DNS = 1.1.1.1,8.8.8.8 Table 定义 VPN 子网使用的路由表,默认不需要设置。该参数有两个特殊的值需要注意: Table = off : 禁止创建路由 Table = auto(默认值) : 将路由添加到系统默认的 table 中,并启用对默认路由的特殊处理。 例如:Table = 1234 MTU 定义连接到对等节点(peer)的 MTU(Maximum Transmission Unit,最大传输单元),默认不需要设置,一般由系统自动确定。 PreUp 启动 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。 例如: 添加路由: PreUp = ip rule add ipproto tcp dport 22 table 1234 PostUp 启动 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。 例如: 从文件或某个命令的输出中读取配置值: PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here) 添加一行日志到文件中: PostUp = echo "$(date +%s) WireGuard Started" >> /var/log/wireguard.log 调用 WebHook: PostUp = curl https://events.example.dev/wireguard/started 添加路由: PostUp = ip rule add ipproto tcp dport 22 table 1234 添加 iptables 规则,启用数据包转发: PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE 强制 WireGuard 重新解析对端域名的 IP 地址: PostUp = resolvectl domain %i "~."; resolvectl dns %i 192.0.2.1; resolvectl dnssec %i yes PreDown 停止 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。 例如: 添加一行日志到文件中: PreDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log PostDown 停止 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。 例如: 添加一行日志到文件中: PostDown = echo "$(date +%s) WireGuard Down" >> /var/log/wireguard.log [Peer] 定义能够为一个或多个地址路由流量的对等节点(peer)的 VPN 设置。对等节点(peer)可以是将流量转发到其他对等节点(peer)的中继服务器,也可以是通过公网或内网直连的客户端。 中继服务器必须将所有的客户端定义为对等节点(peer),除了中继服务器之外,其他客户端都不能将位于 NAT 后面的节点定义为对等节点(peer),因为路由不可达。对于那些只为自己路由流量的客户端,只需将中继服务器作为对等节点(peer),以及其他需要直接访问的节点。 配置示例: 对等节点(peer)是路由可达的客户端,只为自己路由流量 [Peer] # Name = public-server2.example-vpn.dev Endpoint = public-server2.example-vpn.dev:51820 PublicKey = <public key for public-server2.example-vpn.dev> AllowedIPs = 192.0.2.2/32 对等节点(peer)是位于 NAT 后面的客户端,只为自己路由流量 [Peer] # Name = home-server.example-vpn.dev Endpoint = home-server.example-vpn.dev:51820 PublicKey = <public key for home-server.example-vpn.dev> AllowedIPs = 192.0.2.3/32 对等节点(peer)是中继服务器,用来将流量转发到其他对等节点(peer) [Peer] # Name = public-server1.example-vpn.tld Endpoint = public-server1.example-vpn.tld:51820 PublicKey = <public key for public-server1.example-vpn.tld> # 路由整个 VPN 子网的流量 AllowedIPs = 192.0.2.1/24 PersistentKeepalive = 25 Endpoint 指定远端对等节点(peer)的公网地址。如果对等节点(peer)位于 NAT 后面或者没有稳定的公网访问地址,就忽略这个字段。通常只需要指定中继服务器的 Endpoint,当然有稳定公网 IP 的节点也可以指定。例如: 通过 IP 指定: Endpoint = 123.124.125.126:51820 通过域名指定: Endpoint = public-server1.example-vpn.tld:51820 AllowedIPs 允许该对等节点(peer)发送过来的 VPN 流量中的源地址范围。同时这个字段也会作为本机路由表中 wg0 绑定的 IP 地址范围。如果对等节点(peer)是常规的客户端,则将其设置为节点本身的单个 IP;如果对等节点(peer)是中继服务器,则将其设置为可路由的子网范围。可以使用 , 来指定多个 IP 或子网范围。该字段也可以指定多次。 当决定如何对一个数据包进行路由时,系统首先会选择最具体的路由,如果不匹配再选择更宽泛的路由。例如,对于一个发往 192.0.2.3 的数据包,系统首先会寻找地址为 192.0.2.3/32 的对等节点(peer),如果没有再寻找地址为 192.0.2.1/24 的对等节点(peer),以此类推。 例如: 对等节点(peer)是常规客户端,只路由自身的流量: AllowedIPs = 192.0.2.3/32 对等节点(peer)是中继服务器,可以将流量转发到其他对等节点(peer): AllowedIPs = 192.0.2.1/24 对等节点(peer)是中继服务器,可以转发所有的流量,包括外网流量和 VPN 流量: AllowedIPs = 0.0.0.0/0,::/0 对等节点(peer)是中继服务器,可以路由其自身和其他对等节点(peer)的流量: AllowedIPs = 192.0.2.3/32,192.0.2.4/32 对等节点(peer)是中继服务器,可以路由其自身的流量和它所在的内网的流量: AllowedIPs = 192.0.2.3/32,192.168.1.1/24 PublicKey 对等节点(peer)的公钥,所有节点(包括中继服务器)都必须设置。可与其他对等节点(peer)共用同一个公钥。 公钥可通过命令 wg pubkey < example.key > example.key.pub 来生成,其中 example.key 是上面生成的私钥。 PersistentKeepalive 如果连接是从一个位于 NAT 后面的对等节点(peer)到一个公网可达的对等节点(peer),那么 NAT 后面的对等节点(peer)必须定期发送一个出站 ping 包来检查连通性,如果 IP 有变化,就会自动更新Endpoint。 例如: 本地节点与对等节点(peer)可直连:该字段不需要指定,因为不需要连接检查。 对等节点(peer)位于 NAT 后面:该字段不需要指定,因为维持连接是客户端(连接的发起方)的责任。 本地节点位于 NAT 后面,对等节点(peer)公网可达:需要指定该字段 PersistentKeepalive = 25,表示每隔 25 秒发送一次 ping 来检查连接。 共享一个 peers.conf 文件 如果某个 peer 的公钥与本地接口的私钥能够配对,那么 WireGuard 会忽略该 peer。利用这个特性,我们可以在所有节点上共用同一个 peer 列表,每个节点只需要单独定义一个 [Interface] 就行了,即使列表中有本节点,也会被忽略。具体方式如下: 每个对等节点(peer)都有一个单独的 /etc/wireguard/wg0.conf 文件,只包含 [Interface] 部分的配置。 每个对等节点(peer)共用同一个 /etc/wireguard/peers.conf 文件,其中包含了所有的 peer。 Wg0.conf 文件中需要配置一个 PostUp 钩子,内容为 PostUp = wg addconf /etc/wireguard/peers.conf

2023/10/8
articleCard.readMore

K3s 快速入门指南:构建多云环境下的K3S集群

K3s 是轻量级的 Kubernetes。server最低只需要512M内存即可运行。 不同账号甚至不同云服务商, 内网是不通的。所以要想办法实现跨公网的容器网络通信,保障任意一台节点上的pod能访问任意节点上的pod和service,和正常的kubernetes集群体验一致。 参考入门指南和多云解决方案,重新整理 目标:实现混合云(腾讯云服务器+甲骨文服务器+微软Azure服务器)境下的K3S集群 Server安装 # 局域网方案 curl -sfL https://get.k3s.io | sh - # 多云安装方案 curl -sfL https://get.k3s.io | sh -s - --node-external-ip=Server公网地址 --flannel-backend=wireguard-native 中国用户,可以使用以下方法加速安装: # 局域网方案 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh - # 多云安装方案 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -s - --node-external-ip=Server公网地址 --flannel-backend=wireguard-native --flannel-external-ip 运行此安装后: K3s 服务将被配置为在节点重启后或进程崩溃或被杀死时自动重启。 将安装其他实用程序,包括 ```kubectl```、```crictl```、```ctr```、```k3s-killall.sh``` 和 ```k3s-uninstall.sh```。 kubeconfig 文件将写入到 ```/etc/rancher/k3s/k3s.yaml```,由 K3s 安装的 kubectl 将自动使用该文件。 安装其他 Agent 节点 安装其他 Agent 节点并将它们添加到集群,请使用 K3S_URL 和 K3S_TOKEN 环境变量运行安装脚本 # 局域网方案 curl -sfL https://get.k3s.io | K3S_URL=https://myserver:6443 K3S_TOKEN=mynodetoken sh - # 多云安装方案 curl -sfL https://get.k3s.io | K3S_URL=https://Server公网地址:6443 K3S_TOKEN=mynodetoken sh -s - --node-external-ip=Agent公网地址 # 局域网方案 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://myserver:6443 K3S_TOKEN=mynodetoken sh - # 多云安装方案 curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://Server公网地址:6443 K3S_TOKEN=mynodetoken sh -s - --node-external-ip=Agent公网地址 备注: K3S_URL 参数会导致安装程序将 K3s 配置为 Agent 而不是 Server。K3s Agent 将注册到在 URL 上监听的 K3s Server。K3S_TOKEN 使用的值存储在 Server 节点上的 /var/lib/rancher/k3s/server/node-token 中。 每台主机必须具有唯一的主机名。如果你的计算机没有唯一的主机名,请传递 K3S_NODE_NAME 环境变量,并为每个节点提供一个有效且唯一的主机名。 在本机访问k3s集群 安装kubectl brew install kubectl 复制 Server 中 /etc/rancher/k3s/k3s.yaml 的内容 写入本机的~/.kube/config文件. 参考scp复制指令: scp server:/etc/rancher/k3s/k3s.yaml ~/.kube/config 测试指令 查看节点状态: $ kubectl get node NAME STATUS ROLES AGE VERSION vm-4-10-debian Ready <none> 35m v1.27.6+k3s1 vm-4-9-debian Ready control-plane,master 39m v1.27.6+k3s1 检查跨网通讯: $ kubectl get pod -A -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system local-path-provisioner-957fdf8bc-gcgj4 1/1 Running 0 38m 10.42.0.5 vm-4-9-debian <none> <none> kube-system coredns-77ccd57875-vsxmt 1/1 Running 0 38m 10.42.0.6 vm-4-9-debian <none> <none> kube-system helm-install-traefik-crd-sv9jh 0/1 Completed 0 38m 10.42.0.4 vm-4-9-debian <none> <none> kube-system metrics-server-5f8b4ffd8-zd4db 1/1 Running 0 38m 10.42.0.3 vm-4-9-debian <none> <none> kube-system helm-install-traefik-jp8sk 0/1 Completed 2 38m 10.42.0.2 vm-4-9-debian <none> <none> kube-system svclb-traefik-0782c5d1-wr5kd 2/2 Running 0 37m 10.42.0.7 vm-4-9-debian <none> <none> kube-system traefik-64f55bb67d-4lr2g 1/1 Running 0 37m 10.42.0.8 vm-4-9-debian <none> <none> kube-system svclb-traefik-0782c5d1-444jv 2/2 Running 0 34m 10.42.1.2 vm-4-10-debian <none> <none> 查看节点资源使用情况: $ kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% vm-4-9-debian 24m 2% 1369Mi 69% vm-4-10-debian <unknown> <unknown> <unknown> <unknown> 查看POD资源使用情况: $ kubectl top pod -A NAMESPACE NAME CPU(cores) MEMORY(bytes) kube-system coredns-77ccd57875-vsxmt 1m 20Mi kube-system local-path-provisioner-957fdf8bc-gcgj4 1m 14Mi kube-system metrics-server-5f8b4ffd8-zd4db 3m 24Mi kube-system svclb-traefik-0782c5d1-wr5kd 0m 0Mi kube-system traefik-64f55bb67d-4lr2g 1m 33Mi 到此k3s集群部署完成, 如果有更多的主机,可以重复agent的配置步骤进行添加。 K3s Server 节点的入站规则 协议 端口 源 目标 描述 TCP 2379-2380 Servers Servers 只有具有嵌入式 etcd 的 HA 需要 TCP 6443 Agents Servers K3s supervisor 和 Kubernetes API Server UDP 8472 所有节点 所有节点 只有 Flannel VXLAN 需要 TCP 10250 所有节点 所有节点 Kubelet 指标 UDP 51820 所有节点 所有节点 只有使用 IPv4 的 Flannel Wireguard 才需要 UDP 51821 所有节点 所有节点 只有使用 IPv6 的 Flannel Wireguard 才需要 所有出站流量通常都是允许的。 参考: 快速入门指南 嵌入式 k3s 多云解决方案

2023/9/28
articleCard.readMore

Telegram 界面自定义翻译

语言包的获取方法 进入官网翻译界面 https://translations.telegram.org 点击 “Start Translating” 选择 对应语言 比如 简体中文 进入网址 https://translations.telegram.org/zh-hans/ 底部有 “Sharing Link” 在 telegram 中打开这个网址 就可以换界面语言了。 自定义界面语言的话,选择上面的 客户端类型,根据提示 编辑对应翻译内容 即可。 Telegram 中文语言包 https://t.me/setlanguage/zh-hans-beta

2023/9/21
articleCard.readMore

搭建反向代理服务:Telegram Bot Api 反向代理搭建

上面一篇通过nginx和cloudflare workers 搭建的反向代理。 这一篇通过更简单的代码重下搭建个。 通过 Cloudflare Workers 搭建反向代理服务 创建 Cloudflare workers 的步骤都是一样的,代码不一样。 创建一个Worker 在首页选择Workers,若从未创建过则初始化,选择免费套餐,然后创建一个Worker。 编辑Worker内容 进入worker,点击快速编辑,将代码改为下方内容,其中hostname改为你自己的,然后点击保存部署,并可改名为例如cdn的worker。 // 这就是需要代理的网址 const hostname = "https://example.domain" // const hostname = "http://192.168.0.1" // const hostname = "https://your.domain" // const hostname = "https://your.domain/api/path" function handleRequest(request) { let url = new URL(request.url); return fetch(new Request(hostname + url.pathname,request)) } addEventListener("fetch", event => { event.respondWith(handleRequest(event.request)) }) 为域名添加DNS 添加路由,配置 指向步骤1创建的 Worker,到此等待DNS生效即可。 通过 Vercel 搭建反向代理服务 在github中新建个项目 创建一个文件,命名为 vercel.json 文件内容: { "routes": [ { "src": "/.*", "dest": "https://example.domain"} ] } 进入 vercel 后台 部署这个项目 配置好自定义域名,即可使用。 通用代理 json 内容 参考: { "routes": [ { "src": "/redirect", "status": 308, "headers": { "Location": "https://example.domain/" } }, { "src": "/custom-page", "headers": {"cache-control": "s-maxage=1000"}, "dest": "/index.html" }, { "src": "/api", "dest": "/my-api.js" }, { "src": "/users", "methods": ["POST"], "dest": "/users-api.js" }, { "src": "/users/(?<id>[^/]*)", "dest": "/users-api.js?id=$id" }, { "src": "/legacy", "status": 404}, { "src": "/.*", "dest": "https://example.domain/"} ] } 通过Nginx 对网站进行反向代理 创建配置文件 nano /etc/nginx/conf.d/tgapi.conf 输入一下内容并保存 server { listen 80; server_name tgapi.domain; location / { return 444; } location ~* ^/bot { resolver 8.8.8.8; proxy_buffering off; proxy_pass https://example.domain$request_uri; } }

2023/9/20
articleCard.readMore

Telegram Bot Api 反向代理搭建

由于一些原因,配置epusdt需要使用tg反向代理地址才能使用! #telegram代理url(大陆地区服务器可使用一台国外服务器做反代tg的url),如果运行的本来就是境外服务器,则无需填写 tg_proxy= 两种实现方案,根据个人喜好选择使用或发挥 Nginx反代Telegram Api 安装nginx sudo apt update && sudo apt install -y nginx 创建配置文件 nano tgapi.conf 输入一下内容并保存 server { listen 80; server_name tgapi.domain; location / { return 444; } location ~* ^/bot { resolver 8.8.8.8; proxy_buffering off; proxy_pass https://api.telegram.org$request_uri; } } 加载配置 sudo systemctl reload nginx #或 sudo nginx -s reload 测试访问 输入以下命令行,BOT_TOKEN换成自己机器人token。 curl https://tgapi.domain/bot<BOT_TOKEN>/getMe 看的机器人信息,就说明可以使用了。 配置 epusdt telegram代理url epusdt 配置(.env)参考 #telegram代理url(大陆地区服务器可使用一台国外服务器做反代tg的url),如果运行的本来就是境外服务器,则无需填写 tg_proxy=https://tgapi.domain docker 配置 nginx 参考 docker-compose.yam 内容 version: "3" services: nginx: container_name: "nginx" restart: always ports: - "80:80" image: nginx:bookworm volumes: - ./conf.d:/etc/nginx/conf.d - ./log:/var/log/nginx extra_hosts: - "host.docker.internal:host-gateway" 使用 Cloudflare Worker 代理 Telegram Bot Api 使用前提 一个托管在cloudflare的域名 k开启cloudflare的免费worker服务 首先登录Cloudflare以后点击左侧的Workers 和 Pages 点击 创建应用程序-创建 worker 名称 随意填写,点击 部署 创建完成后,点击刚创建的Worker,再点击 快速编辑 在左侧删除原有的代码,填入下面给出的代码 /** * Helper functions to check if the request uses * corresponding method. * */ const Method = (method) => (req) => req.method.toLowerCase() === method.toLowerCase(); const Get = Method('get'); const Post = Method('post'); const Path = (regExp) => (req) => { const url = new URL(req.url); const path = url.pathname; return path.match(regExp) && path.match(regExp)[0] === path; }; /* * The regex to get the bot_token and api_method from request URL * as the first and second backreference respectively. */ const URL_PATH_REGEX = /^\/bot(?<bot_token>[^/]+)\/(?<api_method>[a-z]+)/i; /** * Router handles the logic of what handler is matched given conditions * for each request */ class Router { constructor() { this.routes = []; } handle(conditions, handler) { this.routes.push({ conditions, handler, }); return this; } get(url, handler) { return this.handle([Get, Path(url)], handler); } post(url, handler) { return this.handle([Post, Path(url)], handler); } all(handler) { return this.handler([], handler); } route(req) { const route = this.resolve(req); if (route) { return route.handler(req); } const description = 'No matching route found'; const error_code = 404; return new Response( JSON.stringify({ ok: false, error_code, description, }), { status: error_code, statusText: description, headers: { 'content-type': 'application/json', }, } ); } /** * It returns the matching route that returns true * for all the conditions if any. */ resolve(req) { return this.routes.find((r) => { if (!r.conditions || (Array.isArray(r) && !r.conditions.length)) { return true; } if (typeof r.conditions === 'function') { return r.conditions(req); } return r.conditions.every((c) => c(req)); }); } } /** * Sends a POST request with JSON data to Telegram Bot API * and reads in the response body. * @param {Request} request the incoming request */ async function handler(request) { // Extract the URl method from the request. const { url, ..._request } = request; const { pathname: path, search } = new URL(url); // Leave the first match as we are interested only in backreferences. const { bot_token, api_method } = path.match(URL_PATH_REGEX).groups; // Build the URL const api_url = 'https://api.telegram.org/bot' + bot_token + '/' + api_method + search; // Get the response from API. const response = await fetch(api_url, _request); const result = await response.text(); const res = new Response(result, _request); res.headers.set('Content-Type', 'application/json'); return res; } /** * Handles the incoming request. * @param {Request} request the incoming request. */ async function handleRequest(request) { const r = new Router(); r.get(URL_PATH_REGEX, (req) => handler(req)); r.post(URL_PATH_REGEX, (req) => handler(req)); const resp = await r.route(request); return resp; } /** * Hook into the fetch event. */ addEventListener('fetch', (event) => { event.respondWith(handleRequest(event.request)); }); 保存并部署 回到管理后台首页,点击左侧的网站,在右侧点击你已托管在Cloudflare的域名 选择该域名下的 Workers 路由 选择添加路由 路由 填写你想要用的二级域名,比如:tgapi.domain/* 注意后面必须是/*结尾,Worker选择刚才创建的服务,保存就可以了。

2023/9/19
articleCard.readMore

甲骨文Vps iptables 开放端口设置

在甲骨文的网页后台先开放端口 iptables 开放所有端口 sudo iptables -P INPUT ACCEPT sudo iptables -P FORWARD ACCEPT sudo iptables -P OUTPUT ACCEPT sudo iptables -F Oracle自带的镜像默认设置了Iptable规则,关闭它 sudo apt-get purge netfilter-persistent && sudo reboot 测试端口是否已经开放 附注: 强制删除规则 sudo rm -rf /etc/iptables && sudo reboot 删除oracle-cloud-agent,防止甲骨文监控 snap remove oracle-cloud-agent 检查防火墙服务状态 sudo systemctl status iptables.service sudo systemctl status netfilter-persistent.service 远程检测端口开放状态 nmap ip或域名 附注2(开放指定端口): iptables 开放指定端口 iptables -I INPUT -p tcp --dport 80 -j ACCEPT 中间的 80 为所需要开放的端口,tcp 为传输协议 保存规则 iptables-save 上述命令我们就完成了开放指定的端口,但是如果此时服务器重启,上述规则就没有了,所以我们需要对规则进行一下持久化操作 安装 iptables-persistent sudo apt-get install iptables-persistent 持久化规则 sudo netfilter-persistent save sudo netfilter-persistent reload

2023/9/18
articleCard.readMore

Docker 搭建MTProto协议上网

系统环境: Debian系(其他系只需要更换apt为对应系统的软件包管理工具名称即可) 安装Docker 安装Docker相关工具 sudo apt update && sudo apt -y upgrade && sudo apt install -y docker docker.io docker-compose 启动docker sudo systemctl enable docker && sudo systemctl start docker 验证是否安装成功 sudo docker run hello-world mtg v2版本 docker运行 生成密钥 sudo docker pull baicailin/mtg sudo docker run --rm baicailin/mtg generate-secret --hex trade.aliexpress.com #会看到一串ee开头的密钥,形如: eexxx44f3762c8a97d14f89df8c0174726164652e616c69657870726573732e636f6d 写入简单配置 echo "secret = \"把上面一步生成的密钥填进来\"" >config.toml echo "bind-to = \"0.0.0.0:443\"" >>config.toml 运行docker镜像命令 sudo docker run -d -v $PWD/config.toml:/config.toml -p 443:443 --name baicai_mtg --restart=unless-stopped baicailin/mtg docker-compose 启动(可代替上面这个命令启动方式) 一键启动 docker-compose.yaml 文件参考 version: "3" services: mtg_v2: image: baicailin/mtg container_name: "mtg_v2" restart: unless-stopped command: run /config.toml volumes: - ./config.toml:/config.toml environment: TZ: Asia/Shanghai ports: - "443:443" 生成MTProto协议服务配置 sudo docker exec mtg_v2 /mtg access /config.toml mtg_v1 版本(支持adtag 赞助选项)运行 生成密钥 $ docker run --rm baicailin/mtg:1 generate-secret tls -c bing.com eedf71035a8ed48a623d8e83e66aec4d0562696e672e636f6d docker-compose 启动(可代替上面这个命令启动方式) 一键启动 docker-compose.yaml 文件参考: version: "3" services: mtg_v1: image: baicailin/mtg:1 container_name: "mtg_v1" restart: unless-stopped # deploy: # resources: # limits: # cpus: 0.50 # memory: 256M # reservations: # memory: 128M command: run eedf71035a8ed48a623d8e83e66aec4d0562696e672e636f6d adtag environment: TZ: Asia/Shanghai # MTG_DEBUG: "true" # MTG_BIND: "0.0.0.0:3128" # MTG_IPV4: "公网ip:端口" # MTG_IPV6: "公网ip:端口" # MTG_STATS_BIND: "127.0.0.1:3129" # network_mode: host ports: - "1443:3128" - "1444:3129" 运行参数中的 adtag 可以通过tg机器人 @MTProxybot 创建获得 查看MTProto协议服务配置 在mtg v1 docker-compose.yml文件目录下运行 docker-compose logs

2023/9/7
articleCard.readMore

docker golang:alpine CGO 制作镜像的正确姿势

当程序需要引入C/C++库(比如支持Sqlite数据库)的时候,编译环境需要打开CGO,否则打包完成后的程序就不能顺利运行(数据库初始化失败)。 这时就需要安装 build-base 配置编译环境。 参考 Dockerfile 如下: FROM golang:alpine as builder ENV CGO_ENABLED=1 WORKDIR /app COPY . . RUN apk add --no-cache --update git build-base RUN go mod tidy \ && go build -o api_client_linux ./cmd/api_client/ FROM alpine:latest as runner ENV TZ=Asia/Shanghai RUN apk --no-cache add ca-certificates tzdata libc6-compat libgcc libstdc++ WORKDIR /app COPY --from=builder /app/api_client_linux . VOLUME /app/conf EXPOSE 8080 ENTRYPOINT ["./api_client_linux" ,"-c","/app/conf/config.yaml"] 运行镜像我们也使用 Alpine ,由于 Alpine 极为精简,并没有常用的时区、证书等,会导致不可预料的错误。所以我们需要安装这些东西: 包名 用途 ca-certificates: CA证书,使用TLS tzdata: 时区配置 libc6-compat: C 标准库 libgcc: GCC 相关库,CGO编译程序依赖 libstdc++: C++ 标准库

2023/9/4
articleCard.readMore

网站迁移完成

实施: hugo + github action + git pages + cloudflare 目前通过本地 vscode 写完日志后,直接推送到 github 私有仓库,通过 github action 生成静态内容自动推送到 github pages 仓库,完成部署。 静态内容存放: github pages 域名解析使用: cloudflare 附注: github pages 自定义域名 解析设置(支持A记录) https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site

2023/9/2
articleCard.readMore

SSH使用ProxyCommand通过代理服务器远程连接其他服务器

OpenSSH的客户端有一个 ProxyCommand 的选项,用于 SSH 客户端与服务器之间的隧道通信(tunneling)。所谓的隧道技术,也称代理技术,是网络通信技术的一个普遍概念,就是把一条信道建立于另外一条信道之上。 SSH 会话基于一个 TCP 连接,如果我们把连接的两个端口各自的出口(也即入口)进行截获,就可以用其它的信道来传输。而且 SSH 仍然认为它用的是和另一端连接一条 TCP 连接。 ProxyCommand 指定一个命令(称为 Proxy),SSH 客户端将通过标准输入输出和这个命令启动后的进程进行正常的 SSH 通信,而 Proxy 连接着 SSH 服务器(一般是一个 Server Proxy,再由该 Server Proxy 连接服务器)。 环境说明 远程服务器的IP地址为 0.0.0.1,代号为X; 另一个远程服务器的IP为 0.0.0.2,代号为Y; 目前本机的IP地址为 0.0.0.3,代号为A,本地可以利用SSH客户端通过密钥或密码连接X和Y; 这里全部使用密钥的方式进行访问,本机 A 与 Y 之间无法进行访问。 本地的 ~/.ssh/config 的配置文件信息如下,通过X连接到Y; Host X HostName 0.0.0.1 User root Port 22 PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_1 Host Y HostName 0.0.0.2 User root Port 22 PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_2 Host test HostName 0.0.0.2 User root Port 22 IdentityFile ~/.ssh/id_rsa_2 ProxyCommand ssh X -W %h:%p 测试本机通过 X 连接到 Y 服务器 ssh test 附注: -W host:port Requests that standard input and output on the client be forwarded to host on port over the secure channel. Implies -N, -T, ExitOnForwardFailure and ClearAllForwardings, though these can be overridden in the configuration file or using -o command line options. -W:该参数在OpenSSH 5.4及之后的版本才支持,参考官方的Release信息; 在使用-W之前,通常都是使用nc选项,nc允许你转发TCP/UDP数据包到指定(备用)位置并且基本上与ssh -W相同; 参考: openssh

2023/8/31
articleCard.readMore

检查网络延时测试 URL 汇总,可用于软件测试延时

服务提供者 链接 大陆体验 境外体验 http/https IP Version 备注 Google http://www.gstatic.com/generate_204 5 10 204/204 4+6 Google网络联通性测试地址 Google http://www.google-analytics.com/generate_204 6 10 204/204 4+6 Google网络联通性测试地址 Google http://www.google.com/generate_204 0 10 204/204 4+6 Google网络联通性测试地址 Google http://connectivitycheck.gstatic.com/generate_204 4 10 204/204 4+6 Google网络联通性测试地址 Apple http://captive.apple.com 3 10 200/200 4+6 苹果设备用于检测 Wi-Fi 是否需要认证登陆的链接 Apple http://www.apple.com/library/test/success.html 7 10 200/200 4+6 苹果设备用于检测 Wi-Fi 是否需要认证登陆的链接 MicroSoft http://www.msftconnecttest.com/connecttest.txt 5 10 200/error 4 微软的网络联通性测试地址 Cloudflare http://cp.cloudflare.com/generate_204 4 10 204/204 4+6 Cloudflare的联通性测试地址 Firefox http://detectportal.firefox.com/success.txt 5 10 200/200 4+6 火狐的网络联通性测试地址 V2ex http://www.v2ex.com/generate_204 0 10 204/301 4+6 v2ex 的联通性测试地址 小米 http://connect.rom.miui.com/generate_204 10 4 204/204 4 小米的联通性测试地址 华为 http://connectivitycheck.platform.hicloud.com/generate_204 10 5 204/204 4 华为的联通性测试地址 Vivo http://wifi.vivo.com.cn/generate_204 10 5 204/204 4 vivo 的联通性测试地址 以上大陆指中国大陆,境外指非中国大陆。 体验目前仅进行粗略测试延迟,大概率实际不符,仅作参考。 http/https一列表示使用指定协议进行请求,返回的状态码。部分场合对状态码要求较严格。

2023/8/28
articleCard.readMore

Hugo + GitHub Action + Github Pages,搭建博客自动发布

我的方案由以下两个核心部分: 博客源仓库,对博客配置及所有文章 .md 源文件进行版本管理,配合 GitHub Action 进行自动化部署,自动生成静态站点推送到 GitHub Pages 博客发布仓库。 GitHub Pages 博客发布仓库,使用 GitHub Pages 实现网站部署,可以通过域名 CNAME 解析使用自定义域名。 使用 Hugo 搭建博客 Hugo 是用 Go 实现的博客工具,采用 Markdown 进行文章编辑,生成静态站点文件,支持丰富的主题配置,也可以通过 js 嵌入像是评论系统等插件,高度定制化。除了 Hugo 外, 还有 Gatsby、Jekyll、Hexo、Ghost 等选择,实现和使用都差不多,可以根据自己的偏好进行选择。 安装 Hugo 我使用的是 macOS,所以使用官方推荐的 homebrew 方式进行 hugo 程序的安装,其他系统可参考官方文档。 brew install hugo 完成后,使用以下命令进行验证(查看版本号): hugo version 创建 Hugo 网站 hugo new site blog-demo 配置主题 创建我们的站点后,需要进行主题配置,Hugo 社区有很丰富的主题,可以通过官网 Themes 菜单选择自己喜欢的风格,查看预览效果,选择后可以进入主题项目仓库,一般都会有很详细的安装及配置说明。下面我就以我目前在使用的 smol 这个主题为例,演示一下配置流程。 cd blog-demo git clone git@github.com:colorchestra/smol.git themes/smol cd themes/smol rm -rf .git 初始化主题基础配置后,我们可以在 config.toml 文件中进行站点细节配置,具体配置项参考主题说明文档。 参考config.toml内容 theme = "smol" 发布新文章 hugo new posts/blog-test.md 本地调试站点 进行本地实时调试预览。 hugo server 运行服务后,我们可以通过浏览器 http://localhost:1313 地址访问我们的本地预览网页。 GITHUB PAGES 仓库 GitHub Pages 仓库建立完成后,可以在设置中配置自己注册的自定义域名来指向 GitHub Pages 生成的网址。此外,需要将博客站点配置文件 config.toml 中的 baseURL 改为自己的自定义域名。 GitHub Pages 发布博客 我们现在已经可以通过自定义域名来访问我们的 GitHub Pages 页面了,目前因为项目仓库是空的,访问后会报 404 页面。 Hugo 生成的静态网站通过 GitHub Pages 服务进行托管,因此我们需要上传 Hugo 生成的静态网页文件至 GitHub Page 项目仓库。 手动发布 hugo cd public Hugo 默认会将生成的静态网页文件存放在 public/ 目录下,我们可以通过将 public/ 目录初始化为 git 仓库并关联我们的 clin003/blog_html 远程仓库来推送我们的网页静态文件。 git init git remote add origin git@github.com:baicaime/meBlog git add . git commit -m "debug" git push origin main 推送到 GitHub Pages 仓库,稍等几分钟即可通过我们的自定义域名来访问我们的博客站点了,和 hugo server 本地调试完全一致。 自动发布 因为我们的博客基于 GitHub 与 GitHub Pages,可以通过官方提供的 GitHub Action 进行 CI 自动发布。 GitHub Action 是一个持续集成和持续交付(CI/CD) 平台,可用于自动执行构建、测试和部署管道,可以通过简单的配置即可直接使用。 配置在仓库目录 .github/workflows 下,以 .yml 为后缀。我的 GitHub Action 配置为 deploy.yml 自动发布示例配置如下: name: Deploy Hugo site to Pages on: push: branches: [ "main" ] workflow_dispatch: permissions: contents: read pages: write id-token: write defaults: run: shell: bash env: NAME: BLOG_push # 推送目标仓库 格式 用户名/仓库名 TARGET_REPOSITORY_NAME: baicaime/meBlog # 同步临时目录(可选) CLONE_DIR: tmp_public # 构建临时目录(可选) BUILD_DIR: tmp_build # 配置git用户名 GIT_USERNAME: baicaime jobs: # This workflow contains a single job called "build" build: runs-on: ubuntu-latest env: HUGO_VERSION: 0.117.0 steps: - name: Install Hugo CLI run: | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ && sudo dpkg -i ${{ runner.temp }}/hugo.deb - uses: actions/checkout@v3 - name: Build with Hugo env: # For maximum backward compatibility with Hugo modules HUGO_ENVIRONMENT: production HUGO_ENV: production run: | hugo \ --minify \ --baseURL "${{ steps.pages.outputs.base_url }}/" \ -d ${{ env.BUILD_DIR }} - name: Git Config run: | git config --global user.email "actions-push-noreply@baicai.me" git config --global user.name "${{ env.GIT_USERNAME }}" echo "配置git完成" - name: Git clone run: | echo "同步目标仓库(开始)" git clone --depth 1 https://github.com/${{ env.TARGET_REPOSITORY_NAME }}.git ${{ env.CLONE_DIR }} &> /dev/null echo "同步目标仓库(完成)" - name: Git push run: | cp -rf ${{ env.BUILD_DIR }}/* ${{ env.CLONE_DIR }}/ cd ${{ env.CLONE_DIR }} echo "${{ github.event.head_commit.message }} `date +%FT%T%z`" > _pub_time.html git add . git commit --message "Update ${{ env.NAME }} from ${{ env.TARGET_REPOSITORY_NAME }} ${{ github.event.head_commit.message }}" git push -f -q https://oauth2:${{ secrets.GIT_TOKEN }}@github.com/${{ env.TARGET_REPOSITORY_NAME }}.git main echo "git push ${{ env.TARGET_REPOSITORY_NAME }} (完成)" on 表示 GitHub Action 触发条件,我设置了 push 和 workflow_dispatch 两个条件: push,当这个项目仓库发生推送动作后,执行 GitHub Action workflow_dispatch,可以在 GitHub 项目仓库的 Action 工具栏进行手动调用 jobs 表示 GitHub Action 中的任务,我们设置了一个 build 任务, runs-on 表示 GitHub Action 运行环境,我们选择了 ubuntu-latest。 build 任务包含了 Install Hugo CLI 、Checkout、Build with Hugo、Git Config、Git clone 和 Git push 六个主要步骤, 其中 run 是执行的命令,uses 是 GitHub Action 中的一个插件,我们使用了 actions/checkout 这个插件。 其中 Checkout 步骤中,可以在 with 中配置 submodules 值为 true 同步博客源仓库的子模块(比如主题模块,由于我们没有使用子模块方式安装hugo主题,所以不需要这个参数)。 需要将上述 deploy.yml 中: TARGET_REPOSITORY_NAME 改为自己的 GitHub Pages 仓库,如我的设置为 baicaime/meBlog GIT_USERNAME 改为自己 GitHub Pages 仓库的用户名 因为我们需要从博客仓库推送到外部 GitHub Pages 仓库,需要特定权限,要在 GitHub 账户下 Setting - Developer setting - Personal access tokens 下创建一个 Token。 权限需要: Contents read/write 配置后复制生成的 Token(注:只会出现一次),然后在博客源仓库的 Settings - secrets and variables - Actions 中添加 GIT_TOKEN 环境变量为刚才的 Token,这样 GitHub Action 就可以获取到 Token 了。 推送测试 完成上述配置后,推送代码至仓库,即可触发 GitHub Action,自动生成博客页面并推送至 GitHub Pages 仓库。 GitHub Pages 仓库更新后,又会自动触发官方页面部署 CI,实现网站发布。 现在每当我们本地通过熟悉的 Markdown 语法完成博客内容编辑后,只需要推送代码,等待几分钟,即可通过我们的自定义域名访问更新后的网站。 以上就是我通过 Hugo 与 GitHub Action 实现的博客自动部署系统,我自己的实现仓库在 baicaime/meBlog 仓库中

2023/8/13
articleCard.readMore

从 Debian 11 升级到 Debian 12

如果有必要,可以查阅(Debian 12 发行说明)[https://www.debian.org/releases/stable/releasenotes],内附详细的升级和错误处理指南。 准备工作 一定要备份重要数据! 以下操作需要在 root 用户下完成,请使用 sudo -i 或 su root 切换到 root 用户进行操作 Debian 软件源一般以发行代号如:bullseye、bookworm 引用,但是也可能使用状态名如:stable、unstable、testing 引用。在 Debian 12 稳定版正式发布后,stable 就会从 bullseye 指向 bookworm 了。 检查软件源,确保下面的输出为空,否则请手动更改软件源为 bullseye cat /etc/apt/sources.list | grep stable 更新 apt 源,替换 bullseye 为 bookworm: sed -i 's/bullseye/bookworm/g' /etc/apt/sources.list sed -i 's/bullseye/bookworm/g' /etc/apt/sources.list.d/*.list 对于 Debian 12 以后的版本,所有 Debian 可以分发的打包的非自由固件二进制文件(non-free),比如某些驱动,都被转移到 Debian Archive 中的一个新组件,称为非自由固件(non-free-firmware)。如果您从旧版的 Debian 升级,并且需要这些固件二进制文件,您应该更新您系统上的 /etc/apt/sources.list,以使用这个新组件(来源): sed -i 's/non-free/non-free non-free-firmware/g' /etc/apt/sources.list 默认的系统 apt 源文件 /etc/apt/sources.list 应该是类似这样的: deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb-src http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware 开始升级 更新软件源 sudo apt update 先进行最小系统升级 sudo apt upgrade --without-new-pkgs 没问题的话开始进行全面升级,会下载好几百 MB 文件,下载速度取决于服务器网络带宽 sudo apt full-upgrade 如果修改过 SSH 配置文件,出现提示时,请选择保留本地配置 更新过程种会提示一些软件是否需要自动重启,选 Yes 即可,以及一些软件的配置文件是否需要更新,按照自己的情况选择即可,默认回车即视为使用旧的配置文件,一般会出现在 OpenSSH 等软件的更新上。 在 apt-listchanges: News 界面可以按 q 退出: 全面升级结束后,重新启动 sudo reboot 更新后删除不必要的软件和依赖: apt autoclean apt autoremove -y 查看最新的系统版本: cat /etc/debian_version lsb_release -a uname -a 升级之后 列出已删除的软件包 apt list '~c' 清理已删除的软件包 sudo apt purge '~c'

2023/6/13
articleCard.readMore

Linux 命令

基本命令 uname -m 显示机器的处理器架构 uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作系统信息 arch 显示机器的处理器架构 uname -m 显示机器的处理器架构 uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作 cat /proc/cpuinfo 显示CPU info的信息 cat /proc/interrupts 显示中断 cat /proc/meminfo 校验内存使用 cat /proc/swaps 显示哪些swap被使用 cat /proc/version 显示内核的版本 cat /proc/net/dev 显示网络适配器及统计 cat /proc/mounts 显示已加载的文件系统 lspci -tv 罗列 PCI 设备 lsusb -tv 显示 USB 设备 date 显示系统日期 cal 2007 显示2007年的日历表 date 041217002007.00 设置日期和时间 - 月日时分年.秒 clock -w 将时间修改保存到 BIOS 关机 shutdown -h now 关闭系统(1) init 0 关闭系统(2) telinit 0 关闭系统(3) shutdown -h hours:minutes & 按预定时间关闭系统 shutdown -c 取消按预定时间关闭系统 shutdown -r now 重启(1) reboot 重启(2) logout 注销 文件和目录 cd /home 进入 '/ home' 目录' cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 cd - 返回上次所在的目录 pwd 显示工作路径 ls 查看目录中的文件 ls -F 查看目录中的文件 ls -l 显示文件和目录的详细资料 ls -a 显示隐藏文件 ls *[0-9]* 显示包含数字的文件名和目录名 tree 显示文件和目录由根目录开始的树形结构(1) lstree 显示文件和目录由根目录开始的树形结构(2) mkdir dir1 创建一个叫做 'dir1' 的目录' mkdir dir1 dir2 同时创建两个目录 mkdir -p /tmp/dir1/dir2 创建一个目录树 rm -f file1 删除一个叫做 'file1' 的文件' rmdir dir1 删除一个叫做 'dir1' 的目录' rm -rf dir1 删除一个叫做 'dir1' 的目录并同时删除其内容 rm -rf dir1 dir2 同时删除两个目录及它们的内容 mv dir1 new_dir 重命名/移动 一个目录 cp file1 file2 复制一个文件 cp dir/* . 复制一个目录下的所有文件到当前工作目录 cp -a /tmp/dir1 . 复制一个目录到当前工作目录 cp -a dir1 dir2 复制一个目录 ln -s file1 lnk1 创建一个指向文件或目录的软链接 ln file1 lnk1 创建一个指向文件或目录的物理链接 touch -t 0712250000 file1 修改一个文件或目录的时间戳 - (YYMMDDhhmm) file file1 outputs the mime type of the file as text iconv -l 列出已知的编码 iconv -f fromEncoding -t toEncoding inputFile > outputFile creates a new from the given input file by assuming it is encoded in fromEncoding and converting it to toEncoding. find . -maxdepth 1 -name *.jpg -print -exec convert "{}" -resize 80x60 "thumbs/{}" \; batch resize files in the current directory and send them to a thumbnails directory (requires convert from Imagemagick) 文件搜索 find / -name file1 从 '/' 开始进入根文件系统搜索文件和目录 find / -user user1 搜索属于用户 'user1' 的文件和目录 find /home/user1 -name \*.bin 在目录 '/ home/user1' 中搜索带有'.bin' 结尾的文件 find /usr/bin -type f -atime +100 搜索在过去100天内未被使用过的执行文件 find /usr/bin -type f -mtime -10 搜索在10天内被创建或者修改过的文件 find / -name \*.rpm -exec chmod 755 '{}' \; 搜索以 '.rpm' 结尾的文件并定义其权限 find / -xdev -name \*.rpm 搜索以 '.rpm' 结尾的文件,忽略光驱、捷盘等可移动设备 locate \*.ps 寻找以 '.ps' 结尾的文件 - 先运行 'updatedb' 命令 whereis halt 显示一个二进制文件、源码或man的位置 which halt 显示一个二进制文件或可执行文件的完整路径 挂载一个文件系统 mount /dev/hda2 /mnt/hda2 挂载一个叫做hda2的盘 - 确定目录 '/ mnt/hda2' 已经存在 umount /dev/hda2 卸载一个叫做hda2的盘 - 先从挂载点 '/ mnt/hda2' 退出 fuser -km /mnt/hda2 当设备繁忙时强制卸载 umount -n /mnt/hda2 运行卸载操作而不写入 /etc/mtab 文件- 当文件为只读或当磁盘写满时非常有用 mount /dev/fd0 /mnt/floppy 挂载一个软盘 mount /dev/cdrom /mnt/cdrom 挂载一个cdrom或dvdrom mount /dev/hdc /mnt/cdrecorder 挂载一个cdrw或dvdrom mount /dev/hdb /mnt/cdrecorder 挂载一个cdrw或dvdrom mount -o loop file.iso /mnt/cdrom 挂载一个文件或ISO镜像文件 mount -t vfat /dev/hda5 /mnt/hda5 挂载一个Windows FAT32文件系统 mount /dev/sda1 /mnt/usbdisk 挂载一个usb 捷盘或闪存设备 mount -t smbfs -o username=user,password=pass //WinClient/share /mnt/share 挂载一个windows网络共享 磁盘空间 df -h 显示已经挂载的分区列表 ls -lSr |more 以尺寸大小排列文件和目录 du -sh dir1 估算目录 'dir1' 已经使用的磁盘空间' du -sk * | sort -rn 以容量大小为依据依次显示文件和目录的大小 rpm -q -a --qf '%10{SIZE}t%{NAME}n' | sort -k1,1n 以大小为依据依次显示已安装的rpm包所使用的空间 (fedora, redhat类系统) dpkg-query -W -f='${Installed-Size;10}t${Package}n' | sort -k1,1n 以大小为依据显示已安装的deb包所使用的空间 (ubuntu, debian类系统) 用户和群组 groupadd group_name 创建一个新用户组 groupdel group_name 删除一个用户组 groupmod -n new_group_name old_group_name 重命名一个用户组 useradd -c "Name Surname " -g admin -d /home/user1 -s /bin/bash user1 创建一个属于 "admin" 用户组的用户 useradd user1 创建一个新用户 userdel -r user1 删除一个用户 ( '-r' 排除主目录) usermod -c "User FTP" -g system -d /ftp/user1 -s /bin/nologin user1 修改用户属性 passwd 修改口令 passwd user1 修改一个用户的口令 (只允许root执行) chage -E 2005-12-31 user1 设置用户口令的失效期限 pwck 检查 '/etc/passwd' 的文件格式和语法修正以及存在的用户 grpck 检查 '/etc/passwd' 的文件格式和语法修正以及存在的群组 newgrp group_name 登陆进一个新的群组以改变新创建文件的预设群组 文件的权限,使用“+”设置权限,使用“-”用于取消 chattr +a file1 只允许以追加方式读写文件 chattr +c file1 允许这个文件能被内核自动压缩/解压 chattr +d file1 在进行文件系统备份时,dump程序将忽略这个文件 chattr +i file1 设置成不可变的文件,不能被删除、修改、重命名或者链接 chattr +s file1 允许一个文件被安全地删除 chattr +S file1 一旦应用程序对这个文件执行了写操作,使系统立刻把修改的结果写到磁盘 chattr +u file1 若文件被删除,系统会允许你在以后恢复这个被删除的文件 lsattr 显示特殊的属性 文件的特殊属性 ,使用 “+” 设置权限,使用 “-” 用于取消 chattr +a file1 只允许以追加方式读写文件 chattr +c file1 允许这个文件能被内核自动压缩/解压 chattr +d file1 在进行文件系统备份时,dump程序将忽略这个文件 chattr +i file1 设置成不可变的文件,不能被删除、修改、重命名或者链接 chattr +s file1 允许一个文件被安全地删除 chattr +S file1 一旦应用程序对这个文件执行了写操作,使系统立刻把修改的结果写到磁盘 chattr +u file1 若文件被删除,系统会允许你在以后恢复这个被删除的文件 lsattr 显示特殊的属性 打包和压缩文件 bunzip2 file1.bz2 解压一个叫做 'file1.bz2'的文件 bzip2 file1 压缩一个叫做 'file1' 的文件 gunzip file1.gz 解压一个叫做 'file1.gz'的文件 gzip file1 压缩一个叫做 'file1'的文件 gzip -9 file1 最大程度压缩 rar a file1.rar test_file 创建一个叫做 'file1.rar' 的包 rar a file1.rar file1 file2 dir1 同时压缩 'file1', 'file2' 以及目录 'dir1' rar x file1.rar 解压rar包 unrar x file1.rar 解压rar包 tar -cvf archive.tar file1 创建一个非压缩的 tarball tar -cvf archive.tar file1 file2 dir1 创建一个包含了 'file1', 'file2' 以及 'dir1'的档案文件 tar -tf archive.tar 显示一个包中的内容 tar -xvf archive.tar 释放一个包 tar -xvf archive.tar -C /tmp 将压缩包释放到 /tmp目录下 tar -cvfj archive.tar.bz2 dir1 创建一个bzip2格式的压缩包 tar -xvfj archive.tar.bz2 解压一个bzip2格式的压缩包 tar -cvfz archive.tar.gz dir1 创建一个gzip格式的压缩包 tar -xvfz archive.tar.gz 解压一个gzip格式的压缩包 zip file1.zip file1 创建一个zip格式的压缩包 zip -r file1.zip file1 file2 dir1 将几个文件和目录同时压缩成一个zip格式的压缩包 unzip file1.zip 解压一个zip格式压缩包 RPM 包 rpm -ivh package.rpm 安装一个rpm包 rpm -ivh --nodeeps package.rpm 安装一个rpm包而忽略依赖关系警告 rpm -U package.rpm 更新一个rpm包但不改变其配置文件 rpm -F package.rpm 更新一个确定已经安装的rpm包 rpm -e package_name.rpm 删除一个rpm包 rpm -qa 显示系统中所有已经安装的rpm包 rpm -qa | grep httpd 显示所有名称中包含 "httpd" 字样的rpm包 rpm -qi package_name 获取一个已安装包的特殊信息 rpm -qg "System Environment/Daemons" 显示一个组件的rpm包 rpm -ql package_name 显示一个已经安装的rpm包提供的文件列表 rpm -qc package_name 显示一个已经安装的rpm包提供的配置文件列表 rpm -q package_name --whatrequires 显示与一个rpm包存在依赖关系的列表 rpm -q package_name --whatprovides 显示一个rpm包所占的体积 rpm -q package_name --scripts 显示在安装/删除期间所执行的脚本l rpm -q package_name --changelog 显示一个rpm包的修改历史 rpm -qf /etc/httpd/conf/httpd.conf 确认所给的文件由哪个rpm包所提供 rpm -qp package.rpm -l 显示由一个尚未安装的rpm包提供的文件列表 rpm --import /media/cdrom/RPM-GPG-KEY 导入公钥数字证书 rpm --checksig package.rpm 确认一个rpm包的完整性 rpm -qa gpg-pubkey 确认已安装的所有rpm包的完整性 rpm -V package_name 检查文件尺寸、 许可、类型、所有者、群组、MD5检查以及最后修改时间 rpm -Va 检查系统中所有已安装的rpm包- 小心使用 rpm -Vp package.rpm 确认一个rpm包还未安装 rpm2cpio package.rpm | cpio --extract --make-directories *bin* 从一个rpm包运行可执行文件 rpm -ivh /usr/src/redhat/RPMS/`arch`/package.rpm 从一个rpm源码安装一个构建好的包 rpmbuild --rebuild package_name.src.rpm 从一个rpm源码构建一个 rpm 包 YUM 软件包升级器 yum install package_name 下载并安装一个rpm包 yum localinstall package_name.rpm 将安装一个rpm包,使用你自己的软件仓库为你解决所有依赖关系 yum update package_name.rpm 更新当前系统中所有安装的rpm包 yum update package_name 更新一个rpm包 yum remove package_name 删除一个rpm包 yum list 列出当前系统中安装的所有包 yum search package_name 在rpm仓库中搜寻软件包 yum clean packages 清理rpm缓存删除下载的包 yum clean headers 删除所有头文件 yum clean all 删除所有缓存的包和头文件 deb 包 dpkg -i package.deb 安装/更新一个 deb 包 dpkg -r package_name 从系统删除一个 deb 包 dpkg -l 显示系统中所有已经安装的 deb 包 dpkg -l | grep httpd 显示所有名称中包含 "httpd" 字样的deb包 dpkg -s package_name 获得已经安装在系统中一个特殊包的信息 dpkg -L package_name 显示系统中已经安装的一个deb包所提供的文件列表 dpkg --contents package.deb 显示尚未安装的一个包所提供的文件列表 dpkg -S /bin/ping 确认所给的文件由哪个deb包提供 APT 软件工具 (Debian, Ubuntu 以及类似系统) apt-get install package_name 安装/更新一个 deb 包 apt-cdrom install package_name 从光盘安装/更新一个 deb 包 apt-get update 升级列表中的软件包 apt-get upgrade 升级所有已安装的软件 apt-get remove package_name 从系统删除一个deb包 apt-get check 确认依赖的软件仓库正确 apt-get clean 从下载的软件包中清理缓存 apt-cache search searched-package 返回包含所要搜索字符串的软件包名称 查看文件内容 cat file1 从第一个字节开始正向查看文件的内容 tac file1 从最后一行开始反向查看一个文件的内容 more file1 查看一个长文件的内容 less file1 类似于 'more' 命令,但是它允许在文件中和正向操作一样的反向操作 head -2 file1 查看一个文件的前两行 tail -2 file1 查看一个文件的最后两行 tail -f /var/log/messages 实时查看被添加到一个文件中的内容 文本处理 cat file1 file2 ... | command <> file1_in.txt_or_file1_out.txt general syntax for text manipulation using PIPE, STDIN and STDOUT cat file1 | command( sed, grep, awk, grep, etc...) > result.txt 合并一个文件的详细说明文本,并将简介写入一个新文件中 cat file1 | command( sed, grep, awk, grep, etc...) >> result.txt 合并一个文件的详细说明文本,并将简介写入一个已有的文件中 grep Aug /var/log/messages 在文件 '/var/log/messages'中查找关键词"Aug" grep ^Aug /var/log/messages 在文件 '/var/log/messages'中查找以"Aug"开始的词汇 grep [0-9] /var/log/messages 选择 '/var/log/messages' 文件中所有包含数字的行 grep Aug -R /var/log/* 在目录 '/var/log' 及随后的目录中搜索字符串"Aug" sed 's/stringa1/stringa2/g' example.txt 将example.txt文件中的 "string1" 替换成 "string2" sed '/^$/d' example.txt 从example.txt文件中删除所有空白行 sed '/ *#/d; /^$/d' example.txt 从example.txt文件中删除所有注释和空白行 echo 'esempio' | tr '[:lower:]' '[:upper:]' 合并上下单元格内容 sed -e '1d' result.txt 从文件example.txt 中排除第一行 sed -n '/stringa1/p' 查看只包含词汇 "string1"的行 sed -e 's/ *$//' example.txt 删除每一行最后的空白字符 sed -e 's/stringa1//g' example.txt 从文档中只删除词汇 "string1" 并保留剩余全部 sed -n '1,5p;5q' example.txt 查看从第一行到第5行内容 sed -n '5p;5q' example.txt 查看第5行 sed -e 's/00*/0/g' example.txt 用单个零替换多个零 cat -n file1 标示文件的行数 cat example.txt | awk 'NR%2==1' 删除example.txt文件中的所有偶数行 echo a b c | awk '{print $1}' 查看一行第一栏 echo a b c | awk '{print $1,$3}' 查看一行的第一和第三栏 paste file1 file2 合并两个文件或两栏的内容 paste -d '+' file1 file2 合并两个文件或两栏的内容,中间用"+"区分 sort file1 file2 排序两个文件的内容 sort file1 file2 | uniq 取出两个文件的并集(重复的行只保留一份) sort file1 file2 | uniq -u 删除交集,留下其他的行 sort file1 file2 | uniq -d 取出两个文件的交集(只留下同时存在于两个文件中的文件) comm -1 file1 file2 比较两个文件的内容只删除 'file1' 所包含的内容 comm -2 file1 file2 比较两个文件的内容只删除 'file2' 所包含的内容 comm -3 file1 file2 比较两个文件的内容只删除两个文件共有的部分 字符设置和文件格式转换 dos2unix filedos.txt fileunix.txt 将一个文本文件的格式从MSDOS转换成UNIX unix2dos fileunix.txt filedos.txt 将一个文本文件的格式从UNIX转换成MSDOS recode ..HTML < page.txt > page.html 将一个文本文件转换成html recode -l | more 显示所有允许的转换格式 文件系统分析 badblocks -v /dev/hda1 检查磁盘hda1上的坏磁块 fsck /dev/hda1 修复/检查hda1磁盘上linux文件系统的完整性 fsck.ext2 /dev/hda1 修复/检查hda1磁盘上ext2文件系统的完整性 e2fsck /dev/hda1 修复/检查hda1磁盘上ext2文件系统的完整性 e2fsck -j /dev/hda1 修复/检查hda1磁盘上ext3文件系统的完整性 fsck.ext3 /dev/hda1 修复/检查hda1磁盘上ext3文件系统的完整性 fsck.vfat /dev/hda1 修复/检查hda1磁盘上fat文件系统的完整性 fsck.msdos /dev/hda1 修复/检查hda1磁盘上dos文件系统的完整性 dosfsck /dev/hda1 修复/检查hda1磁盘上dos文件系统的完整性 初始化一个文件系统 mkfs /dev/hda1 在hda1分区创建一个文件系统 mke2fs /dev/hda1 在hda1分区创建一个linux ext2的文件系统 mke2fs -j /dev/hda1 在hda1分区创建一个linux ext3(日志型)的文件系统 mkfs -t vfat 32 -F /dev/hda1 创建一个 FAT32 文件系统 fdformat -n /dev/fd0 格式化一个软盘 mkswap /dev/hda3 创建一个swap文件系统 SWAP文件系统 mkswap /dev/hda3 创建一个swap文件系统 swapon /dev/hda3 启用一个新的swap文件系统 swapon /dev/hda2 /dev/hdb3 启用两个swap分区 备份 dump -0aj -f /tmp/home0.bak /home 制作一个 '/home' 目录的完整备份 dump -1aj -f /tmp/home0.bak /home 制作一个 '/home' 目录的交互式备份 restore -if /tmp/home0.bak 还原一个交互式备份 rsync -rogpav --delete /home /tmp 同步两边的目录 rsync -rogpav -e ssh --delete /home ip_address:/tmp 通过SSH通道rsync rsync -az -e ssh --delete ip_addr:/home/public /home/local 通过ssh和压缩将一个远程目录同步到本地目录 rsync -az -e ssh --delete /home/local ip_addr:/home/public 通过ssh和压缩将本地目录同步到远程目录 dd bs=1M if=/dev/hda | gzip | ssh user@ip_addr 'dd of=hda.gz' 通过ssh在远程主机上执行一次备份本地磁盘的操作 dd if=/dev/sda of=/tmp/file1 备份磁盘内容到一个文件 tar -Puf backup.tar /home/user 执行一次对 '/home/user' 目录的交互式备份操作 ( cd /tmp/local/ && tar c . ) | ssh -C user@ip_addr 'cd /home/share/ && tar x -p' 通过ssh在远程目录中复制一个目录内容 ( tar c /home ) | ssh -C user@ip_addr 'cd /home/backup-home && tar x -p' 通过ssh在远程目录中复制一个本地目录 tar cf - . | (cd /tmp/backup ; tar xf - ) 本地将一个目录复制到另一个地方,保留原有权限及链接 find /home/user1 -name '*.txt' | xargs cp -av --target-directory=/home/backup/ --parents 从一个目录查找并复制所有以 '.txt' 结尾的文件到另一个目录 find /var/log -name '*.log' | tar cv --files-from=- | bzip2 > log.tar.bz2 查找所有以 '.log' 结尾的文件并做成一个bzip包 dd if=/dev/hda of=/dev/fd0 bs=512 count=1 做一个将 MBR (Master Boot Record)内容复制到软盘的动作 dd if=/dev/fd0 of=/dev/hda bs=512 count=1 从已经保存到软盘的备份中恢复MBR内容 光盘 cdrecord -v gracetime=2 dev=/dev/cdrom -eject blank=fast -force 清空一个可复写的光盘内容 mkisofs /dev/cdrom > cd.iso 在磁盘上创建一个光盘的iso镜像文件 mkisofs /dev/cdrom | gzip > cd_iso.gz 在磁盘上创建一个压缩了的光盘iso镜像文件 mkisofs -J -allow-leading-dots -R -V "Label CD" -iso-level 4 -o ./cd.iso data_cd 创建一个目录的iso镜像文件 cdrecord -v dev=/dev/cdrom cd.iso 刻录一个ISO镜像文件 gzip -dc cd_iso.gz | cdrecord dev=/dev/cdrom - 刻录一个压缩了的ISO镜像文件 mount -o loop cd.iso /mnt/iso 挂载一个ISO镜像文件 cd-paranoia -B 从一个CD光盘转录音轨到 wav 文件中 cd-paranoia -- "-3" 从一个CD光盘转录音轨到 wav 文件中(参数-3) cdrecord --scanbus 扫描总线以识别scsi通道 dd if=/dev/hdc | md5sum 校验一个设备的md5sum编码,例如一张 CD 网络(以太网和WIFI无线) ifconfig eth0 显示一个以太网卡的配置 ifup eth0 启用一个 'eth0' 网络设备 ifdown eth0 禁用一个 'eth0' 网络设备 ifconfig eth0 192.168.1.1 netmask 255.255.255.0 控制IP地址 ifconfig eth0 promisc 设置 'eth0' 成混杂模式以嗅探数据包 (sniffing) dhclient eth0 以dhcp模式启用 'eth0' route -n show routing table route add -net 0/0 gw IP_Gateway configura default gateway route add -net 192.168.0.0 netmask 255.255.0.0 gw 192.168.1.1 configure static route to reach network '192.168.0.0/16' route del 0/0 gw IP_gateway remove static route echo "1" > /proc/sys/net/ipv4/ip_forward activate ip routing hostname show hostname of system host www.example.com lookup hostname to resolve name to ip address and viceversa(1) nslookup www.example.com lookup hostname to resolve name to ip address and viceversa(2) ip link show show link status of all interfaces mii-tool eth0 show link status of 'eth0' ethtool eth0 show statistics of network card 'eth0' netstat -tup show all active network connections and their PID netstat -tupl show all network services listening on the system and their PID tcpdump tcp port 80 show all HTTP traffic iwlist scan show wireless networks iwconfig eth1 show configuration of a wireless network card hostname show hostname host www.example.com lookup hostname to resolve name to ip address and viceversa nslookup www.example.com lookup hostname to resolve name to ip address and viceversa whois www.example.com lookup on Whois database 列出目录内容 ls -a:显示所有文件(包括隐藏文件); ls -l:显示详细信息; ls -R:递归显示子目录结构; ls -ld:显示目录和链接信息; ctrl+r:历史记录中所搜命令(输入命令中的任意一个字符); Linux中以.开头的文件是隐藏文件; pwd:显示当前目录 查看文件的类型 file:查看文件的类型 复制文件目录 1、cp:复制文件和目录 cp源文件(文件夹)目标文件(文件夹)。 常用参数:-r:递归复制整个目录树;-v:显示详细信息; 复制文件夹时要在cp命令后面加一个-r参数: 如:cp -r 源文件夹 目标文件夹。 2、touch+文件名:当文件不存在的时候,创建相应的文件;当文件存在的时候,修改文件的创建时间。 功能:生成一个空文件或修改文件的存取/修改的时间记录值; touch * :将当前下的文件时间修改为系统的当前时间; touch –d 20040210 test:将test文件的日期改为20040210; touch abc:若abc文件存在,则修改为系统的当前时间;若不存在,则生成一个为当前时间的空文件。 3、mv 文件 目标目录:移动或重命名文件或目录(如果指定文件名,则可以重命名文件)。可以将文件及目录移到另一目录下,或更改文件及目录的名称。 格式为:mv [参数]<源文件或目录> <目标文件或目录> mva.txt ../:将a.txt文件移动上层目录 mv a.txt b.txt:将a.txt改名为b.txt mvdir2 ../:将dir2目录上移一层 4、rm:删除文件 常用参数:-i:交互式 -r:递归的删除包括目录中的所有内容。 5、mkdir +文件夹名称:创建文件夹 6、rm -r +文件夹名称:删除文件夹(空文件夹和非空文件夹都可删除); rmdir 文件夹名称:删除文件夹(只能删除空文件夹)。 7、mkdir -p dir1/dir2 :在当前目录下创建dir1目录,并在dir1目录下创建dir2目录, 也就是连续创建两个目录(dir1/和dir1/dir2)。 8、rmdir –p dir1/dir2:删除dir1下的dir2目录,若dir1目录为空也删除它。 9、rm * :删除当前目录下的所有文件 10、-f参数:强迫删除文件 rm –f *.txt:强迫删除所有以后缀名为txt文件。 11、-i参数:删除文件时询问 rm –i * :删除当前目录下的所有文件会有如下提示: rm:backup:is a directory    遇到目录会略过 rm: remove ‘myfiles.txt’ ? Y 删除文件时会询问,可按Y或N键表示允许或拒绝删除文件。 12、-r参数:递归删除(连子目录一同删除,这是一个相当常用的参数)。 rm -r test :删除test目录(含test目录下所有文件和子目录); rm -r *:删除所有文件(含当前目录所有文件、所有子目录和子目录下的文件) 一般在删除目录时r和f一起用,避免麻烦; rm -rf test :强行删除、不加询问。 13、grep:功能:在文件中搜索匹配的字符并进行输出。 格式:grep[参数] <要找的字串> <要寻找字 串的源文件> greplinux test.txt:搜索test.txt文件中字符串linux并输出。 14、ln命令 功能:在文件和目录之间建立链接 格式:ln [参数] <源文件或目录> <目标文件或目录> 链接分“软链接”和“硬链接” 1.软链接: ln–s /usr/share/do doc :创建一个链接文件doc,并指向目录/usr/share/do 2.硬链接: ln /usr/share/test hard:创建一个硬链接文件hard,这时对于test文件对应 的存储区域来说,又多了一个文件指向它。 系统常用命令 1、显示命令 date:查看或设置当前系统的时间:格式化显示时间:+%Y--%m--%d; date -s:设置当前系统的时间; hwclock(clock):显示硬件时钟时间(需要管理员权限); cal:查看日历; 格式cal [参数] 月年; cal:显示当月的日历 cal4 2004 :显示2004年4月的日历; cal- y 2003:显示2003年的日历; uptime:查看系统运行时间 2、输出查看命令 2、输出查看命令 echo:显示输入的内容 追加文件echo "liuyazhuang" >> liuyazhuang.txt cat:显示文件内容,也可以将数个文件合并成一个文件; 格式:格式:cat[参数]<文件名> cat test.txt:显示test.txt文件内容; cat test.txt | more :逐页显示test.txt文件中的内容; cat test.txt >> test1.txt :将test.txt的内容附加到test1.txt文件之后; cat test.txt test2.txt >readme.txt : 将test.txt和test2.txt文件合并成readme.txt 文件; head:显示文件的头几行(默认10行) -n:指定显示的行数格式:head -n 文件名; tail:显示文件的末尾几行(默认10行)-n:指定显示的行数 -f:追踪显示文件更新 (一般用于查看日志,命令不会退出,而是持续显示新加入的内容); 格式:格式:tail[参数]<文件名> tail-10 /etc/passwd :显示/etc/passwd/文件的倒数10行内容; tail+10 /etc/passwd :显示/etc/passwd/文件从第10行开始到末尾的内容; more:用于翻页显示文件内容(只能向下翻页); more命令是一般用于要显示的内容会超过一个画面长度的情况。为了避免画 面显示时瞬间就闪过去,用户可以使用more命令,让画面在显示满一页时暂停,此时可按空格健继续显示下一个画面,或按Q键停止显示; ls -al |more:以长格形式显示etc目录下的文件列表,显示满一个画面便暂停,可 按空格键继续显示下一画面,或按Q键跳离; less:翻页显示文件内容(带上下翻页)按下上键分页,按q退出; less命令的用法与more命令类似,也可以用来浏览超过一页的文件。所不同 的是less 命令除了可以按空格键向下显示文件外,还可以利用上下键来卷动文件。当要结束浏览时,只要在less命令的提示符“:”下按Q键即可; ls -al | less:以长格形式列出/etc目录中所有的内容。用户可按上下键浏览或按Q键跳离。 3、查看硬件信息 3、查看硬件信息 Ispci:查看PCI设备 -v:查看详细信息 Isusb:查看USB设备 -v:查看详细信息 Ismod:查看加载的模块(驱动) 4、关机、重启 4、关机、重启 shutdown关闭、重启计算机 shutdown[关机、重启]时间 -h关闭计算机 -r:重启计算机 如:立即关机:shutdown -h now 10分钟后关机:shutdown -h +10 23:30分关机:shutdown -h 23:30 立即重启:shutdown -r now poweroff:立即关闭计算机 reboot:立即重启计算机 5、归档、压缩 5、归档、压缩 zip:压缩文件 zip liuyazhuang.zip myfile 格式为:“zip 压缩后的zip文件文件名” unzip:解压文件 unzip liuyazhuang.zip gzip:压缩文件 gzip 文件名 tar:归档文件 tar -cvf out.tar liuyazhuang 打包一个归档(将文件"liuyazhuang"打包成一个归档) tar -xvf liuyazhuang.tar 释放一个归档(释放liuyazhuang.tar归档) tar -cvzf backup.tar.gz/etc -z参数将归档后的归档文件进行gzip压缩以减少大小。 -c:创建一个新tar文件 -v:显示运行过程的信息 -f:指定文件名 -z:调用gzip压缩命令进行压缩 -t:查看压缩文件的内容 -x:解开tar文件 tar -cvf test.tar *:将所有文件打包成test.tar,扩展名.tar需自行加上 tar -zcvf test.tar.gz *:将所有文件打包成test.tar,再用gzip命令压缩 tar -tf test.tar :查看test.tar文件中包括了哪些文件 tar -xvf test.tar 将test.tar解开 tar -zxvf foo.tar.gz 解压缩 gzip各gunzip命令 gziptest.txt :压缩文件时,不需要任何参数 gizp–l test.txt.gz:显示压缩率 6、查找 6、查找 locate:快速查找文件、文件夹:locate keyword 此命令需要预先建立数据库,数据库默认每天更新一次,可用updatedb命令手工建立、更新数据库。 find查找位置查找参数 如: find . -name *liuyazhuang* 查找当前目录下名称中含有"liuyazhuang"的文件 find / -name *.conf 查找根目录下(整个硬盘)下后缀为.conf的文件 find / -perm 777 查找所有权限是777的文件 find / -type d 返回根目录下所有的目录 find . -name "a*"-exec ls -l {} \; find功能:用来寻找文件或目录 格式:find [<路径>] [匹配条件] find / -name httpd.conf 搜索系统根目录下名为httpd.conf的文件 7、ctrl+c :终止当前的命令 8、who或w命令 8、who或w命令 功能:查看当前系统中有哪些用户登录 格式:who/w[参数] 9、dmesg命令 9、dmesg命令 功能:显示系统诊断信息、操作系统版本号、物理内存的大小以及其它信息。 10、df命令 10、df命令 功能:用于查看文件系统的各个分区的占用情况。 11、du命令 11、du命令 功能:查看某个目录中各级子目录所使用的硬盘空间数。 格式:du [参数] <目录名> 12、free命令 12、free命令 功能:用于查看系统内存,虚拟内存(交换空间)的大小占用情况。 VIM VIM是一款功能强大的命令行文本编辑器,在Linux中通过vim命令可以启动vim编辑器。 一般使用vim + 目标文件路径 的形式使用vim 如果目标文件存在,则vim打开目标文件,如果目标文件不存在,则vim新建并打开该文件。 :q:退出vim编辑器 VIM模式 vim拥有三种模式: (1)命令模式(常规模式) vim启动后,默认进入命令模式,任何模式都可以通过esc键回到命令模式(可以多按几次),命令模式下可以键入不同的命令完成选择、复制、粘贴、撤销等操作。 命名模式常用命令如下: i : 在光标前插入文本 o:在当前行的下面插入新行 dd:删除整行 yy:将当前行的内容放入缓冲区(复制当前行) n+yy :将n行的内容放入缓冲区(复制n行) p:将缓冲区中的文本放入光标后(粘贴) u:撤销上一个操作 r:替换当前字符 / 查找关键字 (2)插入模式 在命令模式下按 " i "键,即可进入插入模式,在插入模式可以输入编辑文本内容,使用esc键可以返回命令模式。 (3)ex模式 在命令模式中按" : "键可以进入ex模式,光标会移动到底部,在这里可以保存修改或退出vim. ext模式常用命令如下: :w :保存当前的修改 :q :退出 :q! :强制退出,保存修改 :x :保存并退出,相当于:wq :set number 显示行号 :! 系统命令 执行一个系统命令并显示结果 :sh :切换到命令行,使用ctrl+d切换回vim 软件包管理命令(RPM) 1、软件包的安装 1、软件包的安装 使用RPM命令的安装模式可以将软件包内所有的组件放到系统中的正确路径,安装软件包的命令是:rpm –ivh wu-ftpd-2.6.2-8.i386.rpm i:作用rpm的安装模式 v: 校验文件信息h: 以#号显示安装进度 2、软件包的删除 2、软件包的删除 删除模式会将指定软件包的内容全部删除,但并不包括已更改过的配置文件,删除RPM软件包的命令如下:rpm –e wu-ftpd 注意:这里必须使用软件名“wu-ftpd”或”wu-ftpd-2.6.2-8而不是使用当初安装时的软件包名.wu-ftpd-2.6.2-8.i386.rpm 3、软件包升级 3、软件包升级 升级模式会安装用户所指定的更新版本,并删除已安装在系统中的相同软件包,升级软件包命令如下:rpm –Uvh wu-ftpd-2.6.2-8.i386.rpm –Uvh:升级参数。 4、软件包更新 4、软件包更新 更新模式下,rpm命令会检查在命令行中所指定的软件包是否比系统中原有的软件 包更新。如果情况属实,rpm命令会自动更新指定的软件包;反之,若系统中并没有指定软件包的较旧版本,rpm命令并不会安装此软件包。而在升级模式下,不管系统中是否有较旧的版本,rpm命令都会安装指定的软件包。 rpm –Fvhwu-ftpd-2.6.2-8.i386.rpm -Fvh:更新参数 5、软件包查询 5、软件包查询 若要获取RPM软件包的相关信息,可以使用查询模式。使用-q参数可查询一个已 安装的软件包的内容。 rpm –q wu-ftpd 查询软件包所安装的位置:rpm –ql package-name rpm –ql xv (l参数:显示文件列表)

2023/4/10
articleCard.readMore

Mac 常用命令

查看指定端口的进程 sudo lsof -i :5353 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME mDNSRespo 185 _mdnsresponder 6u IPv4 0x1111111111111 0t0 UDP *:mdns mDNSRespo 185 _mdnsresponder 7u IPv6 0x1111111111111 0t0 UDP *:mdns 根据进程名称 ps -ef | grep mDNSRespo 65 185 1 0 8:08上午 ?? 0:26.69 /usr/sbin/mDNSResponder 0 223 1 0 8:08上午 ?? 0:02.16 /usr/sbin/mDNSResponderHelper 501 66848 66623 0 11:00下午 ttys000 0:00.00 grep mDNSRespo 根据PID杀进程: sudo kill -9 185

2023/3/31
articleCard.readMore

设置Telegram机器人的webhook

设置Webhook https://api.telegram.org/bot{my_bot_token}/setWebhook?url={url_to_send_updates_to} 例如: https://api.telegram.org/bot123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11/setWebhook?url=https://baicai.me 获取Webhook https://api.telegram.org/bot{my_bot_token}/getWebhookInfo

2023/2/10
articleCard.readMore

安装 debian 后,中文环境下将home目录下文件夹更改为对应的英文

操作环境: Distributor ID: Debian Description: Debian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye 安装 debian 后,中文环境下home目录下文件夹显示的是中文,不方便使用cd命令,用到的软件xdg-user-dirs-gtk(Gnome 环境已默认安装),可以方便更改为英文. #临时转换系统语言为英文,重启后会自动恢复原值的 export LANG=en_US #执行转换命令,弹出的窗口中会询问是否将目录转化为英文路径,同意并关闭 xdg-user-dirs-gtk-update #转换回系统语言为中文,也可以不执行下面的命令,直接重启也一样的 export LANG=zh_CN 下次进入系统后,会提示是否把目录转化为中文,选择不,并选择不再提示,并取消修改。

2023/1/2
articleCard.readMore

在 Debian 11 为 nginx 配置 Let's Encrypt SSL证书

在Debian 11 Nginx配置Let’s Encrypt SSL证书 安装 Certbot 及 certbot nginx 插件 sudo apt update && sudo apt -y install certbot python3-certbot-nginx 为 nginx 网站生成证书并配置 certbot --nginx 如果是首次运行CertBot获取SSL证书,它将会询问你的电子邮件,输入一个你常用的邮件地址,它会在到期之前通知你。 也可以为特定域名获取证书配置 certbot --nginx -d baicai.me 使用Certbot自动更新SSL证书 Let’s Encrypt的SSL证书会在3个月到期即90天,因此你可能需要手动续订,但Certbot软件包附带了一个cron任务和systemd计时器,它将在证书过期之前进行自动续订。 除非你更改配置,否则无需再次手动运行Certbot。 您可以通过运行以下命令来测试证书的自动续订。 certbot renew --dry-run

2022/11/1
articleCard.readMore

在 Mac 系统中制作 Debian U盘启动盘

在Mac系统中制作U盘启动盘 Create a Bootable USB Flash Drive 在Mac系统中,制作Linux启动盘不需要额外下载任何的工具,办法如下 下载 Debian DVD 镜像文件 通过 HTTP/FTP 下载 Debian CD/DVD 映像 https://www.debian.org/CD/http-ftp/ amd64 DVD版 https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/ 格式化USB盘 打开系统自带的磁盘管理工具,插上U盘,格式化U盘,选择格式如下: 格式:Mac OS 扩展(日志式) 方案:GUID 分区图 取消Usb磁盘挂载 # 终端执行以下命令 # 列出磁盘,找到你usb硬盘的盘符 diskutil list # 输出如下:可以看到usb硬盘为/dev/disk2 /dev/disk2 (external, physical): #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *32.0 GB disk2 1: 0xEF 6.4 MB disk2s2 # 取消usb硬盘的挂载 diskutil unmountDisk /dev/disk2 导入镜像 # 执行如下命令 # if是镜像文件路径 # of是导入的目的磁盘 # bs是读写快的大小,太小会增大io,降低效率,一般1m~2m即可。 sudo dd if=~/Downloads/debian-11.5.0-amd64-DVD-1.iso of=/dev/disk2 bs=2m # 或 这里的U盘注意是rdisk2 sudo dd if=~/Downloads/debian-11.5.0-amd64-DVD-1.iso of=/dev/rdisk2 bs=2m if是输入文件。 of是输出文件 bs是传输文件速度 等待导入完成 此导入需要等待一段时间,可能会比较久。耐心等待。 直到出现records in,records out,即成功了。 弹出U盘 在快要完成的时候,系统会出现“磁盘无法识别”的提示,先不要做任何操作,等待终端dd命令执行完成后,执行下面命令弹出U盘。 diskutil eject /dev/disk2 弹出后再选择“忽略”系统提示。

2022/10/20
articleCard.readMore

Debian 安装 Transmission 并实现远程访问

Debian 安装完成后的桌面环境带有 transmission-gtk 这个gui版关闭界面后,无后台服务,导致不能方便使用远程访问。 那么安装Transmission后台守护程序 $ sudo apt install -y transmission-daemon #停止Transmisson后台服务 systemctl stop transmission-daemon.service #修改配置文件 nano /etc/transmission-daemon/settings.json 启动Transmission后台服务 #启动transmission服务 systemctl start transmission-daemon.service #停止transmission服务 systemctl stop transmission-daemon.service #查询transmission运行状态 systemctl status transmission-daemon.service #将transmission设置为开机自启动 systemctl enable transmission-daemon.service #关闭transmission开机自启 systemctl disable transmission-daemon.service

2022/10/18
articleCard.readMore

Debian 安装慢的解决方案

解决 Debian 安装过程慢的可行方案: 如果使用的是Dvd版的iso镜像,可以在安装前拔掉网线,通过离线安装,会非常快。 安装时选择 expert,不要选择 安全更新。 安装过程修改软件源。 Debian DVD-1 镜像下载地址 通过 HTTP/FTP 下载 Debian CD/DVD 映像 https://www.debian.org/CD/http-ftp/ amd64 DVD版 https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/ Debian 安装过程中 修改软件源中的安全源地址 在Debian安装步骤进入到选择安装的桌面环境和软件时, 键入 Ctrl+Alt+F2 可以看到从图形界面转到了tty命令终端, 键入 Enter 这里修改软件源配置文件 nano /target/etc/apt/sources.list 修改debian-security源地址 http://mirrors.ustc.edu.cn 目测最快 #deb http://security.debian.org/debian-security bullseye-security main deb http://mirrors.ustc.edu.cn/debian-security bullseye-security main 修改后 Ctrl+X 退出保存 然后退出终端重新进入界面继续安装,键入 Ctrl+Alt+F5 Debian 安装完成后更新硬件驱动 在设置——》关于——〉更新——》软件源,去掉cdrome的源,勾选合适的源 然后通过 dmesg 查看启动日志。 查找哪些固件加载异常,根据情况安装驱动(比如我的缺失显卡驱动和无线网卡驱动)。 # 更新源 sudo apt-get update dmesg # demsg查看到 缺失显卡驱动 sudo apt-get install firmware-amd-graphics # demsg查看到 缺失 rtl8168e-3.fw 固件程序 # 搜索固件信息 sudo apt search rtl8168e-3.fw # 正在排序... 完成 # 全文搜索... 完成 # firmware-realtek/stable,now 20210315-3 all [已安装] # Binary firmware for Realtek wired/wifi/BT adapters # 根据上面查到的信息 安装驱动包 firmware-realtek sudo apt-get install firmware-realtek

2022/10/18
articleCard.readMore

安装docker

运行环境 Operating System: Debian GNU/Linux 11 (bullseye) Kernel: Linux 5.10.0-18-amd64 Architecture: x86-64 通过Debian官方软件仓库安装 # 更新软件源 并 安装 docker及相关应用 sudo apt update && sudo apt install -y docker docker.io docker-compose # 查看docker运行状态 sudo systemctl status docker # 查看docker信息 sudo docker info # 验证是否安装成功 sudo docker run hello-world 镜像加速器 访问 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 获取自己的阿里云镜像加速器地址 修改 /etc/docker/daemon.json sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "max-concurrent-downloads": 3, "max-concurrent-uploads": 3, "max-download-attempts": 3, "registry-mirrors": ["https://修改为自己的.mirror.aliyuncs.com","https://hub-mirror.c.163.com","https://registry.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker 清理docker对象 prune 命令用来删除不再使用的 docker 对象。 # 删除所有未被 tag 标记和未被容器使用的镜像: $ docker image prune # 删除所有未被容器使用的镜像: $ docker image prune -af # 删除所有停止运行的容器: $ docker container prune # 删除所有未被挂载的卷: $ docker volume prune # 删除所有网络: $ docker network prune # 删除 docker 所有资源: $ docker system prune 查看容器ip # 查看所有容器的ip $ docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq) # 查看指定容器的ip网络配置包含网络,ip等 $ docker inspect containerId # 查看容器的ip $ docker exec -it containerId ip addr 修改docker启动目录 因为装系统时 /var 分区小,需要把docker 启动后的路径改成 /home/docker , /etc/docker/daemon.json 是 docker 的配置文件,默认是没有的,需要手动创建。 具体的操作是: 1: 设置 /etc/docker/daemon.json 文件. 内容参考: { "data-root":"/home/docker" } 2.创建并修改完daemon.json文件后,需要让这个文件生效 # a.修改完成后reload配置文件 sudo systemctl daemon-reload # b.重启docker服务 sudo systemctl restart docker.service # c.查看状态 sudo systemctl status docker -l # d.查看服务 sudo docker info 调试镜像 # 使用--entrypoint设置(额外增加-it选项可直接进入容器),进入容器成功。 docker run -it --entrypoint /bin/bash --name 容器名 镜像:v30.2.9 查看程序动态库依赖关系 # ldd (Debian GLIBC 2.36-9+deb12u1) 2.36 # Copyright (C) 2022 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # Written by Roland McGrath and Ulrich Drepper. ldd --version # 判断某条命令需要哪些共享库文件的支持,以确保指定的命令在独立的系统内可以可靠的运行 ldd /bin/bash 参考 Docker官方安装文档: https://docs.docker.com/engine/install/debian/ docker-daemon.json各配置详解 { "api-cors-header":"", //——————在引擎API中设置CORS标头 "authorization-plugins":[], //——————要加载的授权插件 "bridge":"", //————将容器附加到网桥 "cgroup-parent":"", //——————为所有容器设置父cgroup "cluster-store":"", //——————分布式存储后端的URL "cluster-store-opts":{}, //————————设置集群存储选项(默认map []) "cluster-advertise":"", //————————要通告的地址或接口名称 "debug": true, //————————启用调试模式,启用后,可以看到很多的启动信息。默认false "default-gateway":"", //——————容器默认网关IPv4地址 "default-gateway-v6":"", //——————容器默认网关IPv6地址 "default-runtime":"runc", //————————容器的默认OCI运行时(默认为" runc") "default-ulimits":{}, //——————容器的默认ulimit(默认[]) "dns": ["192.168.1.1"], //——————设定容器DNS的地址,在容器的 /etc/resolv.conf文件中可查看。 "dns-opts": [], //————————容器 /etc/resolv.conf 文件,其他设置 "dns-search": [], //————————设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的 主机时,DNS不仅搜索host,还会搜 //索host.example.com 。 注意:如果不设置, Docker 会默认用主机上的 /etc/resolv.conf 来配置容器。 "exec-opts": [], //————————运行时执行选项 "exec-root":"", //————————执行状态文件的根目录(默认为’/var/run/docker‘) "fixed-cidr":"", //————————固定IP的IPv4子网 "fixed-cidr-v6":"", //————————固定IP的IPv6子网 "data-root":"/var/lib/docker", //————-Docker运行时使用的根路径,默认/var/lib/docker "group": "", //——————UNIX套接字的组(默认为"docker") "hosts": [], //——————设置容器hosts "icc": false, //——————启用容器间通信(默认为true) "ip":"0.0.0.0", //————————绑定容器端口时的默认IP(默认0.0.0.0) "iptables": false, //———————启用iptables规则添加(默认为true) "ipv6": false, //——————启用IPv6网络 "ip-forward": false, //————————默认true, 启用 net.ipv4.ip_forward ,进入容器后使用 sysctl -a | grepnet.ipv4.ip_forward 查看 "ip-masq":false, //——————启用IP伪装(默认为true) "labels":["nodeName=node-121"], //————————docker主机的标签,很实用的功能,例如定义:–label nodeName=host-121 "live-restore": true, //——————在容器仍在运行时启用docker的实时还原 "log-driver":"", //——————容器日志的默认驱动程序(默认为" json-file") "log-level":"", //——————设置日志记录级别("调试","信息","警告","错误","致命")(默认为"信息") "max-concurrent-downloads":3, //——————设置每个请求的最大并发下载量(默认为3) "max-concurrent-uploads":5, //——————设置每次推送的最大同时上传数(默认为5) "mtu": 0, //——————设置容器网络MTU "oom-score-adjust":-500, //——————设置守护程序的oom_score_adj(默认值为-500) "pidfile": "", //——————Docker守护进程的PID文件 "raw-logs": false, //——————全时间戳机制 "selinux-enabled": false, //——————默认 false,启用selinux支持 "storage-driver":"", //——————要使用的存储驱动程序 "swarm-default-advertise-addr":"", //——————设置默认地址或群集广告地址的接口 "tls": true, //————————默认 false, 启动TLS认证开关 "tlscacert": "", //——————默认 ~/.docker/ca.pem,通过CA认证过的的certificate文件路径 "tlscert": "", //————————默认 ~/.docker/cert.pem ,TLS的certificate文件路径 "tlskey": "", //————————默认~/.docker/key.pem,TLS的key文件路径 "tlsverify": true, //————————默认false,使用TLS并做后台进程与客户端通讯的验证 "userland-proxy":false, //——————使用userland代理进行环回流量(默认为true) "userns-remap":"", //————————用户名称空间的用户/组设置 "bip":"192.168.88.0/22", //——————————指定网桥IP "registry-mirrors": ["https://192.498.89.232:89"], //————————设置镜像加速 "insecure-registries": ["192.168.0.123:12312"], //———————设置私有仓库地址可以设为http "storage-opts": [ "overlay2.override_kernel_check=true", "overlay2.size=15G" ], //————————存储驱动程序选项 "log-opts": { "max-file": "3", "max-size": "10m", }, //————————容器默认日志驱动程序选项 "iptables": false //————————启用iptables规则添加(默认为true) }

2022/9/30
articleCard.readMore

nginx 启用目录索引,显示文件列表

在nginx中,如果特定目录中没有index.html 文件,则默认会返回 404 Not Found 的错误。 但是,Nginx 自动索引模块—— ngx_http_autoindex_module 模块,提供了一种自动生成列表的方法,添加自动索引非常容易,使用 autoindex on 即可。下面的配置,将在访问特定请求时返回目录结构。 官方参考: http://nginx.org/en/docs/http/ngx_http_autoindex_module.html server { listen 80; ... ... location /index_dir { autoindex on; } } 除了简单地使用自动索引打开或关闭之外,还可以对其做其他的配置,包括: autoindex_exact_size; 显示输出的确切文件大小,还是最接近的KB,MB或GB。默认为on,显示出文件的确切大小,单位是bytes。改为off后,显示出文件的大概大小,单位是kB或者MB或者GB。 autoindex_format; 该指令指定Nginx索引列表应以什么格式输出。该指令有4个选项:html/xml/json/jsonp。 autoindex_localtime; 显示的文件时间为GMT时间。 注意:改为on后,显示的文件时间为文件的服务器时间。 使用这几个配置后配置内容类似于如下内容: location /index_dir/ { root /data/index_dir/; autoindex on; autoindex_exact_size off; autoindex_format html; autoindex_localtime on; } 如果有中文目录的话会出现乱码问题,所以还需要在下面添加这一句: charset utf-8; 或 charset utf-8,gbk;

2022/7/23
articleCard.readMore

PostgresSQL 基本用法:新建数据库、用户、连接、备份/恢复数据库

系统环境: Debian 11 切换到超级用户: sudo su postgres 进入psql: psql 创建一个用户: CREATE USER username WITH PASSWORD 'password'; 查看角色列表: \du 创建数据库并制定所有者和编码: CREATE DATABASE dbname WITH OWNER username ENCODING UTF8; 创建一个数据库: CREATE DATABASE dbname; 查看数据库列表: \l 更改刚创建的数据库所有权: ALTER DATABASE dbname OWNER TO username; 删除表字段 要删除一个字段,使用下面这样的命令: ALTER TABLE products DROP COLUMN description; 不管字段里有啥数据,都会消失,和这个字段相关的约束也会被删除。不过, 如果这个字段被另一个表的外键约束所引用,PostgreSQL 则不会隐含地删除该约束。你可以通过使用CASCADE指明删除任何依赖该字段的东西: ALTER TABLE products DROP COLUMN description CASCADE; 通过psql连接数据库 psql -h 127.0.0.1 -p 5432 -U username -d dbname 备份数据库 pg_dump dbname > dbname.dump 恢复数据库 psql -f dbname.dump -d dbname

2022/7/22
articleCard.readMore

查看 Debian 系统版本的方式

查看 Dibian 系统发行版本号的方式 1. 使用 lsb_release 命令 lsb_release 命令可用于查看 Linux 发行版操作系统的具体版本。它可能尚未安装在你的操作系统中,因此你需要先安装它。运行以下命令来安装 lsb_release: apt-get install lsb-release 安装完成之后,只需要输入下面的命令就可以查看到你当前系统的版本信息: lsb_release -a 你将看到类似下面的结果: No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye 以上运行结果说明当前使用的操作系统版本是 Debian 11. 2. 查看 /etc/issue 文件 第二种查看当前 Debian 版本的方法是查看位于 /etc 目录中的 issue 文件。你可以使用 cat 命令查看文件的内容,输入下面的命令: cat /etc/issue 你将看到类似下面的结果: Debian GNU/Linux 11 \n \l 3. 查看 /etc/os-release 文件 /etc/os-release 是一个包含操作系统标识数据的文件,它只能在运行 systemd 的最新 Debian 发行版上找到。同样可以使用 cat 命令查看该文件的内容,输入下面的命令: cat /etc/os-release 你将看到类似下面的结果: PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" NAME="Debian GNU/Linux" VERSION_ID="11" VERSION="11 (bullseye)" VERSION_CODENAME=bullseye ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" 如果想知道更具体的小版本号,可以查看 /etc/debian_version 文件,输入下面的命令: cat /etc/debian_version 你将看到类似下面的结果: 11.4 4. 使用 uname 命令 uname 命令可以显示电脑以及操作系统的相关信息,输入下面的命令: uname -a 你将看到类似下面的结果: Linux baicai-l01 5.10.0-16-amd64 #1 SMP Debian 5.10.127-1 (2022-06-30) x86_64 GNU/Linux 5. 使用 hostnamectl 命令 hostnamectl 命令用于配置或修改系统的主机名,不过也使用此命令来获取 Debian 系统的版本,只需要直接输入 hostnamectl 即可: hostnamectl 你将看到类似下面的结果: Static hostname: baicai-l01 Icon name: computer-desktop Chassis: desktop Machine ID: xxx Boot ID: xxx Operating System: Debian GNU/Linux 11 (bullseye) Kernel: Linux 5.10.0-16-amd64 Architecture: x86-64

2022/7/22
articleCard.readMore

删除 Debian Gnome 所有默认安装的游戏

我用以下命令删除Debian 11.5(带有gnome)中的所有预装游戏: sudo apt purge aisleriot gnome-sudoku gnome-nibbles ace-of-penguins gnomine gbrainy gnome-sushi gnome-taquin gnome-tetravex gnome-robots gnome-chess lightsoff swell-foop quadrapassel tali gnome-mahjongg gnome-2048 iagno gnome-klotski five-or-more gnome-mines four-in-a-row hitori && sudo apt autoremove

2022/7/22
articleCard.readMore

如何关闭 Ubuntu 中的开放端口?

如何关闭 Ubuntu 中的开放端口? 问题描述 列出所有打开的端口,以关闭一些应用程序的端口。 最佳办法 如果要关闭端口,则必须终止进程或停止相关服务。 可以使用 netstat -nalp 和 lsof -i:port 工具来识别打开端口后面的进程/二进制文件。 netstat 可用于查看端口统计信息。 要显示所有开放端口的列表: sudo netstat -lnp 列出所有侦听端口号以及每个负责的进程。终止或终止进程以关闭端口。 ( kill , pkill …) 关闭一个打开的端口: sudo fuser -k port_no/tcp 例子: sudo fuser -k 8080/tcp 次佳办法 要在 ubuntu 中关闭开放端口,可以使用以下命令 sudo kill $(sudo lsof -t -i:3000) 代替 3000 你可以指定你的端口号 lsof 命令将提供有关进程打开的文件的信息 -t :此标志指定 lsof 应仅生成带有进程标识符且没有标头的简洁输出 – 例如,以便可以将输出通过管道传输到 kill(1)。此选项选择 -w 选项。 -i :此标志选择任何 Internet 地址与 i 中指定的地址匹配的文件列表。如果未指定地址,此选项将选择所有 Internet 和 x.25 (HP-UX) 网络文件的列表。 防火墙规则应用 sudo ufw allow 22 sudo ufw deny 22 附注 关闭特定进程 kill $(ps -e|grep firefox|awk '{print $1}')

2022/6/19
articleCard.readMore

解决 Firefox 访问weibo搜索或热搜提示 建立安全连接失败

应用版本:firefox 开发者版 v101 问题描述:微博主页可以正常访问,但点击搜索或热搜链接,都提示: 建立安全连接失败 连接到 s.weibo.com 时发生错误。 由于不能验证所收到的数据是否可信,无法显示您想要查看的页面。 建议向此网站的管理员反馈这个问题。 详细了解… 通过curl -v “网址” 反复重试对比发现在访问微博热搜的时候有一个加密协商被“微博账户认证网关服务器”拒绝了。 具体测试指令: 通过 curl -v "https://s.weibo.com/weibo?q=%23%E7%A5%9D%E8%9E%8D%E5%8F%B7%E5%87%86%E5%A4%87%E5%9C%A8%E7%81%AB%E6%98%9F%E8%B6%8A%E5%86%AC%23&topic_ad=" 得到302跳转请求链接 继续 curl -v "https://passport.weibo.com/visitor/visitor?entry=miniblog&a=enter&url=https%3A%2F%2Fs.weibo.com%2Fweibo%3Fq%3D%2523%25E7%25A5%259D%25E8%259E%258D%25E5%258F%25B7%25E5%2587%2586%25E5%25A4%2587%25E5%259C%25A8%25E7%2581%25AB%25E6%2598%259F%25E8%25B6%258A%25E5%2586%25AC%2523%26topic_ad%3D&domain=.weibo.com&sudaref=&ua=php-sso_sdk_client-0.6.29&_rand=1651842429.1375" 返回 …… * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / AES256-GCM-SHA384 * ALPN, server did not agree to a protocol * Server certificate: * subject: C=CN; ST=Beijing; O=Sina.com Technology(China)Co.,ltd; CN=sina.com …… 注意这段提示: ALPN, server did not agree to a protocol ALPN ALPN(Application-Layer Protocol Negotiation):应用层协议协商是 TLS 的一个扩展,故而应用层协议在协商加密协议的过程中,避免了额外的往返通讯开销。 ALPN支持任意应用层协议的协商,目前应用最多是HTTP2的协商。当前主流浏览器,都只支持基于 HTTPS 部署的 HTTP/2,因为浏览器是基于ALPN协议来判断服务器是否支持HTTP2协议。 浏览器协商原理 可以通过WireShark抓包分析ALPN协商协议交互的过程 *浏览器在进行SSL连接,第一次发送Client Hello包时,在扩展字段里携带浏览器支持的版本。 *服务器在返回Server Hello包时,如果服务器支持http 2,则会返回h2,如果不支持,则从客户端支持的协议列表中选取一个它支持的协议,一般为http/1.1。 浏览器和服务端都支持ALPN 协商,是用上 HTTP/2 的大前提。 大部分 Web Server 都依赖 OpenSSL 库提供 https服务,是否支持 ALPN 完全取决于使用的 OpenSSL 版本,OpenSSL 1.0.2 版本才开始支持 ALPN。 关闭Firefox ALPN功能 地址栏输入 about:config 确认风险提示 输入“ALPN” 将 security.ssl.enable_alpn 的值切换为false 参考: ALPN

2022/5/6
articleCard.readMore

上传公钥实现ssh登录

生成密钥和公钥 如果之前已经存在就不用重新生成了 $ ssh-keygen -t rsa 上传公钥 $ ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.0.1 其中root 服务器用户, 192.168.0.1 是服务器ip。之后按照提示输入密码即可 连接到服务器 直接使用ssh直接连接服务器,无需输入密码 $ ssh root@192.168.0.1

2022/3/12
articleCard.readMore

在 Linux 命令行中使用的 6 个元字符

在 Linux 命令行上使用元字符是提高生产力的一个好方法。 许多 Linux 电脑是无头运行的,你可以在命令行上完成所有的管理任务。它使用许多所有人都熟悉的基本命令,如 ls、ls-l、ls-l、cd、pwd、top 等等。 Linux 上的 Shell 元字符 你可以通过使用元字符来扩展这些命令。 管道符 | 假设我想知道我的系统上运行的 Firefox 的所有实例。我可以使用带有 -ef 参数的 ps 命令来列出我系统上运行的所有程序实例。现在我想只看那些涉及 Firefox 的实例。我使用了我最喜欢的元字符之一,管道符 |,将其结果送到 grep,用它来搜索模式: $ ps -ef | grep firefox 输出重定向 > 另一个我最喜欢的元字符是输出重定向 >。我用它来打印 dmesg 命令结果中所有 AMD 相关的结果。你可能会发现这在硬件故障排除中很有帮助: $ dmesg | grep amd > amd.txt $ cat amd.txt [ 0.897] amd_uncore: 4 amd_df counters detected [ 0.897] amd_uncore: 6 amd_l3 counters detected [ 0.898] perf/amd_iommu: Detected AMD IOMMU #0 (2 banks, 4 counters/bank). 星号 * 星号 *(通配符)是寻找具有相同扩展名的文件时我的最爱,如 .jpg 或 .png。我首先进入我的系统中的 Picture 目录,并使用类似以下的命令: $ ls *.png BlountScreenPicture.png DisplaySettings.png EbookStats.png StrategicPlanMenu.png Screenshot from 01-24 19-35-05.png 波浪号 ~ 波浪号 ~ 是在 Linux 系统上通过输入以下命令快速返回你的家目录的一种方法: $ cd ~ $ pwd /home/don 美元符号 $ $ 符号作为一个元字符有不同的含义。当用于匹配模式时,它意味着任何以给定字符串结尾的字符串。例如,当同时使用元字符 | 和 $ 时: $ ls | grep png$ BlountScreenPicture.png DisplaySettings.png EbookStats.png StrategicPlanMenu.png Screenshot from 01-24 19-35-05.png 上尖号 ^ 符号 ^ 将结果限制在以给定字符串开始的项目上。例如,当同时使用元字符 | 和 ^ 时: $ ls | grep ^Screen Screenshot from 01-24 19-35-05.png 这些元字符中有许多是通往 正则表达式 的大门,所以还有很多东西可以探索。你最喜欢的 Linux 元字符是什么,它们是如何节省你的工作的? via: 1 2

2022/2/14
articleCard.readMore

ubuntu上安装微信(wechat)

介绍 由于微信官网 https://weixin.qq.com/ 没有linux版本的下载和安装方法,但微信确实提供了优麒麟发行版的官方版本,所以就有了下面的安装方法。 安装方法 打开优麒麟应用商店官网,然后下载应用,就看到微信了 https://www.ubuntukylin.com/applications/106-cn.html 下载deb包,用命令sudo dpkg -i ,就能安装上了,而且可以用。 这是linux原生的,功能少点,但比wine的要轻巧不少。 安装方法2 下载链接在方法1中,优麒麟应用商店微信应用页面右键 “64位下载” ——> “复制链接” 终端下载Deb安装: wget -O ~/weixin.deb "http://archive.ubuntukylin.com/software/pool/partner/weixin_2.1.1_amd64.deb" sudo dpkg -i ~/weixin.deb

2022/1/30
articleCard.readMore

Docker 运行 postgreSQL

简单步骤 安装docker,可参考 安装docker及简单的使用 或 百度解决; 拉取postgreSQL的docker镜像文件: docker pull postgres 创建 docker volume,名字为“dv_pgdata"(其实可以省略手动创建,直接跑下一步,docker也会自动创建的): docker volume create dv_pgdata 启动容器,用-v来指定把postgres的数据目录映射到上面创建的dv_pgdata里面: docker run --name my_postgres -v dv_pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=xxxxxx -p 5432:5432 -d postgres 这时候查看已存在的docker volume: docker volume ls 查看volume信息: cn2d6@navxin-desktop:~$ docker inspect dv_pgdata 在宿主机,也可以直接查看volume里的内容: cn2d6@navxin-desktop:~$ cd /var/lib/docker/volumes/dv_pgdata/_data cn2d6@navxin-desktop:~$ ll 查看postgresql: cn2d6@navxin-desktop:~$docker exec -it 618 bash root@618f1a4128ee:/# psql -U postgres -d postgres -p 5432 -h 127.0.0.1 更多的时候,我们希望能用图形界面来管理和操作数据库,可以部署pgadmin工具(例如下面),然后在浏览器中访问宿主机的5080端口,便能打开pgadmin。 docker pull dpage/pgadmin4 docker run --name pgadmin -p 5080:80 \ -e 'PGADMIN_DEFAULT_EMAIL=hi@nav.xin' \ -e 'PGADMIN_DEFAULT_PASSWORD=xxxxxx' \ -e 'PGADMIN_CONFIG_ENHANCED_COOKIE_PROTECTION=True' \ -e 'PGADMIN_CONFIG_LOGIN_BANNER="Authorised users only!"' \ -e 'PGADMIN_CONFIG_CONSOLE_LOG_LEVEL=10' \ -d dpage/pgadmin4 用docker-compose部署postgresql和pgAdmin4 准备步骤 安装docker-compose apt install docker-compose 确定已经pull好postgres和pgAdmin4镜像,若未完成: docker pull postgres docker pull dpage/pgadmin4 配置内容 然后在任意目录下新建文件docker-compose.yml: touch ./docker-compose.yml 内容如下: # Use postgres/example user/password credentials # https://hub.docker.com/_/postgres?tab=description # 在当前目录下运行:sudo docker-compose up -d # 若需停止运行,在当前目录运行:sudo docker-compose down # docker路由地址查看: sudo docker inspect postgres_baicai # sudo docker kill $(sudo docker ps -aq) # sudo docker rm $(sudo docker ps -aq) version: '3.1' services: db: image: postgres restart: always privileged: true container_name: postgres_baicai ports: - 5432:5432 environment: POSTGRES_PASSWORD: 你的密码 PGDATA: /var/lib/postgresql/data/pgdata volumes: - /navxin/kn1/baicai_docker/baicai_postgres/pg_data:/var/lib/postgresql/data # - pgdata:/var/lib/postgresql/data pgadmin4: image: dpage/pgadmin4 restart: always container_name: pgadmin_baicai ports: - 5080:80 environment: PGADMIN_DEFAULT_EMAIL: "hi@nav.xin" PGADMIN_DEFAULT_PASSWORD: 你的密码 # volumes: # pgdata: 部署 在当前目录下运行: docker-compose up -d 若需停止运行,在当前目录运行: docker-compose down 连接步骤 打开浏览器,输入localhost:5080,登录pgAdmin4之后,点击添加新服务器,特别注意,在连接地址IP里应该填写docker路由地址,端口填写5432。 docker路由地址查看方法 docker inspect postgres_baicai 在输出内容中找到Gateway,对应的地址即为docker路由地址。 更多参考: 安装docker及简单的使用 postgres dpage/pgadmin4

2021/12/9
articleCard.readMore

Ubuntu使用systemd配置开机运行service

systemd Systemd 是 Linux 系统工具,用来启动守护进程,已成为大多数发行版的标准配置。 由来 历史上,Linux 的启动一直采用init进程。 下面的命令用来启动服务。 $ sudo /etc/init.d/apache2 start # 或者 $ service apache2 start 这种方法有两个缺点。 一是启动时间长。init进程是串行启动,只有前一个进程启动完,才会启动下一个进程。 二是启动脚本复杂。init进程只是执行启动脚本,不管其他事情。脚本需要自己处理各种情况,这往往使得脚本变得很长。 Systemd 概述 Systemd 就是为了解决这些问题而诞生的。它的设计目标是,为系统的启动和管理提供一套完整的解决方案。 根据 Linux 惯例,字母d是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就是它要守护整个系统。 使用了 Systemd,就不需要再用init了。Systemd 取代了initd,成为系统的第一个进程(PID 等于 1),其他进程都是它的子进程。 $ systemctl --version 上面的命令查看 Systemd 的版本。 使用 systemd 实现开机执行 Shell 脚本 通用操作步骤 创建希望开机马上执行的脚本,本文举例脚本存放位置为 /home/navxin/Example/startup.sh,脚本内容如下: #!/bin/bash # 开机时在脚本的同级目录下创建一个名为 StartupTouch.txt 的文件 touch /home/navxin/Example/startup.sh.txt 开机执行的脚本需增加可执行权限才能被 systemd 运行,使用如下命令 chmod u+x /home/navxin/Example/startup.sh chmod g+x /home/navxin/Example/startup.sh 进入 systemd 放置 service 的目录,在该目录下可看到大量服务配置文件,命令如下 # 进入 systemd 的 service 目录 cd /usr/lib/systemd/system # 查看文件列表 ls -al 在该目录创建一个新的 .service 文件用于配置开机启动脚本,本例中的文件名为 StartupExample.service,所执行命令和文件中的配置内容如下: 创建服务配置文件 sudo touch /usr/lib/systemd/system/StartupExample.service 以下为 StartupExample.service 配置文件的内容 [Unit] Description=Startup Example [Service] ExecStart=/home/navxin/Example/startup.sh [Install] WantedBy=multi-user.target 尝试手动运行新创建的 service,使用如下命令: # 手动运行 StartupExample.service sudo systemctl start StartupExample.service # 查看运行日志 systemctl status StartupExample.service # 删除刚测试服务时创建的文件 rm -f /home/navxin/Example/startup.sh.txt # 设置服务为 enable 状态,使之能开机运行 sudo systemctl enable StartupExample.service # 重启机器 systemctl reboot 附注:Unit Systemd 可以管理所有系统资源。不同的资源统称为 Unit(单位)。 Unit 一共分成12种。 Service unit:系统服务 Target unit:多个 Unit 构成的一个组 Device Unit:硬件设备 Mount Unit:文件系统的挂载点 Automount Unit:自动挂载点 Path Unit:文件或路径 Scope Unit:不是由 Systemd 启动的外部进程 Slice Unit:进程组 Snapshot Unit:Systemd 快照,可以切回某个快照 Socket Unit:进程间通信的 socket Swap Unit:swap 文件 Timer Unit:定时器 systemctl list-units命令可以查看当前系统的所有 Unit 。 # 列出正在运行的 Unit $ systemctl list-units # 列出所有Unit,包括没有找到配置文件的或者启动失败的 $ systemctl list-units --all # 列出所有没有运行的 Unit $ systemctl list-units --all --state=inactive # 列出所有加载失败的 Unit $ systemctl list-units --failed # 列出所有正在运行的、类型为 service 的 Unit $ systemctl list-units --type=service # 列出所有正在运行的、类型为 mount 的 Unit $systemctl list-units --type=mount # 命令用于列出所有配置文件。 $systemctl list-unit-files $systemctl list-unit-files --type=mount 修改 myadmin.service文件,增加语句 After=network-online.target remote-fs.target nss-lookup.target navxin-kn1.mount Wants=network-online.target 全部文件内容如下(部分内容参考nginx.service): # /lib/systemd/system/myadmin.service [Unit] Description=Start myAdmin web server Documentation=https://www.lyhuilin.com/ After=network-online.target remote-fs.target nss-lookup.target navxin-kn1.mount Wants=network-online.target [Service] Environment="WELCOME=Baicai myAdmin Base Environment." ExecStartPre=/bin/echo ${WELCOME} ExecStart=/baicai/systemdStart/my_admin/my_admin -c /baicai/systemdStart/my_admin/conf/config.yaml ExecStop=/bin/kill -s TERM ${MAINPID} [Install] WantedBy=multi-user.target

2021/12/1
articleCard.readMore

安装docker及简单的使用

docker的介绍,里面包括了3个基本概念 1.1 docker主要由镜像和容器构成 镜像(Image):docker镜像好比一个模板,相当于一个文件系统 容器(Container):容器需要通过镜像来创建。镜像和容器就像是面向对象中的类和实例一样。容器可以被创建/启动/停止/删除等 仓库(Repository):仓库就是存放镜像的地方,分为私有仓库和公有仓库。类似git 1.2 docker的运行原理 docker是一个Client-Server结构的系统,docker的守护进程运行在主机上,通过socket从客户端访问。dockerServer接收到docker-Client的指令,就会执行这个命令。 一. Mac 系统 docker 的安装 1.1 homebrew的cask应支持Docker for Mac,所以可以直接安装 brew cask install docker 1.2 也可以直接到官网下载,https://download.docker.com/mac/stable/Docker.dmg 1.3 docker的参考文档:https://docs.docker.com 1.4 dockerhub查找镜像源地址:https://hub.docker.com 二. docker的使用 1.1 查看版本 docker --version docker-compose --version docker-machine --version 1.2 查看docker系统信息(包括镜像和容器的数量等) docker info 1.3 帮助命令 docker help 1.4 查看 cpu的状况 docker stats 三. docker的基本命令 镜像相关命令: 1.1 查看镜像可用版本(nginx为例) docker search nginx 1.2 下载一个镜像 docker pull nginx:latest #(:后面跟镜像版本) 1.3 运行一个nginx服务器 docker run -d -p 81:80 --name webserver nginx 可选项: #--name webserver :容器名称,用来区分容器 #-p 81:80 :端口进行映射,将本地的81端口映射到容器内部的80端口 #-v ~/nginx/html:/usr/share/nginx/html 数据卷挂载ro/rw,将主机项目中的目录挂载到容器的目录下,默认rw只能在宿主机外改变,容器内部不能改变 #-d:设置容器中在后台一直运行 #-it:使用交互方式运行,进入容器查看内容 #-P:随机端口 #-e:环境配置设置 注意:后台启动运行,必须要有一个前台进程,docker发现没有应用,就会自动停止 重点:数据卷挂载分为具名/匿名/指定路径挂载,容器数据卷挂载可以实现数据共享,容器的持久化和同步操作,可以使用docker volume 查看卷的情况,可以使用volumes-from实现多个容器之间的数据共享。 1.4 停止nginx服务 docker stop webserver(容器ID) 1.5 删除nginx服务 docker rm webserver 1.6 启动/重启nginx服务 docker start/restart webserver 1.7 列出所有镜像(列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间) docker images ls 说明: REPOSITORY 镜像的仓库源 TAG 镜像的标签 IMAGE ID 镜像的id CREATED 镜像的创建时间 SIZE 镜像的大小 可选项: -a:列出所有的镜像 -q:只显示镜像的id 注意:镜像ID是唯一标识,一个镜像可以对应多个标签 1.8 查看镜像、容器、数据卷所占用的空间 docker system df 1.9 删除镜像 指定镜像: docker rmi [镜像名称/镜像短ID/镜像长ID/镜像摘要] 多个镜像: docker rmi 镜像ID 镜像ID 镜像ID 全部镜像: docker rmi $(docker images -aq) 2.0 删除docker images ls 命令配合 删除所有仓库名为redis的镜像 docker rmi $(docker images ls -q redis) 2.1 查看镜像运行记录 docker history 镜像id 容器相关命令 1.1 列出容器 docker ps 可选项: #-a:显示所有的容器,包括未运行的 #-l:显示最近创建的容器 #-n:列出最近创建的n个容器 #-q:只显示容器的编号 1.2 进入容器 docker exec -it [容器名称] /bin/bash docker atthch 容器id 区别:docker exec 进入容器后开启一个新的终端,可以在里面操作;docker attach 进入容器正在执行的终端,不会启动新的进程 1.3 退出容器 容器停止退回主机 exit 容器不停止推出 ctrl+p+q 1.4 删除容器 指定容器: docker rm [容器id] 多个容器: docker rm 容器id 容器id 容器id 所有容器: docker rm $(docker ps -aq) docker ps -a -q|xargs docker rm 注意:不能删除正在运行的容器,要删除正在运行的容器需要加 -f 参数,docker rm -f 容器id 1.5 启动/重启容器 docker start/restart 容器id 1.6 停止/强制停止容器 docker stop/kill 容器id 1.7 查看容器日志 docker logs -f -t --tail 100 容器id #--tail后面必须加参数条数 1.8 查看容器中的进程信息 docker top 容器id 1.9 查看容器的元数据(重要命令) docker inspect 容器id 2.0 从容器上拷贝数据到主机上 docker cp 容器id:容器内路径 主机路径 四. Dockerfile的指令 FROM 基础镜像,一切从这里开始 MAINTAINER 镜像的作者 姓名<邮箱> RUN 镜像构建需要运行的命令 ADD 步骤,添加内容 WORKDIR 镜像的工作目录 VOLUME 挂载的目录 EXPOST 端口配置 CMD 指定容器启动要运行的命令,只有最后一个会生效,可被替代 ENTRYPOINT 指定这个容器启动要运行的命令,可以追加命令 ONBUILD 当构建一个被继承的Dockerfile时会运行 COPY 类似ADD将我们文件拷贝到镜像中 ENV 构建的时候设置环境变量

2021/10/5
articleCard.readMore

ubuntu上安装firefox

卸载原来Firefox sudo apt-get purge firefox 或 sudo apt-get remove firefox 下载最新Firefox二进制压缩包 wget -O ~/firefox.tar.bz2 "https://download.mozilla.org/?product=firefox-latest&os=linux64" 解压并移动到/opt目录 sudo tar xjf ~/firefox.tar.bz2 -C /opt/ 创建软链接 sudo ln -s /opt/firefox/firefox /usr/lib/firefox/firefox 在/usr/lib/中创建一个firefox可执行链接文件,该文件指向/opt中firefox可执行主程序位置。 启动firefox 终端启动Firefox firefox 创建桌面快捷方式 进入/usr/share/applications目录 touch firefox.desktop nano firefox.desktop 添加内容: [Desktop Entry] Name=firefox Comment=firefox Exec=/opt/firefox/firefox Icon=/opt/firefox/browser/chrome/icons/default/default128.png Terminal=false Type=Application Categories=Application; Encoding=UTF-8 StartupNotify=true 卸载Firefox 通过上面方法安装的Firefox,如果想删除,可以在终端种输入以下命令: sudo rm -rf /opt/firefox sudo rm /usr/share/applications/firefox.desktop

2021/9/29
articleCard.readMore

友情链接

1.申请链接请先在贵站的首页做好本站的友情链接(本站名称:白菜林,本站地址:https://baicai.me/)。 2.优先交换电脑网络和学习类的网站以及博客的友情链接。 3.做好链接后可与我联系,本站将在7个工作日内回复。 ———————————————————— 以上为本站的友情链接,排各不分先后 (每周检测一次)

2021/9/29
articleCard.readMore

如何知道谁在ping我?

tcpdump 是 Linux 上可用的最强大和使用最广泛的 命令行 数据包嗅探器(包分析器)工具。 在您要监控的计算机的终端应用程序中: sudo tcpdump -i ethX icmp and icmp[icmptype]=icmp-echo 选项: -n avoid a (potentially slow) reverse DNS query −i interface icmp[icmptype]=icmp-echo To print all ICMP packets that are echo requests/replies 它将开始侦听 ethX 并等待到达的数据包。 示例:我有 2 台 pc win7 192.168.0.8 , Ubuntu 192.168.0.57 它将监控到达的数据包: 在Win上: ping 192.168.0.57 在 Ubuntu 上: tcpdump -i eth0 icmp and icmp[icmptype]=icmp-echo -n 参考:nixCraft[1]

2021/8/28
articleCard.readMore

使用 df 命令查看 Linux 上的可用磁盘空间

磁盘空间已经不像计算机早期那样珍贵,但无论你有多少磁盘空间,总有耗尽的可能。计算机需要一些磁盘空间才能启动运行,所以为了确保你没有在无意间用尽了所有的硬盘空间,偶尔检查一下是非常必要的。在 Linux 终端,你可以用 df 命令来做这件事。 df 命令可以显示文件系统中可用的磁盘空间。 要想使输出结果易于阅读,你可以加上 –human-readable(或其简写 -h)选项: $ df --human-readable Filesystem Size Used Avail Use% Mounted on /dev/sda1 1.0T 525G 500G 52% / 在这个例子中,计算机的磁盘已经用了 52%,还有 500 GB 可用空间。 由于 Linux 从整体上看待所有挂载设备的文件系统,df 命令会展示出连接到计算机上的每个存储设备的详细信息。如果你有很多磁盘,那么输出结果将会反映出来: $ df --human-readable Filesystem Size Used Avail Use% Mounted on /dev/root 110G 45G 61G 43% / devtmpfs 12G 0 12G 0% /dev tmpfs 12G 848K 12G 1% /run /dev/sda1 1.6T 1.3T 191G 87% /home /dev/sdb1 917G 184G 687G 22% /penguin /dev/sdc1 57G 50G 4.5G 92% /sneaker /dev/sdd1 3.7T 2.4T 1.3T 65% /tux 在这个例子中,计算机的 /home 目录已经用了 87%,剩下 191 GB 的可用空间。 查看总的可用磁盘空间 如果你的文件系统确实很复杂,而你希望看到所有磁盘的总空间,可以使用 –total 选项: $ df --human-readable --total Filesystem Size Used Avail Use% Mounted on /dev/root 110G 45G 61G 43% / devtmpfs 12G 0 12G 0% /dev tmpfs 12G 848K 12G 1% /run /dev/sda1 1.6T 1.3T 191G 87% /home /dev/sdb1 917G 184G 687G 22% /penguin /dev/sdc1 57G 50G 4.5G 92% /sneaker /dev/sdd1 3.7T 2.4T 1.3T 65% /tux total 6.6T 4.0T 2.5T 62% - 输出的最后一行展示了文件系统的总空间、已用总空间、可用总空间。 查看磁盘空间使用情况 如果你想大概了解哪些文件占用了磁盘空间,请阅读我们关于 du 命令[1] 的文章。 via: https://opensource.com/article/21/7/check-disk-space-linux-df https://linux.cn/article-13646-1.html

2021/8/4
articleCard.readMore

Ubuntu安装 Zlib

如果你尝试在 Ubuntu 上安装 zlib,它会抛出 “unable to locate package zlib” 错误。 Zlib 是一个用于数据压缩的开源库。 作为使用者,你可能会遇到需要安装 zlib(或 zlib-devel 包)作为另一个应用程序的依赖项的情况。 但问题来了,如果你尝试在 Ubuntu 上安装 zlib,它会抛出 “unable to locate package zlib” 错误。 sudo apt install zlib Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package zlib 为什么会看到这个 Ubable to locate package 错误呢?因为没有名为 zlib 的包。 如果你 使用 apt search 命令,你会发现有几个包可以让你安装:zlib 1g 和 zlib 1g-dev。当你知道这些后,只需一个 apt 命令就可以安装它们。 在基于 Ubuntu 的 Linux 发行版上安装 Zlib 打开终端,使用以下命令: sudo apt install zlib1g 请记住 g 前面的字母是数字 1,而不是小写的字母 L。很多人在输入命令时都会犯这个错误。 另一个包,zlib 1g-dev 是开发包。只有在你需要时才安装它,否则你应该使用 zlib 1g 包。 sudo apt install zlib1g-dev 你也可以 Zlib 网站 下载源代码并安装它。但是,除非你有充分的理由,否则我不推荐使用源代码方式来安装 zlib。例如,如果你需要最新或特定版本的 zlib,但该版本在发行版的仓库中不可用。 有趣的是,像安装 zlib 这样看似很小的东西可能会变得很麻烦,有两个原因:一个是不同的包名;另一个是包含“隐藏”数字 1,它与小写 L 混淆了。 参考 Zlib [1]

2021/7/12
articleCard.readMore

免费CDN:jsDelivr+Github 使用方法

CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。 用 jsDelivr 免费CDN托管静态资源 国内的免费 CDN 不少,但需要备案 国外的免费 CDN 也不少,但在国内速度慢 但 jsDelivr 是一个特殊的存在,虽然是一家国外 CDN 但是有国内节点 jsDelivr官网: https://www.jsdelivr.com 放在Github的资源在国内加载速度比较慢,因此需要使用CDN加速来优化网站打开速度,jsDelivr + Github便是免费且好用的CDN,非常适合博客网站使用。 使用步骤 1、新建Github仓库 2、克隆Github仓库到本地 执行以下命令: git clone 一键复制的仓库地址 3、上传资源 复制需要上传的资源到本地git仓库(注:jsDelivr不支持加载超过20M的资源),在本地git仓库目录下,执行以下命令: git status #查看状态 git add . #添加所有文件到暂存区 git commit -m '第一次提交' #把文件提交到仓库 git push #推送至远程仓库 4、发布仓库 点击release发布 自定义发布版本号 5、通过jsDelivr引用资源 使用方法:https://cdn.jsdelivr.net/gh/你的用户名/你的仓库名@发布的版本号/文件路径 例如: https://cdn.jsdelivr.net/gh/baicaime/meBlog/index.html https://cdn.jsdelivr.net/gh/baicaime/meBlog/favicon-16x16.png https://cdn.jsdelivr.net/gh/clin003/cdn/v@3.1.3/css.css 注意:版本号不是必需的,是为了区分新旧资源,如果不使用版本号,将会直接引用最新资源,除此之外还可以使用某个范围内的版本,查看所有资源等,具体使用方法如下: // 加载任何Github发布、提交或分支 https://cdn.jsdelivr.net/gh/user/repo@version/file // 加载 jQuery v3.2.1 https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/dist/jquery.min.js // 使用版本范围而不是特定版本 https://cdn.jsdelivr.net/gh/jquery/jquery@3.2/dist/jquery.min.js https://cdn.jsdelivr.net/gh/jquery/jquery@3/dist/jquery.min.js // 完全省略该版本以获取最新版本 https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js // 将“.min”添加到任何JS/CSS文件中以获取缩小版本,如果不存在,将为会自动生成 https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/src/core.min.js // 在末尾添加 / 以获取资源目录列表 https://cdn.jsdelivr.net/gh/jquery/jquery/ // 刷新cnd资源 将域名中的 cdn 改为 purge 即可 https://purge.jsdelivr.net/gh/baicaime/meBlog/index.html // 使用最新版本资源 https://purge.jsdelivr.net/gh/baicaime/meBlog@latest/index.html

2021/7/2
articleCard.readMore

Ubuntu安装 qbittorrent-nox并启动

Qbittorrent-Nox 要在Linux上使用Qbittorrent Web UI,你无需安装完整的Qbittorent桌面应用程序,有一个基于终端的Qbittorrent应用程序可用,它被称为Qbittorrent-Nox。 注意:Web UI功能不仅限于Qbittorrent-Nox应用程序,此功能还可以与传统的Qbittorent Linux桌面应用程序一起使用 安装qbittorrent 安装add-apt-repository命令 sudo apt-get update && sudo apt-get install software-properties-common -y 添加qbittorrent-nox的PPA软件源 sudo add-apt-repository ppa:qbittorrent-team/qbittorrent-stable 安装qbittorrent-nox(webui版) sudo apt-get update && sudo apt-get install qbittorrent-nox 设置开机启动 通过rc.local完成 如果是Ubuntu-16.10及其之后的版本需要先按下面的文章完成设置后,开机启动才会生效 Ubuntu-18.04设置开机启动脚本 起因Ubuntu-16.10(不包括)之前的版本使用的是update-rc.d以及rc.local等方法设置开机启… 编辑rc.local脚本 nano /etc/rc.local 在exit 0前面(前一行)添加以下内容并保存 qbittorrent-nox -d 通过创建自定义服务实现 创建系统服务 sudo apt-get install nano -y && nano /etc/systemd/system/qbittorrent-nox.service 粘贴以下内容,并保存。 [Unit] Description=qBittorrent-nox After=network.target [Service] User=root Type=simple RemainAfterExit=yes ExecStart=/usr/bin/qbittorrent-nox -d [Install] WantedBy=multi-user.target 启动qbittorrent-nox并创建服务配置 systemctl start qbittorrent-nox 设置开机自动启动qbittorrent-nox systemctl enable qbittorrent-nox 查看qbittorrent-nox状态 sudo systemctl status qbittorrent-nox 默认账号:admin 密码: adminadmin 默认登陆网址:ip:8080

2021/7/2
articleCard.readMore

跨域方案Nginx配置

什么是浏览器同源策略? 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。 同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 如何实现跨域? 跨域是个比较古老的命题了,历史上跨域的实现手段有很多,我们现在主要介绍Nginx的跨域方案,其余的方案我们就不深入讨论了。 方便的跨域方案Nginx nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。 现在的新项目中nginx几乎是首选,我们用node或者go开发的服务通常都需要经过nginx的反向代理。 反向代理的原理很简单,即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再讲请求转发给node或者go服务,这样就规避了同源策略。 #进程, 可更具cpu数量调整 worker_processes 1; events { #连接数 worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; #连接超时时间,服务器会在这个时间过后关闭连接。 keepalive_timeout 10; # gizp压缩 gzip on; # 直接请求nginx也是会报跨域错误的这里设置允许跨域 # 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了) add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; # srever模块配置是http模块中的一个子模块,用来定义一个虚拟访问主机 server { listen 80; server_name localhost; # 根路径指到index.html location / { root html; index index.html index.htm; } # localhost/api 的请求会被转发到192.168.0.103:8080 location /api { rewrite ^/b/(.*)$ /$1 break; # 去除本地接口/api前缀, 否则会出现404 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://192.168.0.103:8080; # 转发地址 } # 重定向错误页面到/50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }

2021/7/1
articleCard.readMore

MAC命令快速模糊查找文件

find命令 描述:通过 find命令查找 语法:find ~ -iname “文件名*” /** * 比如我要查找一个以‘vue-’开头的.zip文件, * 但是你忘了它的全名也忘了在那个文件夹, * 查找范围是‘~’节点以内 * 就可以用这种方式进行模糊搜索 */ find ~ -iname "vue-*.zip" /** * 然后它就把所有包含符合条件的文件和路径都打印出来了 */ find不但能查找文件,还能查找文件夹 /** * 比如我要查找所有包含‘vue’的文件或文件夹 */ find ~ -iname "*vue*" /** * 结果它找到了所有包含‘vue’的文件或文件夹 */ find方式很简单但是需要一点专业知识,需要知道一些正则的基本常识,需要指定路径范围,搜索的名字需要加引号等等 mdfind命令 描述:通过 mdfind命令查找 语法:mdfind -name 文件名 /** * 比如我要查找所有包含‘vue’的文件或文件夹 */ mdfind -name vue /** * 看,我直接输入我要找的关键字‘vue’ * 就把所有文件和文件夹都输出出来了,是不是很方便 */ mdfind 简单粗暴,没缺点,但有个前提是你mac电脑要支持Spotlight功能,不过也不用担心,一般mac默认是支持的 在 shell 中执行命令 你是找到这个文件或文件夹了,但是你想直接打开它,那么怎么打开呢,看下面 若要运行当前用户个人文件夹中的命令,请在前面加上文件夹说明符。例如,若要运行 MyCommandLineProg,请使用以下命令: % ~/MyCommandLineProg 若要打开一个 App,请使用打开命令: % open -a MyProg.app 终止命令 在 Mac 上的“终端” App 中,点按正在运行您想要终止的命令的“终端”窗口。 按下 Control-C 键。 这会发出一个让大多数命令终止的信号。 参考 在 Mac 上的“终端”中执行命令和运行工具 [1] MAC命令快速全局查找文件或文件夹,支持模糊搜索 [2]

2021/6/27
articleCard.readMore

Rust 镜像源

通常cargo跑得挺顺畅,不怎么需要proxy。但有备无患。 crates.io 和 rustup 的国内镜像源 字节跳动提供的:https://rsproxy.cn/ 中国科技大学的:https://mirrors.ustc.edu.cn/help/crates.io-index.html 清华大学的:https://mirrors.tuna.tsinghua.edu.cn/help/rustup/ 上海交通大学的:https://git.sjtu.edu.cn/sjtug/crates.io-index 更换国内源 更换为国内源,否则安装太慢了。 新建文件:~/.cargo/config,内容替换为如下,replace-with 这行可自己 ping 文件中各个国内源头,看哪个源快用哪个: [source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" # 替换成你偏好的镜像源 replace-with = 'sjtu' # 清华大学 [source.tuna] registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git" # 中国科学技术大学 [source.ustc] registry = "git://mirrors.ustc.edu.cn/crates.io-index" # 上海交通大学 [source.sjtu] registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index" # rustcc社区 [source.rustcc] registry = "git://crates.rustcc.cn/crates.io-index" # 字节跳动 [source.rsproxy] registry = "https://rsproxy.cn/crates.io-index"

2021/6/25
articleCard.readMore

给我来点酷炫玩意-Sharkle

网页前端是一门技术活!出色的网站除了后台够快够可靠,也非常需要前端吸引眼球。在这个网站里,点击SHOW ME SOMETHING AWESOME就能随机跳转到一个新页面,往往是一些用了平时不太多见的网页设计元素制作出来的页面演示,有时候也会有独到设计的网页游戏,或许还有些别的内容。这个网站收录的内容都挺不错,有些网站的质量完全可以拿出来单独写一篇文章。 传送门 https://sharkle.com/ 方法 进入网站直接点击SHOW ME SOMETHING AWESOME即可欣赏!

2021/6/23
articleCard.readMore

Cargo Wix 创建Windows安装程序的cargo子命令

cargo-wix:创建Windows安装程序的cargo子命令 它使用二进制项目的发行版中的构建Windows安装程序(msi)。 如果可以使用提供的应用程序提供代码签名证书,则它还支持对Windows安装程序进行签名。 快速开始 启动命令提示符(cmd.exe),然后执行以下命令: C:\>cargo install cargo-wix C:\>cd Path\To\Project C:\Path\To\Project\>cargo wix init C:\Path\To\Project\>cargo wix 该项目的Windows安装程序(msi)将位于C:\Path\To\Project\target\wix文件夹中。 官方文档 https://crates.io/crates/cargo-wix

2021/6/22
articleCard.readMore

用 Deskreen 将你的 Linux 屏幕镜像或串流到任何设备上

如果你有多显示器设置,你会意识到拥有多个屏幕的好处。而且,有了 Deskreen,你可以把任何设备变成你的副屏。 Deskreen:将任何设备变成你的 Linux 系统的副屏 Deskreen 是一个自由开源的应用,可以让你使用任何带有 Web 浏览器的设备来作为电脑的副屏。 如果你愿意,它还支持多个设备连接。 Deskreen 很容易使用,当你的所有设备都连接到同一个 Wi-Fi 网络时,它可以正常工作。 让我们来看看它的功能和工作原理。 Deskreen 的功能 Deskreen 的功能包括以下要点: 分享整个屏幕的能力 选择一个特定的应用窗口进行串流 翻转模式,将你的屏幕作为提词器使用 支持多种设备 高级视频质量设置 提供端对端加密 最小的系统要求 黑暗模式 没有一个冗长的功能列表,但对大多数用户来说应该是足够的。 如何使用 Deskreen 应用? Deskreen 使用分为三个简单的步骤,让我为你强调一下,以便你开始使用: 首先,当你启动该应用时,它会显示一个二维码和一个 IP 地址,以帮助你用 Web 浏览器连接其他设备,以串流你的屏幕。 你可以按你喜欢的方式,在你的辅助设备上的 Web 浏览器的帮助下建立连接。 当你扫描二维码或在浏览器的地址栏中输入 IP 地址,你会在 Deskreen 应用上得到一个提示,允许或拒绝连接。除非是你不认识它,否则就允许吧。 接下来,你将被要求选择你想要串流的内容(你的整个屏幕或特定的应用窗口): 你可以选择串流整个屏幕或选择你想串流的窗口。然而,并不是每个应用窗口都能被检测到。 你只需要选择源并确认,就可以了。你应该注意到它在你的副屏(手机/桌面)上开始串流。 Deskreen 还为你提供了管理连接设备的能力。因此,如果你需要断开任何会话或所有会话的连接,你可以从设置中进行操作。 在 Linux 中安装 Deskreen 你会找到一个用于 Linux 机器的 DEB 包和 AppImage 文件。如果你不知道,可以通过我们的 安装 DEB 包 和 使用 AppImage 文件 指南来安装它。 你可以从 官方网站 下载它,或者从它的 GitHub 页面探索更多的信息。 结束语 考虑到它使用 Wi-Fi 网络工作,在串流方面绝对没有问题。这是一种奇妙的方式,可以与别人分享你的屏幕,或者出于任何目的将其串流到第二个设备上。 当然,它不能取代你的电脑的第二个显示器的优势,但在一些使用情况下,你可能不需要第二个屏幕。 参考 With Deskreen, You Can Mirror or Stream Your Linux Computer Screen to Any Device [1] 用 Deskreen 将你的 Linux 屏幕镜像或串流到任何设备上 [2]

2021/6/21
articleCard.readMore

为你营造专注冥想的时间-The Zen Zone

网站为你营造了专注冥想的时间,通过三个简单的小游戏,你可以进入一个精神集中的状态,从而让大脑思维平静下来! 传送门 https://thezen.zone/ 方法 进入网站直接开始冥想吧!

2021/6/21
articleCard.readMore

全球高清实况摄像头-Skylinewebcams

网站收录了全球范围内的公开实况摄像头,你可以看到世界各地的文化遗产、城市风光、人气景点! 传送门 https://www.skylinewebcams.com/ 方法 网站支持中文,进入后直接观看!

2021/6/14
articleCard.readMore

Linux 常用命令

Linux 常用命令 日期 $(date -d '1 day ago' '+%Y-%m-%d') 常用方法 数字格式化 part=`printf "%03d" $i` # 左补0 删除旧文件 # 找出5天前修改的文件名以.tar结尾的文件进行删除 find /www/backup -mtime +5 -name "*.tar" |xargs rm for循环 for ((i=0;i<10;i++)) do _date=$(date +%Y-%m-%d -d "${i} day") echo $_date done # for i in {1..10} do echo $i done 文件合并 find ./ -name "item*" | xargs sed 'a\' > all.txt find ./ -name "item*" | xargs cat > all.txt :s/old/new #替换当前行的第一个old为new :s/old/new/g #替换当前行的所有的old为new :.,$s/old/new #替换当前行到最后行的第一个old为new :.,$s/old/new/g #替换当前行到最后行的所有old为new :N,Ms/old/new #替换第N行到第M行的第一个old为new :N,Ms/old/new/g #替换第N行到第M行的所有old为new :N,Ms/old/new/gc #替换第N行到第M行的所有old为new,且逐一询问是否删除 :%s/old/new #替换所有行的第一个old为new :%s/old/new/g #替换所有行的所有old为new 文件排序、交集、并集、差集 #排序 sort a.txt |uniq -c #一、交集 sort a.txt b.txt | uniq -d #二、并集 sort a.txt b.txt | uniq #三、差集 a.txt-b.txt: sort a.txt b.txt b.txt | uniq -u #差集 b.txt - a.txt: sort b.txt a.txt a.txt | uniq -u 删除重复行 sort -k2n all.txt | uniq > real.out sort -k2n all.txt | awk '{if ($0!=line) print;line=$0}' sort -k2n all.txt | sed '$!N; /^\(.*\)\n\1$/!P; D' 删除空格 cat all.txt |sed s/[[:space:]]//g awk 去重 awk '!($1 in a){a[$1];print $1}' #或 sort $1 | uniq # awk结果使用逗号间隔拼接 awk -F ',' '{print $1}' | xargs | tr ' ' ',' 常用状态查看 # 按CPU和内存倒序前n个进程 ps -aux --sort -pcpu,+pmem | head -n 5 # 按进程名查看 ps -f -C java 文件同步rsync rsync -zvrtopgl --progress --delete /fromDist/ root@s1:/toDist/ 链接状态统计 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' # TCP连接状态详解 # LISTEN: 服务器在侦听来自远方的TCP端口的连接请求 # SYN-SENT: 在发送连接请求后等待匹配的连接请求 # SYN_RECV: 一个连接请求已经到达,等待确认 # SYN-RECEIVED: 再收到和发送一个连接请求后等待对方对连接请求的确认 # ESTABLISHED: 代表一个打开的连接/正常数据传输状态/当前并发连接数 # FIN_WAIT1: 等待远程TCP连接中断请求,或先前的连接中断请求的确认/应用说它已经完成 # FIN_WAIT2: 从远程TCP等待连接中断请求/另一边已同意释放 # CLOSE-WAIT: 等待从本地用户发来的连接中断请求 # CLOSING: 等待远程TCP对连接中断的确认/两边同时尝试关闭 # LAST-ACK: 等待原来的发向远程TCP的连接中断请求的确认/等待所有分组死掉 # TIME-WAIT: 等待足够的时间以确保远程TCP接收到连接中断请求的确认/另一边已初始化一个释放 # ITMED_WAIT: 等待所有分组死掉 # CLOSED: 没有任何连接状态 CPU/内存/系统信息查看 # cpu grep "model name" /proc/cpuinfo cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c cat /proc/cpuinfo | grep physical | uniq -c # cpu位数 echo $HOSTTYPE # 内存 grep MemTotal /proc/meminfo # linux 版本 cat /etc/redhat-release cat /etc/os-release cat /etc/lsb-release # linux 内核版本 uname -a uname -r 常用监控工具 # 网络监控 iftop # IO监控 iotop # 负载监控 htop top 进程监控 pidstat -p 843 1 3 -u -t # -u:代表对 CPU 使用率的监控 # 参数 1 3 代表每秒采样一次,一共三次 # -t:将监控级别细化到线程 ssh相关 # 秘钥生成 ssh-keygen -t rsa -b 4096 -C "your_hostname" # 免密登录 cat ~/.ssh/id_rsa.pub | ssh root@ip "cat >> .ssh/authorized_keys" firewalld防火墙使用 # 禁止ping firewall-cmd --permanent --add-rich-rule='rule protocol value=icmp drop' # 允许192.168.1.0/24主机所有连接 firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0" accept' # 禁止某IP访问 firewall-cmd --permanent --zone=public --add-rich-rule="rule family=ipv4 source address='123.56.247.76/24' reject" # 开放端口 firewall-cmd --zone=public --permanent --add-port=8080/tcp firewall-cmd --reload 文件统计 ls -g |awk 'BEGIN{sum=0}{sum+=$4}END{print sum/(1024*1024*1024)}' history格式及数量修改 export HISTSIZE=10000 export HISTTIMEFORMAT=" %Y-%m-%d %H:%M:%S - `who am i 2>/dev/null | awk '{print $NF}'|sed -e 's/[()]//g'` - `who -u am i |awk '{print $1}'` " export PROMPT_COMMAND="history 1 >> /var/log/.myhistory" #将命令记录输出到文本中 touch /var/log/.myhistory chmod /var/log/.myhistory

2021/6/5
articleCard.readMore

awk 分析 nginx 运行日志常用指令

awk 分析 nginx 运行日志常用指令 1.独立IP awk '{print $1}' access.log | sort -r |uniq -c | wc -l 2.统计PV awk '{print $6}' access.log | wc -l 3.查询访问最频繁的URL awk '{print $7}' access.log|sort | uniq -c |sort -n -k 1 -r|more 4.查询访问最频繁的IP awk '{print $1}' access.log|sort | uniq -c |sort -n -k 1 -r|more 5.UV统计: awk '{print $6}' access.log | sort -r |uniq -c |wc -l 6.按小时统计 cat access.log |awk '{print $4}' | awk -F ':' '{print $1,$2}'|uniq -c | awk '{print $2" "$3" "$1}'

2021/6/5
articleCard.readMore

TOML 语言规范 1.0 正式版

👉 TOML 语言广泛用于 Rust 项目当中,cargo new 一个新项目时都会新建一个 toml 的配置文件,所以学习 Rust 过程中也有必要熟悉 TOML 语言的相关语法,从而熟练在 Rust 项目中编辑配置文件。 👉 本文来自 toml-lang 的中文翻译版本,本文收录在此处,只是方便 Rust 学习者统一查阅,若是发现有任何错误或需要完善地方,请在 toml.io 原项目仓库指出或修改错误。 TOML v1.0.0 全称:Tom 的(语义)明显、(配置)最小化的语言。(Tom’s Obvious, Minimal Language) 宗旨 TOML 旨在成为一个语义明显且易于阅读的最小化配置文件格式。 目录 规格 注释 键值对 键名 字符串 整数 浮点数 布尔值 坐标日期时刻 各地日期时刻 各地日期 各地时刻 数组 表 内联表 表数组 文件扩展名 MIME 类型 ABNF 语法 规格 TOML 是大小写敏感的。 TOML 文件必须是合法的 UTF-8 编码的 Unicode 文档。 空白是指制表符(0x09)或空格(0x20)。 换行是指 LF(0x0A)或 CRLF(0x0D0A)。 注释 井字符将该行余下的部分标记为注释,除非它在字符串中。 # 这是一个全行注释 key = "value" # 这是一个行末注释 another = "# 这不是一个注释" 除制表符以外的控制字符(U+0000 至 U+0008,U+000A 至 U+001F,U+007F)不允许出现在注释中。 键值对 TOML 文档最基本的构成区块是键值对。 键名在等号的左边而值在右边。 key = "value" 值必须是下述类型之一。 字符串 整数 浮点数 布尔值 坐标日期时刻 各地日期时刻 各地日期 各地时刻 数组 内联表 不指定值是非法的。 key = # 非法 键值对后必须换行(或结束文件)。 内联表) first = "Tom" last = "Preston-Werner" # 非法 键名 键名可以是裸露的,引号引起来的,或点分隔的。 裸键只能包含 ASCII 字母,ASCII 数字,下划线和短横线(A-Za-z0-9_-)。 1234,但是是被理解为字符串的。 key = "value" bare_key = "value" bare-key = "value" 1234 = "value" 引号键遵循与基本字符串或字面量字符串相同的规则并允许你使用更为广泛的键名。 "127.0.0.1" = "value" "character encoding" = "value" "ʎǝʞ" = "value" 'key2' = "value" 'quoted "value"' = "value" 裸键中不能为空,但空引号键是允许的(虽然不建议如此)。 = "no key name" # 非法 "" = "blank" # 合法但不鼓励 '' = 'blank' # 合法但不鼓励 点分隔键是一系列通过点相连的裸键或引号键。 name = "Orange" physical.color = "orange" physical.shape = "round" site."google.com" = true 等价于 JSON 的如下结构: { "name": "Orange", "physical": { "color": "orange", "shape": "round" }, "site": { "google.com": true } } 有关点分隔键定义表的详细信息,请参阅后文表一节。 点分隔符周围的空白会被忽略。 fruit.name = "banana" # 这是最佳实践 fruit. color = "yellow" # 等同于 fruit.color fruit . flavor = "banana" # 等同于 fruit.flavor 缩进被作为空白对待而被忽略。 多次定义同一个键是非法的。 # 不要这样做 name = "Tom" name = "Pradyun" 注意裸键和引号键是等价的: # 这是不可行的 spelling = "favorite" "spelling" = "favourite" 只要一个键还没有被直接定义过,你就仍可以对它和它下属的键名赋值。 # 这使“fruit”键作为表存在。 fruit.apple.smooth = true # 所以接下来你可以像中这样对“fruit”表添加内容: fruit.orange = 2 # 以下是非法的 # 这将 fruit.apple 的值定义为一个整数。 fruit.apple = 1 # 但接下来这将 fruit.apple 像表一样对待了。 # 整数不能变成表。 fruit.apple.smooth = true 不鼓励跳跃式地定义点分隔键。 # 合法但不鼓励 apple.type = "水果" orange.type = "水果" apple.skin = "薄" orange.skin = "厚" apple.color = "红" orange.color = "橙" # 建议 apple.type = "水果" apple.skin = "薄" apple.color = "红" orange.type = "水果" orange.skin = "厚" orange.color = "红" 由于裸键可以仅由 ASCII 整数组成,所以可能写出看起来像浮点数、但实际上是两部分的点分隔键。 3.14159 = "派" 上面的 TOML 对应以下 JSON。 { "3": { "14159": "派" } } 字符串 共有四种方式来表示字符串:基本字符串、多行基本字符串、字面量和多行字面量。 基本字符串由引号(")包裹。 str = "我是一个字符串。\"你可以把我引起来\"。姓名\tJos\u00E9\n位置\t旧金山。" 为了方便,一些流行的字符有其简便转义写法。 \b - backspace (U+0008) \t - tab (U+0009) \n - linefeed (U+000A) \f - form feed (U+000C) \r - carriage return (U+000D) \" - quote (U+0022) \\ - backslash (U+005C) \uXXXX - unicode (U+XXXX) \UXXXXXXXX - unicode (U+XXXXXXXX) 任何 Unicode 字符都可以用 \uXXXX 或 \UXXXXXXXX 的形式来转义。 标量值。 所有上面未列出的其它转义序列都是保留的;如果用了,TOML 应当产生错误。 有时你需要表示一小篇文本(例如译文)或者想要对非常长的字符串进行折行。 多行基本字符串由三个引号包裹,允许折行。 str1 = """ Roses are red Violets are blue""" TOML 解析器可以相对灵活地解析成对所在平台有效的换行字符。 # 在 Unix 系统,上面的多行字符串可能等同于: str2 = "Roses are red\nViolets are blue" # 在 Windows 系统,它可能等价于: str3 = "Roses are red\r\nViolets are blue" 想书写长字符串却不想引入无关空白,可以用“行末反斜杠”。 \ 时,它会连同它后面的所有空白(包括换行)一起被去除,直到下一个非空白字符或结束引号为止。 # 下列字符串的每一个字节都完全相同: str1 = "The quick brown fox jumps over the lazy dog." str2 = """ The quick brown \ fox jumps over \ the lazy dog.""" str3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ """ 任何 Unicode 字符都可以使用,除了那些必须被转义的:反斜杠和除制表符、换行符、回车符外的控制字符(U+0000 至 U+0008,U+000B,U+000C,U+000E 至 U+001F,U+007F)。 你可以在多行基本字符串内的任何地方写一个引号或两个毗连的引号。 str4 = """这有两个引号:""。够简单。""" # str5 = """这有两个引号:"""。""" # 非法 str5 = """这有三个引号:""\"。""" str6 = """这有十五个引号:""\"""\"""\"""\"""\"。""" # "这,"她说,"只是个无意义的条款。" str7 = """"这,"她说,"只是个无意义的条款。"""" 如果你常常要指定 Windows 路径或正则表达式,那么必须转义反斜杠就马上成为啰嗦而易错的了。 字面量字符串由单引号包裹。 # 所见即所得。 winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' regex = '<\i\c*\s*>' 由于没有转义,无法在由单引号包裹的字面量字符串中写入单引号。 多行字面量字符串两侧各有三个单引号来包裹,允许换行。 regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' 原始字符串中的 第一个换行被剔除了。 所有其它空白 都保留了。 ''' 你可以在多行字面量字符串中的任何位置写一个或两个单引号,但三个以上的单引号序列不可以。 quot15 = '''这有十五个引号:"""""""""""""""''' # apos15 = '''这有十五个撇号:'''''''''''''''''' # 非法 apos15 = "这有十五个撇号:'''''''''''''''" # '那,'她说,'仍然没有意义。' str = ''''那,'她说,'仍然没有意义。'''' 除制表符以外的所有控制字符都不允许出现在字面量字符串中。 整数 整数是纯数字。 int1 = +99 int2 = 42 int3 = 0 int4 = -17 对于大数,你可以在数字之间用下划线来增强可读性。 int5 = 1_000 int6 = 5_349_221 int7 = 53_49_221 # 印度记数体系分组 int8 = 1_2_3_4_5 # 合法但不鼓励 前导零是不允许的。 -0 与 +0 是有效的,并等同于无前缀的零。 非负整数值也可以用十六进制、八进制或二进制来表示。 + 不被允许,而(前缀后的)前导零是允许的。 # 带有 `0x` 前缀的十六进制 hex1 = 0xDEADBEEF hex2 = 0xdeadbeef hex3 = 0xdead_beef # 带有 `0o` 前缀的八进制 oct1 = 0o01234567 oct2 = 0o755 # 对于表示 Unix 文件权限很有用 # 带有 `0b` 前缀的二进制 bin1 = 0b11010110 任何 64 位有符号整数(从 −2^63 到 2^63−1)都应当被接受并无损处理。 浮点数 浮点数应当被实现为 IEEE 754 binary64 值。 一个浮点数由一个整数部分(遵从与十进制整数值相同的规则)后跟上一个小数部分和/或一个指数部分组成。 # 小数 flt1 = +1.0 flt2 = 3.1415 flt3 = -0.01 # 指数 flt4 = 5e+22 flt5 = 1e06 flt6 = -2E-2 # 都有 flt7 = 6.626e-34 小数部分是一个小数点后跟一个或多个数字。 一个指数部分是一个 E(大小写均可)后跟一个整数部分(遵从与十进制整数值相同的规则,但可以包含前导零)。 小数点,如果有用到的话,每侧必须紧邻至少一个数字。 # 非法的浮点数 invalid_float_1 = .7 invalid_float_2 = 7. invalid_float_3 = 3.e+20 与整数相似,你可以使用下划线来增强可读性。 flt8 = 224_617.445_991_228 浮点数值 -0.0 与 +0.0 是有效的,并且应当遵从 IEEE 754。 特殊浮点值也能够表示。 # 无穷 sf1 = inf # 正无穷 sf2 = +inf # 正无穷 sf3 = -inf # 负无穷 # 非数 sf4 = nan # 实际上对应信号非数码还是静默非数码,取决于实现 sf5 = +nan # 等同于 `nan` sf6 = -nan # 有效,实际码取决于实现 布尔值 布尔值就是你所惯用的那样。 bool1 = true bool2 = false 坐标日期时刻 要准确地表示世上的一个特定时间,你可以使用指定了时区偏移量的 RFC 3339 格式的日期时刻。 odt1 = 1979-05-27T07:32:00Z odt2 = 1979-05-27T00:32:00-07:00 odt3 = 1979-05-27T00:32:00.999999-07:00 出于可读性的目的,你可以用一个空格字符替代日期和时刻之间的 T(RFC 3339 的第 5.6 节中允许了这样做)。 odt4 = 1979-05-27 07:32:00Z 毫秒级的精度是必须的。 各地日期时刻 如果你省略了 RFC 3339 日期时刻中的时区偏移量,这表示该日期时刻的使用并不涉及时区偏移。 ldt1 = 1979-05-27T07:32:00 ldt2 = 1979-05-27T00:32:00.999999 毫秒级的精度是必须的。 各地日期 如果你只写了 RFC 3339 日期时刻中的日期部分,那它表示一整天,同时也不涉及时区偏移。 ld1 = 1979-05-27 各地时刻 如果你只写了 RFC 3339 日期时刻中的时刻部分,它将只表示一天之中的那个时刻,而与任何特定的日期无关、亦不涉及时区偏移。 lt1 = 07:32:00 lt2 = 00:32:00.999999 毫秒级的精度是必须的。 数组 数组是内含值的方括号。 integers = [ 1, 2, 3 ] colors = [ "红", "黄", "绿" ] nested_array_of_ints = [ [ 1, 2 ], [3, 4, 5] ] nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] string_array = [ "所有的", '字符串', """是相同的""", '''类型''' ] # 允许混合类型的数组 numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] contributors = [ "Foo Bar <foo@example.com>", { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } ] 数组可以跨行。 integers2 = [ 1, 2, 3 ] integers3 = [ 1, 2, # 这是可以的 ] 表 表(也被称为哈希表或字典)是键值对的集合。 [table] 在它下方,直至下一个表头或文件结束,都是这个表的键值对。 [table-1] key1 = "some string" key2 = 123 [table-2] key1 = "another string" key2 = 456 表名的规则与键名相同(见前文键名定义)。 [dog."tater.man"] type.name = "pug" 等价于 JSON 的如下结构: { "dog": { "tater.man": { "type": { "name": "pug" } } } } 键名周围的空格会被忽略。 [a.b.c] # 这是最佳实践 [ d.e.f ] # 等同于 [d.e.f] [ g . h . i ] # 等同于 [g.h.i] [ j . "ʞ" . 'l' ] # 等同于 [j."ʞ".'l'] 缩进被作为空白对待而被忽略。 你不必层层完整地写出你不想写的所有途径的父表。 # [x] 你 # [x.y] 不 # [x.y.z] 需要这些 [x.y.z.w] # 来让这生效 [x] # 后置父表定义是可以的 空表是允许的,只要里面没有键值对就行了。 类似于键名,你不能重复定义一个表。 # 不要这样做 [fruit] apple = "红" [fruit] orange = "橙" # 也不要这样做 [fruit] apple = "红" [fruit.apple] texture = "光滑" 不鼓励无序地定义表。 # 有效但不鼓励 [fruit.apple] [animal] [fruit.orange] # 推荐 [fruit.apple] [fruit.orange] [animal] 顶层表,又被称为根表,于文档开始处开始并在第一个表头(或文件结束处)前结束。 # 顶层表开始。 name = "Fido" breed = "pug" # 顶层表结束。 [owner] name = "Regina Dogman" member_since = 1999-08-04 点分隔键为最后一个键名前的每个键名创建并定义一个表,倘若这些表尚未被创建的话。 fruit.apple.color = "red" # 定义一个名为 fruit 的表 # 定义一个名为 fruit.apple 的表 fruit.apple.taste.sweet = true # 定义一个名为 fruit.apple.taste 的表 # fruit 和 fruit.apple 已经创建过了 由于表不能定义多于一次,不允许使用 [table] 头重定义这样的表。 [table] 形式定义过的表也是不允许的。 [table] 形式可以被用来定义通过点分隔键定义的表中的子表。 [fruit] apple.color = "红" apple.taste.sweet = true # [fruit.apple] # 非法 # [fruit.apple.taste] # 非法 [fruit.apple.texture] # 你可以添加子表 smooth = true 内联表 内联表提供了一种更为紧凑的语法来表示表。 { 和 }。 内联表得出现在同一行内。 name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } animal = { type.name = "pug" } 上述内联表等同于下面的标准表定义: [name] first = "Tom" last = "Preston-Werner" [point] x = 1 y = 2 [animal] type.name = "pug" 内联表是独立自足的,在内部定义全部的键与子表。 [product] type = { name = "Nail" } # type.edible = false # 非法 类似地,内联表不能被用于向一个已定义的表添加键或子表。 [product] type.name = "Nail" # type = { edible = false } # 非法 表数组 最后一个还没讲到的语法允许你写表数组。 [[products]] name = "Hammer" sku = 738594937 [[products]] # 数组里的空表 [[products]] name = "Nail" sku = 284758393 color = "gray" 等价于 JSON 的如下结构。 { "products": [ { "name": "Hammer", "sku": 738594937 }, { }, { "name": "Nail", "sku": 284758393, "color": "gray" } ] } 任何对表数组的引用都指向该数组里最近定义的表元素。 [[fruits]] name = "apple" [fruits.physical] # 子表 color = "red" shape = "round" [[fruits.varieties]] # 嵌套表数组 name = "red delicious" [[fruits.varieties]] name = "granny smith" [[fruits]] name = "banana" [[fruits.varieties]] name = "plantain" 上述 TOML 等价于 JSON 的如下结构。 { "fruits": [ { "name": "apple", "physical": { "color": "red", "shape": "round" }, "varieties": [ { "name": "red delicious" }, { "name": "granny smith" } ] }, { "name": "banana", "varieties": [ { "name": "plantain" } ] } ] } 如果一个表或表数组的父级是一个数组元素,该元素必须在定义子级前先定义。 # 非法的 TOML 文档 [fruit.physical] # 子表,但它应该隶属于哪个父元素? color = "red" shape = "round" [[fruit]] # 解析器必须在发现“fruit”是数组而非表时抛出错误 name = "apple" 若试图向一个静态定义的数组追加内容,即便数组尚且为空,也必须在解析时报错。 # 非法的 TOML 文档 fruits = [] [[fruits]] # 不允许 若试图用已经确定为数组的名称定义表,必须在解析时报错。 # 非法的 TOML 文档 [[fruits]] name = "apple" [[fruits.varieties]] name = "red delicious" # 非法:该表与之前的表数组相冲突 [fruits.varieties] name = "granny smith" [fruits.physical] color = "red" shape = "round" # 非法:该表数组与之前的表相冲突 [[fruits.physical]] color = "green" 你也可以适当使用内联表: points = [ { x = 1, y = 2, z = 3 }, { x = 7, y = 8, z = 9 }, { x = 2, y = 4, z = 8 } ] 文件扩展名 TOML 文件应当使用 .toml 扩展名。 MIME 类型 在互联网上传输 TOML 文件时,恰当的 MIME 类型是 application/toml。 ABNF 语法 TOML 语法的严谨说明,由一个 ABNF 文件另行提供。

2021/6/5
articleCard.readMore

塔防游戏-YORG.io

塔防游戏 可玩性很高的一款塔防游戏,需要各类元素合成,通过传送带传送宝石、木材、铁等元素,最后合成弓箭、大炮、闪电塔等来抵御怪兽的袭击,一玩就停不下来。 传送门 https://yorg.io/ 玩法 通过传送带传送宝石、木材、铁等元素,最后合成弓箭、大炮、闪电塔等来抵御怪兽的袭击

2021/6/2
articleCard.readMore

网页版红色警戒2-Red Alert 2: Chrono Divide

在线红警 又是一波人的集体回忆。。。网站高度还原了经典游戏——红色警戒2,原汁原味的画面和音效,仿佛又回到了那个电脑房的年代! 传送门 https://game.chronodivide.com/ 玩法 网站支持多人在线对战,注册登陆后即可开始!

2021/6/2
articleCard.readMore

Goproxy

Go Module代理仓库服务 七牛云提供的:https://goproxy.cn goproxy.io:https://goproxy.io 或 https://proxy.golang.com.cn 百度云BOS提供的:https://goproxy.bj.bcebos.com/ 阿里云提供的:https://mirrors.aliyun.com/goproxy/ 下载 Go 镜像(Golang Downloads Mirrors) 官网:https://go.dev/ 下载 Go 镜像:https://golang.google.cn/ 下载 Go 镜像:https://gomirrors.org/ 下载 Go 镜像:https://studygolang.com/dl

2021/5/27
articleCard.readMore

使用开源工具进行 Linux 内存取证

利用 Volatility 找出应用程序、网络连接、内核模块、文件等方面的情况。 计算机的操作系统和应用使用主内存(RAM)来执行不同的任务。这种易失性内存包含大量关于运行应用、网络连接、内核模块、打开的文件以及几乎所有其他的内容信息,但这些信息每次计算机重启的时候都会被清除。 内存取证是一种从内存中找到和抽取这些有价值的信息的方式。Volatility 是一种使用插件来处理这类信息的开源工具。但是,存在一个问题:在你处理这些信息前,必须将物理内存转储到一个文件中,而 Volatility 没有这种能力。 因此,这篇文章分为两部分: 第一部分是处理获取物理内存并将其转储到一个文件中。 第二部分使用 Volatility 从这个内存转储中读取并处理这些信息。 我在本教程中使用了以下测试系统,不过它可以在任何 Linux 发行版上工作: $ cat /etc/redhat-release Red Hat Enterprise Linux release 8.3 (Ootpa) $ $ uname -r 4.18.0-240.el8.x86_64 $ 注意事项: 部分 1 涉及到编译和加载一个内核模块。不要担心:它并不像听起来那么困难。 一些指南: 按照以下的步骤。 不要在生产系统或你的主要计算机上尝试任何这些步骤。 始终使用测试的虚拟机(VM)来尝试,直到你熟悉使用这些工具并理解它们的工作原理为止。 安装需要的包 在开始之前安装必要的工具。如果你经常使用基于 Debian 的发行版,可以使用 apt-get 命令。这些包大多数提供了需要的内核信息和工具来编译代码: $ yum install kernel-headers kernel-devel gcc elfutils-libelf-devel make git libdwarf-tools python2-devel.x86_64-y 部分 1:使用 LiME 获取内存并将其转储到一个文件中 在开始分析内存之前,你需要一个内存转储供你使用。在实际的取证活动中,这可能来自一个被破坏或者被入侵的系统。这些信息通常会被收集和存储来分析入侵是如何发生的及其影响。由于你可能没有可用的内存转储,你可以获取你的测试 VM 的内存转储,并使用它来执行内存取证。 Linux 内存提取器(LiME)是一个在 Linux 系统上获取内存很常用的工具。使用以下命令获得 LiME: $ git clone https://github.com/504ensicsLabs/LiME.git $ $ cd LiME/src/ $ $ ls deflate.c disk.c hash.c lime.h main.c Makefile Makefile.sample tcp.c $ 构建 LiME 内核模块 在 src 文件夹下运行 make 命令。这会创建一个以 .ko 为扩展名的内核模块。理想情况下,在 make 结束时,lime.ko 文件会使用格式 lime-<your-kernel-version>.ko 被重命名。 $ make make -C /lib/modules/4.18.0-240.el8.x86_64/build M="/root/LiME/src" modules make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64' << 删节 >> make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64' strip --strip-unneeded lime.ko mv lime.ko lime-4.18.0-240.el8.x86_64.ko $ $ $ ls -l lime-4.18.0-240.el8.x86_64.ko -rw-r--r--. 1 root root 25696 Apr 17 14:45 lime-4.18.0-240.el8.x86_64.ko $ $ file lime-4.18.0-240.el8.x86_64.ko lime-4.18.0-240.el8.x86_64.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=1d0b5cf932389000d960a7e6b57c428b8e46c9cf, not stripped $ 加载LiME 内核模块 现在是时候加载内核模块来获取系统内存了。insmod 命令会帮助加载内核模块;模块一旦被加载,会在你的系统上读取主内存(RAM)并且将内存的内容转储到命令行所提供的 path 目录下的文件中。另一个重要的参数是 format;保持 lime 的格式,如下所示。在插入内核模块之后,使用 lsmod 命令验证它是否真的被加载。 $ lsmod | grep lime $ $ insmod ./lime-4.18.0-240.el8.x86_64.ko "path=../RHEL8.3_64bit.mem format=lime" $ $ lsmod | grep lime lime 16384 0 $ 你应该看到给 path 命令的文件已经创建好了,而且文件大小与你系统的物理内存(RAM)大小相同(并不奇怪)。一旦你有了内存转储,你就可以使用 rmmod 命令删除该内核模块: $ $ ls -l ~/LiME/RHEL8.3_64bit.mem -r--r--r--. 1 root root 4294544480 Apr 17 14:47 /root/LiME/RHEL8.3_64bit.mem $ $ du -sh ~/LiME/RHEL8.3_64bit.mem 4.0G /root/LiME/RHEL8.3_64bit.mem $ $ free -m total used free shared buff/cache available Mem: 3736 220 366 8 3149 3259 Swap: 4059 8 4051 $ $ rmmod lime $ $ lsmod | grep lime $ 内存转储中是什么? 这个内存转储文件只是原始数据,就像使用 file 命令可以看到的一样。你不可能通过手动去理解它;是的,在这里边有一些 ASCII 字符,但是你无法用编辑器打开这个文件并把它读出来。hexdump 的输出显示,最初的几个字节是 EmiL;这是因为你的请求格式在上面的命令行中是 lime: $ file ~/LiME/RHEL8.3_64bit.mem /root/LiME/RHEL8.3_64bit.mem: data $ $ hexdump -C ~/LiME/RHEL8.3_64bit.mem | head 00000000 45 4d 69 4c 01 00 00 00 00 10 00 00 00 00 00 00 |EMiL............| 00000010 ff fb 09 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 b8 fe 4c cd 21 44 00 32 20 00 00 2a 2a 2a 2a 2a |..L.!D.2 ..*****| 00000030 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************| 00000040 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 20 00 20 |************* . | 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000080 00 00 00 00 00 00 00 00 00 00 00 00 70 78 65 6c |............pxel| 00000090 69 6e 75 78 2e 30 00 00 00 00 00 00 00 00 00 00 |inux.0..........| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| $ 部分 2:获得 Volatility 并使用它来分析你的内存转储 现在你有了要分析的示例内存转储,使用下面的命令获取 Volatility 软件。Volatility 已经用 Python 3 重写了,但是本教程使用的是用 Python 2 写的原始的 Volatility 包。如果你想用 Volatility 3 进行实验,可以从合适的 Git 仓库下载它,并在以下命令中使用 Python 3 而不是 Python 2: $ git clone https://github.com/volatilityfoundation/volatility.git $ $ cd volatility/ $ $ ls AUTHORS.txt contrib LEGAL.txt Makefile PKG-INFO pyinstaller.spec resources tools vol.py CHANGELOG.txt CREDITS.txt LICENSE.txt MANIFEST.in pyinstaller README.txt setup.py volatility $ Volatility 使用两个 Python 库来实现某些功能,所以使用以下命令来安装它们。否则,在你运行 Volatility 工具时,你可能看到一些导入错误;你可以忽略它们,除非你正在运行的插件需要这些库;这种情况下,工具将会报错: $ pip2 install pycrypto $ pip2 install distorm3 列出 Volatility 的 Linux 配置文件 你将要运行的第一个 Volatility 命令列出了可用的 Linux 配置文件,运行 Volatility 命令的主要入口点是 vol.py 脚本。使用 Python 2 解释器调用它并提供 –info 选项。为了缩小输出,查找以 Linux 开头的字符串。正如你所看到的,并没有很多 Linux 配置文件被列出: $ python2 vol.py --info | grep ^Linux Volatility Foundation Volatility Framework 2.6.1 LinuxAMD64PagedMemory - Linux-specific AMD 64-bit address space. $ 构建你自己的 Linux 配置文件 Linux 发行版是多种多样的,并且是为不同架构而构建的。这就是为什么配置文件是必要的 —— Volatility 在提取信息前必须知道内存转储是从哪个系统和架构获得的。有一些 Volatility 命令可以找到这些信息;但是这个方法很费时。为了加快速度,可以使用以下命令构建一个自定义的 Linux 配置文件: 移动到 Volatility 仓库的 tools/linux目录下,运行 make 命令: $ cd tools/linux/ $ $ pwd /root/volatility/tools/linux $ $ ls kcore Makefile Makefile.enterprise module.c $ $ make make -C //lib/modules/4.18.0-240.el8.x86_64/build CONFIG_DEBUG_INFO=y M="/root/volatility/tools/linux" modules make[1]: Entering directory '/usr/src/kernels/4.18.0-240.el8.x86_64' << 删节 >> make[1]: Leaving directory '/usr/src/kernels/4.18.0-240.el8.x86_64' $ 你应该看到一个新的 module.dwarf 文件。你也需要 /boot 目录下的 System.map 文件,因为它包含了所有与当前运行的内核相关的符号: $ ls kcore Makefile Makefile.enterprise module.c module.dwarf $ $ ls -l module.dwarf -rw-r--r--. 1 root root 3987904 Apr 17 15:17 module.dwarf $ $ ls -l /boot/System.map-4.18.0-240.el8.x86_64 -rw-------. 1 root root 4032815 Sep 23 2020 /boot/System.map-4.18.0-240.el8.x86_64 $ $ 要创建一个自定义配置文件,移动回到 Volatility 目录并且运行下面的命令。第一个参数提供了一个自定义 .zip 文件,文件名是你自己命名的。我经常使用操作系统和内核版本来命名。下一个参数是前边创建的 module.dwarf 文件,最后一个参数是 /boot 目录下的 System.map 文件: $ $ cd volatility/ $ $ zip volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip tools/linux/module.dwarf /boot/System.map-4.18.0-240.el8.x86_64 adding: tools/linux/module.dwarf (deflated 91%) adding: boot/System.map-4.18.0-240.el8.x86_64 (deflated 79%) $ 现在自定义配置文件就准备好了,所以在前边给出的位置检查一下 .zip 文件是否被创建好。如果你想知道 Volatility 是否检测到这个自定义配置文件,再一次运行 –info 命令。现在,你应该可以在下边的列出的内容中看到新的配置文件: $ $ ls -l volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip -rw-r--r--. 1 root root 1190360 Apr 17 15:20 volatility/plugins/overlays/linux/Redhat8.3_4.18.0-240.zip $ $ $ python2 vol.py --info | grep Redhat Volatility Foundation Volatility Framework 2.6.1 LinuxRedhat8_3_4_18_0-240x64 - A Profile for Linux Redhat8.3_4.18.0-240 x64 $ $ 开始使用 Volatility 现在你已经准备好去做一些真正的内存取证了。记住,Volatility 是由自定义的插件组成的,你可以针对内存转储来获得信息。命令的通用格式是: python2 vol.py -f <memory-dump-file-taken-by-Lime> <plugin-name> --profile=<name-of-our-custom-profile> 有了这些信息,运行 linux_banner 插件来看看你是否可从内存转储中识别正确的发行版信息: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_banner --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 Linux version 4.18.0-240.el8.x86_64 ([mockbuild@vm09.test.com][4]) (gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC)) #1 SMP Wed Sep 23 05:13:10 EDT 2020 $ 找到 Linux 插件 到现在都很顺利,所以现在你可能对如何找到所有 Linux 插件的名字比较好奇。有一个简单的技巧:运行 –info 命令并抓取 linux_ 字符串。有各种各样的插件可用于不同的用途。这里列出一部分: $ python2 vol.py --info | grep linux_ Volatility Foundation Volatility Framework 2.6.1 linux_apihooks - Checks for userland apihooks linux_arp - Print the ARP table linux_aslr_shift - Automatically detect the Linux ASLR shift << 删节 >> linux_banner - Prints the Linux banner information linux_vma_cache - Gather VMAs from the vm_area_struct cache linux_volshell - Shell in the memory image linux_yarascan - A shell in the Linux memory image $ 使用 linux_psaux 插件检查内存转储时系统上正在运行哪些进程。注意列表中的最后一个命令:它是你在转储之前运行的 insmod 命令。 $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_psaux --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 Pid Uid Gid Arguments 1 0 0 /usr/lib/systemd/systemd --switched-root --system --deserialize 18 2 0 0 [kthreadd] 3 0 0 [rcu_gp] 4 0 0 [rcu_par_gp] 861 0 0 /usr/libexec/platform-python -Es /usr/sbin/tuned -l -P 869 0 0 /usr/bin/rhsmcertd 875 0 0 /usr/libexec/sssd/sssd_be --domain implicit_files --uid 0 --gid 0 --logger=files 878 0 0 /usr/libexec/sssd/sssd_nss --uid 0 --gid 0 --logger=files << 删节 >> 11064 89 89 qmgr -l -t unix -u 227148 0 0 [kworker/0:0] 227298 0 0 -bash 227374 0 0 [kworker/u2:1] 227375 0 0 [kworker/0:2] 227884 0 0 [kworker/0:3] 228573 0 0 insmod ./lime-4.18.0-240.el8.x86_64.ko path=../RHEL8.3_64bit.mem format=lime 228576 0 0 $ 想要知道系统的网络状态吗?运行 linux_netstat 插件来找到在内存转储期间网络连接的状态: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_netstat --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 UNIX 18113 systemd/1 /run/systemd/private UNIX 11411 systemd/1 /run/systemd/notify UNIX 11413 systemd/1 /run/systemd/cgroups-agent UNIX 11415 systemd/1 UNIX 11416 systemd/1 << 删节 >> $ 接下来,使用 linux_mount 插件来看在内存转储期间哪些文件系统被挂载: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_mount --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec cgroup /sys/fs/cgroup/pids cgroup rw,relatime,nosuid,nodev,noexec systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime /dev/mapper/rhel_kvm--03--guest11-root / xfs rw,relatime tmpfs /dev/shm tmpfs rw,nosuid,nodev selinuxfs /sys/fs/selinux selinuxfs rw,relatime << 删节 >> cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,relatime,nosuid,nodev,noexec cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,relatime,nosuid,nodev,noexec bpf /sys/fs/bpf bpf rw,relatime,nosuid,nodev,noexec cgroup /sys/fs/cgroup/memory cgroup ro,relatime,nosuid,nodev,noexec cgroup /sys/fs/cgroup/cpuset cgroup rw,relatime,nosuid,nodev,noexec mqueue /dev/mqueue mqueue rw,relatime $ 好奇哪些内核模块被加载了吗?Volatility 也为这个提供了一个插件 linux_lsmod: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsmod --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 ffffffffc0535040 lime 20480 ffffffffc0530540 binfmt_misc 20480 ffffffffc05e8040 sunrpc 479232 << 删节 >> ffffffffc04f9540 nfit 65536 ffffffffc0266280 dm_mirror 28672 ffffffffc025e040 dm_region_hash 20480 ffffffffc0258180 dm_log 20480 ffffffffc024bbc0 dm_mod 151552 $ 想知道哪些文件被哪些进程打开了吗?使用 linux_bash 插件可以列出这些信息: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_bash --profile=LinuxRedhat8_3_4_18_0-240x64 -v Volatility Foundation Volatility Framework 2.6.1 Pid Name Command Time Command -------- -------------------- ------------------------------ ------- 227221 bash 2021-04-17 18:38:24 UTC+0000 lsmod 227221 bash 2021-04-17 18:38:24 UTC+0000 rm -f .log 227221 bash 2021-04-17 18:38:24 UTC+0000 ls -l /etc/zzz 227221 bash 2021-04-17 18:38:24 UTC+0000 cat ~/.vimrc 227221 bash 2021-04-17 18:38:24 UTC+0000 ls 227221 bash 2021-04-17 18:38:24 UTC+0000 cat /proc/817/cwd 227221 bash 2021-04-17 18:38:24 UTC+0000 ls -l /proc/817/cwd 227221 bash 2021-04-17 18:38:24 UTC+0000 ls /proc/817/ << 删节 >> 227298 bash 2021-04-17 18:40:30 UTC+0000 gcc prt.c 227298 bash 2021-04-17 18:40:30 UTC+0000 ls 227298 bash 2021-04-17 18:40:30 UTC+0000 ./a.out 227298 bash 2021-04-17 18:40:30 UTC+0000 vim prt.c 227298 bash 2021-04-17 18:40:30 UTC+0000 gcc prt.c 227298 bash 2021-04-17 18:40:30 UTC+0000 ./a.out 227298 bash 2021-04-17 18:40:30 UTC+0000 ls $ 想知道哪些文件被哪些进程打开了吗?使用 linux_lsof 插件可以列出这些信息: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_lsof --profile=LinuxRedhat8_3_4_18_0-240x64 Volatility Foundation Volatility Framework 2.6.1 Offset Name Pid FD Path ------------------ ------------------------------ -------- -------- ---- 0xffff9c83fb1e9f40 rsyslogd 71194 0 /dev/null 0xffff9c83fb1e9f40 rsyslogd 71194 1 /dev/null 0xffff9c83fb1e9f40 rsyslogd 71194 2 /dev/null 0xffff9c83fb1e9f40 rsyslogd 71194 3 /dev/urandom 0xffff9c83fb1e9f40 rsyslogd 71194 4 socket:[83565] 0xffff9c83fb1e9f40 rsyslogd 71194 5 /var/log/messages 0xffff9c83fb1e9f40 rsyslogd 71194 6 anon_inode:[9063] 0xffff9c83fb1e9f40 rsyslogd 71194 7 /var/log/secure << 删节 >> 0xffff9c8365761f40 insmod 228573 0 /dev/pts/0 0xffff9c8365761f40 insmod 228573 1 /dev/pts/0 0xffff9c8365761f40 insmod 228573 2 /dev/pts/0 0xffff9c8365761f40 insmod 228573 3 /root/LiME/src/lime-4.18.0-240.el8.x86_64.ko $ 访问 Linux 插件脚本位置 通过读取内存转储和处理这些信息,你可以获得更多的信息。如果你会 Python,并且好奇这些信息是如何被处理的,可以到存储所有插件的目录,选择一个你感兴趣的,并看看 Volatility 是如何获得这些信息的: $ ls volatility/plugins/linux/ apihooks.py common.py kernel_opened_files.py malfind.py psaux.py apihooks.pyc common.pyc kernel_opened_files.pyc malfind.pyc psaux.pyc arp.py cpuinfo.py keyboard_notifiers.py mount_cache.py psenv.py arp.pyc cpuinfo.pyc keyboard_notifiers.pyc mount_cache.pyc psenv.pyc aslr_shift.py dentry_cache.py ld_env.py mount.py pslist_cache.py aslr_shift.pyc dentry_cache.pyc ld_env.pyc mount.pyc pslist_cache.pyc << 删节 >> check_syscall_arm.py __init__.py lsmod.py proc_maps.py tty_check.py check_syscall_arm.pyc __init__.pyc lsmod.pyc proc_maps.pyc tty_check.pyc check_syscall.py iomem.py lsof.py proc_maps_rb.py vma_cache.py check_syscall.pyc iomem.pyc lsof.pyc proc_maps_rb.pyc vma_cache.pyc $ $ 我喜欢 Volatility 的理由是他提供了许多安全插件。这些信息很难手动获取: linux_hidden_modules - Carves memory to find hidden kernel modules linux_malfind - Looks for suspicious process mappings linux_truecrypt_passphrase - Recovers cached Truecrypt passphrases Volatility 也允许你在内存转储中打开一个 shell,所以你可以运行 shell 命令来代替上面所有命令,并获得相同的信息: $ python2 vol.py -f ~/LiME/RHEL8.3_64bit.mem linux_volshell --profile=LinuxRedhat8_3_4_18_0-240x64 -v Volatility Foundation Volatility Framework 2.6.1 Current context: process systemd, pid=1 DTB=0x1042dc000 Welcome to volshell! Current memory image is: file:///root/LiME/RHEL8.3_64bit.mem To get help, type 'hh()' >>> >>> sc() Current context: process systemd, pid=1 DTB=0x1042dc000 >>> 接下来的步骤 内存转储是了解 Linux 内部情况的好方法。试一试 Volatility 的所有插件,并详细研究它们的输出。然后思考这些信息如何能够帮助你识别入侵或安全问题。深入了解这些插件的工作原理,甚至尝试改进它们。如果你没有找到你想做的事情的插件,那就写一个并提交给 Volatility,这样其他人也可以使用它。 参考 使用开源工具进行 Linux 内存取证 [1] Perform Linux memory forensics with this open source tool [2]

2021/5/26
articleCard.readMore

使用 sed 命令进行复制、剪切和粘贴

很少有 Unix 命令像 sed、grep 和 awk 一样出名,它们经常组合在一起,可能是因为它们具有奇怪的名称和强大的文本解析能力。它们还在一些语法和逻辑上有相似之处。虽然它们都能用于文本解析,但都有其特殊性。本文研究 sed 命令,它是一个 流编辑器。 安装 sed 如果你使用的是 Linux、BSD 或 macOS,那么它们已经安装了 GNU 的或 BSD 的 sed。这些是原始 sed 命令的独特重新实现。虽然它们很相似,但也有一些细微的差别。本文已经在 Linux 和 NetBSD 版本上进行了测试,所以你可以使用你的计算机上找到的任何 sed,但是对于 BSD sed,你必须使用短选项(例如 -n 而不是 –quiet)。 GNU sed 通常被认为是功能最丰富的 sed,因此无论你是否运行 Linux,你可能都想要尝试一下。如果在 Ports 树中找不到 GNU sed(在非 Linux 系统上通常称为 gsed),你可以从 GNU 网站 下载源代码。 安装 GNU sed 的好处是,你可以使用它的额外功能,但是如果需要可移植性,还可以限制它以遵守 sed 的 POSIX 规范。 MacOS 用户可以在 MacPorts 或 Homebrew 上找到 GNU sed。 在 Windows 上,你可以通过 Chocolatey 来 安装 GNU sed。 了解模式空间和保留空间 sed 一次只能处理一行。因为它没有可视化模式,所以会创建一个 模式空间,这是一个内存空间,其中包含来自输入流的当前行(删除了尾部的任何换行符)。填充模式空间后,sed 将执行你的指令。当命令执行完时,sed 将模式空间中的内容打印到输出流,默认是 标准输出,但是可以将输出重定向到文件,甚至使用 --in-place=.bak 选项重定向到同一文件。 然后,循环从下一个输入行再次开始。 为了在遍历文件时提供一点灵活性,sed 还提供了保留空间(有时也称为 保留缓冲区),即 sed 内存中为临时数据存储保留的空间。你可以将保留空间当作剪贴板,实际上,这正是本文所演示的内容:如何使用 sed 复制/剪切和粘贴。 首先,创建一个示例文本文件,其内容如下: Line one Line three Line two 复制数据到保留空间 要将内容放置在 sed 的保留空间,使用 h 或 H 命令。小写的 h 告诉 sed 覆盖保留空间中的当前内容,而大写的 H 告诉 sed 将数据追加到保留空间中已经存在的内容之后。 单独使用,什么都看不到: $ sed --quiet -e '/three/ h' example.txt $ --quiet(缩写为 -n)选项禁止显示所有输出,但 sed 执行了我的搜索需求。在这种情况下,sed 选择包含字符串 three 的任何行,并将其复制到保留空间。我没有告诉 sed 打印任何东西,所以没有输出。 从保留空间复制数据 要了解保留空间,你可以从保留空间复制内容,然后使用 g 命令将其放入模式空间,观察会发生什么: $ sed -n -e '/three/h' -e 'g;p' example.txt Line three Line three 第一个空白行是因为当 sed 第一次复制内容到模式空间时,保留空间为空。 接下来的两行包含 Line three 是因为这是从第二行开始的保留空间。 该命令使用两个唯一的脚本(-e)纯粹是为了帮助提高可读性和组织性。将步骤划分为单独的脚本可能会很有用,但是从技术上讲,以下命令与一个脚本语句一样有效: $ sed -n -e '/three/h ; g ; p' example.txt Line three Line three 将数据追加到模式空间 G 命令会将一个换行符和保留空间的内容添加到模式空间。 $ sed -n -e '/three/h' -e 'G;p' example.txt Line one Line three Line three Line two Line three 此输出的前两行同时包含模式空间(Line one)的内容和空的保留空间。接下来的两行与搜索文本(three)匹配,因此它既包含模式空间又包含保留空间。第三行的保留空间没有变化,因此在模式空间(Line two)的末尾是保留空间(仍然是 Line three)。 用 sed 剪切和粘贴 现在你知道了如何将字符串从模式空间转到保留空间并再次返回,你可以设计一个 sed 脚本来复制、删除,然后在文档中粘贴一行。例如,将示例文件的 Line three 挪至第三行,sed 可以解决这个问题: $ sed -n -e '/three/ h' -e '/three/ d' \ -e '/two/ G;p' example.txt Line one Line two Line three 第一个脚本找到包含字符串 three 的行,并将其从模式空间复制到保留空间,替换当前保留空间中的任何内容。 第二个脚本删除包含字符串 three 的任何行。这样就完成了与文字处理器或文本编辑器中的 剪切 动作等效的功能。 最后一个脚本找到包含字符串 two 的行,并将保留空间的内容_追加_到模式空间,然后打印模式空间。 任务完成。 使用 sed 编写脚本 再说一次,使用单独的脚本语句纯粹是为了视觉和心理上的简单。剪切和粘贴命令作为一个脚本同样有效: $ sed -n -e '/three/ h ; /three/ d ; /two/ G ; p' example.txt Line one Line two Line three 它甚至可以写在一个专门的脚本文件中: #!/usr/bin/sed -nf /three/h /three/d /two/ G p 要运行该脚本,将其加入可执行权限,然后用示例文件尝试: $ chmod +x myscript.sed $ ./myscript.sed example.txt Line one Line two Line three 当然,你需要解析的文本越可预测,则使用 sed 解决问题越容易。发明 sed 操作(例如复制和粘贴)的“配方”通常是不切实际的,因为触发操作的条件可能因文件而异。但是,你对 sed 命令的使用越熟练,就越容易根据需要解析的输入来设计复杂的动作。 重要的事情是识别不同的操作,了解 sed 何时移至下一行,并预测模式和保留空间包含的内容。 sed 很复杂。虽然它只有十几个命令,但它灵活的语法和原生功能意味着它充满了无限的潜力。 参考 How to use the Linux sed command [1] 使用 sed 命令进行复制、剪切和粘贴 [2]

2021/5/24
articleCard.readMore

一杯咖啡

请扫下方二维码打赏一杯咖啡。 多少不重要,1元也是支持

2021/5/19
articleCard.readMore

Swift语法全面解析

Swift介绍 Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。 swift 是一种安全,快速和互动的编程语言。 swift 支持代码预览(playgrounds),这个特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。 Swift 通过采用现代编程模式来避免大量常见编程错误: 变量始终在使用前初始化。 检查数组索引超出范围的错误。 检查整数是否溢出。 可选值确保明确处理 nil 值。 内存被自动管理。 错误处理允许从意外故障控制恢复。 基础部分 常量和变量 声明常量和变量, 常量和变量必须在使用前声明,使用 let 来声明常量,使用 var 来声明变量。 示例: let maximumNumberOfLoginAttempts = 10 var currentLoginAttempt = 0 // 类型注解 var welcomeMessage: String 注释 单行注释双正斜杠(//), 多行注释(/* 多行的 */)。Swift 的多行注释可以嵌套在其它的多行注释之中。 示例: // 这是一个注释 /* 这也是一个注释, 但是是多行的 */ /* 这是第一个多行注释的开头 /* 这是第二个被嵌套的多行注释 */ 这是第一个多行注释的结尾 */ 分号 Swift 并不强制要求你在每条语句的结尾处使用分号(;)。 同一行内写多条独立的语句必须用分号分隔。 let cat = "🐱"; print(cat) // 输出“🐱” 标识符 标识符就是给变量、常量、方法、函数、枚举、结构体、类、协议等指定的名字。构成标识符的字母均有一定的规范,Swift语言中标识符的命名规则如下: 区分大小写,Myname与myname是两个不同的标识符; 标识符首字符可以以下划线(_)或者字母开始,但不能是数字; 标识符中其他字符可以是下划线(_)、字母或数字。 例如: userName、User_Name、_sys_val、身高等为合法的标识符,而2mail、room#和class为非法的标识符。 注意:Swift中的字母采用的是Unicode编码。Unicode叫做统一编码制,它包含了亚洲文字编码,如中文、日文、韩文等字符,甚至是我们在聊天工具中使用的表情符号 如果一定要使用关键字作为标识符,可以在关键字前后添加重音符号(`),例如: let `class` = "xiaobai" 关键字 关键字是类似于标识符的保留字符序列,除非用重音符号(`)将其括起来,否则不能用作标识符。关键字是对编译器具有特殊意义的预定义保留标识符。常见的关键字有以下4种。 与声明有关的关键字 class deinit enum extension func import init internal let operator private protocol public static struct subscript typealias var 与语句有关的关键字 break case continue default do else fallthrough for if in return switch where while 表达式和类型关键字 as dynamicType false is nil self Self super true _COLUMN_ _FILE_ _FUNCTION_ _LINE_ 在特定上下文中使用的关键字 associativity convenience dynamic didSet final get infix inout lazy left mutating none nonmutating optional override postfix precedence prefix Protocol required right set Type unowned weak willSet Swift 空格 Swift对空格的使用有一定的要求。 在Swift中,运算符不能直接跟在变量或常量的后面。例如下面的代码会报错: let a= 1 + 2 错误信息是: error: prefix/postfix '=' is reserved 意思大概是等号直接跟在前面或后面这种用法是保留的。 下面的代码还是会报错(继续注意空格): let a = 1+ 2 错误信息是: error: consecutive statements on a line must be separated by ';' 这是因为Swift认为到1+这个语句就结束了,2就是下一个语句了。 只有这样写才不会报错: let a = 1 + 2; // 编码规范推荐使用这种写法 let b = 3+4 // 这样也是OK的 整数、浮点数 统一使用 Int 可以提高代码的可复用性,避免不同类型数字之间的转换, 并且匹配数字的类型推断。 示例: let minValue = UInt8.min // minValue 为 0,是 UInt8 类型 let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型 类型安全和类型推断 Swift 是一门类型安全的语言,这意味着 Swift 可以让你清楚地知道值的类型。 如果你没有显式指定类型,Swift 会使用类型推断来选择合适的类型。(int、double)。 示例: let meaningOfLife = 42 // meaningOfLife 会被推测为 Int 类型 let pi = 3.14159 // pi 会被推测为 Double 类型 数值型字面量、数值型类型转换 示例: let decimalInteger = 17 let binaryInteger = 0b10001 // 二进制的17 let octalInteger = 0o21 // 八进制的17 let hexadecimalInteger = 0x11 // 十六进制的17 类型别名 类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用 typealias 关键字来定义类型别名。 示例: typealias AudioSample = UInt16 var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound 现在是 0 布尔值 示例: let orangesAreOrange = true let turnipsAreDelicious = false 元组 元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。 示例: let http404Error = (404, "Not Found") // http404Error 的类型是 (Int, String),值是 (404, "Not Found") 可选类型 使用可选类型(optionals)来处理值可能缺失的情况。可选类型表示两种可能:或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。 示例: var serverResponseCode: Int? = 404 // serverResponseCode 包含一个可选的 Int 值 404 serverResponseCode = nil // serverResponseCode 现在不包含值 错误处理 错误处理,应对程序执行中可能会遇到的错误条件。 示例: func makeASandwich() throws { // ... } do { try makeASandwich() eatASandwich() } catch SandwichError.outOfCleanDishes { washDishes() } catch SandwichError.missingIngredients(let ingredients) { buyGroceries(ingredients) } 断言和先决条件 断言和先决条件,是在运行时所做的检查。 let age = -3 assert(age >= 0, "A person's age cannot be less than zero") // 因为 age < 0,所以断言会触发 基本运算符 Swift 支持大部分标准 C 语言的运算符,还提供了 C 语言没有的区间运算符,例如 a..<b 或 a...b。 赋值运算符,算术运算符,组合赋值运算符,比较运算符,三元运算符,空合运算符,区间运算符,逻辑运算符 运算符分为一元、二元和三元运算符。 闭区间运算符(a…b)定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。 半开区间运算符(a..<b)定义一个从 a 到 b 但不包括 b 的区间。 闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间,(a…,…b)。 示例: let names = ["Anna", "Alex", "Brian", "Jack"] let count = names.count for i in 0..<count { print("第 \(i + 1) 个人叫 \(names[i])") } // 第 1 个人叫 Anna // 第 2 个人叫 Alex // 第 3 个人叫 Brian // 第 4 个人叫 Jack 字符串和字符 字符串字面量,字符串插值,计算字符数量,访问和修改字符串,子字符串,比较字符串 初始化空字符串,字符串可变性,字符串是值类型,连接字符串和字符(+,+=)。 使用字符,可通过 for-in 循环来遍历字符串,获取字符串中每一个字符的值。 字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串。 Swift 提供了三种方式来比较文本值:字符串字符相等、前缀相等和后缀相等。 示例: // 多行字符串字面量 let quotation = """ The White Rabbit put on his spectacles. "Where shall I begin, please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on till you come to the end; then stop." """ // 下面两个字符串其实是一样的 let singleLineString = "These are the same." let multilineString = """ These are the same. """ // 字符串插值 let multiplier = 3 let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" // message 是 "3 times 2.5 is 7.5" // 计算字符数量 var word = "cafe" print("the number of characters in \(word) is \(word.count)") // 打印输出“the number of characters in cafe is 4” var emptyString = "" // 空字符串字面量 var anotherEmptyString = String() // 初始化方法 // 两个字符串均为空并等价。 let catCharacters: [Character] = ["C", "a", "t", "!"] let catString = String(catCharacters) print(catString) // 打印输出:“Cat!” 集合类型 Swift 语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。 集合的可变性,数组(Arrays),集合(Sets),集合操作,字典 数组使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。 集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。 集合操作,可以高效地完成集合的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。 字典是一种无序的集合,它存储的是键值对之间的关系,其所有键的值需要是相同的类型,所有值的类型也需要相同。每个值(value)都关联唯一的键(key),键作为字典中这个值数据的标识符。 示例: // 集合 var someInts = [Int]() print("someInts is of type [Int] with \(someInts.count) items.") // 打印“someInts is of type [Int] with 0 items.” var threeDoubles = Array(repeating: 0.0, count: 3) // threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0] var anotherThreeDoubles = Array(repeating: 2.5, count: 3) // anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5] var sixDoubles = threeDoubles + anotherThreeDoubles // sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] // enumerated() 方法遍历数组 var shoppingList: [String] = ["Eggs", "Milk"] for (index, value) in shoppingList.enumerated() { print("Item \(String(index + 1)): \(value)") } 控制流 For-In 循环,While 循环(Repeat-While),条件语句,控制转移语句,提前退出(guard),检测 API 可用性 像 if 语句一样,guard 的执行取决于一个表达式的布尔值。我们可以使用 guard 语句来要求条件必须为真时,以执行 guard 语句后的代码。不同于 if 语句,一个 guard 语句总是有一个 else 从句,如果条件不为真则执行 else 从句中的代码。 Swift 内置支持检查 API 可用性,编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 API,Swift 会在编译时报错。 示例: let names = ["Anna", "Alex", "Brian", "Jack"] for name in names { print("Hello, \(name)!") } let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] for (animalName, legCount) in numberOfLegs { print("\(animalName)s have \(legCount) legs") } // repeat-while 循环的一般格式 repeat { statements } while condition // 提前退出 func greet(person: [String: String]) { guard let name = person["name"] else { return } print("Hello \(name)!") guard let location = person["location"] else { print("I hope the weather is nice near you.") return } print("I hope the weather is nice in \(location).") } greet(person: ["name": "John"]) // 输出“Hello John!” // 输出“I hope the weather is nice near you.” greet(person: ["name": "Jane", "location": "Cupertino"]) // 输出“Hello Jane!” // 输出“I hope the weather is nice in Cupertino.” 函数 函数的定义与调用,函数参数与返回值,函数参数标签和参数名称,函数类型,嵌套函数 可选元组返回类型。 定义一个输入输出参数时,在参数定义前加 inout 关键字。 示例: // 函数 func greet(person: String) -> String { let greeting = "Hello, " + person + "!" return greeting } func greet(person: String, from hometown: String) -> String { return "Hello \(person)! Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // 打印“Hello Bill! Glad you could visit from Cupertino.” // 可选元组返回类型 func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } // 隐式返回的函数 func greeting(for person: String) -> String { "Hello, " + person + "!" } print(greeting(for: "Dave")) // 打印 "Hello, Dave! // 参数标签 func greet(person: String, from hometown: String) -> String { return "Hello \(person)! Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // 打印“Hello Bill! Glad you could visit from Cupertino.” 闭包 闭包是自包含的函数代码块,可以在代码中被传递和使用。与一些编程语言中的匿名函数(Lambdas)比较相似。 闭包表达式,尾随闭包,值捕获,闭包是引用类型,逃逸闭包(@escaping),自动闭包 如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,将这个闭包替换成为尾随闭包的形式很有用。 闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。 示例: // 闭包表达式语法 { (parameters) -> return type in statements } // 尾随闭包 let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine" ] let numbers = [16, 58, 510] let strings = numbers.map { (number) -> String in var number = number var output = "" repeat { output = digitNames[number % 10]! + output number /= 10 } while number > 0 return output } // strings 常量被推断为字符串类型数组,即 [String] // 其值为 ["OneSix", "FiveEight", "FiveOneZero"] // 值捕获 func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } // 自动闭包,延迟求值 var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] print(customersInLine.count) // 打印出“5” let customerProvider = { customersInLine.remove(at: 0) } print(customersInLine.count) // 打印出“5” print("Now serving \(customerProvider())!") // Prints "Now serving Chris!" print(customersInLine.count) // 打印出“4” 枚举 使用 enum 关键词来创建枚举并且把它们的整个定义放在一对大括号内。 枚举语法,使用 Switch 语句匹配枚举值,枚举成员的遍历,关联值,原始值(默认值),递归枚举(indirect) 可以定义 Swift 枚举来存储任意类型的关联值,每个枚举成员的关联值类型可以各不相同。 示例: // 枚举语法 enum SomeEnumeration { // 枚举定义放在这里 } enum CompassPoint { case north case south case east case west } enum Planet { case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune } let somePlanet = Planet.earth switch somePlanet { case .earth: print("Mostly harmless") default: print("Not a safe place for humans") } // 打印“Mostly harmless” // 关联值 enum Barcode { case upc(Int, Int, Int, Int) case qrCode(String) } var productBarcode = Barcode.upc(8, 85909, 51226, 3) productBarcode = .qrCode("ABCDEFGHIJKLMNOP") switch productBarcode { case let .upc(numberSystem, manufacturer, product, check): print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") case let .qrCode(productCode): print("QR code: \(productCode).") } // 打印“QR code: ABCDEFGHIJKLMNOP.” // 递归枚举 indirect enum ArithmeticExpression { case number(Int) case addition(ArithmeticExpression, ArithmeticExpression) case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) // (5 + 4) * 2 func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } print(evaluate(product)) // 打印“18” 结构体和类 结构体和类对比,结构体和枚举是值类型,类是引用类型 结构体和类作为一种通用而又灵活的结构,成为了人们构建代码的基础。你可以使用定义常量、变量和函数的语法,为你的结构体和类定义属性、添加方法。 示例: // 类和结构体 struct SomeStructure { // 在这里定义结构体 } class SomeClass { // 在这里定义类 } struct Resolution { var width = 0 var height = 0 } class VideoMode { var resolution = Resolution() var interlaced = false var frameRate = 0.0 var name: String? } 属性 存储属性,计算属性,属性观察器,属性包装器,全局变量和局部变量,类型属性(static) 属性将值与特定的类、结构体或枚举关联。存储属性会将常量和变量存储为实例的一部分,而计算属性则是直接计算(而不是存储)值。计算属性可以用于类、结构体和枚举,而存储属性只能用于类和结构体。 属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。 willSet 在新的值被设置之前调用 didSet 在新的值被设置之后调用 属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。 类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。 示例: // 属性 struct Point { var x = 0.0, y = 0.0 } struct Size { var width = 0.0, height = 0.0 } struct Rect { var origin = Point() var size = Size() //存储属性 var center: Point { //计算型属性 get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) print("square.origin is now at (\(square.origin.x), \(square.origin.y))") // 打印“square.origin is now at (10.0, 10.0)” // 属性包装器 @propertyWrapper struct TwelveOrLess { private var number = 0 var wrappedValue: Int { get { return number } set { number = min(newValue, 12) } } } 方法 实例方法(Instance Methods),类型方法(static) 方法是与某些特定类型相关联的函数。 类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。 类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。 示例: // 方法 class Counter { var count = 0 func increment() { count += 1 } func increment(by amount: Int) { count += amount } func reset() { count = 0 } } 下标 下标可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式 下标语法(subscript),下标用法,下标选项,类型下标(static) subscript(index: Int) -> Int { get { // 返回一个适当的 Int 类型的值 } set(newValue) { // 执行适当的赋值操作 } } // 示例 struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print("six times three is \(threeTimesTable[6])") // 打印“six times three is 18” var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] numberOfLegs["bird"] = 2 // 类型下标 enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune static subscript(n: Int) -> Planet { return Planet(rawValue: n)! } } let mars = Planet[4] print(mars) 继承 定义一个基类,子类生成,重写(override),防止重写(final) 不继承于其它类的类,称之为基类。 示例: // 继承 class SomeClass: SomeSuperclass { // 这里是子类的定义 } class Vehicle { var currentSpeed = 0.0 var description: String { return "traveling at \(currentSpeed) miles per hour" } func makeNoise() { // 什么也不做——因为车辆不一定会有噪音 } } class Car: Vehicle { var gear = 1 override var description: String { return super.description + " in gear \(gear)" } } class AutomaticCar: Car { override var currentSpeed: Double { didSet { gear = Int(currentSpeed / 10.0) + 1 } } } 构造过程 构造过程是使用类、结构体或枚举类型的实例之前的准备过程。 存储属性的初始赋值,自定义构造过程,默认构造器,值类型的构造器代理,类的继承和构造过程,可失败构造器,必要构造器(required) 构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能避免多个构造器间的代码重复。 Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们被称为指定构造器和便利构造器。 可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在 init 关键字后面添加问号(init?)。 必要构造器,在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器。 示例: // 构造过程 init() { // 在此处执行构造过程 } struct Fahrenheit { var temperature: Double init() { temperature = 32.0 } } var f = Fahrenheit() print("The default temperature is \(f.temperature)° Fahrenheit") // 打印“The default temperature is 32.0° Fahrenheit” struct Color { let red, green, blue: Double init(red: Double, green: Double, blue: Double) { self.red = red self.green = green self.blue = blue } init(white: Double) { red = white green = white blue = white } } 析构过程 析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字 deinit 来标示,类似于构造器要用 init 来标示。 Swift 会自动释放不再需要的实例以释放资源。 示例: // 析构过程 deinit { // 执行析构过程 } class Bank { static var coinsInBank = 10_000 static func distribute(coins numberOfCoinsRequested: Int) -> Int { let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank) coinsInBank -= numberOfCoinsToVend return numberOfCoinsToVend } static func receive(coins: Int) { coinsInBank += coins } } class Player { var coinsInPurse: Int init(coins: Int) { coinsInPurse = Bank.distribute(coins: coins) } func win(coins: Int) { coinsInPurse += Bank.distribute(coins: coins) } deinit { Bank.receive(coins: coinsInPurse) } } 可选链式调用 可选链式调用是一种可以在当前值可能为 nil 的可选值上请求和调用属性、方法及下标的方法。 通过在想调用的属性、方法,或下标的可选值后面放一个问号(?),可以定义一个可选链。类似在可选值后面放一个叹号(!)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。 示例: class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } let john = Person() let roomCount = john.residence!.numberOfRooms // 这会引发运行时错误 if let roomCount = john.residence?.numberOfRooms { print("John's residence has \(roomCount) room(s).") } else { print("Unable to retrieve the number of rooms.") } // 打印“Unable to retrieve the number of rooms.” john.residence = Residence() if let roomCount = john.residence?.numberOfRooms { print("John's residence has \(roomCount) room(s).") } else { print("Unable to retrieve the number of rooms.") } // 打印“John's residence has 1 room(s).” 错误处理 错误处理(Error handling) 是响应错误以及从错误中恢复的过程。Swift 在运行时提供了抛出、捕获、传递和操作可恢复错误(recoverable errors)的一等支持。 表示与抛出错误,处理错误,指定清理操作 在 Swift 中,错误用遵循 Error 协议的类型的值来表示。 Swift 中有 4 种处理错误的方式。可以把函数抛出的错误传递给调用此函数的代码(throws)、用 do-catch 语句处理错误、将错误作为可选类型处理(try?)、或者断言此错误根本不会发生(try!)。 defer 语句将代码的执行延迟到当前的作用域退出之前。 示例: // 错误处理 enum VendingMachineError: Error { case invalidSelection //选择无效 case insufficientFunds(coinsNeeded: Int) //金额不足 case outOfStock //缺货 } throw VendingMachineError.insufficientFunds(coinsNeeded: 5) var vendingMachine = VendingMachine() vendingMachine.coinsDeposited = 8 do { try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine) print("Success! Yum.") } catch VendingMachineError.invalidSelection { print("Invalid Selection.") } catch VendingMachineError.outOfStock { print("Out of Stock.") } catch VendingMachineError.insufficientFunds(let coinsNeeded) { print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.") } catch { print("Unexpected error: \(error).") } // 打印“Insufficient funds. Please insert an additional 2 coins.” // 指定清理操作 func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // 处理文件。 } // close(file) 会在这里被调用,即作用域的最后。 } } 类型转换 类型转换在 Swift 中使用 is 和 as 操作符实现。这两个操作符分别提供了一种简单达意的方式去检查值的类型或者转换它的类型。 为类型转换定义类层次,检查类型(is),向下转型(as? 或 as!),Any 和 AnyObject 的类型转换 可以将类型转换用在类和子类的层次结构上,检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型。 Swift 为不确定类型提供了两种特殊的类型别名: Any 可以表示任何类型,包括函数类型。 AnyObject 可以表示任何类类型的实例。 示例: // 类型转换 // 一个基类 MediaItem class MediaItem { var name: String init(name: String) { self.name = name } } class Movie: MediaItem { var director: String init(name: String, director: String) { self.director = director super.init(name: name) } } class Song: MediaItem { var artist: String init(name: String, artist: String) { self.srtist = artist super.init(name: name) } } let library = [ Movie(name: "Casablanca", director: "Micheal Curtiz"), Song(name: "Blue Suede Shose", artist: "Elvis Presley"), Movie(name: "Citizen Kane", director: "Orson Wells"), Song(name: "The One And Only", artist: "Chesney Hawkes"), Song(name: "Never Gonna Give You Up", artist: "Rick Astley") ] var movieCount = 0 var songCount = 0 for item in library { if item is Movie { movieCount += 1 } else if item is Song { songCount += 1 } } print("Media library contains \(movieCount) movies and \(songCount)") // 打印“Media library contains 2 movies and 3 songs” for item in library { if let movie = item as? Movie { print("Movie: \(movie.name), dir. \(movie.director)") } else if let song = item as? Song { print("Song: \(song.name), by \(song.artist)") } } // Movie: Casablanca, dir. Michael Curtiz // Song: Blue Suede Shoes, by Elvis Presley // Movie: Citizen Kane, dir. Orson Welles // Song: The One And Only, by Chesney Hawkes // Song: Never Gonna Give You Up, by Rick Astley 嵌套类型 Swift 允许定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。 嵌套类型实践,引用嵌套类型 要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的 {} 内,而且可以根据需要定义多级嵌套。 示例: // 嵌套类型 stuct BlackjackCard { // 嵌套的 Suit 枚举 enum Suit: Character { case spades = "1", hearts = "2", diamonds = "3", clubs = "4" } // 嵌套的 Rank 枚举 enum Rank: Int { case two = 2, three, four, five, six, seven, eight, nine, ten case jack, queen, king, ace struct Values { let first: Int, second: Int? } var values: Values { switch self { case .ace: return Values(first: 1, second: 11) case .jack, .queen, .king: return Values(first: 10, second: nil) default: return Values(first: self.rawValue, second: nil) } } } // BlackjackCard 的属性和方法 let rank: Rank, suit: Suit var description: String { var output = "suit is \(suit.rawValue)," output += " value is \(rank.values.first)" if let second = rank.values.second { output += " or \(second)" } return output } } let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades) print("theAceOfSpades: \(theAceOfSpades.description)") // 打印“theAceOfSpades: suit is 1, value is 1 or 11” let heartsSymbol = BlackjackCard.Suit.hearts.rawValue // 2 扩展 扩展可以给一个现有的类,结构体,枚举,还有协议添加新的功能。 扩展的语法,计算型属性,构造器,方法,下标,嵌套类型 Swift 中的扩展可以: 添加计算型实例属性和计算型类属性 定义实例方法和类方法 提供新的构造器 定义下标 定义和使用新的嵌套类型 使已经存在的类型遵循(conform)一个协议 扩展语法: extension SomeType { // 在这里给 SomeType 添加新的功能 } 扩展可以给现有类型添加计算型实例属性和计算型类属性。 扩展可以给现有的类型添加新的构造器。 扩展可以给现有类型添加新的实例方法和类方法。 扩展可以给现有的类型添加新的下标。 扩展可以给现有的类,结构体,还有枚举添加新的嵌套类型。 示例: // 扩展的语法 extension SomeType { // 在这里给 SomeType 添加新的功能 } // 添加一个或多个协议 extension SomeType: SomeProtocol, AnotherProtocol { // 协议所需要的实现写在这里 } struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { var origin = Point() var size = Size() } extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) let originY = center.y - (size.height / 3) self.init(origin: Point(x: originX, y: originY), size: size) } } let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) // centerRect 的 origin 是 (2.5, 2.5) 并且它的 size 是 (3.0, 3.0) extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } } 3.repetitions { print("Hello!") } // Hello! // Hello! // Hello! extension Int { mutating func square() { self = self * self } } var somtInt = 3 someInt.square() // someInt 现在是9 协议 协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西。 类、结构体或枚举都可以遵循协议,并为协议定义的这些要求提供具体实现。 协议语法,属性要求,方法要求,异变方法要求,构造器要求,协议作为类型,委托,协议类型的集合,协议的继承,类专属的协议,协议合成,检查协议一致性,可选的协议要求,协议扩展, 协议语法 protocol SomeProtocol { // 这里是协议的定义部分 } 协议可以要求遵循协议的类型提供特定名称和类型的实例属性或类型属性。 协议可以要求遵循协议的类型实现某些指定的实例方法或类方法。 在值类型(即结构体和枚举)的实例方法中,将 mutating 关键字作为方法的前缀,写在 func 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。 协议可以要求遵循协议的类型实现指定的构造器。 委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。 示例: // 协议语法 protocol SomeProtocol { // 这里是协议的定义部分 } struct SomeStructure: FirstProtocol, AnotherProtocol { // 这里是结构体的定义部分 } class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol { // 这里是类的定义部分 } protocol SomeProtocol { var mustBeSettable: Int { get set } var doesNotNeedToBeSettable: Int { get } } protocol AnotherProtocol { static var someTypeProperty: Int { get set } } protocol FullyNamed { var fullName: String { get } } struct person: FullyNamed { var fullName: String } let john = Person(fullName: "John Appleseed") // john.fullName 为 "John Appleseed" class Starship: FullyNamed { var prefix: String? var name: String init(name: String, prefix: String? = nil) { self.name = name self.prefix = prefix } var fullName: String { return (prefix != nil ? prefix! + " " : "") + name } } var ncc1701 = Starship(name: "Enterprise", prefix: "USS") // ncc1701.fullName 为 "USS Enterprise" 泛型 泛型代码让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。 你可避免编写重复的代码,而是用一种清晰抽象的方式来表达代码的意图。 泛型函数,类型参数,命名类型参数,泛型类型,泛型扩展,类型约束,关联类型 示例: func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } func swapTwoValues<T>(_ a: inout T, _ b: inout T) { let temporaryA = a a = b b = temporaryA } func swapTwoInts(_ a: inout Int, _ b: inout Int) func swapTwoValues<T>(_ a: inout T, _ b: inout T) var someInt = 3 var anotherInt = 107 swapTwoValues(&someInt, &anotherInt) // someInt 现在是 107,anotherInt 现在是 3 var someString = "hello" var anotherString = "world" swapTwoValues(&someString, &anotherString) // someString 现在是“world”,anotherString 现在是“hello” 不透明类型 具有不透明返回类型的函数或方法会隐藏返回值的类型信息。 函数不再提供具体的类型作为返回类型,而是根据它支持的协议来描述返回值。 不透明类型解决的问题,返回不透明类型,不透明类型和协议类型的区别 在处理模块和调用代码之间的关系时,隐藏类型信息非常有用,因为返回的底层数据类型仍然可以保持私有。 不透明类型和泛型相反。不透明类型允许函数实现时,选择一个与调用代码无关的返回类型。 如果函数中有多个地方返回了不透明类型,那么所有可能的返回值都必须是同一类型。返回不透明类型和返回协议类型主要区别,就在于是否需要保证类型一致性。 一个不透明类型只能对应一个具体的类型,即便函数调用者并不能知道是哪一种类型;协议类型可以同时对应多个类型,只要它们都遵循同一协议。 示例: protocol Shape { func draw() -> String } struct Triangle: Shape { var size: Int func draw() -> String { var result = [String]() for length in 1...size { result.append(String(repeating: "*", count: length)) } return result.joined(separator: "\n") } } let smallTriangle = Triangle(size: 3) print(smallTriangle.draw()) // * // ** // *** struct FlippedShape<T: Shape>: Shape { var shape: T func draw() -> String { let lines = shape.draw().split(separator: "\n") return lines.reversed().joined(separator: "\n") } } let flippedTriangle = FlippedShape(shape: smallTriangle) print(flippedTriangle.draw()) // *** // ** // * 自动引用计数 Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。 如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的循环强引用。 Swift提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。 声明属性或者变量时,在前面加上 weak 关键字表明这是一个弱引用。 声明属性或者变量时,在前面加上关键字 unowned 表示这是一个无主引用。 示例: // 自动引用计数实践 class Person { let name: String init(name: String) { self.name = name print("\(name) is being initialized") } deinit { print("\(name) is being deinitialized") } } var reference1: Person? var reference2: Person? var reference3: Person? reference1 = Person(name: "John Appleseed") // 打印“John Appleseed is being initialized” reference2 = reference1 reference3 = reference1 reference1 = nil reference2 = nil reference3 = nil // 打印“John Appleseed is being deinitialized” // 循环强引用 class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized") } } class Apartment { let unit: String init(unit: String) { self.unit = unit } var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized") } } var john: Person? var unit4A: Apartment? john = Person(name: "John Appleseed") unit4A = Apartment(unit: "4A") john = nil unit4A = nil // 弱引用 class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? deinit { print("\(name) is being deinitialized") } } class Apartment { let unit: String init(unit: String) { self.unit = unit } weak var tenant: Person? deinit { print("Apartment \(unit) is being deinitialized") } } var john: Person? var unit4A: Apartment? john = Person(name: "John Appleseed") unit4A = Apartment(unit: "4A") john!.apartment = unit4A unit4A!.tenant = john john = nil // 打印“John Appleseed is being deinitialized” 内存安全 默认情况下,Swift 会阻止你代码里不安全的行为。 理解内存访问冲突,In-Out 参数的访问冲突,方法里 self 的访问冲突,属性的访问冲突 示例: func balance(_ x: inout Int, _ y: inout Int) { let sum = x + y x = sum / 2 y = sum - x } var playerOneScore = 42 var playerTwoScore = 30 balance(&playerOneScore, &playerTwoScore) // 正常 balance(&playerOneScore, &playerOneScore) // 错误:playerOneScore 访问冲突 访问控制 访问控制可以限定其它源文件或模块对你的代码的访问。 open 和 public 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 open 或 public 级别来指定框架的外部接口。 internal 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别。 fileprivate 限制实体只能在其定义的文件内部访问。如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。 private 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。 open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。 open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外能被继承和重写。 示例: public class SomePublicClass {} internal class SomeInternalClass {} fileprivate class SomeFilePrivateClass {} private class SomePrivateClass {} class SomeInternalClass {} // 隐式 internal var someInternalConstant = 0 // 隐式 internal public class SomePublicClass { // 显式 public 类 public var somePublicProperty = 0 // 显式 public 类成员 var someInternalProperty = 0 // 隐式 internal 类成员 fileprivate func someFilePrivateMethod() {} // 显式 fileprivate 类成员 private func somePrivateMethod() {} // 显式 private 类成员 } class SomeInternalClass { // 隐式 internal 类 var someInternalProperty = 0 // 隐式 internal 类成员 fileprivate func someFilePrivateMethod() {} // 显式 fileprivate 类成员 private func somePrivateMethod() {} // 显式 private 类成员 } fileprivate class SomeFilePrivateClass { // 显式 fileprivate 类 func someFilePrivateMethod() {} // 隐式 fileprivate 类成员 private func somePrivateMethod() {} // 显式 private 类成员 } private class SomePrivateClass { // 显式 private 类 func somePrivateMethod() {} // 隐式 private 类成员 } 高级运算符 Swift还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了位运算符和移位运算符。 位运算符、溢出运算符、优先级和结合性、运算符函数、自定义运算符 示例: let initialBits: UInt8 = 0b00001111 let invertedBits = ~initialBits // 等于 0b11110000 var potentialOverflow = Int16.max // potentialOverflow 的值是 32767,这是 Int16 能容纳的最大整数 potentialOverflow += 1 // 这里会报错 struct Vector2D { var x = 0.0, y = 0.0 } extension Vector2D { static func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) } } let vector = Vector2D(x: 3.0, y: 1.0) let anotherVector = Vector2D(x: 2.0, y: 4.0) let combinedVector = vector + anotherVector // combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0)

2021/5/13
articleCard.readMore

使用 Golang 的交叉编译

在 Linux 上测试软件时,我使用各种架构的服务器,例如 Intel、AMD、Arm 等。当我 分配了一台满足我的测试需求的 Linux 机器[1],我仍然需要执行许多步骤: 下载并安装必备软件 验证构建服务器上是否有新的测试软件包 获取并设置依赖软件包所需的 yum 仓库 下载并安装新的测试软件包(基于步骤 2) 获取并设置必需的 SSL 证书 设置测试环境,获取所需的 Git 仓库,更改配置,重新启动守护进程等 做其他需要做的事情 用脚本自动化 这些步骤非常常规,以至于有必要对其进行自动化并将脚本保存到中央位置(例如文件服务器),在需要时可以在此处下载脚本。为此,我编写了 100-120 行的 Bash shell 脚本,它为我完成了所有配置(包括错误检查)。这个脚本通过以下方式简化了我的工作流程: 配置新的 Linux 系统(支持测试的架构) 登录系统并从中央位置下载自动化 shell 脚本 运行它来配置系统 开始测试 学习 Go 语言 我想学习 Go 语言 有一段时间了,将我心爱的 Shell 脚本转换为 Go 程序似乎是一个很好的项目,可以帮助我入门。它的语法看起来很简单,在尝试了一些测试程序后,我开始着手提高自己的知识并熟悉 Go 标准库。 我花了一个星期的时间在笔记本电脑上编写 Go 程序。我经常在我的 x86 服务器上测试程序,清除错误并使程序健壮起来,一切都很顺利。 直到完全转换到 Go 程序前,我继续依赖自己的 shell 脚本。然后,我将二进制文件推送到中央文件服务器上,以便每次配置新服务器时,我要做的就是获取二进制文件,将可执行标志打开,然后运行二进制文件。我对早期的结果很满意: $ wget http://file.example.com/<myuser>/bins/prepnode $ chmod +x ./prepnode $ ./prepnode 然后,出现了一个问题 第二周,我从资源池中分配了一台新的服务器,像往常一样,我下载了二进制文件,设置了可执行标志,然后运行二进制文件。但这次它出错了,是一个奇怪的错误: $ ./prepnode bash: ./prepnode: cannot execute binary file: Exec format error $ 起初,我以为可能没有成功设置可执行标志。但是,它已按预期设置: $ ls -l prepnode -rwxr-xr-x. 1 root root 2640529 Dec 16 05:43 prepnode 发生了什么事?我没有对源代码进行任何更改,编译没有引发任何错误或警告,而且上次运行时效果很好,因此我仔细查看了错误消息 format error。 我检查了二进制文件的格式,一切看起来都没问题: $ file prepnode prepnode: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped 我迅速运行了以下命令,识别所配置的测试服务器的架构以及二进制试图运行的平台。它是 Arm64 架构,但是我编译的二进制文件(在我的 x86 笔记本电脑上)生成的是 x86-64 格式的二进制文件: $ uname -m aarch64 脚本编写人员的编译第一课 在那之前,我从未考虑过这种情况(尽管我知道这一点)。我主要研究脚本语言(通常是 Python)以及 Shell 脚本。在任何架构的大多数 Linux 服务器上都可以使用 Bash Shell 和 Python 解释器。总之,之前一切都很顺利。 但是,现在我正在处理 Go 这种编译语言,它生成可执行的二进制文件。编译后的二进制文件由特定架构的指令码或汇编指令组成,这就是为什么我收到格式错误的原因。由于 Arm64 CPU(运行二进制文件的地方)无法解释二进制文件的 x86-64 指令,因此它抛出错误。以前,shell 和 Python 解释器为我处理了底层指令码或特定架构的指令。 Go 的交叉编译 我检查了 Golang 的文档,发现要生成 Arm64 二进制文件,我要做的就是在运行 go build 命令编译 Go 程序之前设置两个环境变量。 GOOS 指的是操作系统,例如 Linux、Windows、BSD 等,而 GOARCH 指的是要在哪种架构上构建程序。 $ env GOOS=linux GOARCH=arm64 go build -o prepnode_arm64 构建程序后,我重新运行 file 命令,这一次它显示的是 ARM AArch64,而不是之前显示的 x86。因此,我在我的笔记本上能为不同的架构构建二进制文件。 $ file prepnode_arm64 prepnode_arm64: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped 我将二进制文件从笔记本电脑复制到 ARM 服务器上。现在运行二进制文件(将可执行标志打开)不会产生任何错误: $ ./prepnode_arm64 -h Usage of ./prepnode_arm64: -c Clean existing installation -n Do not start test run (default true) -s Use stage environment, default is qa -v Enable verbose output 其他架构呢? x86 和 Arm 是我测试软件所支持的 5 种架构中的两种,我担心 Go 可能不会支持其它架构,但事实并非如此。你可以查看 Go 支持的架构: $ go tool dist list Go 支持多种平台和操作系统,包括: AIX Android Darwin Dragonfly FreeBSD Illumos ios Js/wasm JavaScript Linux NetBSD OpenBSD Plan 9 Solaris Windows 要查找其支持的特定 Linux 架构,运行: $ go tool dist list | grep linux 如下面的输出所示,Go 支持我使用的所有体系结构。尽管 x86_64 不在列表中,但 AMD64 兼容 x86-64,所以你可以生成 AMD64 二进制文件,它可以在 x86 架构上正常运行: $ go tool dist list | grep linux linux/386 linux/amd64 linux/arm linux/arm64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x 处理所有架构 为我测试的所有体系结构生成二进制文件,就像从我的 x86 笔记本电脑编写一个微小的 shell 脚本一样简单: #!/usr/bin/bash archs=(amd64 arm64 ppc64le ppc64 s390x) for arch in ${archs[@]} do env GOOS=linux GOARCH=${arch} go build -o prepnode_${arch} done $ file prepnode_* prepnode_amd64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=y03MzCXoZERH-0EwAAYI/p909FDnk7xEUo2LdHIyo/V2ABa7X_rLkPNHaFqUQ6/5p_q8MZiR2WYkA5CzJiF, not stripped prepnode_arm64: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go BuildID=q-H-CCtLv__jVOcdcOpA/CywRwDz9LN2Wk_fWeJHt/K4-3P5tU2mzlWJa0noGN/SEev9TJFyvHdKZnPaZgb, not stripped prepnode_ppc64: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, Go BuildID=DMWfc1QwOGIq2hxEzL_u/UE-9CIvkIMeNC_ocW4ry/r-7NcMATXatoXJQz3yUO/xzfiDIBuUxbuiyaw5Goq, not stripped prepnode_ppc64le: ELF 64-bit LSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, Go BuildID=C6qCjxwO9s63FJKDrv3f/xCJa4E6LPVpEZqmbF6B4/Mu6T_OR-dx-vLavn1Gyq/AWR1pK1cLz9YzLSFt5eU, not stripped prepnode_s390x: ELF 64-bit MSB executable, IBM S/390, version 1 (SYSV), statically linked, Go BuildID=faC_HDe1_iVq2XhpPD3d/7TIv0rulE4RZybgJVmPz/o_SZW_0iS0EkJJZHANxx/zuZgo79Je7zAs3v6Lxuz, not stripped 现在,每当配置一台新机器时,我就运行以下 wget 命令下载特定体系结构的二进制文件,将可执行标志打开,然后运行: $ wget http://file.domain.com/<myuser>/bins/prepnode_<arch> $ chmod +x ./prepnode_<arch> $ ./prepnode_<arch> 为什么? 你可能想知道,为什么我没有坚持使用 shell 脚本或将程序移植到 Python 而不是编译语言上来避免这些麻烦。所以有舍有得,那样的话我不会了解 Go 的交叉编译功能,以及程序在 CPU 上执行时的底层工作原理。在计算机中,总要考虑取舍,但绝不要让它们阻碍你的学习。 参考 Cross-compiling made easy with Golang [1] 使用 Golang 的交叉编译 [2]

2021/5/13
articleCard.readMore

Linux/Mac 使用 GNU Screen 的小技巧

学习基本的 GNU Screen 终端复用技术,然后下载我们的终端命令备忘录,以便你能够熟悉常用的快捷方式。 对于一般用户而言,命令行终端窗口可能是令人困惑和神秘的。但随着你对 Linux 终端的进一步了解,你很快就会意识到它的高效和强大。不过,也不需要很长时间,你就会想让终端变得更加高效,除了将更多的终端放到你的终端,还有什么高好的方法能够提升你的终端效率呢? 终端复用 终端的许多优点之一是它是一个集中控制的界面。它是一个能让你访问数百个应用程序的窗口,而你与每一个应用程序进行交互所需要的只是一个键盘。但是,现代计算机几乎总是有多余的处理能力,而且现代计算机专家喜欢多任务处理,导致一个窗口处理数百个应用程序的能力是相当有限的。 解决这一问题的常见答案是终端复用:即将虚拟终端叠放在一起,然后在它们之间移动的能力。通过终端复用器,你保持了集中控制,但是当你进行多任务时,你能够进行终端切换。更好的是,你能够在终端中拆分屏幕,使得在同一时间显示多个屏幕窗口。 选择合适的复用器 一些终端提供类似的功能,有标签式界面和分割式视图,但也有细微的差别。首先,这些终端的功能依赖于图形化的桌面环境。其次,许多图形化的终端功能需要鼠标交互或使用不方便的键盘快捷键。终端复用器的功能在文本控制台上和在图形桌面上一样好用,而且键位绑定是针对常见的终端序列设计的,很方便。 现有两种流行的复用器:tmux 和 GNU Screen。尽管你与它们互动的方式略有不同,但它们做同样的事情,而且大多具有相同的功能。这篇文章是 GNU Screen 的入门指南。关于 tmux 的相关介绍,请阅读 Kevin Sonney 的 tmux 介绍。 使用 GNU Screen GNU Screen 的基本用法很简单,通过 screen 命令启动,你将进入 Screen 会话的第 0 个窗口。在你决定需要一个新的终端提示符前,你可能很难注意到有什么变化。 当一个终端窗口被某项活动占用(比如,你启动了文本编辑器 Vim 或 Jove 或者你在处理音视频,或运行批处理任务),你可以新建一个窗口。要打开一个新的窗口,按 Ctrl+A,释放,然后按 c。这将在你现有窗口的基础上创建一个新的窗口。 你会知道当前你是在一个新的窗口中,因为你的终端除了默认的提示符外,似乎没有任何东西。当然,你的另一个终端仍然存在,它只是躲在新窗口的后面。要遍历打开的窗口,按 Ctrl+A,释放,然后按 n(表示下一个)或按 p(表示上一个)。在只打开两个窗口的情况下, n 和 p 的功能是一样的,但你可以随时打开更多的窗口(Ctrl+A,然后 c ),并在它们之间切换。 分屏 GNU Screen 的默认行为更像移动设备的屏幕,而不是桌面:你一次只能看到一个窗口。如果你因为喜欢多任务而使用 GNU Screen ,那么只关注一个窗口可能看起来是一种退步。幸运的是,GNU Screen 可以让你把终端分成窗口中的窗口。 要创建一个水平分割窗口,按 Ctrl+A,然后按 s 。这将把一个窗口置于另一个窗口之上,就像窗格一样。然而,在你告诉它要显示什么之前,分割的空间是没有用途的。因此,在创建一个分割窗后,你可以用 Ctrl+A ,然后用 Tab 移动到分割窗中。一旦进入,使用 Ctrl+A 然后 n 浏览所有可用的窗口,直到你想显示的内容出现在分割窗格中。 你也可以按 Ctrl+A 然后按 | (这是一个管道字符,在大多数键盘上通过按下 shift 键加上 \)创建垂直分割窗口。 自定义 GNU Screen GNU Screen 使用基于 Ctrl+A 的快捷键。根据你的习惯,这可能会让你感觉非常自然,也可能非常不方便,因为你可能会用 Ctrl+A 来移动到一行的开头。无论怎样,GNU Screen 允许通过 .screenrc 配置文件进行各种定制。你可以用这个来改变触发键的绑定(称为 “转义” 键绑定)。 escape ^jJ 你还可以添加一个状态行,以帮助你在 Screen 会话中保持自己不迷失。 # status bar, with current window highlighted hardstatus alwayslastline hardstatus string '%{= kG}[%{G}%H%? %1`%?%{g}][%= %{= kw}%-w%{+b yk} %n*%t%?(%u)%? %{-}%+w %=%{g}][%{B}%m/%d %{W}%C%A%{g}]' # enable 256 colors attrcolor b ".I" termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm' defbce on 在有多个窗口打开的会话中,有一个时刻提醒哪些窗口具有焦点活动,哪些窗口有后台活动的提醒器特别有用。它类似一种终端的任务管理器。 备忘单 当你学习 GNU Screen 的使用方法时,需要记住很多新的键盘命令。有些命令你马上就能记住,但那些你不常使用的命令可能就很难记住了。你可以按 Ctrl+A 然后再按 ? 来访问 GNU Screen 的帮助界面。 学习 GNU Screen 是提高你使用你最喜欢的 终端模拟器 的效率和敏捷性的一个好方法。请试一试吧! 参考 Linux tips for using GNU Screen [1] 使用 GNU Screen 的小技巧 [2]

2021/5/13
articleCard.readMore

使用 cron 调度自动化任务

cron 是一个调度守护进程,它以指定的时间间隔执行任务,这些任务称为 corn 作业,主要用于自动执行系统维护或管理任务。例如,你可以设置一个 cron 作业来自动执行重复的任务,比如备份数据库或数据,使用最新的安全补丁更新系统,检查磁盘空间使用情况,发送电子邮件等等。 cron 作业可以按分钟、小时、日、月、星期或它们的任意组合运行。 cron 的一些优点 以下是使用 cron 作业的一些优点: 你可以更好地控制作业的运行时间。例如,你可以精确到分钟、小时、天等。 它消除了为循环任务逻辑而去写代码的需要,当你不再需要执行任务时,可以直接关闭它。 作业在不执行时不会占用内存,因此你可以节省内存分配。 如果一个作业执行失败并由于某种原因退出,它将在适当的时间再次运行。 安装 cron 守护进程 幸运的是,Fedora Linux 预先配置了运行重要的系统任务来保持系统更新,有几个实用程序可以运行任务例如 cron、anacron、at 和 batch 。本文只关注 cron 实用程序的安装。cron 和 cronie 包一起安装,cronie 包也提供 cron 服务。 要确定软件包是否已经存在,使用 rpm 命令: $ rpm -q cronie Cronie-1.5.2-4.el8.x86_64 如果安装了 cronie ,它将返回 cronie 包的全名。如果你的系统中没有安装,则会显示未安装。 使用以下命令安装: $ dnf install cronie 运行 cron 守护进程 cron 作业由 crond 服务来执行,它会读取配置文件中的信息。在将作业添加到配置文件之前,必须启动 crond 服务,或者安装它。什么是 crond 呢?crond 是 cron 守护程序的简称。要确定 crond 服务是否正在运行,输入以下命令: $ systemctl status crond.service ● crond.service - Command Scheduler Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor pre> Active: active (running) since Sat 2021-03-20 14:12:35 PDT; 1 day 21h ago Main PID: 1110 (crond) 如果你没有看到类似的内容 Active: active (running) since…,你需要启动 crond 守护进程。要在当前会话中运行 crond 服务,输入以下命令: $ systemctl run crond.service 将其配置为开机自启动,输入以下命令: $ systemctl enable crond.service 如果出于某种原因,你希望停止 crond 服务,按以下方式使用 stop 命令: $ systemctl stop crond.service 要重新启动它,只需使用 restart 命令: $ systemctl restart crond.service 定义一个 cron 作业 cron 配置 以下是一个 cron 作业的配置细节示例。它定义了一个简单的 cron 作业,将 git master 分支的最新更改拉取到克隆的仓库中: */59 * * * * username cd /home/username/project/design && git pull origin master 主要有两部分: 第一部分是 */59 * * * *。这表明计时器设置为第 59 分钟执行一次。 该行的其余部分是命令,因为它将从命令行运行。 在此示例中,命令本身包含三个部分: 作业将以用户 username 的身份运行 它将切换到目录 /home/username/project/design 运行 git 命令拉取 master 分支中的最新更改 时间语法 如上所述,时间信息是 cron 作业字符串的第一部分,如上所属。它决定了 cron 作业运行的频率和时间。它按以下顺序包括 5 个部分: 分钟 小时 一个月中的某天 月份 一周中的某天 下面是一种更图形化的方式来解释语法: .--------------- 分钟 (0 - 59) | .------------- 小时 (0 - 23) | | .---------- 一月中的某天 (1 - 31) | | | .------- 月份 (1 - 12) 或 jan、feb、mar、apr … | | | | .---- 一周中的某天 (0-6) (周日=0 或 7) | | | | | 或 sun、mon、tue、wed、thr、fri、sat | | | | | * * * * * user-name command-to-be-executed 星号的使用 星号(*)可以用来替代数字,表示该位置的所有可能值。例如,分钟位置上的星号会使它每分钟运行一次。以下示例可能有助于更好地理解语法。 这个 cron 作业将每分钟运行一次: * * * * [command] 斜杠表示分钟的间隔数。下面的示例将每小时运行 12 次,即每 5 分钟运行一次: */5 * * * * [command] 下一个示例将每月的第二天午夜(例如 1 月 2 日凌晨 12:00,2 月 2 日凌晨 12:00 等等): 0 0 2 * * [command] 关于 cron 时间格式,还有更多格式符号,此处没有展开 使用 crontab 创建一个 cron 作业 cron 作业会在后台运行,它会不断检查 /etc/crontab 文件和 /etc/cron.*/ 以及 /var/spool/cron/ 目录。每个用户在 /var/spool/cron/ 中都有一个唯一的 crontab 文件。 不应该直接编辑这些 cron 文件。crontab 命令是用于创建、编辑、安装、卸载和列出 cron 作业的方法。 更酷的是,在创建新文件或编辑现有文件后,你无需重新启动 cron。 $ crontab -e 这将打开你现有的 crontab 文件,或者创建一个。调用 crontab -e 时,默认情况下会使用 vi 编辑器。注意:要使用 Nano 编辑 crontab 文件,可以设置 EDITOR=nano 环境变量。 使用 -l 选项列出所有 cron 作业。如果需要,使用 -u 选项指定一个用户。 $ crontab -l $ crontab -u username -l 使用以下命令删除所有 cron 作业: $ crontab -r 要删除特定用户的作业,你必须以 root 用户身份运行以下命令: $ crontab -r -u username cron 作业看起来可能只是系统管理员的工具,但它实际上与许多 Web 应用程序和用户任务有关。 参考 Fedora Linux 文档的 [1] 使用 cron 调度任务 [2]

2021/5/12
articleCard.readMore

about me

关于我 一个勤奋的代码搬运工。 为什么建立个人博客? 宣传自己,这是一个信息爆炸、人人自我营销的时代,不懂得宣传自己,就得不到机会的垂青。 个人觉得IT从业者就应该有自己的博客网站,记录与分享自己的经验,收集有价值的文章。 凭借互联网的媒介,结交志同道合的朋友,为未来的事业做准备。 关于本站 Log something useless, but interesting! 学习日记,点滴记录。 也会收集和分享互联网上比较经典且具有价值的文章。 站长是谁: 昵称:白菜 我的主页 https://blog.baicai.me 未来发展: 未来很长,且慢慢耕耘。 白菜的博客小站历程: 2023年9月2日 通过github action自动构建发布到静态仓库(github page)。 特别感谢 非常感谢每一位走进本站的同学对我们的大力支持,你们的走进是我坚持的动力! 如果不介意,请把本站分享给您的同学朋友! 我会一直努力,不求最好,只求更好!

2021/5/11
articleCard.readMore

Hello world

Hello World! ^_^

2021/5/11
articleCard.readMore