<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>wiseAI的小站</title>
    <link>https://blog.wiseai.cn/</link>
    <description>Recent content on wiseAI的小站</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <copyright>陇ICP备15000157号</copyright>
    <lastBuildDate>Fri, 14 Apr 2023 17:39:26 +0800</lastBuildDate><atom:link href="https://blog.wiseai.cn/index.xml" rel="self" type="application/rss+xml" />
    <item>
    <title>时区设置</title>
    <link>https://blog.wiseai.cn/post/2023.04.14-%E6%97%B6%E5%8C%BA%E8%AE%BE%E7%BD%AE/</link>
    <pubDate>Fri, 14 Apr 2023 17:39:26 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.04.14-%E6%97%B6%E5%8C%BA%E8%AE%BE%E7%BD%AE/</guid>
    <description>
        &lt;h2 id=&#34;如何查看所在时区&#34;&gt;如何查看所在时区&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//date用于查看时间
# date
//下面这个命令查看时区
# ls -l /etc/localtime
//&amp;#34;/etc/localtime -&amp;gt; /usr/share/zoneinfo/Asia/Shanghai&amp;#34;
//说明使用的是上海的时区
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;获取时区&#34;&gt;获取时区&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# tzselect
//按照命令提示走，就可以得到对应时区，位置在/usr/share/zoneinfo/Asia/，比如我们国家的时区：
Asia/Shanghai
Asia/Beijing
Asia/Chengdu
Asia/Chongqing
Asia/Nanjing
Asia/Wuhan
Asia/Xian
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;更改系统时区&#34;&gt;更改系统时区&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# sudo rm -f /etc/localtime
# sudo ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
//或者
# sudo timedatectl set-timezone &amp;#39;Asia/Shanghai&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>caddy的配置文件</title>
    <link>https://blog.wiseai.cn/post/2023.03.22-caddy%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/</link>
    <pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.03.22-caddy%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;安装
首先安装xcaddy，这个是可以编译带扩展的程序，具体的看源码说明。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;再使用xcaddy编译caddy程序&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ xcaddy build [&amp;lt;caddy_version&amp;gt;]
    [--output &amp;lt;file&amp;gt;]
    [--with &amp;lt;module[@version][=replacement]&amp;gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;下面的是一个示例文件，是&lt;a href=&#34;https://r2wind.cn/articles/20220412.html&#34;&gt;“小泽”的文章&lt;/a&gt;，很不错。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 此处的配置是用来支持HTTP3的,若不需要删除即可
{
	servers {
		protocol {
			experimental_http3
		}
	}
}
// 一个普通的网站示例，r2wind.com替换成你喜欢的域名
r2wind.com {
  // 此处配置网站根目录，请将页面文件上传至网站根目录
	root * /www/wwwroot/r2wind.com/public
  // 配置SSL证书路径，若不配置，Caddy会帮你自动申请并配上
	tls /etc/ssl/caddy/r2wind.cn.crt /etc/ssl/caddy/r2wind.cn.key
  // 自定义错误页文件，若不需要删除即可
	handle_errors {
		rewrite * /{http.error.status_code}.html
		file_server
	}
  // 日志保存路径，如不需要保存访问日志可删除
	log {
		output file /www/log/r2wind_com.log
	}
  // 开启Gzip压缩，若不需要可删除
	encode gzip
	file_server
  // 用来添加响应头
	header {
    // 禁用了客户端的 MIME 类型嗅探行为，若不需要请删除
		X-content-type-tptions nosniff
    // 拒绝嵌入其他网站，若不需要请删除
		X-frame-options DENY
    // HSTS响应头，若不需要请删除
		Strict-Transport-Security max-age=63072000;includeSubDomains;preload
	}
}
// 一个反向代理示意配置
yjz.hk {
  // 指定代理网页访问地址https://xx.r2w.dev
	reverse_proxy https://xx.r2w.dev {
    // 指定请求域名hk.r2w.dev
		header_up Host {hk.r2w.dev}
	}
  // 下面的配置在上面已经介绍过了，这里不再过多赘述
	tls /etc/ssl/caddy/r2wind.cn.crt /etc/ssl/caddy/r2wind.cn.key
	handle_errors {
		rewrite * /{http.error.status_code}.html
		file_server
	}
	log {
		output file /www/log/yjz_hk.log
	}
	encode gzip
	file_server
	header {
		X-content-type-tptions nosniff
		x-xss-protection: 1; mode=block
		Strict-Transport-Security max-age=63072000;includeSubDomains;preload
	}
}
// 一个重定向示例配置
www.yjz.hk {
  // 指定重定向后的网站地址并携带相关参数
	redir https://yjz.hk{uri}
}
// 一个多域名重定向示例配置，多个域名记得用&amp;#34;,&amp;#34;隔开，逗号后记得先打空格再输入域名
dnstest.cc, www.dnstest.cc, r2wind.net, www.r2wind.net {
	redir https://r2wind.cn
}
// 一个多域名网站示例配置，和普通网站配置一样，只不过多了几个域名
r2wind.com，r2wind.net, r2wind.cn {
  // 此处配置网站根目录，请将页面文件上传至网站根目录
	root * /www/wwwroot/r2wind.com/public
  // 配置SSL证书路径，若不配置，Caddy会帮你自动申请并配上,注意：此处最好不要指定SSL证书，除非你的证书是多域名的
	tls /etc/ssl/caddy/r2wind.cn.crt /etc/ssl/caddy/r2wind.cn.key
  // 自定义错误页文件，若不需要删除即可
	handle_errors {
		rewrite * /{http.error.status_code}.html
		file_server
	}
  // 日志保存路径，如不需要保存访问日志可删除
	log {
		output file /www/log/r2wind_com.log
	}
  // 开启Gzip压缩，若不需要可删除
	encode gzip
	file_server
  // 用来添加响应头
	header {
    // 禁用了客户端的 MIME 类型嗅探行为，若不需要请删除
		X-content-type-tptions nosniff
    // 拒绝嵌入其他网站，若不需要请删除
		X-frame-options DENY
    // HSTS响应头，若不需要请删除
		Strict-Transport-Security max-age=63072000;includeSubDomains;preload
	}
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>dd命令</title>
    <link>https://blog.wiseai.cn/post/2023.03.20-linux%E5%91%BD%E4%BB%A4dd/</link>
    <pubDate>Mon, 20 Mar 2023 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.03.20-linux%E5%91%BD%E4%BB%A4dd/</guid>
    <description>
        &lt;p&gt;dd：用指定大小的块拷贝一个文件，并在拷贝的同时进行指定的转换。&lt;br&gt;
注意：指定数字的地方若以下列字符结尾，则乘以相应的数字：b=512；c=1；k=1024；w=2&lt;br&gt;
参数注释：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. if=文件名：输入文件名，缺省为标准输入。即指定源文件。&amp;lt; if=input file &amp;gt;  
2. of=文件名：输出文件名，缺省为标准输出。即指定目的文件。&amp;lt; of=output file &amp;gt;
3. ibs=bytes：一次读入bytes个字节，即指定一个块大小为bytes个字节。
4. obs=bytes：一次输出bytes个字节，即指定一个块大小为bytes个字节。
5. bs=bytes：同时设置读入/输出的块大小为bytes个字节。
6. cbs=bytes：一次转换bytes个字节，即指定转换缓冲区大小。
7. skip=blocks：从输入文件开头跳过blocks个块后再开始复制。
8. seek=blocks：从输出文件开头跳过blocks个块后再开始复制。
   注意：通常只用当输出文件是磁盘或磁带时才有效，即备份到磁盘或磁带时才有效。
9. count=blocks：仅拷贝blocks个块，块大小等于ibs指定的字节数。
10. conv=conversion：用指定的参数转换文件。
    ascii：转换ebcdic为ascii
    ebcdic：转换ascii为ebcdic
    ibm：转换ascii为alternate ebcdic
    block：把每一行转换为长度为cbs，不足部分用空格填充
    unblock：使每一行的长度都为cbs，不足部分用空格填充
    lcase：把大写字符转换为小写字符
    ucase：把小写字符转换为大写字符
    swab：交换输入的每对字节
    noerror：出错时不停止
    notrunc：不截短输出文件
    sync：将每个输入块填充到ibs个字节，不足部分用空（NUL）字符补齐。
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&#34;例子&#34;&gt;例子：&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;将本地的/dev/sda整盘备份到/dev/sdb
&lt;code&gt;# dd if=/dev/sda of=/dev/sdb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将/dev/sda全盘数据备份到指定路径的image文件
&lt;code&gt;# dd if=/dev/sda of=/root/image&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将备份文件恢复到指定盘
&lt;code&gt;# dd if=/root/image of=/dev/sda&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;备份/dev/hdb全盘数据，并利用gzip工具进行压缩，保存到指定路径
&lt;code&gt;# dd if=/dev/sda | gzip &amp;gt; /root/image.gz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将压缩的备份文件恢复到指定盘
&lt;code&gt;# gzip -dc /root/image.gz | dd of=/dev/sda&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;备份与恢复MBR
备份：磁盘开始的512个字节大小的MBR信息到指定文件：
&lt;code&gt;# dd if=/dev/sda of=/root/image count=1 bs=512&lt;/code&gt;
count=1指仅拷贝一个块；bs=512指块大小为512个字节。
恢复：
&lt;code&gt;# dd if=/root/image of=/dev/sda&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;制作光盘镜像
&lt;code&gt;# dd if=/dev/cdrom of=/root/cd.iso&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;增加swap分区文件大小
第一步：创建一个大小为256M的文件
&lt;code&gt;# dd if=/dev/zero of=/swapfile bs=1024k count=256&lt;/code&gt;
第二步：把这个文件变成swap文件：
&lt;code&gt;# mkswap /swapfile&lt;/code&gt;
第三步：启用这个swap文件：
&lt;code&gt;# swapon /swapfile&lt;/code&gt;
第四步：编辑/etc/fstab文件，使在每次开机时自动加载swap文件：
&lt;code&gt;# /swapfile swap swap default 0 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;销毁磁盘数据
&lt;code&gt;# dd if=/dev/urandom of=/dev/hda1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;测试硬盘的读写速度
&lt;code&gt;# dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file&lt;/code&gt;
&lt;code&gt;# dd if=/root/1Gb.file bs=64k | dd of=/dev/null&lt;/code&gt;
通过以上两个命令输出的命令执行时间，可以计算出硬盘的读、写速度。&lt;/li&gt;
&lt;li&gt;确定硬盘的最佳块大小：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# dd if=/dev/zero bs=1024 count=1000000 of=/root/1Gb.file
# dd if=/dev/zero bs=2048 count=500000 of=/root/1Gb.file
# dd if=/dev/zero bs=4096 count=250000 of=/root/1Gb.file
# dd if=/dev/zero bs=8192 count=125000 of=/root/1Gb.file
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;12&#34;&gt;
&lt;li&gt;修复硬盘
&lt;code&gt;# dd if=/dev/sda of=/dev/sda 或dd if=/dev/sdb of=/dev/sdb&lt;/code&gt;
当硬盘较长时间(一年以上)放置不使用后，磁盘上会产生magnetic flux point，当磁头读到这些区域时会遇到困难，并可能导致I/O错误。当这种情况影响到硬盘的第一个扇区时，可能导致硬盘报废。上边的命令有可能使这些数 据起死回生。并且这个过程是安全、高效的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&#34;https://www.cnblogs.com/ginvip/p/6370836.html&#34;&gt;https://www.cnblogs.com/ginvip/p/6370836.html&lt;/a&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>字体下载网站</title>
    <link>https://blog.wiseai.cn/post/2023.02.27-%E5%AD%97%E4%BD%93%E4%B8%8B%E8%BD%BD%E7%BD%91%E7%AB%99/</link>
    <pubDate>Mon, 27 Feb 2023 09:20:50 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.02.27-%E5%AD%97%E4%BD%93%E4%B8%8B%E8%BD%BD%E7%BD%91%E7%AB%99/</guid>
    <description>
        &lt;p&gt;&lt;a href=&#34;https://ziyouziti.com/&#34; title=&#34;自由字体&#34;&gt;自由字体&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://www.zcool.com.cn/special/zcoolfonts/&#34; title=&#34;站酷免费商用字体&#34;&gt;站酷免费商用字体&lt;/a&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>路由设置和route命令的使用</title>
    <link>https://blog.wiseai.cn/post/2023.02.13-%E8%B7%AF%E7%94%B1%E8%AE%BE%E7%BD%AE%E5%92%8Croute%E5%91%BD%E4%BB%A4%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Mon, 13 Feb 2023 14:44:41 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.02.13-%E8%B7%AF%E7%94%B1%E8%AE%BE%E7%BD%AE%E5%92%8Croute%E5%91%BD%E4%BB%A4%E7%9A%84%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;命令格式：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;route [-f] [-p] [Command [Destination] [mask Netmask] [Gateway] [metric Metric]] [if Interface]] &lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;命令功能：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Route命令是用于操作基于内核ip路由表，它的主要作用是创建一个静态路由让指定一个主机或者一个网络通过一个网络接口，如eth0。当使用&amp;quot;add&amp;quot;或者&amp;quot;del&amp;quot;参数时，路由表被修改，如果没有参数，则显示路由表当前的内容。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;命令参数：&lt;/p&gt;
&lt;p&gt;-c 显示更多信息&lt;/p&gt;
&lt;p&gt;-n 不解析名字&lt;/p&gt;
&lt;p&gt;-v 显示详细的处理信息&lt;/p&gt;
&lt;p&gt;-F 显示发送信息&lt;/p&gt;
&lt;p&gt;-C 显示路由缓存&lt;/p&gt;
&lt;p&gt;-f 清除所有网关入口的路由表。&lt;/p&gt;
&lt;p&gt;-p 与 add 命令一起使用时使路由具有永久性。&lt;/p&gt;
&lt;p&gt;add:添加一条新路由。&lt;/p&gt;
&lt;p&gt;del:删除一条路由。&lt;/p&gt;
&lt;p&gt;-net:目标地址是一个网络。&lt;/p&gt;
&lt;p&gt;-host:目标地址是一个主机。&lt;/p&gt;
&lt;p&gt;netmask:当添加一个网络路由时，需要使用网络掩码。&lt;/p&gt;
&lt;p&gt;gw:路由数据包通过网关。注意，你指定的网关必须能够达到。&lt;/p&gt;
&lt;p&gt;metric：设置路由跳数。&lt;/p&gt;
&lt;p&gt;Command 指定您想运行的命令 (Add/Change/Delete/Print)。&lt;/p&gt;
&lt;p&gt;Destination 指定该路由的网络目标。&lt;/p&gt;
&lt;p&gt;mask Netmask 指定与网络目标相关的网络掩码（也被称作子网掩码）。&lt;/p&gt;
&lt;p&gt;Gateway 指定网络目标定义的地址集和子网掩码可以到达的前进或下一跃点 IP 地址。&lt;/p&gt;
&lt;p&gt;metric Metric 为路由指定一个整数成本值标（从 1 至 9999），当在路由表(与转发的数据包目标地址最匹配)的多个路由中进行选择时可以使用。&lt;/p&gt;
&lt;p&gt;if Interface 为可以访问目标的接口指定接口索引。若要获得一个接口列表和它们相应的接口索引，使用 route print 命令的显示功能。可以使用十进制或十六进制值进行接口索引。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;oute 命令的输出项说明:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Destination	目标网段或者主机
Gateway	网关地址，”*” 表示目标是本主机所属的网络，不需要路由
Genmask	网络掩码
Flags	标记。一些可能的标记如下：
    U — 路由是活动的
    H — 目标是一个主机
    G — 路由指向网关
    R — 恢复动态路由产生的表项
    D — 由路由的后台程序动态地安装
    M — 由路由的后台程序修改
    ! — 拒绝路由
Metric	路由距离，到达指定网络所需的中转数（linux 内核中没有使用）
Ref	路由项引用次数（linux 内核中没有使用）
Use	此路由项被路由软件查找的次数
Iface	该路由表项对应的输出接口
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;route 命令使用举例&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加到主机的路由
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# route add -host 192.168.1.2 dev eth0 
# route add -host 10.20.30.148 gw 10.20.30.40
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;添加到网络的路由
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# route add -net 10.20.30.40 netmask 255.255.255.248 eth0   
# route add -net 10.20.30.48 netmask 255.255.255.248 gw 10.20.30.41 
# route add -net 192.168.1.0/24 eth1
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;添加默认路由
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# route add default gw 192.168.1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;删除路由
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# route del -host 192.168.1.2 dev eth0:0
# route del -host 10.20.30.148 gw 10.20.30.40
# route del -net 10.20.30.40 netmask 255.255.255.248 eth0
# route del -net 10.20.30.48 netmask 255.255.255.248 gw 10.20.30.41
# route del -net 192.168.1.0/24 eth1
# route del default gw 192.168.1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;WINDOWS下的route命令&lt;/p&gt;
&lt;p&gt;查看路由状态：routeprint&lt;/p&gt;
&lt;p&gt;只查看ipv4（ipv6）路由状态：route print-4(-6)&lt;/p&gt;
&lt;p&gt;添加路由：route add 目的网络 mask 子网掩码 网关——重启机器或网卡失效&lt;/p&gt;
&lt;p&gt;route add 192.168.20.0 mask 255.255.255.0192.168.10.1&lt;/p&gt;
&lt;p&gt;添加永久：route -p add 目的网络 mask子网掩码网关&lt;/p&gt;
&lt;p&gt;route -p add 192.168.20.0 mask 255.255.255.0192.168.10.1&lt;/p&gt;
&lt;p&gt;删除路由：route delete 目的网络 mask 子网掩码&lt;/p&gt;
&lt;p&gt;route delete 192.168.20.0 mask255.255.255.0&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

    </description>
    </item>
    
    <item>
    <title>Linux网卡设置</title>
    <link>https://blog.wiseai.cn/post/2023.02.13-linux%E7%BD%91%E5%8D%A1%E8%AE%BE%E7%BD%AE/</link>
    <pubDate>Mon, 13 Feb 2023 10:08:41 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.02.13-linux%E7%BD%91%E5%8D%A1%E8%AE%BE%E7%BD%AE/</guid>
    <description>
        &lt;p&gt;这里主要以树莓派的网卡设置为例，其它的linux发行版都差不多。&lt;br&gt;
树莓派的raspi-config工具，是设置网络最方便的，下面主要说说手动设置的方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有线网卡&lt;br&gt;
树莓派建议一个网口设置两个ip，这样，在固定ip的网络也可以连接，在dhpc的网络也可以连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auto eth0
iface eth0 inet static
        address 192.168.1.2
        netmask 255.255.255.0
        gateway 192.168.1.1

auto eth0:1
iface eth0:1 inet dhcp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个是我的配置文件，位置在/etc/network/interfaces.d/eth0，文件不存在建立一个就好.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;无线网卡&lt;br&gt;
iwconfig 命令查看当前无线网卡状态&lt;br&gt;
iwlist wlan0 scan 扫描周围无线网卡&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;新建/etc/network/interfaces.d/wlan0&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auto wlan0  #或者allow-hotplug
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;生成配置文件：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# wpa_passphrase &amp;lt;yourAPssid&amp;gt; &amp;lt;yourpassphrase&amp;gt; &amp;gt;&amp;gt; /etc/wpa_supplicant/wpa_supplicant.conf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意:&lt;/strong&gt;&amp;quot;&amp;raquo;&amp;ldquo;是追加的意思，不要用&amp;rdquo;&amp;gt;&amp;quot;。&lt;br&gt;
修改/etc/wpa_supplicant/wpa_supplicant.conf&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;network={
    ssid=&amp;#34;&amp;lt;yourAPssid&amp;gt;&amp;#34;
    #psk=&amp;#34;&amp;lt;yourpassphrase&amp;gt;&amp;#34;
    psk=f7caf61012a3fe193ce790164b88c3648bd5fc16cd0f007c84cf154d728b4f2d
}
//这部分是命令生成的，psk是加密后的密码，修改后为：
network={
    ssid=&amp;#34;&amp;lt;yourAPssid&amp;gt;&amp;#34;
    proto=RSN
    key_mgmt=WPA-PSK
    pairwise=CCMP TKIP
    group=CCMP TKIP
    scan_ssid=1
    disabled=1
    #psk=&amp;#34;&amp;lt;yourpassphrase&amp;gt;&amp;#34;
    psk=f7caf61012a3fe193ce790164b88c3648bd5fc16cd0f007c84cf154d728b4f2d
    priority=1
}
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;ssid：无线网络名称。

proto=RSN #Robust Security Network:强健安全网络，表示这个网络配置比WEP模式要更安全。

key_mgmt=WPA-PSK #请无论你是使用WPA-PSK，WPA2-PSK，都请在这里输入 WPA-PSK。这在wpa_supplicant看来WPA-PSK，WPA2-PSK都是 WPA-PSK。企业路由是WPA-EAP。如果没有密码，需要配置为NONE

pairwise=CCMP TKIP #关键点，wpa_supplicant目前还不认AES的加密标准

group=CCMP TKIP #同上

scan_ssid:如果ssid隐藏，这个参数必须配置

disabled=1:禁用这个网络

psk=7b271c9a7c8a6ac07d12403a1f0792d7d92b5957ff8dfd56481ced43ec6a6515 #wpa_supplicant算出来的加密密码。

priority:优先连接那个wifi
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;添加网关的方法：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;//添加默认网关
# route add default gw IP
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;具体看route命令的使用&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>PPTist在线演示文稿ppt</title>
    <link>https://blog.wiseai.cn/post/2023.02.07-pptist%E5%9C%A8%E7%BA%BF%E6%BC%94%E7%A4%BA%E6%96%87%E7%A8%BFppt/</link>
    <pubDate>Tue, 07 Feb 2023 10:06:31 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.02.07-pptist%E5%9C%A8%E7%BA%BF%E6%BC%94%E7%A4%BA%E6%96%87%E7%A8%BFppt/</guid>
    <description>
        &lt;h2 id=&#34;简介&#34;&gt;简介：&lt;/h2&gt;
&lt;p&gt;一个基于 Vue3.x + TypeScript 的在线演示文稿（幻灯片）应用，还原了大部分 Office PowerPoint 常用功能，支持 文字、图片、形状、线条、图表、表格、视频、音频、公式 几种最常用的元素类型，每一种元素都拥有高度可编辑能力，同时支持丰富的快捷键和右键菜单，支持导出本地 PPTX 文件，支持移动端基础编辑和预览，支持 PWA。&lt;/p&gt;
&lt;h2 id=&#34;源码位置&#34;&gt;源码位置：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/pipipi-pikachu/PPTist.git&#34;&gt;github: https://github.com/pipipi-pikachu/PPTist.git&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://gitee.com/mirrors/PPTist.git&#34;&gt;gitee: https://gitee.com/mirrors/PPTist.git&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;编译代码&#34;&gt;编译代码：&lt;/h2&gt;
&lt;p&gt;首先，需要搭建node.js环境&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# git clone https://github.com/pipipi-pikachu/PPTist.git
# npm install
# npm run serve
# npm run build //目录下会生成dist目录，这个就是可以发布的代码
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意：编译最新版本出现一个问题,“opensslErrorStack: [ ’error:03000086:digital envelope routines::initialization error’ ]&amp;quot;,出现这个错误是因为 node.js V17以上的版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制，解决办法是:&lt;br&gt;
&lt;code&gt;# export NODE_OPTIONS=--openssl-legacy-provider&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux性能分析工具</title>
    <link>https://blog.wiseai.cn/post/2023.01.30-linux%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/</link>
    <pubDate>Mon, 30 Jan 2023 18:04:19 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.01.30-linux%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/</guid>
    <description>
        &lt;img src=&#34;https://blog.wiseai.cn/images/xnfx1.png&#34; /&gt;
&lt;h3 id=&#34;vmstat--虚拟内存统计&#34;&gt;vmstat&amp;ndash;虚拟内存统计&lt;/h3&gt;
&lt;p&gt;vmstat（VirtualMeomoryStatistics，虚拟内存统计）是 Linux 中监控内存的常用工具,可对操作系统的虚拟内存、进程、CPU 等的整体情况进行监视。vmstat 的常规用法：&lt;code&gt;vmstat interval times&lt;/code&gt; 即每隔 interval 秒采样一次，共采样 times 次，如果省略 times，则一直采集数据，直到用户手动停止为止。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 1526016 161916  48016 1694424    2   14   257    73   34   90 13  7 80  0  0
 4  0 1526016 159584  48044 1694552    0    0     1   108 14151 28405 17 13 70  0  0
 1  0 1526016 158608  48064 1694568    0    0     1   426 14277 27948 19 12 69  0  0
 4  0 1526016 157600  48072 1694576    3    0     3    19 13744 27644 18 12 69  0  0 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以使用 ctrl+c 停止 vmstat 采集数据。&lt;/p&gt;
&lt;p&gt;第一行显示了系统自启动以来的平均值，第二行开始显示现在正在发生的情况，接下来的行会显示每5秒间隔发生了什么，每一列的含义在头部。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;procs：r 这一列显示了多少进程在等待cpu，b列显示多少进程正在不可中断的休眠（等待IO）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;memory：swapd 列显示了多少块被换出了磁盘（页面交换），剩下的列显示了多少块是空闲的（未被使用），多少块正在被用作缓冲区，以及多少正在被用作操作系统的缓存。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;swap：显示交换活动：每秒有多少块正在被换入（从磁盘）和换出（到磁盘）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;io：显示了多少块从块设备读取（bi）和写出（bo）,通常反映了硬盘I/O。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;system：显示每秒中断(in)和上下文切换（cs）的数量。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cpu：显示所有的cpu时间花费在各类操作的百分比，包括执行用户代码（非内核），执行系统代码（内核），空闲以及等待IO。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;内存不足的表现：free  memory 急剧减少，回收 buffer 和 cache 也无济于事，大量使用交换分区（swpd）,页面交换（swap）频繁，读写磁盘数量（io）增多，缺页中断（in）增多，上下文切换（cs）次数增多，等待IO的进程数（b）增多，大量CPU时间用于等待IO（wa）&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;iostat--用于报告中央处理器统计信息&#34;&gt;iostat&amp;ndash;用于报告中央处理器统计信息&lt;/h3&gt;
&lt;p&gt;iostat 用于报告中央处理器（CPU）统计信息和整个系统、适配器、tty 设备、磁盘和 CD-ROM 的输入/输出统计信息，默认显示了与 vmstat 相同的 cpu 使用信息，使用以下命令显示扩展的设备统计：&lt;/p&gt;
&lt;p&gt;iostat命令包含在sysstat中，所以首先要安装sysstat。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# iostat -dx 5
Device            r/s     w/s     rkB/s     wkB/s   rrqm/s   wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util
nvme0n1          0.20   13.60      0.80    182.40     0.00    17.20   0.00  55.84    0.00    0.06   0.09     4.00    13.41   6.84   9.44
scd0             0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
sda              0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
sdb              0.00    0.00      0.00      0.00     0.00     0.00   0.00   0.00    0.00    0.00   0.00     0.00     0.00   0.00   0.00
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第一行显示的是自系统启动以来的平均值，然后显示增量的平均值，每个设备一行。&lt;/p&gt;
&lt;p&gt;常见 linux 的磁盘 IO 指标的缩写习惯：rq 是 request，r 是 read，w 是 write，qu 是 queue，sz 是 size，a 是verage，tm 是 time，svc 是 service。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;rrqm/s 和 wrqm/s：每秒合并的读和写请求，“合并的”意味着操作系统从队列中拿出多个逻辑请求合并为一个请求到实际磁盘。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;r/s和w/s：每秒发送到设备的读和写请求数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rsec/s和wsec/s：每秒读和写的扇区数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;avgrq –sz：请求的扇区数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;avgqu –sz：在设备队列中等待的请求数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;await：每个IO请求花费的时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;svctm：实际请求（服务）时间。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;%util：至少有一个活跃请求所占时间的百分比。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;dstat--系统监控工具&#34;&gt;dstat&amp;ndash;系统监控工具&lt;/h3&gt;
&lt;p&gt;dstat 显示了 cpu 使用情况，磁盘 io 情况，网络发包情况和换页情况，输出是彩色的，可读性较强，相对于 vmstat 和iostat 的输入更加详细且较为直观。在使用时，直接输入命令即可，当然也可以使用特定参数。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# dstat –cdlmnpsy
--total-cpu-usage-- -dsk/total- ---load-avg--- ------memory-usage----- -net/total- ---procs--- ----swap--- ---system--
usr sys idl wai stl| read  writ| 1m   5m  15m | used  free  buff  cach| recv  send|run blk new| used  free| int   csw 
 13   3  83   1   0|2088k 1071k|4.61 4.94 4.87|4518M  232M  146M 2844M|   0     0 |3.0   0 5.9|  20M   11G|8498    17k
  8   3  89   0   0|   0    80k|4.61 4.94 4.87|4519M  231M  146M 2844M|1422B  888B|  0   0 4.0|  20M   11G|3964  7113 
  5   1  94   0   0|   0     0 |4.61 4.94 4.87|4519M  231M  146M 2844M| 410B  282B|1.0   0   0|  20M   11G|3542  6320 
  7   2  91   0   0|   0     0 |4.61 4.94 4.87|4515M  235M  146M 2844M| 303B   54B|  0   0 2.0|  20M   11G|3882  6835
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;iotop--linux进程实时监控工具&#34;&gt;iotop&amp;ndash;LINUX进程实时监控工具&lt;/h3&gt;
&lt;p&gt;iotop命令是专门显示硬盘IO的命令，界面风格类似top命令，可以显示IO负载具体是由哪个进程产生的。是一个用来监视磁盘I/O使用状况的top类工具，具有与top相似的UI，其中包括PID、用户、I/O、进程等相关信息。&lt;/p&gt;
&lt;p&gt;可以以非交互的方式使用：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# iotop –bod interval&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查看每个进程的 I/O，可以使用&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pidstat，pidstat –d instat&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;pidstat--监控系统资源情况&#34;&gt;pidstat&amp;ndash;监控系统资源情况&lt;/h3&gt;
&lt;p&gt;pidstat 主要用于监控全部或指定进程占用系统资源的情况,如 CPU,内存、设备 IO、任务切换、线程等。&lt;/p&gt;
&lt;p&gt;使用方法：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pidstat –d interval&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;pidstat 还可以用以统计CPU使用信息：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pidstat –u interval&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;统计内存信息：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pidstat –r interval&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;top&#34;&gt;top&lt;/h3&gt;
&lt;p&gt;top 命令的汇总区域显示了五个方面的系统性能信息：&lt;/p&gt;
&lt;p&gt;负载：时间，登陆用户数，系统平均负载；&lt;/p&gt;
&lt;p&gt;进程：运行，睡眠，停止，僵尸；&lt;/p&gt;
&lt;p&gt;cpu:用户态，核心态，NICE,空闲，等待IO,中断等；&lt;/p&gt;
&lt;p&gt;内存：总量，已用，空闲（系统角度），缓冲，缓存；&lt;/p&gt;
&lt;p&gt;交换分区：总量，已用，空闲&lt;/p&gt;
&lt;p&gt;任务区域默认显示：进程 ID，有效用户，进程优先级，NICE 值，进程使用的虚拟内存，物理内存和共享内存，进程状态，CPU 占用率，内存占用率，累计 CPU 时间，进程命令行信息。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;htop&#34;&gt;htop&lt;/h3&gt;
&lt;p&gt;htop 是 Linux 系统中的一个互动的进程查看器,一个文本模式的应用程序(在控制台或者X终端中),需要 ncurses。&lt;/p&gt;
&lt;p&gt;Htop 可让用户交互式操作，支持颜色主题，可横向或纵向滚动浏览进程列表，并支持鼠标操作。&lt;/p&gt;
&lt;p&gt;与 top 相比，htop 有以下优点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;可以横向或者纵向滚动浏览进程列表，以便看到所有的进程和完整的命令行。
在启动上，比top更快。
杀进程时不需要输入进程号。
htop支持鼠标操作。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&#34;mpstat&#34;&gt;mpstat&lt;/h3&gt;
&lt;p&gt;mpstat 是 Multiprocessor Statistics的缩写，是实时系统监控工具。其报告CPU的一些统计信息，这些信息存放在 /proc/stat 文件中。在多 CPUs 系统里，其不但能查看所有 CPU 的平均状况信息，而且能够查看特定 CPU 的信息。常见用法：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mpstat –P ALL interval times&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;netstat&#34;&gt;netstat&lt;/h3&gt;
&lt;p&gt;netstat 用于显示与 IP、TCP、UDP和 ICMP 协议相关的统计数据，一般用于检验本机各端口的网络连接情况。&lt;/p&gt;
&lt;p&gt;常见用法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# netstat –npl   # 可以查看你要打开的端口是否已经打开。

# netstat –rn    # 打印路由表信息。

# netstat –in    # 提供系统上的接口信息，打印每个接口的MTU,输入分组数，输入错误，输出分组数，输出错误，冲突以及当前的输出队列的长度。
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;ps--显示当前进程的状态&#34;&gt;ps&amp;ndash;显示当前进程的状态&lt;/h3&gt;
&lt;p&gt;ps 参数太多，具体使用方法可以参考 man ps&lt;/p&gt;
&lt;p&gt;常用的方法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# ps  aux     

# ps –ef |grep  
杀掉某一程序的方法：

# ps  aux | grep mysqld | grep –v grep | awk ‘{print $2 }’ xargs kill -9
杀掉僵尸进程：

# ps –eal | awk ‘{if ($2 == “Z”){print $4}}’ | xargs kill -9
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;strace&#34;&gt;strace&lt;/h3&gt;
&lt;p&gt;跟踪程序执行过程中产生的系统调用及接收到的信号，帮助分析程序或命令执行中遇到的异常情况。&lt;/p&gt;
&lt;p&gt;举例：查看 mysqld 在 linux 上加载哪种配置文件，可以通过运行下面的命令：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# strace –e stat64 mysqld –print –defaults &amp;gt; /dev/null&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;uptime&#34;&gt;uptime&lt;/h3&gt;
&lt;p&gt;能够打印系统总共运行了多长时间和系统的平均负载，uptime 命令最后输出的三个数字的含义分别是 1分钟，5分钟，15分钟内系统的平均负荷。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;lsof&#34;&gt;lsof&lt;/h3&gt;
&lt;p&gt;lsof（list open files）是一个列出当前系统打开文件的工具。通过 lsof 工具能够查看这个列表对系统检测及排错，常见的用法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;查看文件系统阻塞
# lsof /boot

查看端口号被哪个进程占用
# lsof  -i : 3306

查看用户打开哪些文件
# lsof –u username

查看进程打开哪些文件
# lsof –p  4838

查看远程已打开的网络链接
# lsof –i @192.168.34.128
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;perf&#34;&gt;perf&lt;/h3&gt;
&lt;p&gt;perf 是 Linux kernel 自带的系统性能优化工具。优势在于与 Linux Kernel 的紧密结合，它可以最先应用到加入 Kernel 的new feature，用于查看热点函数，查看 cashe miss 的比率，从而帮助开发者来优化程序性能。&lt;/p&gt;
&lt;p&gt;性能调优工具如 perf，Oprofile 等的基本原理都是对被监测对象进行采样，最简单的情形是根据 tick 中断进行采样，即在 tick 中断内触发采样点，在采样点里判断程序当时的上下文。&lt;/p&gt;
&lt;p&gt;假如一个程序 90% 的时间都花费在函数 foo() 上，那么 90% 的采样点都应该落在函数 foo() 的上下文中。运气不可捉摸，但我想只要采样频率足够高，采样时间足够长，那么以上推论就比较可靠。因此，通过 tick 触发采样，我们便可以了解程序中哪些地方最耗时间，从而重点分析。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;来源：https://rdc.hundsun.com/portal/article/731.html&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Fsearch的安装及使用</title>
    <link>https://blog.wiseai.cn/post/2023.01.27-fsearch%E7%9A%84%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Fri, 27 Jan 2023 17:40:07 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2023.01.27-fsearch%E7%9A%84%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h3 id=&#34;简介&#34;&gt;简介：&lt;/h3&gt;
&lt;p&gt;fsearch是一个全平台的，开源的，文件及文件夹搜索软件，它基于GTK3，用C语言编写，可以说是“Everything”的开源免费版本，速度相当快，体会一下就知道了。&lt;/p&gt;
&lt;p&gt;今天主要说一下编译安装的方法，为什么呢？X86平台的直接去官网下载就可以了，但是arm处理器的怎么办？所以记录一下编译安装的方法，编译的版本为0.2.2,如有更新的版本，如果出现问题请查看官网编译安装说明。&lt;/p&gt;
&lt;h3 id=&#34;编译安装的方法&#34;&gt;编译安装的方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;安装依赖软件：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# sudo apt install git build-essential meson itstool libtool pkg-config intltool libicu-dev libpcre2-dev libglib2.0-dev libgtk-3-dev libxml2-utils appstream-util
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;拉取源文件&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# git clone https://github.com/cboxdoerfer/fsearch.git
# git checkout 0.2.2 //选择编译版本
# cd fsearch
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;开始编译&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;meson builddir
ninja -C builddir install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;完成！&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Goframe开发工具gf的使用</title>
    <link>https://blog.wiseai.cn/post/2022.12.23-goframe%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7gf%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Fri, 23 Dec 2022 15:58:20 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2022.12.23-goframe%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7gf%E7%9A%84%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h3 id=&#34;创建项目&#34;&gt;创建项目&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;在当前目录下初始化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# gf init .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;注：后面那个&amp;quot;.&amp;ldquo;不要忘记&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个单独的项目&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# gf init test&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建一个MonoRepo项目（大仓）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# gf init bigtest -m&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;升级框架&#34;&gt;升级框架&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# gf up -a&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;运行程序&#34;&gt;运行程序&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# gf run main.go&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>systemd延时启动服务</title>
    <link>https://blog.wiseai.cn/post/2022.12.22-systemd%E5%BB%B6%E6%97%B6%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1/</link>
    <pubDate>Thu, 22 Dec 2022 09:54:24 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2022.12.22-systemd%E5%BB%B6%E6%97%B6%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1/</guid>
    <description>
        &lt;p&gt;原因：我的docker容器链接了硬盘上的一个文件夹，但是docker服务在硬盘没有挂载前就启动，容器在目录下自动生成了链接文件夹，导致系统挂载的硬盘名改变，延时启动可以解决这个问题，方法如下：&lt;/p&gt;
&lt;p&gt;编辑&lt;code&gt;/lib/systemd/system/docker.service&lt;/code&gt;文件&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Service]
#在这里添加下面两行，就可以延时5分钟启动服务
TimeoutStartSec=infinity
ExecStartPre=/bin/sleep 300
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;参考：&lt;a href=&#34;https://blog.csdn.net/lggirls/article/details/128133815&#34;&gt;使用systemd配置一个服务再开机后5分钟再启动&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感谢作者！&lt;/strong&gt; 原文如下：&lt;/p&gt;
&lt;p&gt;原因：我们的linux服务器上可能会有许多服务要启动，如果再开机后要立即启动的服务过多，很可能会造成开机速度过慢，这时可以对不同的服务进行时间安排，有序的延迟一部分服务的启动。&lt;/p&gt;
&lt;p&gt;这里以透传软件 frpc.service 为例。&lt;/p&gt;
&lt;h3 id=&#34;一单配置文件的实现模式&#34;&gt;一、单配置文件的实现模式&lt;/h3&gt;
&lt;p&gt;也就是直接对frpc.service进行配置&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Unit]
Description=Frp Client Service
After=network.target
 
[Service]
Type=simple
User=nobody
# 下面两行设定了再开机后300秒再启动服务
TimeoutStartSec=infinity
ExecStartPre=/bin/sleep 300
 
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frpc -c /etc/frp/frpc.ini
ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.ini
LimitNOFILE=1048576
 
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;二利用额外的timer服务实现&#34;&gt;二、利用额外的timer服务实现&lt;/h3&gt;
&lt;p&gt;这样做的好处是，可以额外增加一项设定，配置服务再持续运行24、48小时后重启一次，用于不关机，但需要对某个服务进行定期重启的情况。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对 frpc.service 进行修改&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[Unit]
Description=Frp Client Service
 
[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frpc -c /etc/frp/frpc.ini
ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.ini
LimitNOFILE=1048576
注意，这里的frpc.service 中没有 [Install] 这一部分的设置！！！
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;新建一个用于启动frpc.service的timer&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;vim /lib/systemd/system/frpc.timer

[Unit]
Description=timer for frpc service
 
[Timer]
OnBootSec=5min
OnUnitActiveSec=24h
 
[Install]
WantedBy=timers.target
这里设定了frpc.service 在开机后5分钟再启动，并且运行24小时后重启一次
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;启动相关的服务&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;systemctl disable frpc.service   # 如果frpc.service 之前不是enable状态，则不需要这一步
systemctl start frpc.timer
systemctl enable frpc.timer
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>文件同步Syncthing的使用</title>
    <link>https://blog.wiseai.cn/post/2022.12.21-syncthing%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Wed, 21 Dec 2022 09:46:22 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2022.12.21-syncthing%E7%9A%84%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h3 id=&#34;1下载及编译&#34;&gt;1.下载及编译&lt;/h3&gt;
&lt;p&gt;源码下载地址：https://github.com/syncthing/syncthing&lt;/p&gt;
&lt;p&gt;gitee镜像：https://gitee.com/mirrors/syncthing?_from=gitee_search&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# go build build.go&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在bin目录下会编译生成所有程序，我们主要使用syncthing这个程序&lt;/p&gt;
&lt;h3 id=&#34;2使用&#34;&gt;2.使用&lt;/h3&gt;
&lt;p&gt;直接运行程序&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ./syncthing&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;首次运行会生成配置文件，位置在&lt;code&gt;~/.confing/syncthing/&lt;/code&gt;，配置文件主要是&lt;code&gt;config.xml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同时会打开127.0.0.1：8384网站，用于配置软件。&lt;/p&gt;
&lt;p&gt;修改config.xml中的127.0.0.1为0.0.0.0，就可以远程打开配置网站，但是不建议这么做，存在安全隐患，建议设置为固定IP，用于管理。&lt;/p&gt;
&lt;p&gt;必须修改网站登陆密码，&lt;strong&gt;操作-&amp;gt;设置-&amp;gt;图形用户界面&lt;/strong&gt;，设置用户名及密码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;操作-&amp;gt;设置-&amp;gt;连接&lt;/strong&gt;，可设置NAT、全局发现、本地发现、中继等，在安全要求较高的情况下，建议全部不要选择。&lt;/p&gt;
&lt;p&gt;添加文件夹，文件夹ID非常重要，共享时必须一致。&lt;/p&gt;
&lt;p&gt;添加远程设备，设备ID在&lt;strong&gt;操作-&amp;gt;显示ID&lt;/strong&gt;处查询，在不启用全局发现和本地发现的情况下，必须设置地址列表&lt;strong&gt;添加设备-&amp;gt;高级-&amp;gt;地址列表&lt;/strong&gt;为tcp://ip地址:22000&lt;/p&gt;
&lt;p&gt;设置共享，在文件夹和远程设备选项中都可以设置共享，共享文件夹可以双向同步，也可单向同步，文件夹类型只能在文件夹选项中设置。&lt;/p&gt;
&lt;p&gt;加密，如果在共享时设置了密码，远程设备的文件夹必须为加密接收，否则会出现失去同步的问题。&lt;/p&gt;
&lt;p&gt;解密，加密接收的文件夹可以通过&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# syncthing decrypt (--to=&amp;lt;dir&amp;gt; | --verify-only) [--password=&amp;lt;pw&amp;gt;] [--folder-id=&amp;lt;id&amp;gt;] [--token-path=&amp;lt;file&amp;gt;] [--continue] [--verbose] [--version] [--help] &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;进行解密。&lt;/p&gt;
&lt;p&gt;主要参数有：&lt;code&gt;	--to  解密位置，	--password 加密的密码，	最后的&amp;lt;path&amp;gt;是需要解密的目录&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样基本使用就没有问题了，其它功能遇到了再添加说明。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Nging的使用</title>
    <link>https://blog.wiseai.cn/post/2022.11.12-nging%E7%9A%84%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Sat, 12 Nov 2022 14:29:26 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/2022.11.12-nging%E7%9A%84%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;p&gt;Nging是一个网站服务程序，可以代替Nginx或Apache来搭建Web开发测试环境，并附带了实用的周边工具，例如：计划任务、MySQL管理、Redis管理、FTP管理、SSH管理、服务器管理等。&lt;/p&gt;
&lt;p&gt;这个软件项目不仅仅实现了一些网站服务工具，本身还是一个具有很好扩展性的通用网站后台管理系统，通过本项目，可以很轻松的构建一个全新的网站项目，省去从头构建项目的麻烦，减少重复性劳动。&lt;/p&gt;
&lt;p&gt;下载地址：https://dl.webx.top/nging/latest/&lt;/p&gt;
&lt;p&gt;使用可以看说明，非常简单，&lt;strong&gt;注意：&lt;/strong&gt; 在添加服务的时候要用root权限，其它的操作可以参考systemctl命令，服务名称是Nging，N是大写。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;git clone &lt;a href=&#34;https://github.com/admpub/nging.git&#34;&gt;https://github.com/admpub/nging.git&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这一步必须完整clone，否则会出现找不到文件的错误。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout v4.2.4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;checkout到版本号&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;将&lt;code&gt;go/bin&lt;/code&gt;加入PATH&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# PATH=PATH:~/go/bin&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;进入tool目录下使用bash执行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# bash build-all-platform.sh linux_amd64&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这是如果不加linux_amd64，则编译全平台的。&lt;/p&gt;
&lt;p&gt;最后，如果觉得广告不好可以去掉，但是不建议这么做，毕竟作者开源就很不错了，如果你有强迫症，可以这样做，在template目录下，查找TrackerHTML，这个是自动查检更新的js，作者顺带着加了个广告，去掉就没有广告了，强烈建议不要去。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;手头宽裕的可以支持下作者，支持作者更好开发软件！！！&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;使用80端口&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以使用源码编译，这个需要修改源代码，比较简单的是使用软件自带的网站管理功能。&lt;/p&gt;
&lt;p&gt;添加个网站，监听80端口&lt;code&gt;:80&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选择代理转发&lt;code&gt;Proxy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选择预置功能中的两个选项，websocket和transparent，用于支持基于浏览器的终端服务pty和xterm&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Goframe开发工具gf的编译方法</title>
    <link>https://blog.wiseai.cn/post/goframe%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7gf%E7%9A%84%E7%BC%96%E8%AF%91%E6%96%B9%E6%B3%95/</link>
    <pubDate>Tue, 20 Sep 2022 10:52:06 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/goframe%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7gf%E7%9A%84%E7%BC%96%E8%AF%91%E6%96%B9%E6%B3%95/</guid>
    <description>
        &lt;h4 id=&#34;这里主要说明自行编译的方法&#34;&gt;这里主要说明自行编译的方法&lt;/h4&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/gogf/gf&#34;&gt;点击下载源码&lt;/a&gt; ，这里最好放在&lt;code&gt;~/go/src/github/&lt;/code&gt;下，方便导入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;根据自己的需要修改数据库依赖，文件/cmd/gf/internal/cmd/cmd_gen_dao.go的import。sqlite 和oracle 的驱动需要安装cgo环境。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入到gf cli的源码目录/cmd/gf 编译&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# go build main.go&lt;/code&gt;或者&lt;code&gt;gf build main.go&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这里说明几个需要注意的事项:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;编译的时候最好使用以前编译的gf编译，用&lt;code&gt;go build&lt;/code&gt;不能写入gf的信息&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GoFrame CLI Tool v2.1.4, https://goframe.org
GoFrame Version: v2.1.0 in current go.mod
CLI Installed At: /home/xjc/go/src/github.com/gogf/gf/cmd/gf/gf
Current is a custom installed version, no installation information.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;用&lt;code&gt;gf build&lt;/code&gt;就有编译信息了&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GoFrame CLI Tool v2.1.4, https://goframe.org
GoFrame Version: v2.1.0 in current go.mod
CLI Installed At: /usr/bin/gf
CLI Built Detail:
  Go Version:  go1.19.1
  GF Version:  v2.1.4
  Git Commit:  none
  Build Time:  2022-09-20 10:24:57
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里的Git Commit没有内容，主要是懒得去加。
Git Commit怎么用go语言取值没有去找，比较直接的方法是修改这个文件&lt;code&gt;github.com/gogf/gf/cmd/gf/internal/cmd/cmd_version.go&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if info.Git == &amp;#34;&amp;#34; {
    info.Git = &amp;#34;none&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;意思是如果这个值为空，就为none，最直接的就是把none改为Git Commit的值。&lt;/p&gt;
&lt;hr&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;如果使用sqlite和oracle数据库，必需使用cgo环境编译。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# CGO_ENABLED=1 go build main.go&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;涉及gf编译的文件有以下几个：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;github.com/gogf/gf/cmd/gf/internal/cmd/cmd_gen_dao.go
github.com/gogf/gf/cmd/gf/internal/cmd/cmd_version.go
github.com/gogf/gf/os/gbuild/gbuild.go
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Goframe中数据库配置</title>
    <link>https://blog.wiseai.cn/post/goframe%E4%B8%AD%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AE/</link>
    <pubDate>Fri, 16 Sep 2022 10:55:47 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/goframe%E4%B8%AD%E6%95%B0%E6%8D%AE%E5%BA%93%E9%85%8D%E7%BD%AE/</guid>
    <description>
        &lt;p&gt;goframe中获取数据库操作对象有三种方式，一种是使用g.DB方法（推荐），一种是使用原生gdb.New方法，还有一种是使用包原生单例方法gdb.Instance&lt;/p&gt;
&lt;h3 id=&#34;配置文件&#34;&gt;配置文件&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{
	&amp;#34;database&amp;#34;: {
		&amp;#34;default&amp;#34;: 
			[{&amp;#34;link&amp;#34;: &amp;#34;sqlite:/home/xjc/go/src/gitee/test-gf/testGf.db&amp;#34;, &amp;#34;debug&amp;#34;: &amp;#34;true&amp;#34;}],
		&amp;#34;focus&amp;#34;: 
			[{&amp;#34;link&amp;#34;: &amp;#34;mysql:focus:sadmQHH*i@tcp(192.168.1.1:3306)/focus&amp;#34;, &amp;#34;debug&amp;#34;: &amp;#34;true&amp;#34;, &amp;#34;role&amp;#34;: &amp;#34;master&amp;#34;}]
			[{&amp;#34;link&amp;#34;: &amp;#34;mysql:focus:sadmQHH*i@tcp(192.168.1.2:3306)/focus&amp;#34;, &amp;#34;debug&amp;#34;: &amp;#34;true&amp;#34;, &amp;#34;role&amp;#34;: &amp;#34;slave&amp;#34;}]
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里是json格式，也可以使用yaml，这样配置的原因是可以方便配置集群模式，&lt;a href=&#34;https://goframe.org/&#34;&gt;具体查看文档&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;加载数据库驱动&#34;&gt;加载数据库驱动&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/gogf/gf/tree/master/contrib/drivers&#34;&gt;这里找加载方法&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mport (
    _ &amp;#34;github.com/gogf/gf/contrib/drivers/sqlite/v2&amp;#34; 
    _ &amp;#34;github.com/gogf/gf/contrib/drivers/mysql/v2&amp;#34;
)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;gdb方法推荐&#34;&gt;g.DB方法（推荐）&lt;/h3&gt;
&lt;p&gt;加载数据库驱动，&lt;a href=&#34;https://github.com/gogf/gf/tree/master/contrib/drivers&#34;&gt;这里找加载方法&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import (
    &amp;#34;github.com/gogf/gf/v2/frame/g&amp;#34;
)

// 获取默认配置的数据库对象(配置名称为&amp;#34;default&amp;#34;)
db := g.DB()

// 获取配置分组名称为&amp;#34;user&amp;#34;的数据库对象
db := g.DB(&amp;#34;user&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;g.DB对象管理方式获取的是单例对象，整合了配置文件的管理功能，支持配置文件热更新。&lt;/p&gt;
&lt;h3 id=&#34;gdbnew是根据给定的数据库节点配置创建一个新的数据库对象非单例无法使用配置文件这里有坑&#34;&gt;gdb.New是根据给定的数据库节点配置创建一个新的数据库对象(非单例)，无法使用配置文件。（这里有坑）&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;db, err := gdb.New(gdb.ConfigNode{
        Host     : &amp;#34;/home/xjc/go/src/gitee/test-gf/&amp;#34;,
        Name     : &amp;#34;testGf.db&amp;#34;,
        Type     : &amp;#34;sqlite&amp;#34;,
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;这里不支持link写法，注意这里sqlite的写法&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;gdbinstance是包原生单例管理方法需要结合配置方法一起使用通过分组名称非必需获取对应配置的数据库单例对象&#34;&gt;gdb.Instance是包原生单例管理方法，需要结合配置方法一起使用，通过分组名称(非必需)获取对应配置的数据库单例对象。&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;gdb.SetConfig(gdb.Config {
    &amp;#34;default&amp;#34; : gdb.ConfigGroup {
        gdb.ConfigNode {
            Link: &amp;#34;sqlite:/home/xjc/go/src/gitee/test-gf/testGf.db&amp;#34;,
        },
    },
    &amp;#34;focus&amp;#34; : gdb.ConfigGroup {
        gdb.ConfigNode {
            Link: &amp;#34;mysql:focus:sadmQHH*i@tcp(192.168.1.1:3306)/focus&amp;#34;,
        },
    },
})

//数据库dufault
db, err := gdb.Instance()
//数据库focus
db, err := gdb.Instance(&amp;#34;focus&amp;#34;)
//或
//数据库dufault
db, err := gdb.NewByGroup()
//数据库focus
db, err := gdb.NewByGroup(&amp;#34;focus&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;使用该配置方式时为保证数据库安全默认底层不支持多行sql语句执行为了得到更多配置项控制请参考推荐的简化配置同时建议您务必了解清楚简化配置项中每个连接参数的功能作用&#34;&gt;使用该配置方式时，为保证数据库安全，默认底层不支持多行SQL语句执行。为了得到更多配置项控制，请参考推荐的简化配置，同时建议您务必了解清楚简化配置项中每个连接参数的功能作用。&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;database:
  分组名称:
    host:                  &amp;#34;地址&amp;#34;
    port:                  &amp;#34;端口&amp;#34;
    user:                  &amp;#34;账号&amp;#34;
    pass:                  &amp;#34;密码&amp;#34;
    name:                  &amp;#34;数据库名称&amp;#34;
    type:                  &amp;#34;数据库类型(mysql/pgsql/mssql/sqlite/oracle)&amp;#34;
    link:                  &amp;#34;(可选)自定义数据库链接信息，当该字段被设置值时，以上链接字段(Host,Port,User,Pass,Name)将失效，但是type必须有值&amp;#34;         
    role:                  &amp;#34;(可选)数据库主从角色(master/slave)，不使用应用层的主从机制请均设置为master&amp;#34;
    debug:                 &amp;#34;(可选)开启调试模式&amp;#34;
    prefix:                &amp;#34;(可选)表名前缀&amp;#34;
    dryRun:                &amp;#34;(可选)ORM空跑(只读不写)&amp;#34;
    charset:               &amp;#34;(可选)数据库编码(如: utf8/gbk/gb2312)，一般设置为utf8&amp;#34;
    weight:                &amp;#34;(可选)负载均衡权重，用于负载均衡控制，不使用应用层的负载均衡机制请置空&amp;#34;
    timezone:              &amp;#34;(可选)时区配置，例如:local&amp;#34;
    maxIdle:               &amp;#34;(可选)连接池最大闲置的连接数&amp;#34;
    maxOpen:               &amp;#34;(可选)连接池最大打开的连接数&amp;#34;
    maxLifetime:           &amp;#34;(可选)连接对象可重复使用的时间长度&amp;#34;
    createdAt:             &amp;#34;(可选)自动创建时间字段名称&amp;#34;
    updatedAt:             &amp;#34;(可选)自动更新时间字段名称&amp;#34;
    deletedAt:             &amp;#34;(可选)软删除时间字段名称&amp;#34;
    timeMaintainDisabled:  &amp;#34;(可选)是否完全关闭时间更新特性，true时CreatedAt/UpdatedAt/DeletedAt都将失效&amp;#34;
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>什么是高级的欲望?</title>
    <link>https://blog.wiseai.cn/post/%E4%BB%80%E4%B9%88%E6%98%AF%E9%AB%98%E7%BA%A7%E7%9A%84%E6%AC%B2%E6%9C%9B/</link>
    <pubDate>Wed, 07 Sep 2022 17:35:02 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E4%BB%80%E4%B9%88%E6%98%AF%E9%AB%98%E7%BA%A7%E7%9A%84%E6%AC%B2%E6%9C%9B/</guid>
    <description>
        &lt;p&gt;01&lt;/p&gt;
&lt;p&gt;还记得那个北大数学扫地僧韦东奕吗？&lt;/p&gt;
&lt;p&gt;随着他一手馒头一手矿泉水的照片在网上走红，他在数学方面的惊人战绩也被挖了出来，于是被人尊称为“韦神”。&lt;/p&gt;
&lt;img src=&#34;https://blog.wiseai.cn/images/高级欲望1.png&#34; width=&#34;100%&#34;&gt;
&lt;p&gt;2个月前，有人在北大食堂偶遇“韦神”，于是拍下了这张照片。&lt;/p&gt;
&lt;img src=&#34;https://blog.wiseai.cn/images/高级欲望4.jpeg&#34; width=&#34;100%&#34;&gt;
&lt;p&gt;只见“韦神”穿着朴素，坐在狭小的位置里专心干饭，丝毫未察觉周围人的目光。&lt;/p&gt;
&lt;p&gt;有人好奇他午餐都吃些什么。&lt;/p&gt;
&lt;p&gt;巧的是，前两天又有人在同一地点遇到了他，拍下了他的饭和伞。&lt;/p&gt;
&lt;img src=&#34;https://blog.wiseai.cn/images/高级欲望3.jpeg&#34; width=&#34;100%&#34;&gt;
&lt;img src=&#34;https://blog.wiseai.cn/images/高级欲望2.jpeg&#34; width=&#34;100%&#34;&gt;
&lt;p&gt;这个配置，用“寒酸”二字来形容一点不为过。&lt;/p&gt;
&lt;p&gt;按理说，一个北大的助教，怎么也不至于“沦落”到这番境地。&lt;/p&gt;
&lt;p&gt;但其实，这种“寒酸”并非他被动选择的结果，而是他主动坚持的日常。&lt;/p&gt;
&lt;p&gt;有人不理解：&lt;/p&gt;
&lt;p&gt;“这样的男生在婚恋市场上会受女生欢迎吗？”&lt;/p&gt;
&lt;p&gt;“这样活着，还有什么意思？”&lt;/p&gt;
&lt;p&gt;事实上，“韦神”的世界，不是我们普通人能理解的。&lt;/p&gt;
&lt;p&gt;你觉得他朴素节俭的日常很无趣，他却沉迷在数学的世界里自得其乐。&lt;/p&gt;
&lt;p&gt;数学带给他的乐趣，远远超过了吃什么喝什么穿什么。&lt;/p&gt;
&lt;p&gt;对于他来说，食物就是用来饱腹的，衣服就是用来遮体取暖的，床就是用来睡觉的，有吃有穿有睡就已足够。&lt;/p&gt;
&lt;p&gt;他的物质生活虽然朴素简单，但是他的精神世界，远比我们绝大多数人浩瀚得多。&lt;/p&gt;
&lt;p&gt;02&lt;/p&gt;
&lt;p&gt;“韦神”的朴素，让我想起那个践行“不消费主义”的乔桑。&lt;/p&gt;
&lt;p&gt;在《90后女生“抠门”日常曝光，获赞31万：这样的穷人，活得有多爽？》一文中，我提到了她的极简生活方式：&lt;/p&gt;
&lt;p&gt;快用完的牙膏，要把包装剪开，再多用一个月，直到把剩余的牙膏用得干干净净。和邻居以物换物，用黑豆、燕麦，换来邻居的荔枝、猕猴桃、西瓜、排骨汤。买菜时跟老板要那些长得不好看、有点焉儿、没人要的菜，便宜买。一年四季的衣服只有16件，一件卫衣和运动裤可以穿十几天，脏了就洗，第二天干了接着穿。……&lt;/p&gt;
&lt;p&gt;这种生活方式，乔桑已经坚持了600多天，不是因为穷，而是因为削减了不必要的欲望。&lt;/p&gt;
&lt;p&gt;但很多网友不理解，评论中也充斥着许多这样的言论：&lt;/p&gt;
&lt;p&gt;“为什么要活得像开工厂一样？”“这样的生活其实和行尸走肉没有区别。”“什么时候生活只剩穿衣服和洗衣服了，我可能没生存在地球上，无法理解。”&lt;/p&gt;
&lt;p&gt;在他们眼里，乔桑的生活方式很极端也很无趣。&lt;/p&gt;
&lt;p&gt;但在她看来，只为必须的物品付钱，珍惜粮食和蔬菜，把简化生活后空出来的时间和精力，关注健康和心灵，是让生活回归生活的最好方式。&lt;/p&gt;
&lt;p&gt;虽然乔桑的物质生活很简陋，但她的精神世界却很富足。&lt;/p&gt;
&lt;p&gt;因为原本用于购物、消费、以及打理过多物品的时间，都被她腾出来思考、读书和做瑜伽。&lt;/p&gt;
&lt;p&gt;在这个过程中，她发现自己喜欢上了读书，通勤和工作之余，她都在“啃书”，两个月可以看20多本。&lt;/p&gt;
&lt;p&gt;“原来看一本书，比买一件衣服充实多了”，这是她坚持“不消费主义”后的真实感受。&lt;/p&gt;
&lt;p&gt;为了让阅读的好习惯惠及他人，她还开了自己的“流浪书店”，把网友、朋友和自己的旧书收集起来，消毒、贴标签，一排排码在墙角，为周围邻居提供免费借阅。&lt;/p&gt;
&lt;p&gt;目前，她已经收集到了整整一面墙的书。&lt;/p&gt;
&lt;p&gt;03&lt;/p&gt;
&lt;p&gt;“韦神”和乔桑，都不是物质真正匮乏的一类人。&lt;/p&gt;
&lt;p&gt;他们所呈现出的“抠门”状态，其实是高级欲望替代了低级欲望之后的结果。&lt;/p&gt;
&lt;h3 id=&#34;什么是高级的欲望&#34;&gt;什么是高级的欲望？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;周国平说，高级欲望指人的精神需要，它也是人性的组成部分。人一旦品尝到和陶醉于更高层次的快乐，他面对形形色色的较低快乐的诱惑就自然有了“定力”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;简单来说，高级的欲望是一个创造的过程，它回馈给人的是幸福感和成就感，比如学习欲。&lt;/p&gt;
&lt;p&gt;而低级的欲望是一个消费的过程，它带来的是失落、沮丧和懊恼，比如物质欲。&lt;/p&gt;
&lt;p&gt;这里的消费，除了真金白银的消耗，也包括时间和精力的消耗。比如刷短视频。&lt;/p&gt;
&lt;p&gt;前顺丰集团CTO吴建平，在《超级学习》一书中提到刷手机上瘾的机制：&lt;/p&gt;
&lt;p&gt;推荐系统加入一些奇怪的算法，越来越讨用户喜欢。只要打开短视频就停不下来上滑，总是忍不住想看下一个视频是什么，有免费开盲盒的感觉。……空闲时间刷一下，时间嗖嗖地没了，这些应用像是一个偷时间的贼。&lt;/p&gt;
&lt;p&gt;因为APP的复杂算法，让你只要刷几个自己喜欢的内容，系统就会一直推荐类似的给你，直到你刷得停不下来。&lt;/p&gt;
&lt;p&gt;吴建平说，这在心理学上叫做“随机性奖励”，专用于刺激多巴胺分泌。&lt;/p&gt;
&lt;p&gt;为什么有人喜欢喝咖啡？因为咖啡因能够让人对多巴胺更敏感，而多巴胺是上瘾的关键。&lt;/p&gt;
&lt;p&gt;刷手机上瘾也是如此，手指往上滑动的时候，你不知道系统会推荐什么样的视频。当你发现是自己喜欢的，多巴胺就被刺激了，大脑感受到愉悦，你就会一直为这种愉悦买单。&lt;/p&gt;
&lt;p&gt;但这样的愉悦很短暂，愉悦过后，你收获的往往只有空虚和失落。&lt;/p&gt;
&lt;p&gt;而阅读或学习带来的愉悦，不但会让你收获成长，还能让你感受长时间的快感。&lt;/p&gt;
&lt;p&gt;04&lt;/p&gt;
&lt;p&gt;也有人意识到“低级欲望上瘾”的坏处，于是想方设法去克制乃至灭绝这种欲望，但效果往往不佳。&lt;/p&gt;
&lt;p&gt;毕竟在学习和娱乐这两件事中，多数人都会选择娱乐。&lt;/p&gt;
&lt;p&gt;因为学习是长期主义，立足未来，但是令人感到痛苦；娱乐聚焦眼前，令人放松。&lt;/p&gt;
&lt;p&gt;所以，对抗“低级欲望上瘾”的最好办法不是压制它，而是唤醒、发展和满足高级的欲望。&lt;/p&gt;
&lt;p&gt;一旦在阅读或学习上获得过快乐，日积月累，获得的成就越多，甚至超过购物和娱乐带来的愉悦，低级欲望的诱惑就不再有吸引力了。&lt;/p&gt;
&lt;p&gt;想要发展高级欲望，并以此替代低级欲望，不妨试试最小努力原则：&lt;/p&gt;
&lt;p&gt;把阻力降到最小，让自己一想起来，就能够即刻去行动。&lt;/p&gt;
&lt;p&gt;比如你想提升学习的欲望，就保证手边随时都有一本书，一有空闲时间，就抓起来翻。&lt;/p&gt;
&lt;p&gt;只要初期投入一定的努力，让这个“轮子”转动起来，后面就可以慢慢减少投入，它会一刻不停地转下去。&lt;/p&gt;
&lt;p&gt;这就是“飞轮效应”（正向反馈）起作用的结果。&lt;/p&gt;
&lt;p&gt;最后，值得一提的是，自律的人并非不娱乐，而是懂得适度娱乐。&lt;/p&gt;
&lt;p&gt;无论是带来短期愉悦的低级欲望，还是带来持久愉悦的高级欲望，只要懂得平衡，并始终让高级欲望处于主导地位，幸福就不会离你太遥远。&lt;/p&gt;
&lt;p&gt;共勉。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的时间</title>
    <link>https://blog.wiseai.cn/post/go%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%97%B6%E9%97%B4/</link>
    <pubDate>Thu, 25 Aug 2022 10:25:07 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/go%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%97%B6%E9%97%B4/</guid>
    <description>
        &lt;p&gt;&lt;strong&gt;Time包定义的类型&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Time: 时间类型, 包含了秒和纳秒以及 Location&lt;/p&gt;
&lt;p&gt;Month: type Month int 月份.&lt;/p&gt;
&lt;p&gt;定义了十二个月的常量&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (
	January Month = 1 + iota
	February
	March
	April
	May
	June
	July
	August
	September
	October
	November
	December
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Weekday 类型: type Weekday int 周&lt;/p&gt;
&lt;p&gt;定义了一周的七天&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (
	Sunday Weekday = iota
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Duration： type Duration int64 持续时间.&lt;/p&gt;
&lt;p&gt;定义了以下持续时间类型.&lt;/p&gt;
&lt;p&gt;多用于时间的加减 需要传入Duration做为参数的时候.&lt;/p&gt;
&lt;p&gt;可以直接传入 time.Second&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (
 Nanosecond Duration = 1
 Microsecond   = 1000 * Nanosecond
 Millisecond   = 1000 * Microsecond
 Second    = 1000 * Millisecond
 Minute    = 60 * Second
 Hour     = 60 * Minute
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Location&lt;/p&gt;
&lt;p&gt;在time包里有两个时区变量:&lt;/p&gt;
&lt;p&gt;time.UTC utc时间&lt;/p&gt;
&lt;p&gt;time.Local 本地时间&lt;/p&gt;
&lt;p&gt;时间格式化&lt;/p&gt;
&lt;p&gt;时间格式Time:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fmt.Println(time.Now())
// 输出: 2019-04-30 14:41:59.661602 +0800 CST m=+0.000225294

fmt.Println(time.Now().String())
// 输出: 2019-04-30 14:41:59.661826 +0800 CST m=+0.000448434
获取当前时间戳：

// 获取当前unix时间戳（秒）
fmt.Println(time.Now().Unix()) // 输出: 1556615702

// 获取当前unix时间戳（毫秒）
fmt.Println(time.Now().UnixNano() / 1e6) // 输出: 1556615702009

// 获取当前unix时间戳（纳秒）
fmt.Println(time.Now().UnixNano()) // 输出: 1556615702009257000
字符串转化成时间戳:

x := &amp;#34;2018-12-27 18:44:55&amp;#34;
p, _ := time.Parse(&amp;#34;2006-01-02 15:04:05&amp;#34;, x)
fmt.Println( p.Unix() ) // 输出: 1545936295
将当前时间转成年月日时分秒格式:

t = time.Now()
fmt.Println(t.Format(&amp;#34;2006-01-02&amp;#34;))   // 输出: 2019-04-30
fmt.Println(t.Format(&amp;#34;2006-01-02 15:04:05&amp;#34;)) // 输出: 2019-04-30 14:43:26
fmt.Println(t.Format(&amp;#34;2006-01-02 00:00:00&amp;#34;)) // 输出: 2019-04-30 00:00:00
fmt.Println(t.Format(&amp;#34;2006/01/02 15:04&amp;#34;)) // 输出: 2019-04-30 14:43
fmt.Println(t.Format(&amp;#34;2006/Jan/02 15:04&amp;#34;)) // 输出: 2019/Apr/30 17:28

// 指定时间
t2 := time.Date(2019, time.November, 28, 11, 35, 46, 0, time.UTC)
// 返回 Time 类型

fmt.Printf(&amp;#34;=&amp;gt;日期格式: %s\n&amp;#34;, t2.Format(&amp;#34;06/01/02 15:04:05&amp;#34;))
// 输出: =&amp;gt;日期格式: 19/11/28 11:35:46
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意：
在Go语言中，&amp;ldquo;Y-m-d H:i:s&amp;quot;&amp;ldquo;yyyy-MM-dd HH:mm:ss&amp;rdquo; 为特定的数字 “2006-01-02 15:04:05”是Go语言的创建时间，且必须为这几个准确的数字。&lt;/p&gt;
&lt;p&gt;使用 time.Now().Date() 获取年月日：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// Date()返回三个参数: 年月日
year1, month1, day1 := time.Now().Date()
 
fmt.Printf(&amp;#34;year: %v, type: %T \n&amp;#34;, year1, year1)
// 输出: year: 2019, type: int
 
fmt.Printf(&amp;#34;month: %v, type: %T \n&amp;#34;, month1, month1)
// 输出: month: April, type: time.Month
 
fmt.Printf(&amp;#34;day: %v, type: %T \n&amp;#34;, day1, day1)
// 输出: day: 30, type: int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;golang的time.Format设计的和其他语言都不一样, 其他语言总是使用一些格式化字符进行标示, 而golang使用&amp;quot;2006-01-02 15:04:05.999999999 -0700 MST&amp;rdquo;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// String returns the time formatted using the format string
// &amp;#34;2006-01-02 15:04:05.999999999 -0700 MST&amp;#34;
func (t Time) String() string {
 return t.Format(&amp;#34;2006-01-02 15:04:05.999999999 -0700 MST&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例子:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func nowTime() string {
 return time.Now().Format(&amp;#34;2006-01-02 15:04:05&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>golang使用smtp发送Email</title>
    <link>https://blog.wiseai.cn/post/email/</link>
    <pubDate>Tue, 23 Aug 2022 10:33:39 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/email/</guid>
    <description>
        &lt;p&gt;几个开源的代码：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/jordan-wright/email.git&#34; title=&#34;email&#34;&gt;email&lt;/a&gt;
这个代码比较清晰，我现在用的就是这个，Star2.1k&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/go-gomail/gomail.git&#34; title=&#34;gomail&#34;&gt;gomail&lt;/a&gt;
没用过，Star3.7k&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/mailhog/MailHog.git&#34; title=&#34;mailhog&#34;&gt;mailhog&lt;/a&gt;
这个Star10.7，看着是不错功能强大，但是依赖太多，没深入学习&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mianshigee.com/project/t/go-email-creation-and-sending&#34; title=&#34;邮件相关汇总&#34;&gt;邮件相关汇总&lt;/a&gt;
这个是一个邮件相关代码汇总的帖子，内容比较旧了，但是可以参考学习下&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用ssl的例子：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import (
	&amp;#34;tls.Config&amp;#34;
	&amp;#34;email&amp;#34;//引入包，看怎么引入了
	)
func SendMail(s string) error {
	e := NewEmail()
	e.From = &amp;#34;发送邮箱&amp;#34;
	e.To = []string{&amp;#34;目标邮箱&amp;#34;}
	// e.Bcc = []string{&amp;#34;bcc@junzhe.net&amp;#34;}
	// e.Cc = []string{&amp;#34;cc@junzhe.net&amp;#34;}
	//这两个基本不用
	e.Subject = &amp;#34;这个是主题&amp;#34;
	// e.Text = []byte(&amp;#34;这里是内容&amp;#34;)
	e.HTML = []byte(&amp;#34;这是HTML格式的内容&amp;#34;)
	t := &amp;amp;tls.Config{InsecureSkipVerify: true, ServerName: &amp;#34;这里是smtp服务器&amp;#34;}
	err := e.SendWithTLS(&amp;#34;smtp服务器:465&amp;#34;, smtp.PlainAuth(&amp;#34;&amp;#34;, &amp;#34;邮箱名&amp;#34;, &amp;#34;密码&amp;#34;, &amp;#34;smtp服务器&amp;#34;), t)
	return err
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的字符串、数组和切片的应用</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.31-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%95%B0%E7%BB%84%E5%92%8C%E5%88%87%E7%89%87%E7%9A%84%E5%BA%94%E7%94%A8/</link>
    <pubDate>Sun, 31 Jul 2022 11:53:44 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.31-%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%95%B0%E7%BB%84%E5%92%8C%E5%88%87%E7%89%87%E7%9A%84%E5%BA%94%E7%94%A8/</guid>
    <description>
        &lt;h1 id=&#34;76-字符串数组和切片的应用&#34;&gt;7.6 字符串、数组和切片的应用&lt;/h1&gt;
&lt;h2 id=&#34;761-从字符串生成字节切片&#34;&gt;7.6.1 从字符串生成字节切片&lt;/h2&gt;
&lt;p&gt;假设 &lt;code&gt;s&lt;/code&gt; 是一个字符串（本质上是一个字节数组），那么就可以直接通过 &lt;code&gt;c := []byte(s)&lt;/code&gt; 来获取一个字节的切片 &lt;code&gt;c&lt;/code&gt; 。另外，您还可以通过 &lt;code&gt;copy()&lt;/code&gt; 函数来达到相同的目的：&lt;code&gt;copy(dst []byte, src string)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;同样的，还可以使用 for-range 来获得每个元素（Listing 7.13 — &lt;a href=&#34;examples/chapter_7/for_string.go&#34;&gt;for_string.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
    s := &amp;#34;\u00ff\u754c&amp;#34;
    for i, c := range s {
        fmt.Printf(&amp;#34;%d:%c &amp;#34;, i, c)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0:ÿ 2:界
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们知道，Unicode 字符会占用 2 个字节，有些甚至需要 3 个或者 4 个字节来进行表示。如果发现错误的 UTF8 字符，则该字符会被设置为 &lt;code&gt;U+FFFD&lt;/code&gt; 并且索引向前移动一个字节。和字符串转换一样，您同样可以使用 &lt;code&gt;c := []int32(s)&lt;/code&gt; 语法，这样切片中的每个 &lt;code&gt;int&lt;/code&gt; 都会包含对应的 Unicode 代码，因为字符串中的每次字符都会对应一个整数。类似的，您也可以将字符串转换为元素类型为 &lt;code&gt;rune&lt;/code&gt; 的切片：&lt;code&gt;r := []rune(s)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;可以通过代码 &lt;code&gt;len([]int32(s))&lt;/code&gt; 来获得字符串中字符的数量，但使用 &lt;code&gt;utf8.RuneCountInString(s)&lt;/code&gt; 效率会更高一点。(参考 &lt;a href=&#34;exercises/chapter_4/count_characters.go&#34;&gt;count_characters.go&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;您还可以将一个字符串追加到某一个字节切片的尾部：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var b []byte
var s string
b = append(b, s...)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;762-获取字符串的某一部分&#34;&gt;7.6.2 获取字符串的某一部分&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;substr := str[start:end]&lt;/code&gt; 可以从字符串 str 获取到从索引 &lt;code&gt;start&lt;/code&gt; 开始到 &lt;code&gt;end-1&lt;/code&gt; 位置的子字符串。同样的，&lt;code&gt;str[start:]&lt;/code&gt; 则表示获取从 &lt;code&gt;start&lt;/code&gt; 开始到 &lt;code&gt;len(str)-1&lt;/code&gt; 位置的子字符串。而 &lt;code&gt;str[:end]&lt;/code&gt; 表示获取从 0 开始到 &lt;code&gt;end-1&lt;/code&gt; 的子字符串。&lt;/p&gt;
&lt;h2 id=&#34;763-字符串和切片的内存结构&#34;&gt;7.6.3 字符串和切片的内存结构&lt;/h2&gt;
&lt;p&gt;在内存中，一个字符串实际上是一个双字结构，即一个指向实际数据的指针和记录字符串长度的整数（见图 7.4）。因为指针对用户来说是完全不可见，因此我们可以依旧把字符串看做是一个值类型，也就是一个字符数组。&lt;/p&gt;
&lt;p&gt;字符串 &lt;code&gt;string s = &amp;quot;hello&amp;quot;&lt;/code&gt; 和子字符串 &lt;code&gt;t = s[2:3]&lt;/code&gt; 在内存中的结构可以用下图表示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/7.6_fig7.4.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;764-修改字符串中的某个字符&#34;&gt;7.6.4 修改字符串中的某个字符&lt;/h2&gt;
&lt;p&gt;Go 语言中的字符串是不可变的，也就是说 &lt;code&gt;str[index]&lt;/code&gt; 这样的表达式是不可以被放在等号左侧的。如果尝试运行 &lt;code&gt;str[i] = &#39;D&#39;&lt;/code&gt; 会得到错误：&lt;code&gt;cannot assign to str[i]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;因此，您必须先将字符串转换成字节数组，然后再通过修改数组中的元素值来达到修改字符串的目的，最后将字节数组转换回字符串格式。&lt;/p&gt;
&lt;p&gt;例如，将字符串 &lt;code&gt;&amp;quot;hello&amp;quot;&lt;/code&gt; 转换为 &lt;code&gt;&amp;quot;cello&amp;quot;&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;s := &amp;#34;hello&amp;#34;
c := []byte(s)
c[0] = &amp;#39;c&amp;#39;
s2 := string(c) // s2 == &amp;#34;cello&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所以，您可以通过操作切片来完成对字符串的操作。&lt;/p&gt;
&lt;h2 id=&#34;765-字节数组对比函数&#34;&gt;7.6.5 字节数组对比函数&lt;/h2&gt;
&lt;p&gt;下面的 &lt;code&gt;Compare()&lt;/code&gt; 函数会返回两个字节数组字典顺序的整数对比结果，即 &lt;code&gt;0 if a == b, -1 if a &amp;lt; b, 1 if a &amp;gt; b&lt;/code&gt;。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Compare(a, b[]byte) int {
    for i:=0; i &amp;lt; len(a) &amp;amp;&amp;amp; i &amp;lt; len(b); i++ {
        switch {
        case a[i] &amp;gt; b[i]:
            return 1
        case a[i] &amp;lt; b[i]:
            return -1
        }
    }
    // 数组的长度可能不同
    switch {
    case len(a) &amp;lt; len(b):
        return -1
    case len(a) &amp;gt; len(b):
        return 1
    }
    return 0 // 数组相等
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;766-搜索及排序切片和数组&#34;&gt;7.6.6 搜索及排序切片和数组&lt;/h2&gt;
&lt;p&gt;标准库提供了 &lt;code&gt;sort&lt;/code&gt; 包来实现常见的搜索和排序操作。您可以使用 &lt;code&gt;sort&lt;/code&gt; 包中的函数 &lt;code&gt;func Ints(a []int)&lt;/code&gt; 来实现对 &lt;code&gt;int&lt;/code&gt; 类型的切片排序。例如 &lt;code&gt;sort.Ints(arri)&lt;/code&gt;，其中变量 &lt;code&gt;arri&lt;/code&gt; 就是需要被升序排序的数组或切片。为了检查某个数组是否已经被排序，可以通过函数 &lt;code&gt;IntsAreSorted(a []int) bool&lt;/code&gt; 来检查，如果返回 &lt;code&gt;true&lt;/code&gt; 则表示已经被排序。&lt;/p&gt;
&lt;p&gt;类似的，可以使用函数 &lt;code&gt;func Float64s(a []float64)&lt;/code&gt; 来排序 &lt;code&gt;float64&lt;/code&gt; 的元素，或使用函数 &lt;code&gt;func Strings(a []string)&lt;/code&gt; 排序字符串元素。&lt;/p&gt;
&lt;p&gt;想要在数组或切片中搜索一个元素，该数组或切片必须先被排序（因为标准库的搜索算法使用的是二分法）。然后，您就可以使用函数 &lt;code&gt;func SearchInts(a []int, n int) int&lt;/code&gt; 进行搜索，并返回对应结果的索引值。&lt;/p&gt;
&lt;p&gt;当然，还可以搜索 &lt;code&gt;float64&lt;/code&gt; 和字符串：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func SearchFloat64s(a []float64, x float64) int
func SearchStrings(a []string, x string) int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;您可以通过查看 &lt;a href=&#34;http://golang.org/pkg/sort/&#34;&gt;官方文档&lt;/a&gt; 来获取更详细的信息。&lt;/p&gt;
&lt;p&gt;这就是如何使用 &lt;code&gt;sort&lt;/code&gt; 包的方法，我们会在&lt;a href=&#34;11.7.md&#34;&gt;第 11.7 节&lt;/a&gt; 对它的细节进行深入，并实现一个属于我们自己的版本。&lt;/p&gt;
&lt;h2 id=&#34;767-append-函数常见操作&#34;&gt;7.6.7 append() 函数常见操作&lt;/h2&gt;
&lt;p&gt;我们在&lt;a href=&#34;07.5.md&#34;&gt;第 7.5 节&lt;/a&gt;提到的 &lt;code&gt;append()&lt;/code&gt; 非常有用，它能够用于各种方面的操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;将切片 &lt;code&gt;b&lt;/code&gt; 的元素追加到切片 &lt;code&gt;a&lt;/code&gt; 之后：&lt;code&gt;a = append(a, b...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;复制切片 &lt;code&gt;a&lt;/code&gt; 的元素到新的切片 &lt;code&gt;b&lt;/code&gt; 上：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;b = make([]T, len(a))
copy(b, a)
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除位于索引 &lt;code&gt;i&lt;/code&gt; 的元素：&lt;code&gt;a = append(a[:i], a[i+1:]...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;切除切片 &lt;code&gt;a&lt;/code&gt; 中从索引 &lt;code&gt;i&lt;/code&gt; 至 &lt;code&gt;j&lt;/code&gt; 位置的元素：&lt;code&gt;a = append(a[:i], a[j:]...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为切片 &lt;code&gt;a&lt;/code&gt; 扩展 &lt;code&gt;j&lt;/code&gt; 个元素长度：&lt;code&gt;a = append(a, make([]T, j)...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在索引 &lt;code&gt;i&lt;/code&gt; 的位置插入元素 &lt;code&gt;x&lt;/code&gt;：&lt;code&gt;a = append(a[:i], append([]T{x}, a[i:]...)...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在索引 &lt;code&gt;i&lt;/code&gt; 的位置插入长度为 &lt;code&gt;j&lt;/code&gt; 的新切片：&lt;code&gt;a = append(a[:i], append(make([]T, j), a[i:]...)...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在索引 &lt;code&gt;i&lt;/code&gt; 的位置插入切片 &lt;code&gt;b&lt;/code&gt; 的所有元素：&lt;code&gt;a = append(a[:i], append(b, a[i:]...)...)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;取出位于切片 &lt;code&gt;a&lt;/code&gt; 最末尾的元素 &lt;code&gt;x&lt;/code&gt;：&lt;code&gt;x, a = a[len(a)-1], a[:len(a)-1]&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将元素 &lt;code&gt;x&lt;/code&gt; 追加到切片 &lt;code&gt;a&lt;/code&gt;：&lt;code&gt;a = append(a, x)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，您可以使用切片和 &lt;code&gt;append()&lt;/code&gt; 操作来表示任意可变长度的序列。&lt;/p&gt;
&lt;p&gt;从数学的角度来看，切片相当于向量，如果需要的话可以定义一个向量作为切片的别名来进行操作。&lt;/p&gt;
&lt;p&gt;如果您需要更加完整的方案，可以学习一下 Eleanor McHugh 编写的几个包：&lt;a href=&#34;http://github.com/feyeleanor/slices&#34;&gt;&lt;code&gt;slices&lt;/code&gt;&lt;/a&gt;、&lt;a href=&#34;http://github.com/feyeleanor/chain&#34;&gt;&lt;code&gt;chain&lt;/code&gt;&lt;/a&gt; 和 &lt;a href=&#34;http://github.com/feyeleanor/lists&#34;&gt;&lt;code&gt;lists&lt;/code&gt;&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;768-切片和垃圾回收&#34;&gt;7.6.8 切片和垃圾回收&lt;/h2&gt;
&lt;p&gt;切片的底层指向一个数组，该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候，底层的数组内存才会被释放，这种特性有时会导致程序占用多余的内存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt; 函数 &lt;code&gt;FindDigits()&lt;/code&gt; 将一个文件加载到内存，然后搜索其中所有的数字并返回一个切片。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var digitRegexp = regexp.MustCompile(&amp;#34;[0-9]+&amp;#34;)

func FindDigits(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    return digitRegexp.Find(b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这段代码可以顺利运行，但返回的 &lt;code&gt;[]byte&lt;/code&gt; 指向的底层是整个文件的数据。只要该返回的切片不被释放，垃圾回收器就不能释放整个文件所占用的内存。换句话说，一点点有用的数据却占用了整个文件的内存。&lt;/p&gt;
&lt;p&gt;想要避免这个问题，可以通过拷贝我们需要的部分到一个新的切片中：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func FindDigits(filename string) []byte {
   b, _ := ioutil.ReadFile(filename)
   b = digitRegexp.Find(b)
   c := make([]byte, len(b))
   copy(c, b)
   return c
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;事实上，上面这段代码只能找到第一个匹配正则表达式的数字串。要想找到所有的数字，可以尝试下面这段代码：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func FindFileDigits(filename string) []byte {
   fileBytes, _ := ioutil.ReadFile(filename)
   b := digitRegexp.FindAll(fileBytes, len(fileBytes))
   c := make([]byte, 0)
   for _, bytes := range b {
      c = append(c, bytes...)
   }
   return c
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;练习 7.12&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/split_string.go&#34;&gt;split_string.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个函数，要求其接受两个参数，原始字符串 &lt;code&gt;str&lt;/code&gt; 和分割索引 &lt;code&gt;i&lt;/code&gt;，然后返回两个分割后的字符串。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.13&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/string_split2.go&#34;&gt;string_split2.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;假设有字符串 &lt;code&gt;str&lt;/code&gt;，那么 &lt;code&gt;str[len(str)/2:] + str[:len(str)/2]&lt;/code&gt; 的结果是什么？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.14&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/string_reverse.go&#34;&gt;string_reverse.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个程序，要求能够反转字符串，即将 &lt;code&gt;&amp;quot;Google&amp;quot;&lt;/code&gt; 转换成 &lt;code&gt;&amp;quot;elgooG&amp;quot;&lt;/code&gt;（提示：使用 &lt;code&gt;[]byte&lt;/code&gt; 类型的切片）。&lt;/p&gt;
&lt;p&gt;如果您使用两个切片来实现反转，请再尝试使用一个切片（提示：使用交换法）。&lt;/p&gt;
&lt;p&gt;如果您想要反转 Unicode 编码的字符串，请使用 &lt;code&gt;[]int32&lt;/code&gt; 类型的切片。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.15&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/uniq.go&#34;&gt;Q29_uniq.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个程序，要求能够遍历一个字符数组，并将当前字符和前一个字符不相同的字符拷贝至另一个数组。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.16&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/bubblesort.go&#34;&gt;bubblesort.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个程序，使用冒泡排序的方法排序一个包含整数的切片（算法的定义可参考 &lt;a href=&#34;http://en.wikipedia.org/wiki/Bubble_sort&#34;&gt;维基百科&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.17&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/map_function.go&#34;&gt;map_function.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在函数式编程语言中，一个 map-function 是指能够接受一个函数原型和一个列表，并使用列表中的值依次执行函数原型，公式为：&lt;code&gt;map ( F(), (e1,e2, . . . ,en) ) = ( F(e1), F(e2), ... F(en) )&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;编写一个函数 &lt;code&gt;mapFunc&lt;/code&gt; 要求接受以下 2 个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个将整数乘以 10 的函数&lt;/li&gt;
&lt;li&gt;一个整数列表&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后返回保存运行结果的整数列表。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的切片的复制与追加</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.30-%E5%88%87%E7%89%87%E7%9A%84%E5%A4%8D%E5%88%B6%E4%B8%8E%E8%BF%BD%E5%8A%A0/</link>
    <pubDate>Sat, 30 Jul 2022 11:52:29 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.30-%E5%88%87%E7%89%87%E7%9A%84%E5%A4%8D%E5%88%B6%E4%B8%8E%E8%BF%BD%E5%8A%A0/</guid>
    <description>
        &lt;h1 id=&#34;75-切片的复制与追加&#34;&gt;7.5 切片的复制与追加&lt;/h1&gt;
&lt;p&gt;如果想增加切片的容量，我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 &lt;code&gt;copy&lt;/code&gt; 函数和向切片追加新元素的 &lt;code&gt;append()&lt;/code&gt; 函数。&lt;/p&gt;
&lt;p&gt;示例 7.12 &lt;a href=&#34;examples/chapter_7/copy_append_slice.go&#34;&gt;copy_append_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	slFrom := []int{1, 2, 3}
	slTo := make([]int, 10)

	n := copy(slTo, slFrom)
	fmt.Println(slTo)
	fmt.Printf(&amp;#34;Copied %d elements\n&amp;#34;, n) // n == 3

	sl3 := []int{1, 2, 3}
	sl3 = append(sl3, 4, 5, 6)
	fmt.Println(sl3)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;func append(s[]T, x ...T) []T&lt;/code&gt; 其中 &lt;code&gt;append()&lt;/code&gt; 方法将 0 个或多个具有相同类型 &lt;code&gt;s&lt;/code&gt; 的元素追加到切片后面并且返回新的切片；追加的元素必须和原切片的元素是同类型。如果 &lt;code&gt;s&lt;/code&gt; 的容量不足以存储新增元素，&lt;code&gt;append()&lt;/code&gt; 会分配新的切片来保证已有切片元素和新增元素的存储。因此，返回的切片可能已经指向一个不同的相关数组了。&lt;code&gt;append()&lt;/code&gt; 方法总是返回成功，除非系统内存耗尽了。&lt;/p&gt;
&lt;p&gt;如果你想将切片 &lt;code&gt;y&lt;/code&gt; 追加到切片 &lt;code&gt;x&lt;/code&gt; 后面，只要将第二个参数扩展成一个列表即可：&lt;code&gt;x = append(x, y...)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;： &lt;code&gt;append()&lt;/code&gt; 在大多数情况下很好用，但是如果你想完全掌控整个追加过程，你可以实现一个这样的 &lt;code&gt;AppendByte()&lt;/code&gt; 方法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func AppendByte(slice []byte, data ...byte) []byte {
	m := len(slice)
	n := m + len(data)
	if n &amp;gt; cap(slice) { // if necessary, reallocate
		// allocate double what&amp;#39;s needed, for future growth.
		newSlice := make([]byte, (n+1)*2)
		copy(newSlice, slice)
		slice = newSlice
	}
	slice = slice[0:n]
	copy(slice[m:n], data)
	return slice
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;func copy(dst, src []T) int&lt;/code&gt; 方法将类型为 &lt;code&gt;T&lt;/code&gt; 的切片从源地址 &lt;code&gt;src&lt;/code&gt; 拷贝到目标地址 &lt;code&gt;dst&lt;/code&gt;，覆盖 &lt;code&gt;dst&lt;/code&gt; 的相关元素，并且返回拷贝的元素个数。源地址和目标地址可能会有重叠。拷贝个数是 &lt;code&gt;src&lt;/code&gt; 和 &lt;code&gt;dst&lt;/code&gt; 的长度最小值。如果 &lt;code&gt;src&lt;/code&gt; 是字符串那么元素类型就是 &lt;code&gt;byte&lt;/code&gt;。如果你还想继续使用 &lt;code&gt;src&lt;/code&gt;，在拷贝结束后执行 &lt;code&gt;src = dst&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.9&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/magnify_slice.go&#34;&gt;magnify_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;给定一个切片 &lt;code&gt;s []int&lt;/code&gt; 和一个 &lt;code&gt;int&lt;/code&gt; 类型的因子 &lt;code&gt;factor&lt;/code&gt;，扩展 &lt;code&gt;s&lt;/code&gt; 使其长度为 &lt;code&gt;len(s) * factor&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;**练习 7.10 ** &lt;a href=&#34;exercises/chapter_7/filter_slice.go&#34;&gt;filter_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;用顺序函数过滤容器：&lt;code&gt;s&lt;/code&gt; 是前 10 个整型的切片。构造一个函数 &lt;code&gt;Filter&lt;/code&gt;，第一个参数是 &lt;code&gt;s&lt;/code&gt;，第二个参数是一个 &lt;code&gt;fn func(int) bool&lt;/code&gt;，返回满足函数 &lt;code&gt;fn&lt;/code&gt; 的元素切片。通过 &lt;code&gt;fn&lt;/code&gt; 测试方法测试当整型值是偶数时的情况。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.11&lt;/strong&gt;  &lt;a href=&#34;exercises/chapter_7/insert_slice.go&#34;&gt;insert_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写一个函数 &lt;code&gt;InsertStringSlice()&lt;/code&gt; 将切片插入到另一个切片的指定位置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.12&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/remove_slice.go&#34;&gt;remove_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写一个函数 &lt;code&gt;RemoveStringSlice()&lt;/code&gt; 将从 &lt;code&gt;start&lt;/code&gt; 到 &lt;code&gt;end&lt;/code&gt; 索引的元素从切片中移除。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的切片重组reslice</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.29-%E5%88%87%E7%89%87%E9%87%8D%E7%BB%84reslice/</link>
    <pubDate>Fri, 29 Jul 2022 11:51:11 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.29-%E5%88%87%E7%89%87%E9%87%8D%E7%BB%84reslice/</guid>
    <description>
        &lt;h1 id=&#34;74-切片重组-reslice&#34;&gt;7.4 切片重组 (reslice)&lt;/h1&gt;
&lt;p&gt;我们已经知道切片创建的时候通常比相关数组小，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;slice1 := make([]type, start_length, capacity)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其中 &lt;code&gt;start_length&lt;/code&gt; 作为切片初始长度而 &lt;code&gt;capacity&lt;/code&gt; 作为相关数组的长度。&lt;/p&gt;
&lt;p&gt;这么做的好处是我们的切片在达到容量上限后可以扩容。改变切片长度的过程称之为切片重组 &lt;strong&gt;reslicing&lt;/strong&gt;，做法如下：&lt;code&gt;slice1 = slice1[0:end]&lt;/code&gt;，其中 &lt;code&gt;end&lt;/code&gt; 是新的末尾索引（即长度）。&lt;/p&gt;
&lt;p&gt;将切片扩展 1 位可以这么做：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sl = sl[0:len(sl)+1]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;切片可以反复扩展直到占据整个相关数组。&lt;/p&gt;
&lt;p&gt;示例 7.11 &lt;a href=&#34;examples/chapter_7/reslicing.go&#34;&gt;reslicing.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	slice1 := make([]int, 0, 10)
	// load the slice, cap(slice1) is 10:
	for i := 0; i &amp;lt; cap(slice1); i++ {
		slice1 = slice1[0:i+1]
		slice1[i] = i
		fmt.Printf(&amp;#34;The length of slice is %d\n&amp;#34;, len(slice1))
	}

	// print the slice:
	for i := 0; i &amp;lt; len(slice1); i++ {
		fmt.Printf(&amp;#34;Slice at %d is %d\n&amp;#34;, i, slice1[i])
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The length of slice is 1
The length of slice is 2
The length of slice is 3
The length of slice is 4
The length of slice is 5
The length of slice is 6
The length of slice is 7
The length of slice is 8
The length of slice is 9
The length of slice is 10
Slice at 0 is 0
Slice at 1 is 1
Slice at 2 is 2
Slice at 3 is 3
Slice at 4 is 4
Slice at 5 is 5
Slice at 6 is 6
Slice at 7 is 7
Slice at 8 is 8
Slice at 9 is 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另一个例子：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var ar = [10]int{0,1,2,3,4,5,6,7,8,9}
var a = ar[5:7] // reference to subarray {5,6} - len(a) is 2 and cap(a) is 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;将 &lt;code&gt;a&lt;/code&gt; 重新分片：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a = a[0:4] // ref of subarray {5,6,7,8} - len(a) is now 4 but cap(a) is still 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;问题 7.7&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;如果 &lt;code&gt;a&lt;/code&gt; 是一个切片，那么 &lt;code&gt;a[n:n]&lt;/code&gt; 的长度是多少？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;a[n:n+1]&lt;/code&gt; 的长度又是多少？&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的for Range结构</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.28-for-range%E7%BB%93%E6%9E%84/</link>
    <pubDate>Thu, 28 Jul 2022 11:49:16 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.28-for-range%E7%BB%93%E6%9E%84/</guid>
    <description>
        &lt;h1 id=&#34;73-for-range-结构&#34;&gt;7.3 For-range 结构&lt;/h1&gt;
&lt;p&gt;这种构建方法可以应用于数组和切片:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for ix, value := range slice1 {
	...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第一个返回值 &lt;code&gt;ix&lt;/code&gt; 是数组或者切片的索引，第二个是在该索引位置的值；他们都是仅在 &lt;code&gt;for&lt;/code&gt; 循环内部可见的局部变量。&lt;code&gt;value&lt;/code&gt; 只是 &lt;code&gt;slice1&lt;/code&gt; 某个索引位置的值的一个拷贝，不能用来修改 &lt;code&gt;slice1&lt;/code&gt; 该索引位置的值。&lt;/p&gt;
&lt;p&gt;示例 7.9 &lt;a href=&#34;examples/chapter_7/slices_forrange.go&#34;&gt;slices_forrange.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var slice1 []int = make([]int, 4)

	slice1[0] = 1
	slice1[1] = 2
	slice1[2] = 3
	slice1[3] = 4

	for ix, value := range slice1 {
		fmt.Printf(&amp;#34;Slice at %d is: %d\n&amp;#34;, ix, value)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;示例 7.10 &lt;a href=&#34;examples/chapter_7/slices_forrange2.go&#34;&gt;slices_forrange2.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	seasons := []string{&amp;#34;Spring&amp;#34;, &amp;#34;Summer&amp;#34;, &amp;#34;Autumn&amp;#34;, &amp;#34;Winter&amp;#34;}
	for ix, season := range seasons {
		fmt.Printf(&amp;#34;Season %d is: %s\n&amp;#34;, ix, season)
	}

	var season string
	for _, season = range seasons {
		fmt.Printf(&amp;#34;%s\n&amp;#34;, season)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;slices_forrange2.go 给出了一个关于字符串的例子， &lt;code&gt;_&lt;/code&gt; 可以用于忽略索引。&lt;/p&gt;
&lt;p&gt;如果你只需要索引，你可以忽略第二个变量，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for ix := range seasons {
	fmt.Printf(&amp;#34;%d&amp;#34;, ix)
}
// Output: 0 1 2 3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你需要修改 &lt;code&gt;seasons[ix]&lt;/code&gt; 的值可以使用这个版本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多维切片下的 for-range：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过计算行数和矩阵值可以很方便的写出如（参考&lt;a href=&#34;07.1.md&#34;&gt;第 7.1.3 节&lt;/a&gt;）的 &lt;code&gt;for&lt;/code&gt; 循环来，例如（参考&lt;a href=&#34;07.5.md&#34;&gt;第 7.5 节&lt;/a&gt;的例子 &lt;a href=&#34;exercises/chapter_7/multidim_array.go&#34;&gt;multidim_array.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for row := range screen {
	for column := range screen[row] {
		screen[row][column] = 1
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;问题 7.5&lt;/strong&gt; 假设我们有如下数组：&lt;code&gt;items := [...]int{10, 20, 30, 40, 50}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;a) 如果我们写了如下的 &lt;code&gt;for&lt;/code&gt; 循环，那么执行完 &lt;code&gt;for&lt;/code&gt; 循环后的 &lt;code&gt;items&lt;/code&gt; 的值是多少？如果你不确定的话可以测试一下:)&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for _, item := range items {
	item *= 2
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;b) 如果 a) 无法正常工作，写一个 &lt;code&gt;for&lt;/code&gt; 循环让值可以变成自身的两倍。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 7.6&lt;/strong&gt; 通过使用省略号操作符 &lt;code&gt;...&lt;/code&gt; 来实现累加方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.7&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/sum_array.go&#34;&gt;sum_array.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;a) 写一个 &lt;code&gt;Sum()&lt;/code&gt; 函数，传入参数为一个 &lt;code&gt;float32&lt;/code&gt; 数组成的数组 &lt;code&gt;arrF&lt;/code&gt;，返回该数组的所有数字和。&lt;/p&gt;
&lt;p&gt;如果把数组修改为切片的话代码要做怎样的修改？如果用切片形式方法实现不同长度数组的的和呢？&lt;/p&gt;
&lt;p&gt;b) 写一个 &lt;code&gt;SumAndAverage()&lt;/code&gt; 方法，返回两个 int 和 &lt;code&gt;float32&lt;/code&gt; 类型的未命名变量的和与平均值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.8&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_7/min_max.go&#34;&gt;min_max.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写一个 &lt;code&gt;minSlice()&lt;/code&gt; 方法，传入一个 &lt;code&gt;int&lt;/code&gt; 的切片并且返回最小值，再写一个 &lt;code&gt;maxSlice()&lt;/code&gt; 方法返回最大值。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的切片</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.27-%E5%88%87%E7%89%87/</link>
    <pubDate>Wed, 27 Jul 2022 11:47:31 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.27-%E5%88%87%E7%89%87/</guid>
    <description>
        &lt;h1 id=&#34;72-切片&#34;&gt;7.2 切片&lt;/h1&gt;
&lt;h2 id=&#34;721-概念&#34;&gt;7.2.1 概念&lt;/h2&gt;
&lt;p&gt;切片 (slice) 是对数组一个连续片段的引用（该数组我们称之为相关数组，通常是匿名的），所以切片是一个引用类型（因此更类似于 C/C++ 中的数组类型，或者 Python 中的 list 类型）。这个片段可以是整个数组，或者是由起始和终止索引标识的一些项的子集。需要注意的是，终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。&lt;/p&gt;
&lt;p&gt;切片是可索引的，并且可以由 &lt;code&gt;len()&lt;/code&gt; 函数获取长度。&lt;/p&gt;
&lt;p&gt;给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是，切片的长度可以在运行时修改，最小为 0， 最大为相关数组的长度：切片是一个 &lt;strong&gt;长度可变的数组&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;切片提供了计算容量的函数 &lt;code&gt;cap()&lt;/code&gt; 可以测量切片最长可以达到多少：它等于切片的长度 + 数组除切片之外的长度。如果 &lt;code&gt;s&lt;/code&gt; 是一个切片，&lt;code&gt;cap(s)&lt;/code&gt; 就是从 &lt;code&gt;s[0]&lt;/code&gt; 到数组末尾的数组长度。切片的长度永远不会超过它的容量，所以对于切片 &lt;code&gt;s&lt;/code&gt; 来说该不等式永远成立：&lt;code&gt;0 &amp;lt;= len(s) &amp;lt;= cap(s)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;多个切片如果表示同一个数组的片段，它们可以共享数据；因此一个切片和相关数组的其他切片是共享存储的，相反，不同的数组总是代表不同的存储。数组实际上是切片的构建块。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;优点&lt;/strong&gt; 因为切片是引用，所以它们不需要使用额外的内存并且比使用数组更有效率，所以在 Go 代码中切片比数组更常用。&lt;/p&gt;
&lt;p&gt;声明切片的格式是： &lt;code&gt;var identifier []type&lt;/code&gt;（不需要说明长度）。&lt;/p&gt;
&lt;p&gt;一个切片在未初始化之前默认为 &lt;code&gt;nil&lt;/code&gt;，长度为 0。&lt;/p&gt;
&lt;p&gt;切片的初始化格式是：&lt;code&gt;var slice1 []type = arr1[start:end]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这表示 &lt;code&gt;slice1&lt;/code&gt; 是由数组 &lt;code&gt;arr1&lt;/code&gt; 从 &lt;code&gt;start&lt;/code&gt; 索引到 &lt;code&gt;end-1&lt;/code&gt; 索引之间的元素构成的子集（切分数组，&lt;code&gt;start:end&lt;/code&gt; 被称为切片表达式）。所以 &lt;code&gt;slice1[0]&lt;/code&gt; 就等于 &lt;code&gt;arr1[start]&lt;/code&gt;。这可以在 &lt;code&gt;arr1&lt;/code&gt; 被填充前就定义好。&lt;/p&gt;
&lt;p&gt;如果某个人写：&lt;code&gt;var slice1 []type = arr1[:]&lt;/code&gt; 那么 &lt;code&gt;slice1&lt;/code&gt; 就等于完整的 &lt;code&gt;arr1&lt;/code&gt; 数组（所以这种表示方式是 &lt;code&gt;arr1[0:len(arr1)]&lt;/code&gt; 的一种缩写）。另外一种表述方式是：&lt;code&gt;slice1 = &amp;amp;arr1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;arr1[2:]&lt;/code&gt; 和 &lt;code&gt;arr1[2:len(arr1)]&lt;/code&gt; 相同，都包含了数组从第三个到最后的所有元素。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;arr1[:3]&lt;/code&gt; 和 &lt;code&gt;arr1[0:3]&lt;/code&gt; 相同，包含了从第一个到第三个元素（不包括第四个）。&lt;/p&gt;
&lt;p&gt;如果你想去掉 &lt;code&gt;slice1&lt;/code&gt; 的最后一个元素，只要 &lt;code&gt;slice1 = slice1[:len(slice1)-1]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;一个由数字 1、2、3 组成的切片可以这么生成：&lt;code&gt;s := [3]int{1,2,3}[:]&lt;/code&gt;（注：应先用 &lt;code&gt;s := [3]int{1, 2, 3}&lt;/code&gt; 生成数组, 再使用 &lt;code&gt;s[:]&lt;/code&gt; 转成切片）甚至更简单的 &lt;code&gt;s := []int{1,2,3}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;s2 := s[:]&lt;/code&gt; 是用切片组成的切片，拥有相同的元素，但是仍然指向相同的相关数组。&lt;/p&gt;
&lt;p&gt;一个切片 &lt;code&gt;s&lt;/code&gt; 可以这样扩展到它的大小上限：&lt;code&gt;s = s[:cap(s)]&lt;/code&gt;，如果再扩大的话就会导致运行时错误（参见第 7.7 节）。&lt;/p&gt;
&lt;p&gt;对于每一个切片（包括 &lt;code&gt;string&lt;/code&gt;），以下状态总是成立的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;s == s[:i] + s[i:] // i是一个整数且: 0 &amp;lt;= i &amp;lt;= len(s)
len(s) &amp;lt;= cap(s)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;切片也可以用类似数组的方式初始化：&lt;code&gt;var x = []int{2, 3, 5, 7, 11}&lt;/code&gt;。这样就创建了一个长度为 5 的数组并且创建了一个相关切片。&lt;/p&gt;
&lt;p&gt;切片在内存中的组织方式实际上是一个有 3 个域的结构体：指向相关数组的指针，切片长度以及切片容量。下图给出了一个长度为 2，容量为 4 的切片 &lt;code&gt;y&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;y[0] = 3&lt;/code&gt; 且 &lt;code&gt;y[1] = 5&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;切片 &lt;code&gt;y[0:4]&lt;/code&gt; 由 元素 &lt;code&gt;3&lt;/code&gt;，&lt;code&gt;5&lt;/code&gt;，&lt;code&gt;7&lt;/code&gt; 和 &lt;code&gt;11&lt;/code&gt; 组成。&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&#34;images/7.2_fig7.2.png?raw=true&#34; style=&#34;zoom: 50%;&#34; /&gt;
&lt;p&gt;示例 7.7 &lt;a href=&#34;examples/chapter_7/array_slices.go&#34;&gt;array_slices.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	var arr1 [6]int
	var slice1 []int = arr1[2:5] // item at index 5 not included!

	// load the array with integers: 0,1,2,3,4,5
	for i := 0; i &amp;lt; len(arr1); i++ {
		arr1[i] = i
	}

	// print the slice
	for i := 0; i &amp;lt; len(slice1); i++ {
		fmt.Printf(&amp;#34;Slice at %d is %d\n&amp;#34;, i, slice1[i])
	}

	fmt.Printf(&amp;#34;The length of arr1 is %d\n&amp;#34;, len(arr1))
	fmt.Printf(&amp;#34;The length of slice1 is %d\n&amp;#34;, len(slice1))
	fmt.Printf(&amp;#34;The capacity of slice1 is %d\n&amp;#34;, cap(slice1))

	// grow the slice
	slice1 = slice1[0:4]
	for i := 0; i &amp;lt; len(slice1); i++ {
		fmt.Printf(&amp;#34;Slice at %d is %d\n&amp;#34;, i, slice1[i])
	}
	fmt.Printf(&amp;#34;The length of slice1 is %d\n&amp;#34;, len(slice1))
	fmt.Printf(&amp;#34;The capacity of slice1 is %d\n&amp;#34;, cap(slice1))

	// grow the slice beyond capacity
	//slice1 = slice1[0:7 ] // panic: runtime error: slice bound out of range
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
The length of arr1 is 6  
The length of slice1 is 3  
The capacity of slice1 is 4  
Slice at 0 is 2  
Slice at 1 is 3  
Slice at 2 is 4  
Slice at 3 is 5  
The length of slice1 is 4  
The capacity of slice1 is 4  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;s2&lt;/code&gt; 是一个切片，你可以将 &lt;code&gt;s2&lt;/code&gt; 向后移动一位 &lt;code&gt;s2 = s2[1:]&lt;/code&gt;，但是末尾没有移动。切片只能向后移动，&lt;code&gt;s2 = s2[-1:]&lt;/code&gt; 会导致编译错误。切片不能被重新分片以获取数组的前一个元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt; 绝对不要用指针指向切片。切片本身已经是一个引用类型，所以它本身就是一个指针！！&lt;/p&gt;
&lt;p&gt;问题 7.2： 给定切片 &lt;code&gt;b:= []byte{&#39;g&#39;, &#39;o&#39;, &#39;l&#39;, &#39;a&#39;, &#39;n&#39;, &#39;g&#39;}&lt;/code&gt;，那么 &lt;code&gt;b[1:4]&lt;/code&gt;、&lt;code&gt;b[:2]&lt;/code&gt;、&lt;code&gt;b[2:]&lt;/code&gt; 和 &lt;code&gt;b[:]&lt;/code&gt; 分别是什么？&lt;/p&gt;
&lt;h2 id=&#34;722-将切片传递给函数&#34;&gt;7.2.2 将切片传递给函数&lt;/h2&gt;
&lt;p&gt;如果你有一个函数需要对数组做操作，你可能总是需要把参数声明为切片。当你调用该函数时，把数组分片，创建为一个切片引用并传递给该函数。这里有一个计算数组元素和的方法:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func sum(a []int) int {
	s := 0
	for i := 0; i &amp;lt; len(a); i++ {
		s += a[i]
	}
	return s
}

func main() {
	var arr = [5]int{0, 1, 2, 3, 4}
	sum(arr[:])
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;723-用-make-创建一个切片&#34;&gt;7.2.3 用 make() 创建一个切片&lt;/h2&gt;
&lt;p&gt;当相关数组还没有定义时，我们可以使用 &lt;code&gt;make()&lt;/code&gt; 函数来创建一个切片，同时创建好相关数组：&lt;code&gt;var slice1 []type = make([]type, len)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;也可以简写为 &lt;code&gt;slice1 := make([]type, len)&lt;/code&gt;，这里 &lt;code&gt;len&lt;/code&gt; 是数组的长度并且也是 &lt;code&gt;slice&lt;/code&gt; 的初始长度。&lt;/p&gt;
&lt;p&gt;所以定义 &lt;code&gt;s2 := make([]int, 10)&lt;/code&gt;，那么 &lt;code&gt;cap(s2) == len(s2) == 10&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make()&lt;/code&gt; 接受 2 个参数：元素的类型以及切片的元素个数。&lt;/p&gt;
&lt;p&gt;如果你想创建一个 &lt;code&gt;slice1&lt;/code&gt;，它不占用整个数组，而只是占用以 &lt;code&gt;len&lt;/code&gt; 为个数个项，那么只要：&lt;code&gt;slice1 := make([]type, len, cap)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;make()&lt;/code&gt; 的使用方式是：&lt;code&gt;func make([]T, len, cap)&lt;/code&gt;，其中 &lt;code&gt;cap&lt;/code&gt; 是可选参数。&lt;/p&gt;
&lt;p&gt;所以下面两种方法可以生成相同的切片:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;make([]int, 50, 100)
new([100]int)[0:50]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;下图描述了使用 &lt;code&gt;make()&lt;/code&gt; 方法生成的切片的内存结构：&lt;/p&gt;
&lt;img src=&#34;images/7.2_fig7.2.1.png?raw=true&#34; style=&#34;zoom:50%;&#34; /&gt;
&lt;p&gt;示例 7.8 &lt;a href=&#34;examples/chapter_7/make_slice.go&#34;&gt;make_slice.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	var slice1 []int = make([]int, 10)
	// load the array/slice:
	for i := 0; i &amp;lt; len(slice1); i++ {
		slice1[i] = 5 * i
	}

	// print the slice:
	for i := 0; i &amp;lt; len(slice1); i++ {
		fmt.Printf(&amp;#34;Slice at %d is %d\n&amp;#34;, i, slice1[i])
	}
	fmt.Printf(&amp;#34;\nThe length of slice1 is %d\n&amp;#34;, len(slice1))
	fmt.Printf(&amp;#34;The capacity of slice1 is %d\n&amp;#34;, cap(slice1))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Slice at 0 is 0  
Slice at 1 is 5  
Slice at 2 is 10  
Slice at 3 is 15  
Slice at 4 is 20  
Slice at 5 is 25  
Slice at 6 is 30  
Slice at 7 is 35  
Slice at 8 is 40  
Slice at 9 is 45  

The length of slice1 is 10  
The capacity of slice1 is 10  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为字符串是纯粹不可变的字节数组，它们也可以被切分成切片。&lt;/p&gt;
&lt;p&gt;练习 7.4： &lt;a href=&#34;examples/chapter_7/fibonacci_funcarray.go&#34;&gt;fibonacci_funcarray.go&lt;/a&gt;: 为练习 7.3 写一个新的版本，主函数调用一个使用序列个数作为参数的函数，该函数返回一个大小为序列个数的 Fibonacci 切片。&lt;/p&gt;
&lt;h2 id=&#34;724-new-和-make-的区别&#34;&gt;7.2.4 new() 和 make() 的区别&lt;/h2&gt;
&lt;p&gt;看起来二者没有什么区别，都在堆上分配内存，但是它们的行为不同，适用于不同的类型。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new(T)&lt;/code&gt; 为每个新的类型 &lt;code&gt;T&lt;/code&gt; 分配一片内存，初始化为 &lt;code&gt;0&lt;/code&gt; 并且返回类型为 &lt;code&gt;*T&lt;/code&gt; 的内存地址：这种方法 &lt;strong&gt;返回一个指向类型为 &lt;code&gt;T&lt;/code&gt;，值为 &lt;code&gt;0&lt;/code&gt; 的地址的指针&lt;/strong&gt;，它适用于值类型如数组和结构体（参见&lt;a href=&#34;10.0.md&#34;&gt;第 10 章&lt;/a&gt;）；它相当于 &lt;code&gt;&amp;amp;T{}&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make(T)&lt;/code&gt; &lt;strong&gt;返回一个类型为 T 的初始值&lt;/strong&gt;，它只适用于 3 种内建的引用类型：切片、&lt;code&gt;map&lt;/code&gt; 和 &lt;code&gt;channel&lt;/code&gt;（参见&lt;a href=&#34;08.0.md&#34;&gt;第 8 章&lt;/a&gt;和&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;换言之，&lt;code&gt;new()&lt;/code&gt; 函数分配内存，&lt;code&gt;make()&lt;/code&gt; 函数初始化；下图给出了区别：&lt;/p&gt;
&lt;img src=&#34;images/7.2_fig7.3.png?raw=true&#34; style=&#34;zoom:50%;&#34; /&gt;
&lt;p&gt;在图 7.3 的第一幅图中：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var p *[]int = new([]int) // *p == nil; with len and cap 0
p := new([]int)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在第二幅图中， &lt;code&gt;p := make([]int, 0)&lt;/code&gt; ，切片 已经被初始化，但是指向一个空的数组。&lt;/p&gt;
&lt;p&gt;以上两种方式实用性都不高。下面的方法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var v []int = make([]int, 10, 50)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或者&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;v := make([]int, 10, 50)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这样分配一个有 50 个 &lt;code&gt;int&lt;/code&gt; 值的数组，并且创建了一个长度为 10，容量为 50 的切片 &lt;code&gt;v&lt;/code&gt;，该切片指向数组的前 10 个元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 7.3&lt;/strong&gt; 给定 &lt;code&gt;s := make([]byte, 5)&lt;/code&gt;，&lt;code&gt;len(s)&lt;/code&gt; 和 &lt;code&gt;cap(s)&lt;/code&gt; 分别是多少？&lt;code&gt;s = s[2:4]&lt;/code&gt;，&lt;code&gt;len(s)&lt;/code&gt; 和 &lt;code&gt;cap(s)&lt;/code&gt; 又分别是多少？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 7.4&lt;/strong&gt; 假设 &lt;code&gt;s1 := []byte{&#39;p&#39;, &#39;o&#39;, &#39;e&#39;, &#39;m&#39;}&lt;/code&gt; 且 &lt;code&gt;s2 := s1[2:]&lt;/code&gt;，&lt;code&gt;s2&lt;/code&gt; 的值是多少？如果我们执行 &lt;code&gt;s2[1] = &#39;t&#39;&lt;/code&gt;，&lt;code&gt;s1&lt;/code&gt; 和 &lt;code&gt;s2&lt;/code&gt; 现在的值又分别是多少？&lt;/p&gt;
&lt;p&gt;&lt;em&gt;译者注：如何理解 new、make、slice、map、channel 的关系&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;1.slice、map 以及 channel 都是 golang 内建的一种引用类型，三者在内存中存在多个组成部分，
需要对内存组成部分初始化后才能使用，而 make 就是对三者进行初始化的一种操作方式&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2. new 获取的是存储指定变量内存地址的一个变量，对于变量内部结构并不会执行相应的初始化操作，
所以 slice、map、channel 需要 make 进行初始化并获取对应的内存地址，而非 new 简单的获取内存地址&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;725-多维切片&#34;&gt;7.2.5 多维切片&lt;/h2&gt;
&lt;p&gt;和数组一样，切片通常也是一维的，但是也可以由一维组合成高维。通过分片的分片（或者切片的数组），长度可以任意动态变化，所以 Go 语言的多维切片可以任意切分。而且，内层的切片必须单独分配（通过 &lt;code&gt;make()&lt;/code&gt; 函数）。&lt;/p&gt;
&lt;h2 id=&#34;726-bytes-包&#34;&gt;7.2.6 bytes 包&lt;/h2&gt;
&lt;p&gt;类型 &lt;code&gt;[]byte&lt;/code&gt; 的切片十分常见，Go 语言有一个 &lt;code&gt;bytes&lt;/code&gt; 包专门用来提供这种类型的操作方法。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bytes&lt;/code&gt; 包和字符串包十分类似（参见&lt;a href=&#34;04.7.md&#34;&gt;第 4.7 节&lt;/a&gt;）。而且它还包含一个十分有用的类型 &lt;code&gt;Buffer&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import &amp;#34;bytes&amp;#34;

type Buffer struct {
	...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这是一个长度可变的 &lt;code&gt;bytes&lt;/code&gt; 的 buffer，提供 &lt;code&gt;Read()&lt;/code&gt; 和 &lt;code&gt;Write()&lt;/code&gt; 方法，因为读写长度未知的 &lt;code&gt;bytes&lt;/code&gt; 最好使用 &lt;code&gt;buffer&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Buffer&lt;/code&gt; 可以这样定义：&lt;code&gt;var buffer bytes.Buffer&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;或者使用 &lt;code&gt;new()&lt;/code&gt; 获得一个指针：&lt;code&gt;var r *bytes.Buffer = new(bytes.Buffer)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;或者通过函数：&lt;code&gt;func NewBuffer(buf []byte) *Buffer&lt;/code&gt;，创建一个 &lt;code&gt;Buffer&lt;/code&gt; 对象并且用 &lt;code&gt;buf&lt;/code&gt; 初始化好；&lt;code&gt;NewBuffer&lt;/code&gt; 最好用在从 &lt;code&gt;buf&lt;/code&gt; 读取的时候使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;通过 buffer 串联字符串&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;类似于 Java 的 StringBuilder 类。&lt;/p&gt;
&lt;p&gt;在下面的代码段中，我们创建一个 &lt;code&gt;buffer&lt;/code&gt;，通过 &lt;code&gt;buffer.WriteString(s)&lt;/code&gt; 方法将字符串 &lt;code&gt;s&lt;/code&gt; 追加到后面，最后再通过 &lt;code&gt;buffer.String()&lt;/code&gt; 方法转换为 &lt;code&gt;string&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var buffer bytes.Buffer
for {
	if s, ok := getNextString(); ok { //method getNextString() not shown here
		buffer.WriteString(s)
	} else {
		break
	}
}
fmt.Print(buffer.String(), &amp;#34;\n&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种实现方式比使用 &lt;code&gt;+=&lt;/code&gt; 要更节省内存和 CPU，尤其是要串联的字符串数目特别多的时候。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.5&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给定切片 &lt;code&gt;sl&lt;/code&gt;，将一个 &lt;code&gt;[]byte&lt;/code&gt; 数组追加到 &lt;code&gt;sl&lt;/code&gt; 后面。写一个函数 &lt;code&gt;Append(slice, data []byte) []byte&lt;/code&gt;，该函数在 &lt;code&gt;sl&lt;/code&gt; 不能存储更多数据的时候自动扩容。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 7.6&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把一个缓存 &lt;code&gt;buf&lt;/code&gt; 分片成两个切片：第一个是前 &lt;code&gt;n&lt;/code&gt; 个 bytes，后一个是剩余的，用一行代码实现。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的数组声明和初始化</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.26-%E6%95%B0%E7%BB%84%E5%A3%B0%E6%98%8E%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96/</link>
    <pubDate>Tue, 26 Jul 2022 11:45:21 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.26-%E6%95%B0%E7%BB%84%E5%A3%B0%E6%98%8E%E5%92%8C%E5%88%9D%E5%A7%8B%E5%8C%96/</guid>
    <description>
        &lt;h1 id=&#34;71-声明和初始化&#34;&gt;7.1 声明和初始化&lt;/h1&gt;
&lt;h2 id=&#34;711-概念&#34;&gt;7.1.1 概念&lt;/h2&gt;
&lt;p&gt;数组是具有相同 &lt;strong&gt;唯一类型&lt;/strong&gt; 的一组已编号且长度固定的数据项序列（这是一种同构的数据结构）；这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。数组长度必须是一个常量表达式，并且必须是一个非负整数。数组长度也是数组类型的一部分，所以 &lt;code&gt;[5]int&lt;/code&gt; 和 &lt;code&gt;[10]int&lt;/code&gt; 是属于不同类型的。数组的编译时值初始化是按照数组顺序完成的（如下）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt; 如果我们想让数组元素类型为任意类型的话可以使用空接口作为类型（参考 &lt;a href=&#34;11.9.md&#34;&gt;第 11 章&lt;/a&gt;）。当使用值时我们必须先做一个类型判断（参考 &lt;a href=&#34;11.3.md&#34;&gt;第 11 章&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;数组元素可以通过 &lt;strong&gt;索引&lt;/strong&gt;（位置）来读取（或者修改），索引从 &lt;code&gt;0&lt;/code&gt; 开始，第一个元素索引为 &lt;code&gt;0&lt;/code&gt;，第二个索引为 &lt;code&gt;1&lt;/code&gt;，以此类推（数组以 0 开始在所有类 C 语言中是相似的）。元素的数目（也称为长度或者数组大小）必须是固定的并且在声明该数组时就给出（编译时需要知道数组长度以便分配内存）；数组长度最大为 2GB。&lt;/p&gt;
&lt;p&gt;声明的格式是：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var identifier [len]type
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var arr1 [5]int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在内存中的结构是：&lt;img src=&#34;images/7.1_fig7.1.png?raw=true&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;每个元素是一个整型值，当声明数组时所有的元素都会被自动初始化为默认值 0。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;arr1&lt;/code&gt; 的长度是 5，索引范围从 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;len(arr1)-1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第一个元素是 &lt;code&gt;arr1[0]&lt;/code&gt;，第三个元素是 &lt;code&gt;arr1[2]&lt;/code&gt;；总体来说索引 &lt;code&gt;i&lt;/code&gt; 代表的元素是 &lt;code&gt;arr1[i]&lt;/code&gt;，最后一个元素是 &lt;code&gt;arr1[len(arr1)-1]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;对索引项为 &lt;code&gt;i&lt;/code&gt; 的数组元素赋值可以这么操作：&lt;code&gt;arr[i] = value&lt;/code&gt;，所以数组是 &lt;strong&gt;可变的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;只有有效的索引可以被使用，当使用等于或者大于 &lt;code&gt;len(arr1)&lt;/code&gt; 的索引时：如果编译器可以检测到，会给出索引超限的提示信息；如果检测不到的话编译会通过而运行时会 &lt;code&gt;panic()&lt;/code&gt;:（参考&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt;）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;runtime error: index out of range
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于索引的存在，遍历数组的方法自然就是使用 &lt;code&gt;for&lt;/code&gt; 结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过 &lt;code&gt;for&lt;/code&gt; 初始化数组项&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;for&lt;/code&gt; 打印数组元素&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;for&lt;/code&gt; 依次处理元素&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例 7.1 &lt;a href=&#34;examples/chapter_7/for_arrays.go&#34;&gt;for_arrays.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	var arr1 [5]int

	for i:=0; i &amp;lt; len(arr1); i++ {
		arr1[i] = i * 2
	}

	for i:=0; i &amp;lt; len(arr1); i++ {
		fmt.Printf(&amp;#34;Array at index %d is %d\n&amp;#34;, i, arr1[i])
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Array at index 0 is 0
Array at index 1 is 2
Array at index 2 is 4
Array at index 3 is 6
Array at index 4 is 8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;for&lt;/code&gt; 循环中的条件非常重要：&lt;code&gt;i &amp;lt; len(arr1)&lt;/code&gt;，如果写成 &lt;code&gt;i &amp;lt;= len(arr1)&lt;/code&gt; 的话会产生越界错误。&lt;/p&gt;
&lt;p&gt;IDIOM:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i:=0; i &amp;lt; len(arr1); i++｛
	arr1[i] = ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也可以使用 for-range 的生成方式：&lt;/p&gt;
&lt;p&gt;IDIOM:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i,_:= range arr1 {
...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在这里 &lt;code&gt;i&lt;/code&gt; 也是数组的索引。当然这两种 &lt;code&gt;for&lt;/code&gt; 结构对于切片（&lt;code&gt;slices&lt;/code&gt;）（参考 &lt;a href=&#34;07.2.md&#34;&gt;第 7 章&lt;/a&gt;）来说也同样适用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 7.1&lt;/strong&gt; 下面代码段的输出是什么？&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a := [...]string{&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;, &amp;#34;c&amp;#34;, &amp;#34;d&amp;#34;}
for i := range a {
	fmt.Println(&amp;#34;Array item&amp;#34;, i, &amp;#34;is&amp;#34;, a[i])
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go 语言中的数组是一种 &lt;strong&gt;值类型&lt;/strong&gt;（不像 C/C++ 中是指向首元素的指针），所以可以通过 &lt;code&gt;new()&lt;/code&gt; 来创建： &lt;code&gt;var arr1 = new([5]int)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那么这种方式和 &lt;code&gt;var arr2 [5]int&lt;/code&gt; 的区别是什么呢？&lt;code&gt;arr1&lt;/code&gt; 的类型是 &lt;code&gt;*[5]int&lt;/code&gt;，而 &lt;code&gt;arr2&lt;/code&gt; 的类型是 &lt;code&gt;[5]int&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这样的结果就是当把一个数组赋值给另一个时，需要再做一次数组内存的拷贝操作。例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;arr2 := *arr1
arr2[2] = 100
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这样两个数组就有了不同的值，在赋值后修改 &lt;code&gt;arr2&lt;/code&gt; 不会对 &lt;code&gt;arr1&lt;/code&gt; 生效。&lt;/p&gt;
&lt;p&gt;所以在函数中数组作为参数传入时，如 &lt;code&gt;func1(arr2)&lt;/code&gt;，会产生一次数组拷贝，&lt;code&gt;func1()&lt;/code&gt; 方法不会修改原始的数组 &lt;code&gt;arr2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果你想修改原数组，那么 &lt;code&gt;arr2&lt;/code&gt; 必须通过 &lt;code&gt;&amp;amp;&lt;/code&gt; 操作符以引用方式传过来，例如 &lt;code&gt;func1(&amp;amp;arr2)&lt;/code&gt;，下面是一个例子：&lt;/p&gt;
&lt;p&gt;示例 7.2 &lt;a href=&#34;examples/chapter_7/pointer_array.go&#34;&gt;pointer_array.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func f(a [3]int) { fmt.Println(a) }
func fp(a *[3]int) { fmt.Println(a) }

func main() {
	var ar [3]int
	f(ar) 	// passes a copy of ar
	fp(&amp;amp;ar) // passes a pointer to ar
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[0 0 0]
&amp;amp;[0 0 0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另一种方法就是生成数组切片并将其传递给函数（详见&lt;a href=&#34;07.1.md&#34;&gt;第 7.1.4 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;练习7.1：&lt;a href=&#34;examples/chapter_7/array_value.go&#34;&gt;array_value.go&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;证明当数组赋值时，发生了数组内存拷贝。&lt;/p&gt;
&lt;p&gt;练习7.2：&lt;a href=&#34;examples/chapter_7/for_array.go&#34;&gt;for_array.go&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;写一个循环并用下标给数组赋值（从 0 到 15）并且将数组打印在屏幕上。&lt;/p&gt;
&lt;p&gt;练习7.3：&lt;a href=&#34;examples/chapter_7/fibonacci_array.go&#34;&gt;fibonacci_array.go&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;在&lt;a href=&#34;06.6.md&#34;&gt;第 6.6 节&lt;/a&gt; 我们看到了一个递归计算 Fibonacci 数值的方法。但是通过数组我们可以更快的计算出 Fibonacci 数。完成该方法并打印出前 50 个 Fibonacci 数字。&lt;/p&gt;
&lt;h2 id=&#34;712-数组常量&#34;&gt;7.1.2 数组常量&lt;/h2&gt;
&lt;p&gt;如果数组值已经提前知道了，那么可以通过 &lt;strong&gt;数组常量&lt;/strong&gt; 的方法来初始化数组，而不用依次使用 &lt;code&gt;[]=&lt;/code&gt; 方法（所有的组成元素都有相同的常量语法）。&lt;/p&gt;
&lt;p&gt;示例 7.3 &lt;a href=&#34;examples/chapter_7/array_literals.go&#34;&gt;array_literals.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	// var arrAge = [5]int{18, 20, 15, 22, 16}
	// var arrLazy = [...]int{5, 6, 7, 8, 22}
	// var arrLazy = []int{5, 6, 7, 8, 22}	//注：初始化得到的实际上是切片slice
	var arrKeyValue = [5]string{3: &amp;#34;Chris&amp;#34;, 4: &amp;#34;Ron&amp;#34;}
	// var arrKeyValue = []string{3: &amp;#34;Chris&amp;#34;, 4: &amp;#34;Ron&amp;#34;}	//注：初始化得到的实际上是切片slice

	for i:=0; i &amp;lt; len(arrKeyValue); i++ {
		fmt.Printf(&amp;#34;Person at %d is %s\n&amp;#34;, i, arrKeyValue[i])
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第一种变化：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var arrAge = [5]int{18, 20, 15, 22, 16}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意 &lt;code&gt;[5]int&lt;/code&gt; 可以从左边起开始忽略：&lt;code&gt;[10]int {1, 2, 3}&lt;/code&gt; :这是一个有 10 个元素的数组，除了前三个元素外其他元素都为 &lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第二种变化：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var arrLazy = [...]int{5, 6, 7, 8, 22}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;...&lt;/code&gt; 同样可以忽略，从技术上说它们其实变成了切片。&lt;/p&gt;
&lt;p&gt;第三种变化：&lt;code&gt;key: value 语法&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var arrKeyValue = [5]string{3: &amp;#34;Chris&amp;#34;, 4: &amp;#34;Ron&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;只有索引 3 和 4 被赋予实际的值，其他元素都被设置为空的字符串，所以输出结果为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Person at 0 is
Person at 1 is
Person at 2 is
Person at 3 is Chris
Person at 4 is Ron
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里数组长度同样可以写成 &lt;code&gt;...&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可以取任意数组常量的地址来作为指向新实例的指针。&lt;/p&gt;
&lt;p&gt;示例 7.4 &lt;a href=&#34;examples/chapter_7/pointer_array2.go&#34;&gt;pointer_array2.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func fp(a *[3]int) { fmt.Println(a) }

func main() {
	for i := 0; i &amp;lt; 3; i++ {
		fp(&amp;amp;[3]int{i, i * i, i * i * i})
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;amp;[0 0 0]
&amp;amp;[1 1 1]
&amp;amp;[2 4 8]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;几何点（或者数学向量）是一个使用数组的经典例子。为了简化代码通常使用一个别名：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type Vector3D [3]float32
var vec Vector3D
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;713-多维数组&#34;&gt;7.1.3 多维数组&lt;/h2&gt;
&lt;p&gt;数组通常是一维的，但是可以用来组装成多维数组，例如：&lt;code&gt;[3][5]int&lt;/code&gt;，&lt;code&gt;[2][2][2]float64&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;内部数组总是长度相同的。Go 语言的多维数组是矩形式的（唯一的例外是切片的数组，参见&lt;a href=&#34;07.2.md&#34;&gt;第 7.2.5 节&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;示例 7.5 &lt;a href=&#34;examples/chapter_7/multidim_array.go&#34;&gt;multidim_array.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
const (
	WIDTH  = 1920
	HEIGHT = 1080
)

type pixel int
var screen [WIDTH][HEIGHT]pixel

func main() {
	for y := 0; y &amp;lt; HEIGHT; y++ {
		for x := 0; x &amp;lt; WIDTH; x++ {
			screen[x][y] = 0
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;714-将数组传递给函数&#34;&gt;7.1.4 将数组传递给函数&lt;/h2&gt;
&lt;p&gt;把一个大数组传递给函数会消耗很多内存。有两种方法可以避免这种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;传递数组的指针&lt;/li&gt;
&lt;li&gt;使用数组的切片&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来的例子阐明了第一种方法：&lt;/p&gt;
&lt;p&gt;示例 7.6 &lt;a href=&#34;examples/chapter_7/array_sum.go&#34;&gt;array_sum.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	array := [3]float64{7.0, 8.5, 9.1}
	x := Sum(&amp;amp;array) // Note the explicit address-of operator
	// to pass a pointer to the array
	fmt.Printf(&amp;#34;The sum of the array is: %f&amp;#34;, x)
}

func Sum(a *[3]float64) (sum float64) {
	for _, v := range a { // derefencing *a to get back to the array is not necessary!
		sum += v
	}
	return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The sum of the array is: 24.600000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但这在 Go 中并不常用，通常使用切片（参考 &lt;a href=&#34;07.2.md&#34;&gt;第 7.2 节&lt;/a&gt;）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的应用闭包：将函数作为返回值</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.25-%E5%BA%94%E7%94%A8%E9%97%AD%E5%8C%85%E5%B0%86%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E8%BF%94%E5%9B%9E%E5%80%BC/</link>
    <pubDate>Mon, 25 Jul 2022 11:41:39 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.25-%E5%BA%94%E7%94%A8%E9%97%AD%E5%8C%85%E5%B0%86%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E8%BF%94%E5%9B%9E%E5%80%BC/</guid>
    <description>
        &lt;h1 id=&#34;69-应用闭包将函数作为返回值&#34;&gt;6.9 应用闭包：将函数作为返回值&lt;/h1&gt;
&lt;p&gt;在程序 &lt;a href=&#34;examples/chapter_6/function_return.go&#34;&gt;function_return.go&lt;/a&gt; 中我们将会看到函数 &lt;code&gt;Add2()&lt;/code&gt; 和 &lt;code&gt;Adder()&lt;/code&gt; 均会返回签名为 &lt;code&gt;func(b int) int&lt;/code&gt; 的函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Add2() (func(b int) int)
func Adder(a int) (func(b int) int)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数 &lt;code&gt;Add2()&lt;/code&gt; 不接受任何参数，但函数 &lt;code&gt;Adder()&lt;/code&gt; 接受一个 &lt;code&gt;int&lt;/code&gt; 类型的整数作为参数。&lt;/p&gt;
&lt;p&gt;我们也可以将 &lt;code&gt;Adder()&lt;/code&gt; 返回的函数存到变量中 (&lt;a href=&#34;examples/chapter_6/function_return.go&#34;&gt;function_return.go&lt;/a&gt;)。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	// make an Add2 function, give it a name p2, and call it:
	p2 := Add2()
	fmt.Printf(&amp;#34;Call Add2 for 3 gives: %v\n&amp;#34;, p2(3))
	// make a special Adder function, a gets value 2:
	TwoAdder := Adder(2)
	fmt.Printf(&amp;#34;The result is: %v\n&amp;#34;, TwoAdder(3))
}

func Add2() func(b int) int {
	return func(b int) int {
		return b + 2
	}
}

func Adder(a int) func(b int) int {
	return func(b int) int {
		return a + b
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Call Add2 for 3 gives: 5
The result is: 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;下例为一个略微不同的实现 (&lt;a href=&#34;examples/chapter_6/function_closure.go&#34;&gt;function_closure.go&lt;/a&gt;)：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var f = Adder()
	fmt.Print(f(1), &amp;#34; - &amp;#34;)
	fmt.Print(f(20), &amp;#34; - &amp;#34;)
	fmt.Print(f(300))
}

func Adder() func(int) int {
	var x int
	return func(delta int) int {
		x += delta
		return x
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数 &lt;code&gt;Adder()&lt;/code&gt; 现在被赋值到变量 &lt;code&gt;f&lt;/code&gt; 中（类型为 &lt;code&gt;func(int) int&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 - 21 - 321
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;三次调用函数 &lt;code&gt;f&lt;/code&gt; 的过程中函数 &lt;code&gt;Adder()&lt;/code&gt; 中变量 &lt;code&gt;delta&lt;/code&gt; 的值分别为：1、20 和 300。&lt;/p&gt;
&lt;p&gt;我们可以看到，在多次调用中，变量 &lt;code&gt;x&lt;/code&gt; 的值是被保留的，即 &lt;code&gt;0 + 1 = 1&lt;/code&gt;，然后 &lt;code&gt;1 + 20 = 21&lt;/code&gt;，最后 &lt;code&gt;21 + 300 = 321&lt;/code&gt;：闭包函数保存并积累其中的变量的值，不管外部函数退出与否，它都能够继续操作外部函数中的局部变量。&lt;/p&gt;
&lt;p&gt;这些局部变量同样可以是参数，例如之前例子中的 &lt;code&gt;Adder(as int)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这些例子清楚地展示了如何在 Go 语言中使用闭包。&lt;/p&gt;
&lt;p&gt;在闭包中使用到的变量可以是在闭包函数体内声明的，也可以是在外部函数声明的：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var g int
go func(i int) {
	s := 0
	for j := 0; j &amp;lt; i; j++ { s += j }
	g = s
}(1000) // Passes argument 1000 to the function literal.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这样闭包函数就能够被应用到整个集合的元素上，并修改它们的值。然后这些变量就可以用于表示或计算全局或平均值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 6.9&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/fibonacci_closure.go&#34;&gt;fibonacci_closure&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不使用递归但使用闭包改写第 6.6 节中的斐波那契数列程序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 6.10&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;学习并理解以下程序的工作原理：&lt;/p&gt;
&lt;p&gt;一个返回值为另一个函数的函数可以被称之为工厂函数，这在您需要创建一系列相似的函数的时候非常有用：书写一个工厂函数而不是针对每种情况都书写一个函数。下面的函数演示了如何动态返回追加后缀的函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func MakeAddSuffix(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;现在，我们可以生成如下函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;addBmp := MakeAddSuffix(&amp;#34;.bmp&amp;#34;)
addJpeg := MakeAddSuffix(&amp;#34;.jpeg&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后调用它们：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;addBmp(&amp;#34;file&amp;#34;) // returns: file.bmp
addJpeg(&amp;#34;file&amp;#34;) // returns: file.jpeg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可以返回其它函数的函数和接受其它函数作为参数的函数均被称之为高阶函数，是函数式语言的特点。我们已经在&lt;a href=&#34;06.7.md&#34;&gt;第 6.7 节&lt;/a&gt;中得知函数也是一种值，因此很显然 Go 语言具有一些函数式语言的特性。闭包在 Go 语言中非常常见，常用于 goroutine 和管道操作（详见第 &lt;a href=&#34;14.8.md&#34;&gt;14.8&lt;/a&gt;-&lt;a href=&#34;14.9.md&#34;&gt;14.9&lt;/a&gt; 节）。在&lt;a href=&#34;11.14.md&#34;&gt;第 11.14 节&lt;/a&gt;的程序中，我们将会看到 Go 语言中的函数在处理混合对象时的强大能力。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的闭包</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.24-%E9%97%AD%E5%8C%85/</link>
    <pubDate>Sun, 24 Jul 2022 11:40:02 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.24-%E9%97%AD%E5%8C%85/</guid>
    <description>
        &lt;h1 id=&#34;68-闭包&#34;&gt;6.8 闭包&lt;/h1&gt;
&lt;p&gt;当我们不希望给函数起名字的时候，可以使用匿名函数，例如：&lt;code&gt;func(x, y int) int { return x + y }&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这样的一个函数不能够独立存在（编译器会返回错误：&lt;code&gt;non-declaration statement outside function body&lt;/code&gt;），但可以被赋值于某个变量，即保存函数的地址到变量中：&lt;code&gt;fplus := func(x, y int) int { return x + y }&lt;/code&gt;，然后通过变量名对函数进行调用：&lt;code&gt;fplus(3,4)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当然，您也可以直接对匿名函数进行调用：&lt;code&gt;func(x, y int) int { return x + y } (3, 4)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;下面是一个计算从 1 到 100 万整数的总和的匿名函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func() {
	sum := 0
	for i := 1; i &amp;lt;= 1e6; i++ {
		sum += i
	}
}()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;表示参数列表的第一对括号必须紧挨着关键字 &lt;code&gt;func&lt;/code&gt;，因为匿名函数没有名称。花括号 &lt;code&gt;{}&lt;/code&gt; 涵盖着函数体，最后的一对括号表示对该匿名函数的调用。&lt;/p&gt;
&lt;p&gt;下面的例子展示了如何将匿名函数赋值给变量并对其进行调用（&lt;a href=&#34;examples/chapter_6/function_literal.go&#34;&gt;function_literal.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	f()
}
func f() {
	for i := 0; i &amp;lt; 4; i++ {
		g := func(i int) { fmt.Printf(&amp;#34;%d &amp;#34;, i) }
		g(i)
		fmt.Printf(&amp;#34; - g is of type %T and has value %v\n&amp;#34;, g, g)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0 - g is of type func(int) and has value 0x681a80
1 - g is of type func(int) and has value 0x681b00
2 - g is of type func(int) and has value 0x681ac0
3 - g is of type func(int) and has value 0x681400
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;我们可以看到变量 &lt;code&gt;g&lt;/code&gt; 代表的是 &lt;code&gt;func(int)&lt;/code&gt;，变量的值是一个内存地址。&lt;/p&gt;
&lt;p&gt;所以我们实际上拥有的是一个函数值：匿名函数可以被赋值给变量并作为值使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 6.8&lt;/strong&gt; 在 &lt;code&gt;main()&lt;/code&gt; 函数中写一个用于打印 &lt;code&gt;Hello World&lt;/code&gt; 字符串的匿名函数并赋值给变量 &lt;code&gt;fv&lt;/code&gt;，然后调用该函数并打印变量 &lt;code&gt;fv&lt;/code&gt; 的类型。&lt;/p&gt;
&lt;p&gt;匿名函数像所有函数一样可以接受或不接受参数。下面的例子展示了如何传递参数到匿名函数中：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func (u string) {
	fmt.Println(u)
	…
}(v)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请学习以下示例并思考（&lt;a href=&#34;examples/chapter_6/return_defer.go&#34;&gt;return_defer.go&lt;/a&gt;）：函数 &lt;code&gt;f&lt;/code&gt; 返回时，变量 &lt;code&gt;ret&lt;/code&gt; 的值是什么？&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func f() (ret int) {
	defer func() {
		ret++
	}()
	return 1
}
func main() {
	fmt.Println(f())
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;变量 &lt;code&gt;ret&lt;/code&gt; 的值为 &lt;code&gt;2&lt;/code&gt;，因为 &lt;code&gt;ret++&lt;/code&gt; 是在执行 &lt;code&gt;return 1&lt;/code&gt; 语句后发生的。&lt;/p&gt;
&lt;p&gt;这可用于在返回语句之后修改返回的 &lt;code&gt;error&lt;/code&gt; 时使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;defer 语句和匿名函数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;关键字 &lt;code&gt;defer&lt;/code&gt; （详见&lt;a href=&#34;06.4.md&#34;&gt;第 6.4 节&lt;/a&gt;）经常配合匿名函数使用，它可以用于改变函数的命名返回值。&lt;/p&gt;
&lt;p&gt;匿名函数还可以配合 &lt;code&gt;go&lt;/code&gt; 关键字来作为 goroutine 使用（详见&lt;a href=&#34;14.0.md&#34;&gt;第 14 章&lt;/a&gt;和&lt;a href=&#34;16.9.md&#34;&gt;第 16.9 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;匿名函数同样被称之为闭包（函数式语言的术语）：它们被允许调用定义在其它环境下的变量。闭包可使得某个函数捕捉到一些外部状态，例如：函数被创建时的状态。另一种表示方式为：一个闭包继承了函数所声明时的作用域。这种状态（作用域内的变量）都被共享到闭包的环境中，因此这些变量可以在闭包中被操作，直到被销毁，详见&lt;a href=&#34;06.9.md&#34;&gt;第 6.9 节&lt;/a&gt; 中的示例。闭包经常被用作包装函数：它们会预先定义好 1 个或多个参数以用于包装，详见下一节中的示例。另一个不错的应用就是使用闭包来完成更加简洁的错误检查（详见&lt;a href=&#34;16.10.md&#34;&gt;第 16.10.2 节&lt;/a&gt;）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序中将函数作为参数</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.23-%E5%B0%86%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0/</link>
    <pubDate>Sat, 23 Jul 2022 18:06:32 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.23-%E5%B0%86%E5%87%BD%E6%95%B0%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0/</guid>
    <description>
        &lt;h1 id=&#34;67-将函数作为参数&#34;&gt;6.7 将函数作为参数&lt;/h1&gt;
&lt;p&gt;函数可以作为其它函数的参数进行传递，然后在其它函数内调用执行，一般称之为回调。下面是一个将函数作为参数的简单例子（&lt;a href=&#34;examples/chapter_6/function_parameter.go&#34;&gt;function_parameter.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;fmt&amp;#34;
)

func main() {
	callback(1, Add)
}

func Add(a, b int) {
	fmt.Printf(&amp;#34;The sum of %d and %d is: %d\n&amp;#34;, a, b, a+b)
}

func callback(y int, f func(int, int)) {
	f(y, 2) // this becomes Add(1, 2)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The sum of 1 and 2 is: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将函数作为参数的最好的例子是函数 &lt;code&gt;strings.IndexFunc()&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;该函数的签名是 &lt;code&gt;func IndexFunc(s string, f func(c rune) bool) int&lt;/code&gt;，它的返回值是字符串 s 中第一个使函数 &lt;code&gt;f(c)&lt;/code&gt; 返回 &lt;code&gt;true&lt;/code&gt; 的 Unicode 字符的索引值。如果找不到，则返回 -1。&lt;/p&gt;
&lt;p&gt;例如 &lt;code&gt;strings.IndexFunc(line, unicode.IsSpace)&lt;/code&gt; 就会返回 &lt;code&gt;line&lt;/code&gt; 中第一个空白字符的索引值。当然，您也可以书写自己的函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func IsAscii(c int) bool {
	if c &amp;gt; 255 {
		return false
	}
	return true
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在&lt;a href=&#34;14.10.md&#34;&gt;第 14.10.1 节&lt;/a&gt; 中，我们将会根据一个客户端/服务端程序作为示例对这个用法进行深入讨论。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type binOp func(a, b int) int
func run(op binOp, req *Request) { … }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;练习 6.7&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/strings_map.go&#34;&gt;strings_map.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;包 &lt;code&gt;strings&lt;/code&gt; 中的 &lt;code&gt;Map()&lt;/code&gt; 函数和 &lt;code&gt;strings.IndexFunc()&lt;/code&gt; 一样都是非常好的使用例子。请学习它的源代码并基于该函数书写一个程序，要求将指定文本内的所有非 ASCII 字符替换成问号 &lt;code&gt;&#39;?&#39;&lt;/code&gt; 或空格 &lt;code&gt;&#39; &#39;&lt;/code&gt;。您需要怎么做才能删除这些字符呢？&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的递归函数</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.22-%E9%80%92%E5%BD%92%E5%87%BD%E6%95%B0/</link>
    <pubDate>Fri, 22 Jul 2022 16:19:22 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.22-%E9%80%92%E5%BD%92%E5%87%BD%E6%95%B0/</guid>
    <description>
        &lt;h1 id=&#34;66-递归函数&#34;&gt;6.6 递归函数&lt;/h1&gt;
&lt;p&gt;当一个函数在其函数体内调用自身，则称之为递归。最经典的例子便是计算斐波那契数列，即前两个数为 1，从第三个数开始每个数均为前两个数之和。&lt;/p&gt;
&lt;p&gt;数列如下所示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, …
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面的程序可用于生成该数列（示例 6.13 &lt;a href=&#34;examples/chapter_6/fibonacci.go&#34;&gt;fibonacci.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	result := 0
	for i := 0; i &amp;lt;= 10; i++ {
		result = fibonacci(i)
		fmt.Printf(&amp;#34;fibonacci(%d) is: %d\n&amp;#34;, i, result)
	}
}

func fibonacci(n int) (res int) {
	if n &amp;lt;= 1 {
		res = 1
	} else {
		res = fibonacci(n-1) + fibonacci(n-2)
	}
	return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fibonacci(0) is: 1
fibonacci(1) is: 1
fibonacci(2) is: 2
fibonacci(3) is: 3
fibonacci(4) is: 5
fibonacci(5) is: 8
fibonacci(6) is: 13
fibonacci(7) is: 21
fibonacci(8) is: 34
fibonacci(9) is: 55
fibonacci(10) is: 89
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;许多问题都可以使用优雅的递归来解决，比如说著名的快速排序算法。&lt;/p&gt;
&lt;p&gt;在使用递归函数时经常会遇到的一个重要问题就是栈溢出：一般出现在大量的递归调用导致的程序栈内存分配耗尽。这个问题可以通过一个名为 &lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC&#34;&gt;懒惰求值&lt;/a&gt; 的技术解决，在 Go 语言中，我们可以使用管道 (channel) 和 goroutine（详见&lt;a href=&#34;14.8.md&#34;&gt;第 14.8 节&lt;/a&gt;）来实现。&lt;a href=&#34;14.8.md&#34;&gt;练习 14.12&lt;/a&gt; 也会通过这个方案来优化斐波那契数列的生成问题。&lt;/p&gt;
&lt;p&gt;Go 语言中也可以使用相互调用的递归函数：多个函数之间相互调用形成闭环。因为 Go 语言编译器的特殊性，这些函数的声明顺序可以是任意的。下面这个简单的例子展示了函数 &lt;code&gt;odd()&lt;/code&gt; 和 &lt;code&gt;even()&lt;/code&gt; 之间的相互调用（示例 6.14 &lt;a href=&#34;examples/chapter_6/mut_recurs.go&#34;&gt;mut_recurs.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;fmt&amp;#34;
)

func main() {
	fmt.Printf(&amp;#34;%d is even: is %t\n&amp;#34;, 16, even(16)) // 16 is even: is true
	fmt.Printf(&amp;#34;%d is odd: is %t\n&amp;#34;, 17, odd(17))
	// 17 is odd: is true
	fmt.Printf(&amp;#34;%d is odd: is %t\n&amp;#34;, 18, odd(18))
	// 18 is odd: is false
}

func even(nr int) bool {
	if nr == 0 {
		return true
	}
	return odd(RevSign(nr) - 1)
}

func odd(nr int) bool {
	if nr == 0 {
		return false
	}
	return even(RevSign(nr) - 1)
}

func RevSign(nr int) int {
	if nr &amp;lt; 0 {
		return -nr
	}
	return nr
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;练习题&#34;&gt;练习题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;练习 6.4&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/fibonacci2.go&#34;&gt;fibonacci2.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;重写本节中生成斐波那契数列的程序并返回两个命名返回值（详见&lt;a href=&#34;06.2.md&#34;&gt;第 6.2 节&lt;/a&gt;），即数列中的位置和对应的值，例如 5 与 4，89 与 10。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 6.5&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/10to1_recursive.go&#34;&gt;10to1_recursive.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;使用递归函数从 10 打印到 1。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 6.6&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/factorial.go&#34;&gt;factorial.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;实现一个输出前 30 个整数的阶乘的程序。&lt;/p&gt;
&lt;p&gt;n 的阶乘定义为：&lt;code&gt;n! = n * (n-1)!, 0! = 1&lt;/code&gt;，因此它非常适合使用递归函数来实现。&lt;/p&gt;
&lt;p&gt;然后，使用命名返回值来实现这个程序的第二个版本。&lt;/p&gt;
&lt;p&gt;特别注意的是，使用 &lt;code&gt;int&lt;/code&gt; 类型最多只能计算到 12 的阶乘，因为一般情况下 &lt;code&gt;int&lt;/code&gt; 类型的大小为 32 位，继续计算会导致溢出错误。那么，如何才能解决这个问题呢？&lt;/p&gt;
&lt;p&gt;最好的解决方案就是使用 &lt;code&gt;big&lt;/code&gt; 包（详见&lt;a href=&#34;09.4.md&#34;&gt;第 9.4 节&lt;/a&gt;）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的内置函数</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.21-%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0/</link>
    <pubDate>Thu, 21 Jul 2022 22:37:24 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.21-%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0/</guid>
    <description>
        &lt;h1 id=&#34;65-内置函数&#34;&gt;6.5 内置函数&lt;/h1&gt;
&lt;p&gt;Go 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作，例如：&lt;code&gt;len()&lt;/code&gt;、&lt;code&gt;cap()&lt;/code&gt; 和 &lt;code&gt;append()&lt;/code&gt;，或必须用于系统级的操作，例如：&lt;code&gt;panic()&lt;/code&gt;。因此，它们需要直接获得编译器的支持。&lt;/p&gt;
&lt;p&gt;以下是一个简单的列表，我们会在后面的章节中对它们进行逐个深入的讲解。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;close()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于管道通信&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;len()&lt;/code&gt;、&lt;code&gt;cap()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;len()&lt;/code&gt; 用于返回某个类型的长度或数量（字符串、数组、切片、&lt;code&gt;map&lt;/code&gt; 和管道）；&lt;code&gt;cap()&lt;/code&gt; 是容量的意思，用于返回某个类型的最大容量（只能用于数组、切片和管道，不能用于 &lt;code&gt;map&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new()&lt;/code&gt;、&lt;code&gt;make()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;new()&lt;/code&gt; 和 &lt;code&gt;make()&lt;/code&gt; 均是用于分配内存：&lt;code&gt;new()&lt;/code&gt; 用于值类型和用户定义的类型，如自定义结构，&lt;code&gt;make&lt;/code&gt; 用于内置引用类型（切片、&lt;code&gt;map&lt;/code&gt; 和管道）。它们的用法就像是函数，但是将类型作为参数：&lt;code&gt;new(type)&lt;/code&gt;、&lt;code&gt;make(type)&lt;/code&gt;。&lt;code&gt;new(T)&lt;/code&gt; 分配类型 &lt;code&gt;T&lt;/code&gt; 的零值并返回其地址，也就是指向类型 &lt;code&gt;T&lt;/code&gt; 的指针（详见&lt;a href=&#34;10.1.md&#34;&gt;第 10.1 节&lt;/a&gt;）。它也可以被用于基本类型：&lt;code&gt;v := new(int)&lt;/code&gt;。&lt;code&gt;make(T)&lt;/code&gt; 返回类型 &lt;code&gt;T&lt;/code&gt; 的初始化之后的值，因此它比 &lt;code&gt;new()&lt;/code&gt; 进行更多的工作（详见&lt;a href=&#34;07.2.md&#34;&gt;第 7.2.3/4 节&lt;/a&gt;、&lt;a href=&#34;08.1.md&#34;&gt;第 8.1.1 节&lt;/a&gt;和&lt;a href=&#34;14.2.md&#34;&gt;第 14.2.1 节&lt;/a&gt;）。&lt;strong&gt;&lt;code&gt;new()&lt;/code&gt; 是一个函数，不要忘记它的括号&lt;/strong&gt;。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;copy()&lt;/code&gt;、&lt;code&gt;append()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于复制和连接切片&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;panic()&lt;/code&gt;、&lt;code&gt;recover()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;两者均用于错误处理机制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;print()&lt;/code&gt;、&lt;code&gt;println()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;底层打印函数（详见&lt;a href=&#34;04.2.md&#34;&gt;第 4.2 节&lt;/a&gt;），在部署环境中建议使用 &lt;code&gt;fmt&lt;/code&gt; 包&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;complex()&lt;/code&gt;、&lt;code&gt;real ()&lt;/code&gt;、&lt;code&gt;imag()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;用于创建和操作复数（详见&lt;a href=&#34;04.5.md&#34;&gt;第 4.5.2.2 节&lt;/a&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的Defer和追踪</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.20-defer%E5%92%8C%E8%BF%BD%E8%B8%AA/</link>
    <pubDate>Wed, 20 Jul 2022 22:35:40 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.20-defer%E5%92%8C%E8%BF%BD%E8%B8%AA/</guid>
    <description>
        &lt;h1 id=&#34;64-defer-和追踪&#34;&gt;6.4 defer 和追踪&lt;/h1&gt;
&lt;p&gt;关键字 &lt;code&gt;defer&lt;/code&gt; 允许我们推迟到函数返回之前（或任意位置执行 &lt;code&gt;return&lt;/code&gt; 语句之后）一刻才执行某个语句或函数（为什么要在返回之后才执行这些语句？因为 &lt;code&gt;return&lt;/code&gt; 语句同样可以包含一些操作，而不是单纯地返回某个值）。&lt;/p&gt;
&lt;p&gt;关键字 &lt;code&gt;defer&lt;/code&gt; 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块，它一般用于释放某些已分配的资源。&lt;/p&gt;
&lt;p&gt;示例 6.8 &lt;a href=&#34;examples/chapter_6/defer.go&#34;&gt;defer.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

func main() {
	function1()
}

func function1() {
	fmt.Printf(&amp;#34;In function1 at the top\n&amp;#34;)
	defer function2()
	fmt.Printf(&amp;#34;In function1 at the bottom!\n&amp;#34;)
}

func function2() {
	fmt.Printf(&amp;#34;Function2: Deferred until the end of the calling function!&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;In Function1 at the top
In Function1 at the bottom!
Function2: Deferred until the end of the calling function!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请将 &lt;code&gt;defer&lt;/code&gt; 关键字去掉并对比输出结果。&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;defer&lt;/code&gt; 的语句同样可以接受参数，下面这个例子就会在执行 &lt;code&gt;defer&lt;/code&gt; 语句时打印 &lt;code&gt;0&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func a() {
	i := 0
	defer fmt.Println(i)
	i++
	return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;当有多个 &lt;code&gt;defer&lt;/code&gt; 行为被注册时，它们会以逆序执行（类似栈，即后进先出）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func f() {
	for i := 0; i &amp;lt; 5; i++ {
		defer fmt.Printf(&amp;#34;%d &amp;#34;, i)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面的代码将会输出：&lt;code&gt;4 3 2 1 0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;关键字 &lt;code&gt;defer&lt;/code&gt; 允许我们进行一些函数执行完成后的收尾工作，例如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;关闭文件流 （详见 &lt;a href=&#34;12.2.md&#34;&gt;第 12.2 节&lt;/a&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// open a file  
defer file.Close()
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;解锁一个加锁的资源 （详见 &lt;a href=&#34;09.3.md&#34;&gt;第 9.3 节&lt;/a&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mu.Lock()  
defer mu.Unlock() 
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;打印最终报告&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;printHeader()  
defer printFooter()
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;关闭数据库链接&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// open a database connection  
defer disconnectFromDB()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;合理使用 &lt;code&gt;defer&lt;/code&gt; 语句能够使得代码更加简洁。&lt;/p&gt;
&lt;p&gt;以下代码模拟了上面描述的第 4 种情况：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	doDBOperations()
}

func connectToDB() {
	fmt.Println(&amp;#34;ok, connected to db&amp;#34;)
}

func disconnectFromDB() {
	fmt.Println(&amp;#34;ok, disconnected from db&amp;#34;)
}

func doDBOperations() {
	connectToDB()
	fmt.Println(&amp;#34;Defering the database disconnect.&amp;#34;)
	defer disconnectFromDB() //function called here with defer
	fmt.Println(&amp;#34;Doing some DB operations ...&amp;#34;)
	fmt.Println(&amp;#34;Oops! some crash or network error ...&amp;#34;)
	fmt.Println(&amp;#34;Returning from function here!&amp;#34;)
	return //terminate the program
	// deferred function executed here just before actually returning, even if
	// there is a return or abnormal termination before
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ok, connected to db
Defering the database disconnect.
Doing some DB operations ...
Oops! some crash or network error ...
Returning from function here!
ok, disconnected from db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;defer&lt;/code&gt; 语句实现代码追踪&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个基础但十分实用的实现代码执行追踪的方案就是在进入和离开某个函数打印相关的消息，即可以提炼为下面两个函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func trace(s string) { fmt.Println(&amp;#34;entering:&amp;#34;, s) }
func untrace(s string) { fmt.Println(&amp;#34;leaving:&amp;#34;, s) }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以下代码展示了何时调用这两个函数：&lt;/p&gt;
&lt;p&gt;示例 6.10 &lt;a href=&#34;examples/chapter_6/defer_tracing.go&#34;&gt;defer_tracing.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func trace(s string)   { fmt.Println(&amp;#34;entering:&amp;#34;, s) }
func untrace(s string) { fmt.Println(&amp;#34;leaving:&amp;#34;, s) }

func a() {
	trace(&amp;#34;a&amp;#34;)
	defer untrace(&amp;#34;a&amp;#34;)
	fmt.Println(&amp;#34;in a&amp;#34;)
}

func b() {
	trace(&amp;#34;b&amp;#34;)
	defer untrace(&amp;#34;b&amp;#34;)
	fmt.Println(&amp;#34;in b&amp;#34;)
	a()
}

func main() {
	b()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;entering: b
in b
entering: a
in a
leaving: a
leaving: b
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面的代码还可以修改为更加简便的版本（示例 6.11 &lt;a href=&#34;examples/chapter_6/defer_tracing2.go&#34;&gt;defer_tracing2.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func trace(s string) string {
	fmt.Println(&amp;#34;entering:&amp;#34;, s)
	return s
}

func un(s string) {
	fmt.Println(&amp;#34;leaving:&amp;#34;, s)
}

func a() {
	defer un(trace(&amp;#34;a&amp;#34;))
	fmt.Println(&amp;#34;in a&amp;#34;)
}

func b() {
	defer un(trace(&amp;#34;b&amp;#34;))
	fmt.Println(&amp;#34;in b&amp;#34;)
	a()
}

func main() {
	b()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;使用 &lt;code&gt;defer&lt;/code&gt; 语句来记录函数的参数与返回值&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面的代码展示了另一种在调试时使用 &lt;code&gt;defer&lt;/code&gt; 语句的手法（示例 6.12 &lt;a href=&#34;examples/chapter_6/defer_logvalues.go&#34;&gt;defer_logvalues.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;io&amp;#34;
	&amp;#34;log&amp;#34;
)

func func1(s string) (n int, err error) {
	defer func() {
		log.Printf(&amp;#34;func1(%q) = %d, %v&amp;#34;, s, n, err)
	}()
	return 7, io.EOF
}

func main() {
	func1(&amp;#34;Go&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Output: 2011/10/04 10:46:11 func1(&amp;quot;Go&amp;quot;) = 7, EOF&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序传递变长参数</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.19-%E4%BC%A0%E9%80%92%E5%8F%98%E9%95%BF%E5%8F%82%E6%95%B0/</link>
    <pubDate>Tue, 19 Jul 2022 22:34:09 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.19-%E4%BC%A0%E9%80%92%E5%8F%98%E9%95%BF%E5%8F%82%E6%95%B0/</guid>
    <description>
        &lt;h1 id=&#34;63-传递变长参数&#34;&gt;6.3 传递变长参数&lt;/h1&gt;
&lt;p&gt;如果函数的最后一个参数是采用 &lt;code&gt;...type&lt;/code&gt; 的形式，那么这个函数就可以处理一个变长的参数，这个长度可以为 0，这样的函数称为变参函数。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func myFunc(a, b, arg ...int) {}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个函数接受一个类似于切片 (slice) 的参数（详见&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt;），该参数可以通过&lt;a href=&#34;05.4.md&#34;&gt;第 5.4.4 节&lt;/a&gt; 中提到的 &lt;code&gt;for&lt;/code&gt; 循环结构迭代。&lt;/p&gt;
&lt;p&gt;示例函数和调用：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Greeting(prefix string, who ...string)
Greeting(&amp;#34;hello:&amp;#34;, &amp;#34;Joe&amp;#34;, &amp;#34;Anna&amp;#34;, &amp;#34;Eileen&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 &lt;code&gt;Greeting()&lt;/code&gt; 函数中，变量 &lt;code&gt;who&lt;/code&gt; 的值为 &lt;code&gt;[]string{&amp;quot;Joe&amp;quot;, &amp;quot;Anna&amp;quot;, &amp;quot;Eileen&amp;quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果参数被存储在一个 slice 类型的变量 &lt;code&gt;slice&lt;/code&gt; 中，则可以通过 &lt;code&gt;slice...&lt;/code&gt; 的形式来传递参数，调用变参函数。&lt;/p&gt;
&lt;p&gt;示例 6.7 &lt;a href=&#34;examples/chapter_6/varnumpar.go&#34;&gt;varnumpar.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	x := min(1, 3, 2, 0)
	fmt.Printf(&amp;#34;The minimum is: %d\n&amp;#34;, x)
	slice := []int{7,9,3,5,1}
	x = min(slice...)
	fmt.Printf(&amp;#34;The minimum in the slice is: %d&amp;#34;, x)
}

func min(s ...int) int {
	if len(s)==0 {
		return 0
	}
	min := s[0]
	for _, v := range s {
		if v &amp;lt; min {
			min = v
		}
	}
	return min
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The minimum is: 0
The minimum in the slice is: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;练习 6.3&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_6/varargs.go&#34;&gt;varargs.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写一个函数，该函数接受一个变长参数并对每个元素进行换行打印。&lt;/p&gt;
&lt;p&gt;一个接受变长参数的函数可以将这个参数作为其它函数的参数进行传递：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func F1(s ...string) {
	F2(s...)
	F3(s)
}

func F2(s ...string) { }
func F3(s []string) { }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;变长参数可以作为对应类型的 slice 进行二次传递。&lt;/p&gt;
&lt;p&gt;但是如果变长参数的类型并不是都相同的呢？使用 5 个参数来进行传递并不是很明智的选择，有 2 种方案可以解决这个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用结构（详见&lt;a href=&#34;10.0.md&#34;&gt;第 10 章&lt;/a&gt;）：&lt;/p&gt;
&lt;p&gt;定义一个结构类型，假设它叫 &lt;code&gt;Options&lt;/code&gt;，用以存储所有可能的参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type Options struct {
	par1 type1,
	par2 type2,
	...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数 &lt;code&gt;F1()&lt;/code&gt; 可以使用正常的参数 &lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt;，以及一个没有任何初始化的 &lt;code&gt;Options&lt;/code&gt; 结构： &lt;code&gt;F1(a, b, Options {})&lt;/code&gt;。如果需要对选项进行初始化，则可以使用 &lt;code&gt;F1(a, b, Options {par1:val1, par2:val2})&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用空接口：&lt;/p&gt;
&lt;p&gt;如果一个变长参数的类型没有被指定，则可以使用默认的空接口 &lt;code&gt;interface{}&lt;/code&gt;，这样就可以接受任何类型的参数（详见&lt;a href=&#34;11.9.md&#34;&gt;第 11.9 节&lt;/a&gt; ）。该方案不仅可以用于长度未知的参数，还可以用于任何不确定类型的参数。一般而言我们会使用一个 for-range 循环以及 &lt;code&gt;switch&lt;/code&gt; 结构对每个参数的类型进行判断：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func typecheck(..,..,values … interface{}) {
	for _, value := range values {
		switch v := value.(type) {
			case int: …
			case float: …
			case string: …
			case bool: …
			default: …
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的函数参数与返回值</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.18-%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0%E4%B8%8E%E8%BF%94%E5%9B%9E%E5%80%BC/</link>
    <pubDate>Mon, 18 Jul 2022 22:32:36 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.18-%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0%E4%B8%8E%E8%BF%94%E5%9B%9E%E5%80%BC/</guid>
    <description>
        &lt;h1 id=&#34;62-函数参数与返回值&#34;&gt;6.2 函数参数与返回值&lt;/h1&gt;
&lt;p&gt;函数能够接收参数供自己使用，也可以返回零个或多个值（我们通常把返回多个值称为返回一组值）。相比与 C、C++、Java 和 C#，多值返回是 Go 的一大特性，为我们判断一个函数是否正常执行（参考 &lt;a href=&#34;05.2.md&#34;&gt;第 5.2 节&lt;/a&gt;）提供了方便。&lt;/p&gt;
&lt;p&gt;我们通过 &lt;code&gt;return&lt;/code&gt; 关键字返回一组值。事实上，任何一个有返回值（单个或多个）的函数都必须以 &lt;code&gt;return&lt;/code&gt; 或 &lt;code&gt;panic&lt;/code&gt;（参考 &lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt;）结尾。&lt;/p&gt;
&lt;p&gt;在函数块里面，&lt;code&gt;return&lt;/code&gt; 之后的语句都不会执行。如果一个函数需要返回值，那么这个函数里面的每一个代码分支 (code-path) 都要有 &lt;code&gt;return&lt;/code&gt; 语句。&lt;/p&gt;
&lt;p&gt;问题 6.1：下面的函数将不会被编译，为什么呢？大家可以试着纠正过来。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func (st *Stack) Pop() int {
    v := 0
    for ix := len(st) - 1; ix &amp;gt;= 0; ix-- {
        if v = st[ix]; v != 0 {
            st[ix] = 0
            return v
        }
    }
}    
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数定义时，它的形参一般是有名字的，不过我们也可以定义没有形参名的函数，只有相应的形参类型，就像这样：&lt;code&gt;func f(int, int, float64)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;没有参数的函数通常被称为 &lt;strong&gt;niladic&lt;/strong&gt; 函数 (niladic function)，就像 &lt;code&gt;main.main()&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;621-按值传递-call-by-value-按引用传递-call-by-reference&#34;&gt;6.2.1 按值传递 (call by value) 按引用传递 (call by reference)&lt;/h2&gt;
&lt;p&gt;Go 默认使用按值传递来传递参数，也就是传递参数的副本。函数接收参数副本之后，在使用变量的过程中可能对副本的值进行更改，但不会影响到原来的变量，比如 &lt;code&gt;Function(arg1)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果你希望函数可以直接修改参数的值，而不是对参数的副本进行操作，你需要将参数的地址（变量名前面添加 &lt;code&gt;&amp;amp;&lt;/code&gt; 符号，比如 &lt;code&gt;&amp;amp;variable&lt;/code&gt;）传递给函数，这就是按引用传递，比如 &lt;code&gt;Function(&amp;amp;arg1)&lt;/code&gt;，此时传递给函数的是一个指针。如果传递给函数的是一个指针，指针的值（一个地址）会被复制，但指针的值所指向的地址上的值不会被复制；我们可以通过这个指针的值来修改这个值所指向的地址上的值。（&lt;strong&gt;译者注：指针也是变量类型，有自己的地址和值，通常指针的值指向一个变量的地址。所以，按引用传递也是按值传递。&lt;/strong&gt;）&lt;/p&gt;
&lt;p&gt;几乎在任何情况下，传递指针（一个32位或者64位的值）的消耗都比传递副本来得少。&lt;/p&gt;
&lt;p&gt;在函数调用时，像切片 (slice)、字典 (map)、接口 (interface)、通道 (channel) 这样的引用类型都是默认使用引用传递（即使没有显式的指出指针）。&lt;/p&gt;
&lt;p&gt;有些函数只是完成一个任务，并没有返回值。我们仅仅是利用了这种函数的副作用 (side-effect)，就像输出文本到终端，发送一个邮件或者是记录一个错误等。&lt;/p&gt;
&lt;p&gt;但是绝大部分的函数还是带有返回值的。&lt;/p&gt;
&lt;p&gt;如下，simple_function.go 里的 &lt;code&gt;MultiPly3Nums&lt;/code&gt; 函数带有三个形参，分别是 &lt;code&gt;a&lt;/code&gt;、&lt;code&gt;b&lt;/code&gt;、&lt;code&gt;c&lt;/code&gt;，还有一个 &lt;code&gt;int&lt;/code&gt; 类型的返回值（被注释的代码具有和未注释部分同样的功能，只是多引入了一个本地变量）：&lt;/p&gt;
&lt;p&gt;示例 6.2 &lt;a href=&#34;examples/chapter_6/simple_function.go&#34;&gt;simple_function.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
    fmt.Printf(&amp;#34;Multiply 2 * 5 * 6 = %d\n&amp;#34;, MultiPly3Nums(2, 5, 6))
    // var i1 int = MultiPly3Nums(2, 5, 6)
    // fmt.Printf(&amp;#34;MultiPly 2 * 5 * 6 = %d\n&amp;#34;, i1)
}

func MultiPly3Nums(a int, b int, c int) int {
    // var product int = a * b * c
    // return product
    return a * b * c
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出显示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Multiply 2 * 5 * 6 = 60
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果一个函数需要返回四到五个值，我们可以传递一个切片给函数（如果返回值具有相同类型）或者是传递一个结构体（如果返回值具有不同的类型）。因为传递一个指针允许直接修改变量的值，消耗也更少。&lt;/p&gt;
&lt;p&gt;问题 6.2：&lt;/p&gt;
&lt;p&gt;如下的两个函数调用有什么不同：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(A) func DoSomething(a *A) {
        b = a
    }

(B) func DoSomething(a A) {
        b = &amp;amp;a
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;622-命名的返回值-named-return-variables&#34;&gt;6.2.2 命名的返回值 (named return variables)&lt;/h2&gt;
&lt;p&gt;如下 multiple_return.go 里的函数带有一个 &lt;code&gt;int&lt;/code&gt; 参数，返回两个 &lt;code&gt;int&lt;/code&gt; 值；其中一个函数的返回值在函数调用时就已经被赋予了一个初始零值。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getX2AndX3&lt;/code&gt; 与 &lt;code&gt;getX2AndX3_2&lt;/code&gt; 两个函数演示了如何使用非命名返回值与命名返回值的特性。当需要返回多个非命名返回值时，需要使用 &lt;code&gt;()&lt;/code&gt; 把它们括起来，比如 &lt;code&gt;(int, int)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;命名返回值作为结果形参 (result parameters) 被初始化为相应类型的零值，当需要返回的时候，我们只需要一条简单的不带参数的 &lt;code&gt;return&lt;/code&gt; 语句。需要注意的是，即使只有一个命名返回值，也需要使用 &lt;code&gt;()&lt;/code&gt; 括起来（参考&lt;a href=&#34;06.6.md&#34;&gt;第 6.6 节&lt;/a&gt; 的 &lt;a href=&#34;./examples/chapter_6/fibonacci.go&#34;&gt;fibonacci.go&lt;/a&gt; 函数）。&lt;/p&gt;
&lt;p&gt;示例 6.3 &lt;a href=&#34;examples/chapter_6/multiple_return.go&#34;&gt;multiple_return.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

var num int = 10
var numx2, numx3 int

func main() {
    numx2, numx3 = getX2AndX3(num)
    PrintValues()
    numx2, numx3 = getX2AndX3_2(num)
    PrintValues()
}

func PrintValues() {
    fmt.Printf(&amp;#34;num = %d, 2x num = %d, 3x num = %d\n&amp;#34;, num, numx2, numx3)
}

func getX2AndX3(input int) (int, int) {
    return 2 * input, 3 * input
}

func getX2AndX3_2(input int) (x2 int, x3 int) {
    x2 = 2 * input
    x3 = 3 * input
    // return x2, x3
    return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;num = 10, 2x num = 20, 3x num = 30    
num = 10, 2x num = 20, 3x num = 30 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提示：&lt;/p&gt;
&lt;p&gt;虽然 &lt;code&gt;return&lt;/code&gt; 或 &lt;code&gt;return var&lt;/code&gt; 都是可以的，但是 &lt;code&gt;return var = expression&lt;/code&gt;（表达式） 会引发一个编译错误：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;syntax error: unexpected =, expecting semicolon or newline or }&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;即使函数使用了命名返回值，你依旧可以无视它而返回明确的值。&lt;/p&gt;
&lt;p&gt;任何一个非命名返回值（使用非命名返回值是很糟的编程习惯）在 &lt;code&gt;return&lt;/code&gt; 语句里面都要明确指出包含返回值的变量或是一个可计算的值（就像上面警告所指出的那样）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;尽量使用命名返回值：会使代码更清晰、更简短，同时更加容易读懂。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;练习 6.1 &lt;a href=&#34;exercises/chapter_6/mult_returnval.go&#34;&gt;mult_returnval.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个函数，接收两个整数，然后返回它们的和、积与差。编写两个版本，一个是非命名返回值，一个是命名返回值。&lt;/p&gt;
&lt;p&gt;练习 6.2 &lt;a href=&#34;exercises/chapter_6/error_returnval.go&#34;&gt;error_returnval.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;编写一个名字为 &lt;code&gt;MySqrt()&lt;/code&gt; 的函数，计算一个 &lt;code&gt;float64&lt;/code&gt; 类型浮点数的平方根，如果参数是一个负数的话将返回一个错误。编写两个版本，一个是非命名返回值，一个是命名返回值。&lt;/p&gt;
&lt;h2 id=&#34;623-空白符-blank-identifier&#34;&gt;6.2.3 空白符 (blank identifier)&lt;/h2&gt;
&lt;p&gt;空白符用来匹配一些不需要的值，然后丢弃掉，下面的 blank_identifier.go 就是很好的例子。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ThreeValues&lt;/code&gt; 是拥有三个返回值的不需要任何参数的函数，在下面的例子中，我们将第一个与第三个返回值赋给了 &lt;code&gt;i1&lt;/code&gt; 与 &lt;code&gt;f1&lt;/code&gt;。第二个返回值赋给了空白符 &lt;code&gt;_&lt;/code&gt;，然后自动丢弃掉。&lt;/p&gt;
&lt;p&gt;示例 6.4 &lt;a href=&#34;examples/chapter_6/blank_identifier.go&#34;&gt;blank_identifier.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
    var i1 int
    var f1 float32
    i1, _, f1 = ThreeValues()
    fmt.Printf(&amp;#34;The int: %d, the float: %f \n&amp;#34;, i1, f1)
}

func ThreeValues() (int, int, float32) {
    return 5, 6, 7.5
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The int: 5, the float: 7.500000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外一个示例，函数接收两个参数，比较它们的大小，然后按小-大的顺序返回这两个数，示例代码为 minmax.go。&lt;/p&gt;
&lt;p&gt;示例 6.5 &lt;a href=&#34;examples/chapter_6/minmax.go&#34;&gt;minmax.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
    var min, max int
    min, max = MinMax(78, 65)
    fmt.Printf(&amp;#34;Minmium is: %d, Maximum is: %d\n&amp;#34;, min, max)
}

func MinMax(a int, b int) (min int, max int) {
    if a &amp;lt; b {
        min = a
        max = b
    } else { // a = b or a &amp;lt; b
        min = b
        max = a
    }
    return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Minimum is: 65, Maximum is 78
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;624-改变外部变量-outside-variable&#34;&gt;6.2.4 改变外部变量 (outside variable)&lt;/h2&gt;
&lt;p&gt;传递指针给函数不但可以节省内存（因为没有复制变量的值），而且赋予了函数直接修改外部变量的能力，所以被修改的变量不再需要使用 &lt;code&gt;return&lt;/code&gt; 返回。如下的例子，&lt;code&gt;reply&lt;/code&gt; 是一个指向 &lt;code&gt;int&lt;/code&gt; 变量的指针，通过这个指针，我们在函数内修改了这个 &lt;code&gt;int&lt;/code&gt; 变量的数值。&lt;/p&gt;
&lt;p&gt;示例 6.6 &lt;a href=&#34;examples/chapter_6/side_effect.go&#34;&gt;side_effect.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
    &amp;#34;fmt&amp;#34;
)

// this function changes reply:
func Multiply(a, b int, reply *int) {
    *reply = a * b
}

func main() {
    n := 0
    reply := &amp;amp;n
    Multiply(10, 5, reply)
    fmt.Println(&amp;#34;Multiply:&amp;#34;, *reply) // Multiply: 50
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这仅仅是个指导性的例子，当需要在函数内改变一个占用内存比较大的变量时，性能优势就更加明显了。然而，如果不小心使用的话，传递一个指针很容易引发一些不确定的事，所以，我们要十分小心那些可以改变外部变量的函数，在必要时，需要添加注释以便其他人能够更加清楚的知道函数里面到底发生了什么。&lt;/p&gt;
&lt;h2 id=&#34;链接&#34;&gt;链接&lt;/h2&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序通过内存缓存来提升性能</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.17-%E9%80%9A%E8%BF%87%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98%E6%9D%A5%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD/</link>
    <pubDate>Sun, 17 Jul 2022 22:29:35 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.17-%E9%80%9A%E8%BF%87%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98%E6%9D%A5%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD/</guid>
    <description>
        &lt;h1 id=&#34;612-通过内存缓存来提升性能&#34;&gt;6.12 通过内存缓存来提升性能&lt;/h1&gt;
&lt;p&gt;当在进行大量的计算时，提升性能最直接有效的一种方式就是避免重复计算。通过在内存中缓存和重复利用相同计算的结果，称之为内存缓存。最明显的例子就是生成斐波那契数列的程序（详见第 &lt;a href=&#34;06.6.md&#34;&gt;6.6&lt;/a&gt; 和 &lt;a href=&#34;06.11.md&#34;&gt;6.11&lt;/a&gt; 节）：&lt;/p&gt;
&lt;p&gt;要计算数列中第 n 个数字，需要先得到之前两个数的值，但很明显绝大多数情况下前两个数的值都是已经计算过的。即每个更后面的数都是基于之前计算结果的重复计算，正如示例 6.11 &lt;a href=&#34;examples/chapter_6/fibonacci.go&#34;&gt;fibonnaci.go&lt;/a&gt; 所展示的那样。&lt;/p&gt;
&lt;p&gt;而我们要做就是将第 n 个数的值存在数组中索引为 n 的位置（详见&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt;），然后在数组中查找是否已经计算过，如果没有找到，则再进行计算。&lt;/p&gt;
&lt;p&gt;程序 Listing 6.17 - &lt;a href=&#34;examples/chapter_6/fibonacci_memoization.go&#34;&gt;fibonacci_memoization.go&lt;/a&gt; 就是依照这个原则实现的，下面是计算到第 40 位数字的性能对比：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通写法：4.730270 秒&lt;/li&gt;
&lt;li&gt;内存缓存：0.001000 秒&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;内存缓存的优势显而易见，而且您还可以将它应用到其它类型的计算中，例如使用 &lt;code&gt;map&lt;/code&gt;（详见&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt;）而不是数组或切片（Listing 6.21 - &lt;a href=&#34;examples/chapter_6/fibonacci_memoization.go&#34;&gt;fibonacci_memoization.go&lt;/a&gt;）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;time&amp;#34;
)

const LIM = 41

var fibs [LIM]uint64

func main() {
	var result uint64 = 0
	start := time.Now()
	for i := 0; i &amp;lt; LIM; i++ {
		result = fibonacci(i)
		fmt.Printf(&amp;#34;fibonacci(%d) is: %d\n&amp;#34;, i, result)
	}
	end := time.Now()
	delta := end.Sub(start)
	fmt.Printf(&amp;#34;longCalculation took this amount of time: %s\n&amp;#34;, delta)
}
func fibonacci(n int) (res uint64) {
	// memoization: check if fibonacci(n) is already known in array:
	if fibs[n] != 0 {
		res = fibs[n]
		return
	}
	if n &amp;lt;= 1 {
		res = 1
	} else {
		res = fibonacci(n-1) + fibonacci(n-2)
	}
	fibs[n] = res
	return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;内存缓存的技术在使用计算成本相对昂贵的函数时非常有用（不仅限于例子中的递归），譬如大量进行相同参数的运算。这种技术还可以应用于纯函数中，即相同输入必定获得相同输出的函数。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序计算函数执行时间</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.16-%E8%AE%A1%E7%AE%97%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E6%97%B6%E9%97%B4/</link>
    <pubDate>Sat, 16 Jul 2022 22:27:37 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.16-%E8%AE%A1%E7%AE%97%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E6%97%B6%E9%97%B4/</guid>
    <description>
        &lt;h1 id=&#34;611-计算函数执行时间&#34;&gt;6.11 计算函数执行时间&lt;/h1&gt;
&lt;p&gt;有时候，能够知道一个计算执行消耗的时间是非常有意义的，尤其是在对比和基准测试中。最简单的一个办法就是在计算开始之前设置一个起始时间，再记录计算结束时的结束时间，最后计算它们的差值，就是这个计算所消耗的时间。想要实现这样的做法，可以使用 &lt;code&gt;time&lt;/code&gt; 包中的 &lt;code&gt;Now()&lt;/code&gt; 和 &lt;code&gt;Sub()&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf(&amp;#34;longCalculation took this amount of time: %s\n&amp;#34;, delta)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;您可以查看示例 6.20 &lt;a href=&#34;examples/chapter_6/fibonacci.go&#34;&gt;fibonacci.go&lt;/a&gt; 作为实例学习。&lt;/p&gt;
&lt;p&gt;如果您对一段代码进行了所谓的优化，请务必对它们之间的效率进行对比再做出最后的判断。在接下来的章节中，我们会学习如何进行有价值的优化操作。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序使用闭包调试</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.15-%E4%BD%BF%E7%94%A8%E9%97%AD%E5%8C%85%E8%B0%83%E8%AF%95/</link>
    <pubDate>Fri, 15 Jul 2022 22:25:10 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.15-%E4%BD%BF%E7%94%A8%E9%97%AD%E5%8C%85%E8%B0%83%E8%AF%95/</guid>
    <description>
        &lt;h1 id=&#34;610-使用闭包调试&#34;&gt;6.10 使用闭包调试&lt;/h1&gt;
&lt;p&gt;当您在分析和调试复杂的程序时，无数个函数在不同的代码文件中相互调用，如果这时候能够准确地知道哪个文件中的具体哪个函数正在执行，对于调试是十分有帮助的。您可以使用 &lt;code&gt;runtime&lt;/code&gt; 或 &lt;code&gt;log&lt;/code&gt; 包中的特殊函数来实现这样的功能。包 &lt;code&gt;runtime&lt;/code&gt; 中的函数 &lt;code&gt;Caller()&lt;/code&gt; 提供了相应的信息，因此可以在需要的时候实现一个 &lt;code&gt;where()&lt;/code&gt; 闭包函数来打印函数执行的位置：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;where := func() {
	_, file, line, _ := runtime.Caller(1)
	log.Printf(&amp;#34;%s:%d&amp;#34;, file, line)
}
where()
// some code
where()
// some more code
where()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;您也可以设置 &lt;code&gt;log&lt;/code&gt; 包中的 &lt;code&gt;flag&lt;/code&gt; 参数来实现：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;log.SetFlags(log.Llongfile)
log.Print(&amp;#34;&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或使用一个更加简短版本的 &lt;code&gt;where()&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var where = log.Print
func func1() {
where()
... some code
where()
... some code
where()
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的函数</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.14-%E5%87%BD%E6%95%B0%E7%9A%84%E4%BB%8B%E7%BB%8D/</link>
    <pubDate>Thu, 14 Jul 2022 11:46:19 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.14-%E5%87%BD%E6%95%B0%E7%9A%84%E4%BB%8B%E7%BB%8D/</guid>
    <description>
        &lt;h1 id=&#34;61-介绍&#34;&gt;6.1 介绍&lt;/h1&gt;
&lt;p&gt;每一个程序都包含很多的函数：函数是基本的代码块。&lt;/p&gt;
&lt;p&gt;Go是编译型语言，所以函数编写的顺序是无关紧要的；鉴于可读性的需求，最好把 &lt;code&gt;main()&lt;/code&gt; 函数写在文件的前面，其他函数按照一定逻辑顺序进行编写（例如函数被调用的顺序）。&lt;/p&gt;
&lt;p&gt;编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务（那就是函数）来解决。而且，同一个任务（函数）可以被调用多次，有助于代码重用。&lt;/p&gt;
&lt;p&gt;（事实上，好的程序是非常注意 DRY 原则的，即不要重复你自己 (Don&amp;rsquo;t Repeat Yourself)，意思是执行特定任务的代码只能在程序里面出现一次。）&lt;/p&gt;
&lt;p&gt;当函数执行到代码块最后一行（&lt;code&gt;}&lt;/code&gt; 之前）或者 &lt;code&gt;return&lt;/code&gt; 语句的时候会退出，其中 &lt;code&gt;return&lt;/code&gt; 语句可以带有零个或多个参数；这些参数将作为返回值（参考 &lt;a href=&#34;06.2.md&#34;&gt;第 6.2 节&lt;/a&gt;）供调用者使用。简单的 &lt;code&gt;return&lt;/code&gt; 语句也可以用来结束 &lt;code&gt;for&lt;/code&gt; 死循环，或者结束一个协程 (goroutine)。&lt;/p&gt;
&lt;p&gt;Go 里面有三种类型的函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;普通的带有名字的函数&lt;/li&gt;
&lt;li&gt;匿名函数或者lambda函数（参考 &lt;a href=&#34;06.8.md&#34;&gt;第 6.8 节&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;方法（Methods，参考 &lt;a href=&#34;10.6.md&#34;&gt;第 10.6 节&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了 &lt;code&gt;main()&lt;/code&gt;、&lt;code&gt;init()&lt;/code&gt; 函数外，其它所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。&lt;/p&gt;
&lt;p&gt;作为提醒，提前介绍一个语法：&lt;/p&gt;
&lt;p&gt;这样是不正确的 Go 代码：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func g()
{
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;它必须是这样的：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func g() {
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数被调用的基本格式如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pack1.Function(arg1, arg2, …, argn)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Function&lt;/code&gt; 是 &lt;code&gt;pack1&lt;/code&gt; 包里面的一个函数，括号里的是被调用函数的实参 (argument)：这些值被传递给被调用函数的&lt;em&gt;形参&lt;/em&gt;（parameter，参考&lt;a href=&#34;06.2.md&#34;&gt;第 6.2 节&lt;/a&gt;）。函数被调用的时候，这些实参将被复制（简单而言）然后传递给被调用函数。函数一般是在其他函数里面被调用的，这个其他函数被称为调用函数 (calling function)。函数能多次调用其他函数，这些被调用函数按顺序（简单而言）执行，理论上，函数调用其他函数的次数是无穷的（直到函数调用栈被耗尽）。&lt;/p&gt;
&lt;p&gt;一个简单的函数调用其他函数的例子：&lt;/p&gt;
&lt;p&gt;示例 6.1 &lt;a href=&#34;examples/chapter_6/greeting.go&#34;&gt;greeting.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

func main() {
    println(&amp;#34;In main before calling greeting&amp;#34;)
    greeting()
    println(&amp;#34;In main after calling greeting&amp;#34;)
}

func greeting() {
    println(&amp;#34;In greeting: Hi!!!!!&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;代码输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In main before calling greeting
In greeting: Hi!!!!!
In main after calling greeting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数可以将其他函数调用作为它的参数，只要这个被调用函数的返回值个数、返回值类型和返回值的顺序与调用函数所需求的实参是一致的，例如：&lt;/p&gt;
&lt;p&gt;假设 &lt;code&gt;f1&lt;/code&gt; 需要 3 个参数 &lt;code&gt;f1(a, b, c int)&lt;/code&gt;，同时 &lt;code&gt;f2&lt;/code&gt; 返回 3 个参数 &lt;code&gt;f2(a, b int) (int, int, int)&lt;/code&gt;，就可以这样调用 &lt;code&gt;f1&lt;/code&gt;：&lt;code&gt;f1(f2(a, b))&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;函数重载 (function overloading) 指的是可以编写多个同名函数，只要它们拥有不同的形参/或者不同的返回值，在 Go 里面函数重载是不被允许的。这将导致一个编译错误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;funcName redeclared in this book, previous declaration at lineno
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go 语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能；没有重载意味着只是一个简单的函数调度。所以你需要给不同的函数使用不同的名字，我们通常会根据函数的特征对函数进行命名（参考 &lt;a href=&#34;11.12.md&#34;&gt;第 11.12.5 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;如果需要申明一个在外部定义的函数，你只需要给出函数名与函数签名，不需要给出函数体：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func flushICache(begin, end uintptr) // implemented externally
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;函数也可以以申明的方式被使用，作为一个函数类型&lt;/strong&gt;，就像：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type binOp func(int, int) int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在这里，不需要函数体 &lt;code&gt;{}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;函数是一等值 (first-class value)：它们可以赋值给变量，就像 &lt;code&gt;add := binOp&lt;/code&gt; 一样。&lt;/p&gt;
&lt;p&gt;这个变量知道自己指向的函数的签名，所以给它赋一个具有不同签名的函数值是不可能的。&lt;/p&gt;
&lt;p&gt;函数值 (functions value) 之间可以相互比较：如果它们引用的是相同的函数或者都是 &lt;code&gt;nil&lt;/code&gt; 的话，则认为它们是相同的函数。函数不能在其它函数里面声明（不能嵌套），不过我们可以通过使用匿名函数（参考 &lt;a href=&#34;06.8.md&#34;&gt;第 6.8 节&lt;/a&gt;）来破除这个限制。&lt;/p&gt;
&lt;p&gt;目前 Go 没有泛型 (generic) 的概念，也就是说它不支持那种支持多种类型的函数。不过在大部分情况下可以通过接口 (interface)，特别是空接口与类型选择（type switch，参考 &lt;a href=&#34;11.12.md&#34;&gt;第 11.12 节&lt;/a&gt;）与/或者通过使用反射（reflection，参考 &lt;a href=&#34;06.8.md&#34;&gt;第 6.8 节&lt;/a&gt;）来实现相似的功能。使用这些技术将导致代码更为复杂、性能更为低下，所以在非常注意性能的的场合，最好是为每一个类型单独创建一个函数，而且代码可读性更强。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的标签与goto</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.13-%E6%A0%87%E7%AD%BE%E4%B8%8Egoto/</link>
    <pubDate>Wed, 13 Jul 2022 17:47:55 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.13-%E6%A0%87%E7%AD%BE%E4%B8%8Egoto/</guid>
    <description>
        &lt;h1 id=&#34;56-标签与-goto&#34;&gt;5.6 标签与 goto&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;for&lt;/code&gt;、&lt;code&gt;switch&lt;/code&gt; 或 &lt;code&gt;select&lt;/code&gt; 语句都可以配合标签 (label) 形式的标识符使用，即某一行第一个以冒号 (&lt;code&gt;:&lt;/code&gt;) 结尾的单词（gofmt 会将后续代码自动移至下一行）。&lt;/p&gt;
&lt;p&gt;示例 5.13 &lt;a href=&#34;examples/chapter_5/for6.go&#34;&gt;for6.go&lt;/a&gt;：&lt;/p&gt;
&lt;p&gt;（标签的名称是大小写敏感的，为了提升可读性，一般建议使用全部大写字母）&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {

LABEL1:
	for i := 0; i &amp;lt;= 5; i++ {
		for j := 0; j &amp;lt;= 5; j++ {
			if j == 4 {
				continue LABEL1
			}
			fmt.Printf(&amp;#34;i is: %d, and j is: %d\n&amp;#34;, i, j)
		}
	}

}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;本例中，&lt;code&gt;continue&lt;/code&gt; 语句指向 &lt;code&gt;LABEL1&lt;/code&gt;，当执行到该语句的时候，就会跳转到 &lt;code&gt;LABEL1&lt;/code&gt; 标签的位置。&lt;/p&gt;
&lt;p&gt;您可以看到当 &lt;code&gt;j==4&lt;/code&gt; 和 &lt;code&gt;j==5&lt;/code&gt; 的时候，没有任何输出：标签的作用对象为外部循环，因此 &lt;code&gt;i&lt;/code&gt; 会直接变成下一个循环的值，而此时 &lt;code&gt;j&lt;/code&gt; 的值就被重设为 &lt;code&gt;0&lt;/code&gt;，即它的初始值。如果将 &lt;code&gt;continue&lt;/code&gt; 改为 &lt;code&gt;break&lt;/code&gt;，则不会只退出内层循环，而是直接退出外层循环了。另外，还可以使用 &lt;code&gt;goto&lt;/code&gt; 语句和标签配合使用来模拟循环。&lt;/p&gt;
&lt;p&gt;示例 5.14 &lt;a href=&#34;examples/chapter_5/goto.go&#34;&gt;goto.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

func main() {
	i:=0
	HERE:
		print(i)
		i++
		if i==5 {
			return
		}
		goto HERE
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面的代码会输出 &lt;code&gt;01234&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;使用逆向的 &lt;code&gt;goto&lt;/code&gt; 会很快导致意大利面条式的代码，所以不应当使用而选择更好的替代方案。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;特别注意&lt;/strong&gt; 使用标签和 &lt;code&gt;goto&lt;/code&gt; 语句是不被鼓励的：它们会很快导致非常糟糕的程序设计，而且总有更加可读的替代方案来实现相同的需求。&lt;/p&gt;
&lt;p&gt;一个建议使用 &lt;code&gt;goto&lt;/code&gt; 语句的示例会在&lt;a href=&#34;15.1.md&#34;&gt;第 15.1 章&lt;/a&gt; 的 &lt;a href=&#34;./examples/chapter_15/simple_tcp_server.go&#34;&gt;simple_tcp_server.go&lt;/a&gt; 中出现：示例中在发生读取错误时，使用 goto 来跳出无限读取循环并关闭相应的客户端链接。&lt;/p&gt;
&lt;p&gt;定义但未使用标签会导致编译错误：&lt;code&gt;label … defined and not used&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果您必须使用 &lt;code&gt;goto&lt;/code&gt;，应当只使用正序的标签（标签位于 &lt;code&gt;goto&lt;/code&gt; 语句之后），但注意标签和 &lt;code&gt;goto&lt;/code&gt; 语句之间不能出现定义新变量的语句，否则会导致编译失败。&lt;/p&gt;
&lt;p&gt;示例 5.15 &lt;a href=&#34;examples/chapter_5/got2o.go&#34;&gt;goto2.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// compile error goto2.go:8: goto TARGET jumps over declaration of b at goto2.go:8
package main

import &amp;#34;fmt&amp;#34;

func main() {
		a := 1
		goto TARGET // compile error
		b := 9
	TARGET:  
		b += a
		fmt.Printf(&amp;#34;a is %v *** b is %v&amp;#34;, a, b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;问题 5.3&lt;/strong&gt; 请描述下面 &lt;code&gt;for&lt;/code&gt; 循环的输出：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;i := 0
for { //since there are no checks, this is an infinite loop
	if i &amp;gt;= 3 { break }
	//break out of this for loop when this condition is met
	fmt.Println(&amp;#34;Value of i is:&amp;#34;, i)
	i++
}
fmt.Println(&amp;#34;A statement just after for loop.&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i := 0; i&amp;lt;7 ; i++ {
	if i%2 == 0 { continue }
	fmt.Println(&amp;#34;Odd:&amp;#34;, i)
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序执行系统命令</title>
    <link>https://blog.wiseai.cn/post/go%E7%A8%8B%E5%BA%8F%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Tue, 12 Jul 2022 17:56:39 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/go%E7%A8%8B%E5%BA%8F%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;直接上代码:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;bytes&amp;#34;
	&amp;#34;fmt&amp;#34;
	&amp;#34;os/exec&amp;#34;
)

func main() {
	var err error
	var stdout, stderr string

	Loop:
	err = nil
	stdout, stderr = &amp;#34;&amp;#34;, &amp;#34;&amp;#34;

	fmt.Println(&amp;#34;1.列出当前目录中的目录和文件&amp;#34;)
	fmt.Println(&amp;#34;2.当前目录占用空间&amp;#34;)
	fmt.Println(&amp;#34;3.重启打印服务&amp;#34;)
	fmt.Println(&amp;#34;退出请输入9&amp;#34;)
	fmt.Println(&amp;#34;-----------------------------&amp;#34;)

	var key uint8
	fmt.Scanf(&amp;#34;%d&amp;#34;, &amp;amp;key)
	switch key {
	case 1:
		err, stdout, stderr = shellRun(&amp;#34;ls -lh&amp;#34;)
	case 3:
		err, stdout, stderr = shellRun(&amp;#34;systemctl restart cups&amp;#34;)
		fmt.Println(&amp;#34;请输入密码!&amp;#34;)
	case 2:
		err, stdout, stderr = shellRun(&amp;#34;du -h&amp;#34;)
		fmt.Println(&amp;#34;当前目录占用空间为:&amp;#34;)
	case 9:
		fmt.Println(&amp;#34;安全退出&amp;#34;)
		return
	default:
		fmt.Println(&amp;#34;-----------------------------&amp;#34;)
		fmt.Println(&amp;#34;请输入正确的数字!&amp;#34;)
	}
	if err != nil {
		fmt.Print(&amp;#34;出现错误：&amp;#34;)
		fmt.Println(err)
	}
	if stdout != &amp;#34;&amp;#34; {
		fmt.Println(stdout)
	}
	if stderr != &amp;#34;&amp;#34; {
		fmt.Println(&amp;#34;错误提示:&amp;#34;)
		fmt.Println(stderr)
	}

	fmt.Println(&amp;#34;-----------------------------&amp;#34;)
	goto Loop
}

func shellRun(command string) (error, string, string) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command(&amp;#34;/bin/bash&amp;#34;, &amp;#34;-c&amp;#34;, command)
	cmd.Stdout = &amp;amp;stdout
	cmd.Stderr = &amp;amp;stderr
	err := cmd.Run()
	return err, stdout.String(), stderr.String()
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的Break与continue</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.12-break%E4%B8%8Econtinue/</link>
    <pubDate>Tue, 12 Jul 2022 17:46:18 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.12-break%E4%B8%8Econtinue/</guid>
    <description>
        &lt;h1 id=&#34;55-break-与-continue&#34;&gt;5.5 break 与 continue&lt;/h1&gt;
&lt;p&gt;您可以使用 &lt;code&gt;break&lt;/code&gt; 语句重写 &lt;a href=&#34;examples/chapter_5/for2.go&#34;&gt;for2.go&lt;/a&gt; 的代码：&lt;/p&gt;
&lt;p&gt;示例 5.10 &lt;a href=&#34;examples/chapter_5/for3.go&#34;&gt;for3.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for {
	i = i - 1
	fmt.Printf(&amp;#34;The variable i is now: %d\n&amp;#34;, i)
	if i &amp;lt; 0 {
		break
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;因此每次迭代都会对条件进行检查（&lt;code&gt;i &amp;lt; 0&lt;/code&gt;），以此判断是否需要停止循环。如果退出条件满足，则使用 &lt;code&gt;break&lt;/code&gt; 语句退出循环。&lt;/p&gt;
&lt;p&gt;一个 &lt;code&gt;break&lt;/code&gt; 的作用范围为该语句出现后的最内部的结构，它可以被用于任何形式的 &lt;code&gt;for&lt;/code&gt; 循环（计数器、条件判断等）。但在 &lt;code&gt;switch&lt;/code&gt; 或 &lt;code&gt;select&lt;/code&gt; 语句中（详见&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt;），&lt;code&gt;break&lt;/code&gt; 语句的作用结果是跳过整个代码块，执行后续的代码。&lt;/p&gt;
&lt;p&gt;下面的示例中包含了嵌套的循环体（for4.go），&lt;code&gt;break&lt;/code&gt; 只会退出最内层的循环：&lt;/p&gt;
&lt;p&gt;示例 5.11 &lt;a href=&#34;examples/chapter_5/for4.go&#34;&gt;for4.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

func main() {
	for i:=0; i&amp;lt;3; i++ {
		for j:=0; j&amp;lt;10; j++ {
			if j&amp;gt;5 {
			    break   
			}
			print(j)
		}
		print(&amp;#34;  &amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;012345 012345 012345
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键字 &lt;code&gt;continue&lt;/code&gt; 忽略剩余的循环体而直接进入下一次循环的过程，但不是无条件执行下一次循环，执行之前依旧需要满足循环的判断条件。&lt;/p&gt;
&lt;p&gt;示例 5.12 &lt;a href=&#34;examples/chapter_5/for5.go&#34;&gt;for5.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

func main() {
	for i := 0; i &amp;lt; 10; i++ {
		if i == 5 {
			continue
		}
		print(i)
		print(&amp;#34; &amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0 1 2 3 4 6 7 8 9
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;显然，&lt;code&gt;5&lt;/code&gt; 被跳过了。&lt;/p&gt;
&lt;p&gt;另外，关键字 &lt;code&gt;continue&lt;/code&gt; 只能被用于 &lt;code&gt;for&lt;/code&gt; 循环中。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的for结构</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.11-for%E7%BB%93%E6%9E%84/</link>
    <pubDate>Mon, 11 Jul 2022 17:44:25 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.11-for%E7%BB%93%E6%9E%84/</guid>
    <description>
        &lt;h1 id=&#34;54-for-结构&#34;&gt;5.4 for 结构&lt;/h1&gt;
&lt;p&gt;如果想要重复执行某些语句，Go 语言中您只有 &lt;code&gt;for&lt;/code&gt; 结构可以使用。不要小看它，这个 &lt;code&gt;for&lt;/code&gt; 结构比其它语言中的更为灵活。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt; 其它许多语言中也没有发现和 do-while 完全对等的 &lt;code&gt;for&lt;/code&gt; 结构，可能是因为这种需求并不是那么强烈。&lt;/p&gt;
&lt;h2 id=&#34;541-基于计数器的迭代&#34;&gt;5.4.1 基于计数器的迭代&lt;/h2&gt;
&lt;p&gt;文件 for1.go 中演示了最简单的基于计数器的迭代，基本形式为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for 初始化语句; 条件语句; 修饰语句 {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;示例 5.6 &lt;a href=&#34;examples/chapter_5/for1.go&#34;&gt;for1.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	for i := 0; i &amp;lt; 5; i++ {
		fmt.Printf(&amp;#34;This is the %d iteration\n&amp;#34;, i)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This is the 0 iteration
This is the 1 iteration
This is the 2 iteration
This is the 3 iteration
This is the 4 iteration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由花括号括起来的代码块会被重复执行已知次数，该次数是根据计数器（此例为 &lt;code&gt;i&lt;/code&gt;）决定的。循环开始前，会执行且仅会执行一次初始化语句 &lt;code&gt;i := 0;&lt;/code&gt;；这比在循环之前声明更为简短。紧接着的是条件语句 &lt;code&gt;i &amp;lt; 5;&lt;/code&gt;，在每次循环开始前都会进行判断，一旦判断结果为 &lt;code&gt;false&lt;/code&gt;，则退出循环体。最后一部分为修饰语句 &lt;code&gt;i++&lt;/code&gt;，一般用于增加或减少计数器。&lt;/p&gt;
&lt;p&gt;这三部分组成的循环的头部，它们之间使用分号 &lt;code&gt;;&lt;/code&gt; 相隔，但并不需要括号 &lt;code&gt;()&lt;/code&gt; 将它们括起来。例如：&lt;code&gt;for (i = 0; i &amp;lt; 10; i++) { }&lt;/code&gt;，这是无效的代码！&lt;/p&gt;
&lt;p&gt;同样的，左花括号 &lt;code&gt;{&lt;/code&gt; 必须和 for 语句在同一行，计数器的生命周期在遇到右花括号 &lt;code&gt;}&lt;/code&gt; 时便终止。一般习惯使用 i、j、z 或 ix 等较短的名称命名计数器。&lt;/p&gt;
&lt;p&gt;特别注意，永远不要在循环体内修改计数器，这在任何语言中都是非常差的实践！&lt;/p&gt;
&lt;p&gt;您还可以在循环中同时使用多个计数器：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i, j := 0, N; i &amp;lt; j; i, j = i+1, j-1 {}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这得益于 Go 语言具有的平行赋值的特性（可以查看&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt; &lt;a href=&#34;./examples/chapter_7/string_reverse.go&#34;&gt;string_reverse.go&lt;/a&gt; 中反转数组的示例）。&lt;/p&gt;
&lt;p&gt;您可以将两个 for 循环嵌套起来：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i:=0; i&amp;lt;5; i++ {
	for j:=0; j&amp;lt;10; j++ {
		println(j)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果您使用 for 循环迭代一个 Unicode 编码的字符串，会发生什么？&lt;/p&gt;
&lt;p&gt;示例 5.7 &lt;a href=&#34;examples/chapter_5/for_string.go&#34;&gt;for_string.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	str := &amp;#34;Go is a beautiful language!&amp;#34;
	fmt.Printf(&amp;#34;The length of str is: %d\n&amp;#34;, len(str))
	for ix :=0; ix &amp;lt; len(str); ix++ {
		fmt.Printf(&amp;#34;Character on position %d is: %c \n&amp;#34;, ix, str[ix])
	}
	str2 := &amp;#34;日本語&amp;#34;
	fmt.Printf(&amp;#34;The length of str2 is: %d\n&amp;#34;, len(str2))
	for ix :=0; ix &amp;lt; len(str2); ix++ {
		fmt.Printf(&amp;#34;Character on position %d is: %c \n&amp;#34;, ix, str2[ix])
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The length of str is: 27
Character on position 0 is: G 
Character on position 1 is: o 
Character on position 2 is:   
Character on position 3 is: i 
Character on position 4 is: s 
Character on position 5 is:   
Character on position 6 is: a 
Character on position 7 is:   
Character on position 8 is: b 
Character on position 9 is: e 
Character on position 10 is: a 
Character on position 11 is: u 
Character on position 12 is: t 
Character on position 13 is: i 
Character on position 14 is: f 
Character on position 15 is: u 
Character on position 16 is: l 
Character on position 17 is:   
Character on position 18 is: l 
Character on position 19 is: a 
Character on position 20 is: n 
Character on position 21 is: g 
Character on position 22 is: u 
Character on position 23 is: a 
Character on position 24 is: g 
Character on position 25 is: e 
Character on position 26 is: ! 
The length of str2 is: 9
Character on position 0 is: æ 
Character on position 1 is:  
Character on position 2 is: ¥ 
Character on position 3 is: æ 
Character on position 4 is:  
Character on position 5 is: ¬ 
Character on position 6 is: è 
Character on position 7 is: ª 
Character on position 8 is:  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们打印 &lt;code&gt;str&lt;/code&gt; 和 &lt;code&gt;str2&lt;/code&gt; 的长度，会分别得到 &lt;code&gt;27&lt;/code&gt; 和 &lt;code&gt;9&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;由此我们可以发现，ASCII 编码的字符占用 1 个字节，既每个索引都指向不同的字符，而非 ASCII 编码的字符（占有 2 到 4 个字节）不能单纯地使用索引来判断是否为同一个字符。我们会在&lt;a href=&#34;05.4.md&#34;&gt;第 5.4.4 节&lt;/a&gt; 解决这个问题。&lt;/p&gt;
&lt;h3 id=&#34;练习题&#34;&gt;练习题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;练习 5.4&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_5/for_loop.go&#34;&gt;for_loop.go&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用 &lt;code&gt;for&lt;/code&gt; 结构创建一个简单的循环。要求循环 15 次然后使用 &lt;code&gt;fmt&lt;/code&gt; 包来打印计数器的值。&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;goto&lt;/code&gt; 语句重写循环，要求不能使用 &lt;code&gt;for&lt;/code&gt; 关键字。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;练习 5.5&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_5/for_character.go&#34;&gt;for_character.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;创建一个程序，要求能够打印类似下面的结果（尾行达 25 个字符为止）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;G
GG
GGG
GGGG
GGGGG
GGGGGG
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;使用 2 层嵌套 for 循环。&lt;/li&gt;
&lt;li&gt;仅用 1 层 for 循环以及字符串连接。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;练习 5.6&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_5/bitwise_complement.go&#34;&gt;bitwise_complement.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;使用按位补码从 0 到 10，使用位表达式 &lt;code&gt;%b&lt;/code&gt; 来格式化输出。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 5.7&lt;/strong&gt; Fizz-Buzz 问题：&lt;a href=&#34;exercises/chapter_5/fizzbuzz.go&#34;&gt;fizzbuzz.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写一个从 1 打印到 100 的程序，但是每当遇到 3 的倍数时，不打印相应的数字，但打印一次 &amp;ldquo;Fizz&amp;rdquo;。遇到 5 的倍数时，打印 &lt;code&gt;Buzz&lt;/code&gt; 而不是相应的数字。对于同时为 3 和 5 的倍数的数，打印 &lt;code&gt;FizzBuzz&lt;/code&gt;（提示：使用 switch 语句）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 5.8&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_5/rectangle_stars.go&#34;&gt;rectangle_stars.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;*&lt;/code&gt; 符号打印宽为 20，高为 10 的矩形。&lt;/p&gt;
&lt;h2 id=&#34;542-基于条件判断的迭代&#34;&gt;5.4.2 基于条件判断的迭代&lt;/h2&gt;
&lt;p&gt;for 结构的第二种形式是没有头部的条件判断迭代（类似其它语言中的 while 循环），基本形式为：&lt;code&gt;for 条件语句 {}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;您也可以认为这是没有初始化语句和修饰语句的 for 结构，因此 &lt;code&gt;;;&lt;/code&gt; 便是多余的了。&lt;/p&gt;
&lt;p&gt;Listing 5.8 &lt;a href=&#34;examples/chapter_5/for2.go&#34;&gt;for2.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var i int = 5

	for i &amp;gt;= 0 {
		i = i - 1
		fmt.Printf(&amp;#34;The variable i is now: %d\n&amp;#34;, i)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The variable i is now: 4
The variable i is now: 3
The variable i is now: 2
The variable i is now: 1
The variable i is now: 0
The variable i is now: -1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;543-无限循环&#34;&gt;5.4.3 无限循环&lt;/h2&gt;
&lt;p&gt;条件语句是可以被省略的，如 &lt;code&gt;i:=0; ; i++&lt;/code&gt; 或 &lt;code&gt;for { }&lt;/code&gt; 或 &lt;code&gt;for ;; { }&lt;/code&gt;（&lt;code&gt;;;&lt;/code&gt; 会在使用 gofmt 时被移除）：这些循环的本质就是无限循环。最后一个形式也可以被改写为 &lt;code&gt;for true { }&lt;/code&gt;，但一般情况下都会直接写 &lt;code&gt;for { }&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果 for 循环的头部没有条件语句，那么就会认为条件永远为 true，因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。&lt;/p&gt;
&lt;p&gt;想要直接退出循环体，可以使用 break 语句（第 5.5 节）或 return 语句直接返回（第 6.1 节）。&lt;/p&gt;
&lt;p&gt;但这两者之间有所区别，break 只是退出当前的循环体，而 return 语句提前对函数进行返回，不会执行后续的代码。&lt;/p&gt;
&lt;p&gt;无限循环的经典应用是服务器，用于不断等待和接受新的请求。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for t, err = p.Token(); err == nil; t, err = p.Token() {
	...
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;544-for-range-结构&#34;&gt;5.4.4 for-range 结构&lt;/h2&gt;
&lt;p&gt;这是 Go 特有的一种的迭代结构，您会发现它在许多情况下都非常有用。它可以迭代任何一个集合（包括数组和 &lt;code&gt;map&lt;/code&gt;，详见第 &lt;a href=&#34;07.0.md&#34;&gt;7&lt;/a&gt; 和 &lt;a href=&#34;08.0.md&#34;&gt;8&lt;/a&gt; 章）。语法上很类似其它语言中的 foreach 语句，但您依旧可以获得每次迭代所对应的索引。一般形式为：&lt;code&gt;for ix, val := range coll { }&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;要注意的是，&lt;code&gt;val&lt;/code&gt; 始终为集合中对应索引的值拷贝，因此它一般只具有只读性质，对它所做的任何修改都不会影响到集合中原有的值（&lt;strong&gt;译者注：如果 &lt;code&gt;val&lt;/code&gt; 为指针，则会产生指针的拷贝，依旧可以修改集合中的原值&lt;/strong&gt;）。一个字符串是 Unicode 编码的字符（或称之为 &lt;code&gt;rune&lt;/code&gt;）集合，因此您也可以用它迭代字符串：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for pos, char := range str {
...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个 &lt;code&gt;rune&lt;/code&gt; 字符和索引在 for-range 循环中是一一对应的。它能够自动根据 UTF-8 规则识别 Unicode 编码的字符。&lt;/p&gt;
&lt;p&gt;示例 5.9 &lt;a href=&#34;examples/chapter_5/range_string.go&#34;&gt;range_string.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	str := &amp;#34;Go is a beautiful language!&amp;#34;
	fmt.Printf(&amp;#34;The length of str is: %d\n&amp;#34;, len(str))
	for pos, char := range str {
		fmt.Printf(&amp;#34;Character on position %d is: %c \n&amp;#34;, pos, char)
	}
	fmt.Println()
	str2 := &amp;#34;Chinese: 日本語&amp;#34;
	fmt.Printf(&amp;#34;The length of str2 is: %d\n&amp;#34;, len(str2))
	for pos, char := range str2 {
    	fmt.Printf(&amp;#34;character %c starts at byte position %d\n&amp;#34;, char, pos)
	}
	fmt.Println()
	fmt.Println(&amp;#34;index int(rune) rune    char bytes&amp;#34;)
	for index, rune := range str2 {
    	fmt.Printf(&amp;#34;%-2d      %d      %U &amp;#39;%c&amp;#39; % X\n&amp;#34;, index, rune, rune, rune, []byte(string(rune)))
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;The length of str is: 27
Character on position 0 is: G 
Character on position 1 is: o 
Character on position 2 is:   
Character on position 3 is: i 
Character on position 4 is: s 
Character on position 5 is:   
Character on position 6 is: a 
Character on position 7 is:   
Character on position 8 is: b 
Character on position 9 is: e 
Character on position 10 is: a 
Character on position 11 is: u 
Character on position 12 is: t 
Character on position 13 is: i 
Character on position 14 is: f 
Character on position 15 is: u 
Character on position 16 is: l 
Character on position 17 is:   
Character on position 18 is: l 
Character on position 19 is: a 
Character on position 20 is: n 
Character on position 21 is: g 
Character on position 22 is: u 
Character on position 23 is: a 
Character on position 24 is: g 
Character on position 25 is: e 
Character on position 26 is: ! 

The length of str2 is: 18
character C starts at byte position 0
character h starts at byte position 1
character i starts at byte position 2
character n starts at byte position 3
character e starts at byte position 4
character s starts at byte position 5
character e starts at byte position 6
character : starts at byte position 7
character   starts at byte position 8
character 日 starts at byte position 9
character 本 starts at byte position 12
character 語 starts at byte position 15

index int(rune) rune    char bytes
0       67      U+0043 &amp;#39;C&amp;#39; 43
1       104      U+0068 &amp;#39;h&amp;#39; 68
2       105      U+0069 &amp;#39;i&amp;#39; 69
3       110      U+006E &amp;#39;n&amp;#39; 6E
4       101      U+0065 &amp;#39;e&amp;#39; 65
5       115      U+0073 &amp;#39;s&amp;#39; 73
6       101      U+0065 &amp;#39;e&amp;#39; 65
7       58      U+003A &amp;#39;:&amp;#39; 3A
8       32      U+0020 &amp;#39; &amp;#39; 20
9       26085      U+65E5 &amp;#39;日&amp;#39; E6 97 A5
12      26412      U+672C &amp;#39;本&amp;#39; E6 9C AC
15      35486      U+8A9E &amp;#39;語&amp;#39; E8 AA 9E
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;请将输出结果和 Listing 5.7（&lt;a href=&#34;examples/chapter_5/for_string.go&#34;&gt;for_string.go&lt;/a&gt;）进行对比。&lt;/p&gt;
&lt;p&gt;我们可以看到，常用英文字符使用 1 个字节表示，而汉字（&lt;strong&gt;译者注：严格来说，“Chinese: 日本語”的 Chinese 应该是 Japanese&lt;/strong&gt;）使用 3 个字符表示。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 5.9&lt;/strong&gt; 以下程序的输出结果是什么？&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i := 0; i &amp;lt; 5; i++ {
	var v int
	fmt.Printf(&amp;#34;%d &amp;#34;, v)
	v = 5
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;问题 5.2：&lt;/strong&gt; 请描述以下 for 循环的输出结果：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i := 0; ; i++ {
	fmt.Println(&amp;#34;Value of i is now:&amp;#34;, i)
}
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i := 0; i &amp;lt; 3; {
	fmt.Println(&amp;#34;Value of i:&amp;#34;, i)
}
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;s := &amp;#34;&amp;#34;
for ; s != &amp;#34;aaaaa&amp;#34;; {
	fmt.Println(&amp;#34;Value of s:&amp;#34;, s)
	s = s + &amp;#34;a&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;for i, j, s := 0, 5, &amp;#34;a&amp;#34;; i &amp;lt; 3 &amp;amp;&amp;amp; j &amp;lt; 100 &amp;amp;&amp;amp; s != &amp;#34;aaaaa&amp;#34;; i, j,
	s = i+1, j+1, s + &amp;#34;a&amp;#34; {
	fmt.Println(&amp;#34;Value of i, j, s:&amp;#34;, i, j, s)
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的switch结构</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.10-switch%E7%BB%93%E6%9E%84/</link>
    <pubDate>Sun, 10 Jul 2022 17:42:49 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.10-switch%E7%BB%93%E6%9E%84/</guid>
    <description>
        &lt;h1 id=&#34;53-switch-结构&#34;&gt;5.3 switch 结构&lt;/h1&gt;
&lt;p&gt;相比较 C 和 Java 等其它语言而言，Go 语言中的 &lt;code&gt;switch&lt;/code&gt; 结构使用上更加灵活。它接受任意形式的表达式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch var1 {
	case val1:
		...
	case val2:
		...
	default:
		...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;变量 &lt;code&gt;var1&lt;/code&gt; 可以是任何类型，而 &lt;code&gt;val1&lt;/code&gt; 和 &lt;code&gt;val2&lt;/code&gt; 则可以是同类型的任意值。类型不被局限于常量或整数，但必须是相同的类型；或者最终结果为相同类型的表达式。前花括号 &lt;code&gt;{&lt;/code&gt; 必须和 &lt;code&gt;switch&lt;/code&gt; 关键字在同一行。&lt;/p&gt;
&lt;p&gt;您可以同时测试多个可能符合条件的值，使用逗号分割它们，例如：&lt;code&gt;case val1, val2, val3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;每一个 &lt;code&gt;case&lt;/code&gt; 分支都是唯一的，从上至下逐一测试，直到匹配为止。（ Go 语言使用快速的查找算法来测试 &lt;code&gt;switch&lt;/code&gt; 条件与 &lt;code&gt;case&lt;/code&gt; 分支的匹配情况，直到算法匹配到某个 &lt;code&gt;case&lt;/code&gt; 或者进入 &lt;code&gt;default&lt;/code&gt; 条件为止。）&lt;/p&gt;
&lt;p&gt;一旦成功地匹配到某个分支，在执行完相应代码后就会退出整个 &lt;code&gt;switch&lt;/code&gt; 代码块，也就是说您不需要特别使用 &lt;code&gt;break&lt;/code&gt; 语句来表示结束。&lt;/p&gt;
&lt;p&gt;因此，程序也不会自动地去执行下一个分支的代码。如果在执行完每个分支的代码后，还希望继续执行后续分支的代码，可以使用 &lt;code&gt;fallthrough&lt;/code&gt; 关键字来达到目的。&lt;/p&gt;
&lt;p&gt;因此：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch i {
	case 0: // 空分支，只有当 i == 0 时才会进入分支
	case 1:
		f() // 当 i == 0 时函数不会被调用
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;并且：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch i {
	case 0: fallthrough
	case 1:
		f() // 当 i == 0 时函数也会被调用
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 &lt;code&gt;case ...:&lt;/code&gt; 语句之后，您不需要使用花括号将多行语句括起来，但您可以在分支中进行任意形式的编码。当代码块只有一行时，可以直接放置在 &lt;code&gt;case&lt;/code&gt; 语句之后。&lt;/p&gt;
&lt;p&gt;您同样可以使用 &lt;code&gt;return&lt;/code&gt; 语句来提前结束代码块的执行。当您在 &lt;code&gt;switch&lt;/code&gt; 语句块中使用 &lt;code&gt;return&lt;/code&gt; 语句，并且您的函数是有返回值的，您还需要在 switch 之后添加相应的 &lt;code&gt;return&lt;/code&gt; 语句以确保函数始终会返回。&lt;/p&gt;
&lt;p&gt;可选的 &lt;code&gt;default&lt;/code&gt; 分支可以出现在任何顺序，但最好将它放在最后。它的作用类似与 if-else 语句中的 &lt;code&gt;else&lt;/code&gt;，表示不符合任何已给出条件时，执行相关语句。&lt;/p&gt;
&lt;p&gt;示例 5.4 &lt;a href=&#34;examples/chapter_5/switch1.go&#34;&gt;switch1.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var num1 int = 100

	switch num1 {
	case 98, 99:
		fmt.Println(&amp;#34;It&amp;#39;s equal to 98&amp;#34;)
	case 100: 
		fmt.Println(&amp;#34;It&amp;#39;s equal to 100&amp;#34;)
	default:
		fmt.Println(&amp;#34;It&amp;#39;s not equal to 98 or 100&amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;It&#39;s equal to 100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在第 12.1 节，我们会使用 &lt;code&gt;switch&lt;/code&gt; 语句判断从键盘输入的字符（详见&lt;a href=&#34;12.2.md&#34;&gt;第 12.2 节&lt;/a&gt; 的 &lt;a href=&#34;./examples/chapter_12/switch.go&#34;&gt;switch.go&lt;/a&gt;）。&lt;code&gt;switch&lt;/code&gt; 语句的第二种形式是不提供任何被判断的值（实际上默认为判断是否为 &lt;code&gt;true&lt;/code&gt;），然后在每个 &lt;code&gt;case&lt;/code&gt; 分支中进行测试不同的条件。当任一分支的测试结果为 &lt;code&gt;true&lt;/code&gt; 时，该分支的代码会被执行。这看起来非常像链式的 if-else 语句，但是在测试条件非常多的情况下，提供了可读性更好的书写方式。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch {
	case condition1:
		...
	case condition2:
		...
	default:
		...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch {
	case i &amp;lt; 0:
		f1()
	case i == 0:
		f2()
	case i &amp;gt; 0:
		f3()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;任何支持进行相等判断的类型都可以作为测试表达式的条件，包括 &lt;code&gt;int&lt;/code&gt;、&lt;code&gt;string&lt;/code&gt;、指针等。&lt;/p&gt;
&lt;p&gt;示例 5.4 &lt;a href=&#34;examples/chapter_5/switch2.go&#34;&gt;switch2.go&lt;/a&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var num1 int = 7

	switch {
	    case num1 &amp;lt; 0:
		    fmt.Println(&amp;#34;Number is negative&amp;#34;)
	    case num1 &amp;gt; 0 &amp;amp;&amp;amp; num1 &amp;lt; 10:
		    fmt.Println(&amp;#34;Number is between 0 and 10&amp;#34;)
	    default:
		    fmt.Println(&amp;#34;Number is 10 or greater&amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Number is between 0 and 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;switch 语句的第三种形式是包含一个初始化语句：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch initialization {
	case val1:
		...
	case val2:
		...
	default:
		...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种形式可以非常优雅地进行条件判断：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch result := calculate(); {
	case result &amp;lt; 0:
		...
	case result &amp;gt; 0:
		...
	default:
		// 0
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在下面这个代码片段中，变量 &lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt; 被平行初始化，然后作为判断条件：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;switch a, b := x[i], y[j]; {
	case a &amp;lt; b: t = -1
	case a == b: t = 0
	case a &amp;gt; b: t = 1
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;switch&lt;/code&gt; 语句还可以被用于 type-switch（详见&lt;a href=&#34;11.4.md&#34;&gt;第 11.4 节&lt;/a&gt;）来判断某个 &lt;code&gt;interface&lt;/code&gt; 变量中实际存储的变量类型。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 5.1：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;请说出下面代码片段输出的结果：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;	k := 6
	switch k {
	case 4:
		fmt.Println(&amp;#34;was &amp;lt;= 4&amp;#34;)
		fallthrough
	case 5:
		fmt.Println(&amp;#34;was &amp;lt;= 5&amp;#34;)
		fallthrough
	case 6:
		fmt.Println(&amp;#34;was &amp;lt;= 6&amp;#34;)
		fallthrough
	case 7:
		fmt.Println(&amp;#34;was &amp;lt;= 7&amp;#34;)
		fallthrough
	case 8:
		fmt.Println(&amp;#34;was &amp;lt;= 8&amp;#34;)
		fallthrough
	default:
		fmt.Println(&amp;#34;default case&amp;#34;)
	}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;练习 5.2：&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_5/season.go&#34;&gt;season.go&lt;/a&gt;：&lt;/p&gt;
&lt;p&gt;写一个 &lt;code&gt;Season()&lt;/code&gt; 函数，要求接受一个代表月份的数字，然后返回所代表月份所在季节的名称（不用考虑月份的日期）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的测试多返回值函数的错误</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.09-%E6%B5%8B%E8%AF%95%E5%A4%9A%E8%BF%94%E5%9B%9E%E5%80%BC%E5%87%BD%E6%95%B0%E7%9A%84%E9%94%99%E8%AF%AF/</link>
    <pubDate>Sat, 09 Jul 2022 17:39:04 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.09-%E6%B5%8B%E8%AF%95%E5%A4%9A%E8%BF%94%E5%9B%9E%E5%80%BC%E5%87%BD%E6%95%B0%E7%9A%84%E9%94%99%E8%AF%AF/</guid>
    <description>
        &lt;h1 id=&#34;52-测试多返回值函数的错误&#34;&gt;5.2 测试多返回值函数的错误&lt;/h1&gt;
&lt;p&gt;Go 语言的函数经常使用两个返回值来表示执行是否成功：返回某个值以及 &lt;code&gt;true&lt;/code&gt; 表示成功；返回零值（或 &lt;code&gt;nil&lt;/code&gt;）和 &lt;code&gt;false&lt;/code&gt; 表示失败（&lt;a href=&#34;04.4.md&#34;&gt;第 4.4 节&lt;/a&gt;）。当不使用 &lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt; 的时候，也可以使用一个 &lt;code&gt;error&lt;/code&gt; 类型的变量来代替作为第二个返回值：成功执行的话，&lt;code&gt;error&lt;/code&gt; 的值为 &lt;code&gt;nil&lt;/code&gt;，否则就会包含相应的错误信息（Go 语言中的错误类型为 &lt;code&gt;error&lt;/code&gt;: &lt;code&gt;var err error&lt;/code&gt;，我们将会在&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt; 进行更多地讨论）。这样一来，就很明显需要用一个 &lt;code&gt;if&lt;/code&gt; 语句来测试执行结果；由于其符号的原因，这样的形式又称之为“逗号 ok 模式”(comma, ok pattern)。&lt;/p&gt;
&lt;p&gt;在&lt;a href=&#34;04.7.md&#34;&gt;第 4.7 节&lt;/a&gt; 的程序 &lt;a href=&#34;examples/chapter_4/string_conversion.go&#34;&gt;string_conversion.go&lt;/a&gt; 中，函数 &lt;code&gt;strconv.Atoi()&lt;/code&gt; 的作用是将一个字符串转换为一个整数。之前我们忽略了相关的错误检查：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;anInt, _ = strconv.Atoi(origStr)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果 &lt;code&gt;origStr&lt;/code&gt; 不能被转换为整数，&lt;code&gt;anInt&lt;/code&gt; 的值会变成 &lt;code&gt;0&lt;/code&gt; 而 &lt;code&gt;_&lt;/code&gt; 无视了错误，程序会继续运行。&lt;/p&gt;
&lt;p&gt;这样做是非常不好的：程序应该在最接近的位置检查所有相关的错误，至少需要暗示用户有错误发生并对函数进行返回，甚至中断程序。&lt;/p&gt;
&lt;p&gt;我们在第二个版本中对代码进行了改进：&lt;/p&gt;
&lt;p&gt;示例 1：&lt;/p&gt;
&lt;p&gt;示例 5.3 &lt;a href=&#34;examples/chapter_5/string_conversion2.go&#34;&gt;string_conversion2.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strconv&amp;#34;
)

func main() {
	var orig string = &amp;#34;ABC&amp;#34;
	// var an int
	var newS string
	// var err error

	fmt.Printf(&amp;#34;The size of ints is: %d\n&amp;#34;, strconv.IntSize)	  
	// anInt, err = strconv.Atoi(origStr)
	an, err := strconv.Atoi(orig)
	if err != nil {
		fmt.Printf(&amp;#34;orig %s is not an integer - exiting with error\n&amp;#34;, orig)
		return
	} 
	fmt.Printf(&amp;#34;The integer is %d\n&amp;#34;, an)
	an = an + 5
	newS = strconv.Itoa(an)
	fmt.Printf(&amp;#34;The new string is: %s\n&amp;#34;, newS)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这是测试 &lt;code&gt;err&lt;/code&gt; 变量是否包含一个真正的错误（&lt;code&gt;if err != nil&lt;/code&gt;）的习惯用法。如果确实存在错误，则会打印相应的错误信息然后通过 &lt;code&gt;return&lt;/code&gt; 提前结束函数的执行。我们还可以使用携带返回值的 &lt;code&gt;return&lt;/code&gt; 形式，例如 &lt;code&gt;return err&lt;/code&gt;。这样一来，函数的调用者就可以检查函数执行过程中是否存在错误了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;习惯用法&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;value, err := pack1.Function1(param1)
if err != nil {
	fmt.Printf(&amp;#34;An error occured in pack1.Function1 with parameter %v&amp;#34;, param1)
	return err
}
// 未发生错误，继续执行：
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;由于本例的函数调用者属于 &lt;code&gt;main&lt;/code&gt; 函数，所以程序会直接停止运行。&lt;/p&gt;
&lt;p&gt;如果我们想要在错误发生的同时终止程序的运行，我们可以使用 &lt;code&gt;os&lt;/code&gt; 包的 &lt;code&gt;Exit&lt;/code&gt; 函数：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;习惯用法&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if err != nil {
	fmt.Printf(&amp;#34;Program stopping with error %v&amp;#34;, err)
	os.Exit(1)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;（此处的退出代码 &lt;code&gt;1&lt;/code&gt; 可以使用外部脚本获取到）&lt;/p&gt;
&lt;p&gt;有时候，你会发现这种习惯用法被连续重复地使用在某段代码中。&lt;/p&gt;
&lt;p&gt;当没有错误发生时，代码继续运行就是唯一要做的事情，所以 &lt;code&gt;if&lt;/code&gt; 语句块后面不需要使用 &lt;code&gt;else&lt;/code&gt; 分支。&lt;/p&gt;
&lt;p&gt;示例 2：我们尝试通过 &lt;code&gt;os.Open&lt;/code&gt; 方法打开一个名为 &lt;code&gt;name&lt;/code&gt; 的只读文件：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;f, err := os.Open(name)
if err != nil {
	return err
}
doSomething(f) // 当没有错误发生时，文件对象被传入到某个函数中
doSomething
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;练习 5.1&lt;/strong&gt; 尝试改写 &lt;a href=&#34;examples/chapter_5/string_conversion2.go&#34;&gt;string_conversion2.go&lt;/a&gt; 中的代码，要求使用 &lt;code&gt;:=&lt;/code&gt; 方法来对 &lt;code&gt;err&lt;/code&gt; 进行赋值，哪些地方可以被修改？&lt;/p&gt;
&lt;p&gt;示例 3：可以将错误的获取放置在 &lt;code&gt;if&lt;/code&gt; 语句的初始化部分：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;习惯用法&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if err := file.Chmod(0664); err != nil {
	fmt.Println(err)
	return err
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;示例 4：或者将 ok-pattern 的获取放置在 &lt;code&gt;if&lt;/code&gt; 语句的初始化部分，然后进行判断：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;习惯用法&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if value, ok := readData(); ok {
…
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果您像下面一样，没有为多返回值的函数准备足够的变量来存放结果：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func mySqrt(f float64) (v float64, ok bool) {
	if f &amp;lt; 0 { return } // error case
	return math.Sqrt(f),true
}

func main() {
	t := mySqrt(25.0)
	fmt.Println(t)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;您会得到一个编译错误：&lt;code&gt;multiple-value mySqrt() in single-value context&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;正确的做法是：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;t, ok := mySqrt(25.0)
if ok { fmt.Println(t) }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意事项 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当您将字符串转换为整数时，且确定转换一定能够成功时，可以将 &lt;code&gt;Atoi()&lt;/code&gt; 函数进行一层忽略错误的封装：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func atoi (s string) (n int) {
	n, _ = strconv.Atoi(s)
	return
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实际上，&lt;code&gt;fmt&lt;/code&gt; 包（&lt;a href=&#34;04.4.md&#34;&gt;第 4.4.3 节&lt;/a&gt;）最简单的打印函数也有 2 个返回值：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;count, err := fmt.Println(x) // number of bytes printed, nil or 0, error
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;当打印到控制台时，可以将该函数返回的错误忽略；但当输出到文件流、网络流等具有不确定因素的输出对象时，应该始终检查是否有错误发生（另见&lt;a href=&#34;06.1.md&#34;&gt;练习 6.1b&lt;/a&gt;）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的if-else结构</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.08-if-else%E7%BB%93%E6%9E%84/</link>
    <pubDate>Fri, 08 Jul 2022 17:29:52 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.08-if-else%E7%BB%93%E6%9E%84/</guid>
    <description>
        &lt;h1 id=&#34;51-if-else-结构&#34;&gt;5.1 if-else 结构&lt;/h1&gt;
&lt;p&gt;if 是用于测试某个条件（布尔型或逻辑型）的语句，如果该条件成立，则会执行 if 后由大括号括起来的代码块，否则就忽略该代码块继续执行后续的代码。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if condition {
	// do something	
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果存在第二个分支，则可以在上面代码的基础上添加 &lt;code&gt;else&lt;/code&gt; 关键字以及另一代码块，这个代码块中的代码只有在条件不满足时才会执行。&lt;code&gt;if&lt;/code&gt; 和 &lt;code&gt;else&lt;/code&gt; 后的两个代码块是相互独立的分支，只可能执行其中一个。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if condition {
	// do something	
} else {
	// do something	
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果存在第三个分支，则可以使用下面这种三个独立分支的形式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if condition1 {
	// do something	
} else if condition2 {
	// do something else	
} else {
	// catch-all or default
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;else-if 分支的数量是没有限制的，但是为了代码的可读性，还是不要在 &lt;code&gt;if&lt;/code&gt; 后面加入太多的 else-if 结构。如果你必须使用这种形式，则把尽可能先满足的条件放在前面。&lt;/p&gt;
&lt;p&gt;即使当代码块之间只有一条语句时，大括号也不可被省略（尽管有些人并不赞成，但这还是符合了软件工程原则的主流做法）。&lt;/p&gt;
&lt;p&gt;关键字 &lt;code&gt;if&lt;/code&gt; 和 &lt;code&gt;else&lt;/code&gt; 之后的左大括号 &lt;code&gt;{&lt;/code&gt; 必须和关键字在同一行，如果你使用了 else-if 结构，则前段代码块的右大括号 &lt;code&gt;}&lt;/code&gt; 必须和 else-if 关键字在同一行。这两条规则都是被编译器强制规定的。&lt;/p&gt;
&lt;p&gt;非法的 Go 代码:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if x{
}
else {	// 无效的
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;要注意的是，在你使用 &lt;code&gt;gofmt&lt;/code&gt; 格式化代码之后，每个分支内的代码都会缩进 4 个或 8 个空格，或者是 1 个 tab，并且右大括号与对应的 &lt;code&gt;if&lt;/code&gt; 关键字垂直对齐。&lt;/p&gt;
&lt;p&gt;在有些情况下，条件语句两侧的括号是可以被省略的；当条件比较复杂时，则可以使用括号让代码更易读。条件允许是符合条件，需使用 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;、&lt;code&gt;||&lt;/code&gt; 或 &lt;code&gt;!&lt;/code&gt;，你可以使用括号来提升某个表达式的运算优先级，并提高代码的可读性。&lt;/p&gt;
&lt;p&gt;一种可能用到条件语句的场景是测试变量的值，在不同的情况执行不同的语句，不过将在第 5.3 节讲到的 switch 结构会更适合这种情况。&lt;/p&gt;
&lt;p&gt;示例 5.1 &lt;a href=&#34;examples/chapter_5/booleans.go&#34;&gt;booleans.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func main() {
	bool1 := true
	if bool1 {
		fmt.Printf(&amp;#34;The value is true\n&amp;#34;)
	} else {
		fmt.Printf(&amp;#34;The value is false\n&amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The value is true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt; 这里不需要使用 &lt;code&gt;if bool1 == true&lt;/code&gt; 来判断，因为 &lt;code&gt;bool1&lt;/code&gt; 本身已经是一个布尔类型的值。&lt;/p&gt;
&lt;p&gt;这种做法一般都用在测试 &lt;code&gt;true&lt;/code&gt; 或者有利条件时，但你也可以使用取反 &lt;code&gt;!&lt;/code&gt; 来判断值的相反结果，如：&lt;code&gt;if !bool1&lt;/code&gt; 或者 &lt;code&gt;if !(condition)&lt;/code&gt;。后者的括号大多数情况下是必须的，如这种情况：&lt;code&gt;if !(var1 == var2)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当 if 结构内有 &lt;code&gt;break&lt;/code&gt;、&lt;code&gt;continue&lt;/code&gt;、&lt;code&gt;goto&lt;/code&gt; 或者 &lt;code&gt;return&lt;/code&gt; 语句时，Go 代码的常见写法是省略 &lt;code&gt;else&lt;/code&gt; 部分（另见&lt;a href=&#34;05.2.md&#34;&gt;第 5.2 节&lt;/a&gt;）。无论满足哪个条件都会返回 &lt;code&gt;x&lt;/code&gt; 或者 &lt;code&gt;y&lt;/code&gt; 时，一般使用以下写法：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if condition {
	return x
}
return y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt; 不要同时在 if-else 结构的两个分支里都使用 &lt;code&gt;return&lt;/code&gt; 语句，这将导致编译报错 &lt;code&gt;function ends without a return statement&lt;/code&gt;（你可以认为这是一个编译器的 Bug 或者特性）。（ &lt;strong&gt;译者注：该问题已经在 Go 1.1 中被修复或者说改进&lt;/strong&gt; ）&lt;/p&gt;
&lt;p&gt;这里举一些有用的例子：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;判断一个字符串是否为空：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if str == &amp;quot;&amp;quot; { ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;if len(str) == 0 {...}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;判断运行 Go 程序的操作系统类型，这可以通过常量 &lt;code&gt;runtime.GOOS&lt;/code&gt; 来判断（&lt;a href=&#34;02.2.md&#34;&gt;第 2.2 节&lt;/a&gt;）。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if runtime.GOOS == &amp;#34;windows&amp;#34;	 {
	.	..
} else { // Unix-like
	.	..
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这段代码一般被放在 &lt;code&gt;init()&lt;/code&gt; 函数中执行。这儿还有一段示例来演示如何根据操作系统来决定输入结束的提示：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var prompt = &amp;#34;Enter a digit, e.g. 3 &amp;#34;+ &amp;#34;or %s to quit.&amp;#34;

func init() {
	if runtime.GOOS == &amp;#34;windows&amp;#34; {
		prompt = fmt.Sprintf(prompt, &amp;#34;Ctrl+Z, Enter&amp;#34;)		
	} else { //Unix-like
		prompt = fmt.Sprintf(prompt, &amp;#34;Ctrl+D&amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;函数 &lt;code&gt;Abs()&lt;/code&gt; 用于返回一个整型数字的绝对值:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Abs(x int) int {
if x &amp;lt; 0 {
	return -x
}
return x	
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;isGreater&lt;/code&gt; 用于比较两个整型数字的大小:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func isGreater(x, y int) bool {
	if x &amp;gt; y {
		return true	
	}
	return false
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在第四种情况中，&lt;code&gt;if&lt;/code&gt; 可以包含一个初始化语句（如：给一个变量赋值）。这种写法具有固定的格式（在初始化语句后方必须加上分号）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if initialization; condition {
	// do something
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例如:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;val := 10
if val &amp;gt; max {
	// do something
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你也可以这样写:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if val := 10; val &amp;gt; max {
	// do something
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但要注意的是，使用简短方式 &lt;code&gt;:=&lt;/code&gt; 声明的变量的作用域只存在于 &lt;code&gt;if&lt;/code&gt; 结构中（在 &lt;code&gt;if&lt;/code&gt; 结构的大括号之间，如果使用 if-else 结构则在 &lt;code&gt;else&lt;/code&gt; 代码块中变量也会存在）。如果变量在 &lt;code&gt;if&lt;/code&gt; 结构之前就已经存在，那么在 &lt;code&gt;if&lt;/code&gt; 结构中，该变量原来的值会被隐藏。最简单的解决方案就是不要在初始化语句中声明变量（见&lt;a href=&#34;05.2.md&#34;&gt;5.2 节的例 3&lt;/a&gt; 了解更多)。&lt;/p&gt;
&lt;p&gt;示例 5.2 &lt;a href=&#34;examples/chapter_5/ifelse.go&#34;&gt;ifelse.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var first int = 10
	var cond int

	if first &amp;lt;= 0 {
		fmt.Printf(&amp;#34;first is less than or equal to 0\n&amp;#34;)
	} else if first &amp;gt; 0 &amp;amp;&amp;amp; first &amp;lt; 5 {
		fmt.Printf(&amp;#34;first is between 0 and 5\n&amp;#34;)
	} else {
		fmt.Printf(&amp;#34;first is 5 or greater\n&amp;#34;)
	}
	if cond = 5; cond &amp;gt; 10 {
		fmt.Printf(&amp;#34;cond is greater than 10\n&amp;#34;)
	} else {
		fmt.Printf(&amp;#34;cond is not greater than 10\n&amp;#34;)
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;first is 5 or greater
cond is not greater than 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面的代码片段展示了如何通过在初始化语句中获取函数 &lt;code&gt;process()&lt;/code&gt; 的返回值，并在条件语句中作为判定条件来决定是否执行 &lt;code&gt;if&lt;/code&gt; 结构中的代码：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if value := process(data); value &amp;gt; max {
	...
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>nohup及重定向</title>
    <link>https://blog.wiseai.cn/post/nohup%E5%8F%8A%E9%87%8D%E5%AE%9A%E5%90%91/</link>
    <pubDate>Fri, 08 Jul 2022 16:30:38 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/nohup%E5%8F%8A%E9%87%8D%E5%AE%9A%E5%90%91/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;
&lt;p&gt;语法：nohup Command [ Arg … ] [　&amp;amp; ]&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;描述：nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令，忽略所有挂断（SIGHUP）信号。要在后台运行 nohup 命令，添加 &lt;code&gt;&amp;amp;&lt;/code&gt; 到命令的尾部。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;操作系统中有三个常用的流：&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;0：标准输入流 stdin
1：标准输出流 stdout
2：标准错误流 stderr
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;gt; info.txt&lt;/code&gt;实际是 &lt;code&gt;1 &amp;gt; info.txt&lt;/code&gt;的省略用法；&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt; info.txt&lt;/code&gt;实际是 &lt;code&gt;0 &amp;lt; info.txt&lt;/code&gt;的省略用法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下面步入正题：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;nohup ./text.sh &amp;gt;output 2&amp;gt;&amp;amp;1 &amp;amp;
&amp;gt;su – wiseai -c ‘/usr/local/bin/jupyter notebook &amp;amp;&amp;gt; /dev/null &amp;amp;’
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;解释：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;带&amp;amp;的命令行，即使terminal（终端）关闭，或者电脑死机程序依然运行（前提是你把程序递交到服务器上)；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;和&lt;code&gt;&amp;amp;&amp;gt;&lt;/code&gt;相同:意思是把标准错误（2）重定向到标准输出中（1），而标准输出又导入文件output里面，所以结果是标准错误和标准输出都导入文件output里面了。 至于为什么需要将标准错误重定向到标准输出的原因，那就归结为标准错误没有缓冲区，而stdout有。这就会导致 &lt;code&gt;&amp;gt;output 2&amp;gt;output&lt;/code&gt; 文件output被两次打开，而stdout和stderr将会竞争覆盖，这肯定不是我门想要的。&lt;/li&gt;
&lt;li&gt;/dev/null文件的作用，这是一个无底洞，任何东西都可以定向到这里，但是却无法打开。 所以一般很大的stdou和stderr当你不关心的时候可以利用stdout和stderr定向到这里。&lt;/li&gt;
&lt;/ol&gt;

    </description>
    </item>
    
    <item>
    <title>Golang执行系统命令</title>
    <link>https://blog.wiseai.cn/post/golang%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Fri, 08 Jul 2022 16:06:32 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;直接上代码:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import (
	&amp;#34;bytes&amp;#34;
	&amp;#34;fmt&amp;#34;
	&amp;#34;os/exec&amp;#34;
)

func main() {
	var err error
	var stdout, stderr string

	Loop:
	err = nil
	stdout, stderr = &amp;#34;&amp;#34;, &amp;#34;&amp;#34;

	fmt.Println(&amp;#34;1.列出当前目录中的目录和文件&amp;#34;)
	fmt.Println(&amp;#34;2.重启打印服务&amp;#34;)
	fmt.Println(&amp;#34;退出请输入9&amp;#34;)
	fmt.Println(&amp;#34;-----------------------------&amp;#34;)

	var key uint8
	fmt.Scanf(&amp;#34;%d&amp;#34;, &amp;amp;key)
	switch key {
	case 1:
		err, stdout, stderr = shellRun(&amp;#34;ls -l&amp;#34;)
	case 2:
		err, stdout, stderr = shellRun(&amp;#34;systemctl restart cups&amp;#34;)
		fmt.Println(&amp;#34;请输入密码!&amp;#34;)
	case 9:
		fmt.Println(&amp;#34;安全退出&amp;#34;)
		return
	default:
		fmt.Println(&amp;#34;-----------------------------&amp;#34;)
		fmt.Println(&amp;#34;请输入正确的数字!&amp;#34;)
	}
	if err != nil {
		fmt.Print(&amp;#34;出现错误：&amp;#34;)
		fmt.Println(err)
	}
	if stdout != &amp;#34;&amp;#34; {
		fmt.Println(stdout)
	}
	if stderr != &amp;#34;&amp;#34; {
		fmt.Println(&amp;#34;错误提示:&amp;#34;)
		fmt.Println(stderr)
	}

	fmt.Println(&amp;#34;-----------------------------&amp;#34;)
	goto Loop
}

func shellRun(command string) (error, string, string) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer
	cmd := exec.Command(&amp;#34;/bin/bash&amp;#34;, &amp;#34;-c&amp;#34;, command)
	cmd.Stdout = &amp;amp;stdout
	cmd.Stderr = &amp;amp;stderr
	err := cmd.Run()
	return err, stdout.String(), stderr.String()
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的控制结构</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.07-%E6%8E%A7%E5%88%B6%E7%BB%93%E6%9E%84/</link>
    <pubDate>Thu, 07 Jul 2022 17:25:40 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.07-%E6%8E%A7%E5%88%B6%E7%BB%93%E6%9E%84/</guid>
    <description>
        &lt;h1 id=&#34;50-控制结构&#34;&gt;5.0 控制结构&lt;/h1&gt;
&lt;p&gt;到目前为止，我们看到的 Go 程序都是从 &lt;code&gt;main()&lt;/code&gt; 函数开始执行，然后按顺序执行该函数体中的代码。但我们经常会需要只有在满足一些特定情况时才执行某些代码，也就是说在代码里进行条件判断。针对这种需求，Go 提供了下面这些条件结构和分支结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if&lt;/code&gt;-&lt;code&gt;else&lt;/code&gt; 结构&lt;/li&gt;
&lt;li&gt;&lt;code&gt;switch&lt;/code&gt; 结构&lt;/li&gt;
&lt;li&gt;&lt;code&gt;select&lt;/code&gt; 结构，用于 channel 的选择（&lt;a href=&#34;14.4.md&#34;&gt;第 14.4 节&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以使用迭代或循环结构来重复执行一次或多次某段代码（任务）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;for&lt;/code&gt; (&lt;code&gt;range&lt;/code&gt;) 结构&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一些如 &lt;code&gt;break&lt;/code&gt; 和 &lt;code&gt;continue&lt;/code&gt; 这样的关键字可以用于中途改变循环的状态。&lt;/p&gt;
&lt;p&gt;此外，你还可以使用 &lt;code&gt;return&lt;/code&gt; 来结束某个函数的执行，或使用 &lt;code&gt;goto&lt;/code&gt; 和标签来调整程序的执行位置。&lt;/p&gt;
&lt;p&gt;Go 完全省略了 &lt;code&gt;if&lt;/code&gt;、&lt;code&gt;switch&lt;/code&gt; 和 &lt;code&gt;for&lt;/code&gt; 结构中条件语句两侧的括号，相比 Java、C++ 和 C# 中减少了很多视觉混乱的因素，同时也使你的代码更加简洁。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>aria2命令</title>
    <link>https://blog.wiseai.cn/post/aria2%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:26:36 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/aria2%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;安装
&lt;code&gt;# dnf install aria2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;配置文件aria2.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    #基本配置
    dir=/home/tzb/aria2/
    log=/home/tzb/aria2/aria2.log
    input-file=/home/tzb/aria2/session/aria2.session
    save-session=/home/tzb/aria2/session/
    save-session-interval=60
    force-save=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    log-level=error  

    #see --split option
    max-concurrent-downloads=5
    continue=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    max-overall-download-limit=0
    max-overall-upload-limit=50K
    max-upload-limit=20  

    # Http/FTP options
    connect-timeout=120
    lowest-speed-limit=10K
    max-connection-per-server=10
    max-file-not-found=2
    min-split-size=1M
    split=5
    check-certificate=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;false
    http-no-cache=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true  

    # FTP Specific Options  

    # BT/PT Setting
    bt-enable-lpd=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    #bt-max-peers=55
    follow-torrent=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    enable-dht6=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;false
    bt-seed-unverified
    rpc-save-upload-metadata=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    bt-hash-check-seed
    bt-remove-unselected-file
    bt-request-peer-speed-limit=100K
    seed-ratio=0.0

    # Metalink Specific Options

    # RPC Options
    enable-rpc=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    pause=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;false
    rpc-allow-origin-all=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    rpc-listen-all=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    rpc-save-upload-metadata=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    rpc-secure=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;false

    # Advanced Options
    daemon=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    disable-ipv6=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    enable-mmap=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    file-allocation=falloc
    max-download-result=120
    #no-file-allocation-limit=32M
    force-sequential=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
    parameterized-uri=&amp;lt;span class=&amp;#34;hljs-literal&amp;#34;&amp;gt;true
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;开机启动
&lt;code&gt;编辑rc.local添加 aria2c --conf-path=/path/to/aria2/aria2.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;图形界面
软件：uget或YAAMfirefox：flashgotjavascript：webui-aria2或AriaNg&lt;/li&gt;
&lt;li&gt;命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    aria2c http://AAA.BBB.CCC/file.zip  普通下载
    aria2c -s 2 http://AAA.BBB.CCC/file.zip  最大两个连接下载
    aria2c -Z http://aaa.bbb.ccc/file.zip http://ddd.eee.fff/file.zip 下载多个文件
    aria2c -c http://AAA.BBB.CCC/file.zip 断点续传
    aria2c -i download.txt 从文件下载
    aria2c -x2 http://AAA.BBB.CCC/file.zip 开2个线程下载
    aria2c http://AAA.BBB.CCC/file.zipftp://DDD.EEE.FFF/GGG/file.zip 从不同的地址下载同一文件
    aria2c http://AAA.BBB.CCC/file.zipftp://DDD.EEE.FFF/GGG/file.zip  支持不同的协议下载同一文件
    aria2c -o test.torrent http://AAA.BBB.CCC/file.torrent 下载BT种子
    aria2c -max-upload-limit 40K -T file.torrent 设定BT最大上传速度
    aria2c http://AAA.BBB.CCC/file.metalink  从metalink下载文件
    aria2c &amp;#39;magnet:?xt=urn:btih:248D0A1CD08284299DE78D5C1ED359BB46717D8C&amp;#39;  下载 BitTorrent 磁力链接
    aria2c https://curl.haxx.se/metalink.cgi?curl=tar.bz2  下载 BitTorrent Metalink 种子
    aria2c -S 打印 .torrent，.meta4 和 .metalink 这类文件内含的文件列表
    ＃从密码保护的网站下载文件
    aria2c --http-user=xxx --http-password=xxx https://download.owncloud.org/community/owncloud-9.0.0.tar.bz2
    aria2c --ftp-user=xxx --ftp-password=xxx ftp://ftp.gnu.org/gnu/wget/wget-1.17.tar.gz
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;注：https下载依赖于安装gnutls或openssl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;bt下载依赖于安装gnutls+libgcrypt或opensslmetalink下载依赖于安装libxml2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;##文件保存相关 ##

#文件保存目录
dir=/home/naonao/Downloads
# 启用磁盘缓存, 0为禁用缓存, 需1.16以上版本, 默认:16M
disk-cache=32M
#断点续传
continue=true

# 文件预分配方式, 能有效降低磁盘碎片, 默认:prealloc
# 预分配所需时间: none &amp;lt; falloc ? trunc &amp;lt; prealloc
# falloc和trunc则需要文件系统和内核支持
# NTFS建议使用falloc, EXT3/4建议trunc, MAC 下需要注释此项
file-allocation=trunc

## 下载连接相关 ##

# 最大同时下载任务数, 运行时可修改, 默认:5
#max-concurrent-downloads=100
# 同一服务器连接数, 添加时可指定, 默认:1
# 官方的aria2最高设置为16, 如果需要设置任意数值请重新编译aria2
max-connection-per-server=256
# 整体下载速度限制, 运行时可修改, 默认:0（不限制）
#max-overall-download-limit=0
# 单个任务下载速度限制, 默认:0（不限制）
#max-download-limit=0
# 整体上传速度限制, 运行时可修改, 默认:0（不限制）
#max-overall-upload-limit=0
# 单个任务上传速度限制, 默认:0（不限制）
#max-upload-limit=0
# 禁用IPv6, 默认:false
# disable-ipv6=true

# 最小文件分片大小, 添加时可指定, 取值范围1M -1024M, 默认:20M
# 假定size=10M, 文件为20MiB 则使用两个来源下载; 文件为15MiB 则使用一个来源下载
min-split-size=10M
# 单个任务最大线程数, 添加时可指定, 默认:5
# 建议同max-connection-per-server设置为相同值
split=256

## 进度保存相关 ##

# 从会话文件中读取下载任务
input-file=/etc/aria2/aria2.session
# 在Aria2退出时保存错误的、未完成的下载任务到会话文件
save-session=/etc/aria2/aria2.session
# 定时保存会话, 0为退出时才保存, 需1.16.1以上版本, 默认:0
save-session-interval=60

## RPC相关设置 ##

# 启用RPC, 默认:false
enable-rpc=true
# 允许所有来源, 默认:false
rpc-allow-origin-all=true
# 允许外部访问, 默认:false
rpc-listen-all=true
# RPC端口, 仅当默认端口被占用时修改
# rpc-listen-port=6800
# 设置的RPC授权令牌, v1.18.4新增功能, 取代 --rpc-user 和 --rpc-passwd 选项
rpc-secret=yourpassword
# 启动SSL
# rpc-secure=true
# 证书文件, 如果启用SSL则需要配置证书文件, 例如用https连接aria2
# rpc-certificate=
# rpc-private-key=

## BT/PT下载相关 ##

# 当下载的是一个种子(以.torrent结尾)时, 自动开始BT任务, 默认:true
follow-torrent=true
# 客户端伪装, PT需要
peer-id-prefix=-TR2770-
user-agent=Transmission/2.77
# 强制保存会话, 即使任务已经完成, 默认:false
# 较新的版本开启后会在任务完成后依然保留.aria2文件
#force-save=false
# 继续之前的BT任务时, 无需再次校验, 默认:false
bt-seed-unverified=true
# 保存磁力链接元数据为种子文件(.torrent文件), 默认:false
# bt-save-metadata=true
# 单个种子最大连接数, 默认:55 0表示不限制
bt-max-peers=0
# 最小做种时间, 单位:分
# seed-time = 60
# 分离做种任务
bt-detach-seed-only=true
&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>find命令</title>
    <link>https://blog.wiseai.cn/post/find%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:24:28 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/find%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;&lt;strong&gt;一.find命令&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;基本格式：find  path expression&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.按照文件名查找&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(1)find / -name httpd.conf　　#在根目录下查找文件httpd.conf，表示在整个硬盘查找

(2)find /etc -name httpd.conf　　#在/etc目录下文件httpd.conf

(3)find /etc -name &amp;#39;*srm*&amp;#39;　　#使用通配符*(0或者任意多个)。表示在/etc目录下查找文件名中含有字符串&amp;#39;srm&amp;#39;的文件

(4)find . -name &amp;#39;srm*&amp;#39; 　　#表示当前目录下查找文件名开头是字符串&amp;#39;srm&amp;#39;的文件
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;2.按照文件特征查找&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;(1)find / -amin -10 　　# 查找在系统中最后10分钟访问的文件(access time)

(2)find / -atime -2　　 # 查找在系统中最后48小时访问的文件

(3)find / -empty 　　# 查找在系统中为空的文件或者文件夹

(4)find / -group cat 　　# 查找在系统中属于 group为cat的文件

(5)find / -mmin -5 　　# 查找在系统中最后5分钟里修改过的文件(modify time)

(6)find / -mtime -1 　　#查找在系统中最后24小时里修改过的文件

(7)find / -user fred 　　#查找在系统中属于fred这个用户的文件

(8)find / -size +10000c　　#查找出大于10000000字节的文件(c:字节，w:双字，k:KB，M:MB，G:GB)

(9)find / -size -1000k 　　#查找出小于1000KB的文件
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;3.使用混合查找方式查找文件&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;参数有： ！，-and(-a)，-or(-o)。

(1)find /tmp -size +10000c -and -mtime +2 　　#在/tmp目录下查找大于10000字节并在最后2分钟内修改的文件

(2)find / -user fred -or -user george 　　#在/目录下查找用户是fred或者george的文件文件

(3)find /tmp ! -user panda　　#在/tmp目录中查找所有不属于panda用户的文件
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>grep命令</title>
    <link>https://blog.wiseai.cn/post/grep%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:21:46 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/grep%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;基本格式：grep  expression&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.主要参数&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;[options]主要参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-c：只输出匹配行的计数。
-i：不区分大小写
-h：查询多文件时不显示文件名。
-l：查询多文件时只输出包含匹配字符的文件名。
-n：显示匹配行及行号。
-s：不显示不存在或无匹配文本的错误信息。
-v：显示不包含匹配文本的所有行。
-r：查询目录及子目录下的文件包含匹配字符的文件名。
-A：后面跟一个数字（有无空格都可以），例如-A2表示打印符合要求的行以及下面两行。
-B：后面跟一个数字，例如-B2表示打印符合要求的行以及上面两行。
-C：后面跟一个数字，例如-C2表示打印符合要求的行以及上下各两行。
--color=auto：颜色显示
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;pattern正则表达式主要参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\： 忽略正则表达式中特殊字符的原有含义。
^：匹配正则表达式的开始行。
$: 匹配正则表达式的结束行。
\&amp;lt;：从匹配正则表达 式的行开始。
\&amp;gt;：到匹配正则表达式的行结束。
[ ]：单个字符，如[A]即A符合要求 。
[ - ]：范围，如[A-Z]，即A、B、C一直到Z都符合要求 。
.：所有的单个字符。
* ：有字符，长度可以为0。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2.实例&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(1)grep &#39;test&#39; d*　　#显示所有以d开头的文件中包含 test的行

(2)grep &#39;test&#39; aa bb cc 　　 #显示在aa，bb，cc文件中包含test的行

(3)grep &#39;[a-z]\{5\}&#39; aa 　　#显示所有包含每行字符串至少有5个连续小写字符的字符串的行

(4)grep magic /usr/src　　#显示/usr/src目录下的文件(不含子目录)包含magic的行

(5)grep -r magic /usr/src　　#显示/usr/src目录下的文件(包含子目录)包含magic的行

(6)grep -w pattern files ：只匹配整个单词，而不是字符串的一部分(如匹配&#39;magic&#39;，而不是&#39;magical&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;过滤出带有某个关键词的行，并输出行号&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep -n &amp;#39;root&amp;#39; /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
10:operator:x:11:0:operator:/root:/sbin/nologin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明: 前面的数字显示为绿色，表示行号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;过滤出不带有某个关键词的行，并输出行号&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep -nv &amp;#39;nologin&amp;#39; /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
6:sync:x:5:0:sync:/sbin:/bin/sync
7:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8:halt:x:7:0:halt:/sbin:/sbin/halt
45:aminglinux:x:1000:1000:aminglinux:/home/aminglinux:/bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;过滤出所有包含数字的行&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep &amp;#39;[0-9]&amp;#39; /etc/inittab
# multi-user.target: analogous to runlevel 3
# graphical.target: analogous to runlevel 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明: 只要有一个数字就算匹配到了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;过滤出所有不包含数字的行&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep -v &amp;#39;[0-9]&amp;#39; /etc/inittab
# inittab is no longer used.
#
# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
#
# systemd uses &amp;#39;targets&amp;#39; instead of runlevels. By default, there are two main targets:
#
#
# To view current default target, run:
# systemctl get-default
#
# To set a default target, run:
# systemctl set-default TARGET.target
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明：和上一例的结果正好相反，只要是包含一个数字，就不显示。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;过滤掉所有以#开头的行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;操作样例文档/etc/sos.conf的内容如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[plugins]
#disable = rpm, selinux, dovecot

[tunables]
#rpm.rpmva = off

#general.syslogsize = 15
# grep -v &amp;#39;^#&amp;#39; /etc/sos.conf
[plugins]

[tunables]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明：这里面是含有空行的。&lt;/p&gt;
&lt;p&gt;那如何将空行删除呢?示例命令如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep -v &amp;#39;^#&amp;#39; /etc/sos.conf |grep -v &amp;#39;^$&amp;#39;
[plugins]
[tunables]
在正则表达式中，^表示行的开始，$表示行的结尾，那么空行则可以用^$表示。如何打印出不以英文字母开头的行呢？我们先来自定义一个文件，如下所示：
# mkdir /tmp/1
# cd /tmp/1
# vim test.txt //内容如下
123
abc
456
abc2323
#laksdjf
Alllllllll
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;接下来看两个例子：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep &amp;#39;^[^a-zA-Z]&amp;#39; test.txt
123
456
#laksdjf
# grep &amp;#39;[^a-zA-Z]&amp;#39; test.txt
123
456
abc2323
#laksdjf
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;前面也提到过中括号[]的应用，如果是数字就用[0-9]这样的形式（当遇到类似[15]的形式时，表示只含有1或者5）。如果要过滤数字以及大小写字母，则要写成类似[0-9a-zA-Z]的形式。另外，[^字符]表示除[]内字符之外的字符。请注意，把^写到方括号里面和外面是有区别的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;过滤出任意一个字符和重复字符&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep &amp;#39;r.o&amp;#39; /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;.表示任意一个字符。上例中，r.o表示把r与o之间有一个任意字符的行过滤出来。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep &amp;#39;ooo*&amp;#39; /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
setroubleshoot:x:981:979::/var/lib/setroubleshoot:/sbin/nologin
*表示零个或多个*前面的字符。上例中，ooo*表示oo、ooo、oooo...或者更多的o。

# grep &amp;#39;.*&amp;#39; /etc/passwd |wc -l
45
# wc -l /etc/passwd
45 /etc/passwd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上例中，.*表示零个或多个任意字符，空行也包含在内，它会把/etc/passwd文件里面的所有行都匹配到，你也可以不加|wc -l看一下效果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;指定要过滤出的字符出现次数&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# grep &amp;#39;o\{2\}&amp;#39; /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
setroubleshoot:x:981:979::/var/lib/setroubleshoot:/sbin/nologin
这里用到了符号{}，其内部为数字，表示前面的字符要重复的次数。需要强调的是，{}左右都需要加上转义字符\。另外，使用“{ }”还可以表示一个范围，具体格式为{n1,n2}，其中n1 &amp;lt; n2，表示重复n1到n2次前面的字符，n2还可以为空，这时表示大于等于n1次。
除grep工具外，阿铭也常常用到egrep这个工具，后者是前者的扩展版本，可以完成grep不能完成的工作。下面阿铭介绍egrep不同于grep的几个用法。为了试验方便，阿铭把test.txt编辑成如下内容：
rot:x:0:0:/rot:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
1111111111111111111111111111111
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;过滤出一个或多个指定的字符&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# egrep &amp;#39;o+&amp;#39; test.txt
rot:x:0:0:/rot:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
# egrep &amp;#39;oo+&amp;#39; test.txt
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
# egrep &amp;#39;ooo+&amp;#39; test.txt
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
和grep不同，这里egrep使用的是符号+，它表示匹配1个或多个+前面的字符，这个“+”是不支持被grep直接使用的。包括上面的{}，也是可以直接被egrep使用，而不用加\转义。示例如下：
# egrep &amp;#39;o{2}&amp;#39; /etc/passwd
root:x:0:0:root:/root:/bin/bash
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
setroubleshoot:x:981:979::/var/lib/setroubleshoot:/sbin/nologin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;过滤出零个或一个指定的字符&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# egrep &amp;#39;o?&amp;#39; test.txt
rot:x:0:0:/rot:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
1111111111111111111111111111111
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# egrep &amp;#39;ooo?&amp;#39; test.txt
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
# egrep &amp;#39;oooo?&amp;#39; test.txt
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;过滤出字符串1或者字符串2&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# egrep &amp;#39;aaa|111|ooo&amp;#39; test.txt
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
1111111111111111111111111111111
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;egrep中()的应用&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# egrep &amp;#39;r(oo|at)o&amp;#39; test.txt
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
这里用()表示一个整体，上例中会把包含rooo或者rato的行过滤出来，另外也可以把()和其他符号组合在一起，例如(oo)+就表示1个或者多个oo。如下所示：
# egrep &amp;#39;(oo)+&amp;#39; test.txt
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的指针</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.06-%E6%8C%87%E9%92%88/</link>
    <pubDate>Wed, 06 Jul 2022 17:19:23 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.06-%E6%8C%87%E9%92%88/</guid>
    <description>
        &lt;h1 id=&#34;49-指针&#34;&gt;4.9 指针&lt;/h1&gt;
&lt;p&gt;不像 Java 和 .NET，Go 语言为程序员提供了控制数据结构的指针的能力；但是，你不能进行指针运算。通过给予程序员基本内存布局，Go 语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式，这些对构建运行良好的系统是非常重要的：指针对于性能的影响是不言而喻的，而如果你想要做的是系统编程、操作系统或者网络应用，指针更是不可或缺的一部分。&lt;/p&gt;
&lt;p&gt;由于各种原因，指针对于使用面向对象编程的现代程序员来说可能显得有些陌生，不过我们将会在这一小节对此进行解释，并在未来的章节中展开深入讨论。&lt;/p&gt;
&lt;p&gt;程序在内存中存储它的值，每个内存块（或字）有一个地址，通常用十六进制数表示，如：&lt;code&gt;0x6b0820&lt;/code&gt; 或 &lt;code&gt;0xf84001d7f0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Go 语言的取地址符是 &lt;code&gt;&amp;amp;&lt;/code&gt;，放到一个变量前使用就会返回相应变量的内存地址。&lt;/p&gt;
&lt;p&gt;下面的代码片段（示例 4.9 &lt;a href=&#34;examples/chapter_4/pointer.go&#34;&gt;pointer.go&lt;/a&gt;）可能输出 &lt;code&gt;An integer: 5, its location in memory: 0x6b0820&lt;/code&gt;（这个值随着你每次运行程序而变化）。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var i1 = 5
fmt.Printf(&amp;#34;An integer: %d, it&amp;#39;s location in memory: %p\n&amp;#34;, i1, &amp;amp;i1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个地址可以存储在一个叫做指针的特殊数据类型中，在本例中这是一个指向 int 的指针，即 &lt;code&gt;i1&lt;/code&gt;：此处使用 &lt;code&gt;*int&lt;/code&gt; 表示。如果我们想调用指针 &lt;code&gt;intP&lt;/code&gt;，我们可以这样声明它：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var intP *int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后使用 &lt;code&gt;intP = &amp;amp;i1&lt;/code&gt; 是合法的，此时 &lt;code&gt;intP&lt;/code&gt; 指向 &lt;code&gt;i1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;（指针的格式化标识符为 &lt;code&gt;%p&lt;/code&gt;）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;intP&lt;/code&gt; 存储了 &lt;code&gt;i1&lt;/code&gt; 的内存地址；它指向了 &lt;code&gt;i1&lt;/code&gt; 的位置，它引用了变量 &lt;code&gt;i1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一个指针变量可以指向任何一个值的内存地址&lt;/strong&gt; 它指向那个值的内存地址，在 32 位机器上占用 4 个字节，在 64 位机器上占用 8 个字节，并且与它所指向的值的大小无关。当然，可以声明指针指向任何类型的值来表明它的原始性或结构性；你可以在指针类型前面加上 &lt;code&gt;*&lt;/code&gt; 号（前缀）来获取指针所指向的内容，这里的 &lt;code&gt;*&lt;/code&gt; 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。&lt;/p&gt;
&lt;p&gt;当一个指针被定义后没有分配到任何变量时，它的值为 &lt;code&gt;nil&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;一个指针变量通常缩写为 &lt;code&gt;ptr&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在书写表达式类似 &lt;code&gt;var p *type&lt;/code&gt; 时，切记在 * 号和指针名称间留有一个空格，因为 &lt;code&gt;- var p*type&lt;/code&gt; 是语法正确的，但是在更复杂的表达式中，它容易被误认为是一个乘法表达式！&lt;/p&gt;
&lt;p&gt;符号 * 可以放在一个指针前，如 &lt;code&gt;*intP&lt;/code&gt;，那么它将得到这个指针指向地址上所存储的值；这被称为反引用（或者内容或者间接引用）操作符；另一种说法是指针转移。&lt;/p&gt;
&lt;p&gt;对于任何一个变量 &lt;code&gt;var&lt;/code&gt;， 如下表达式都是正确的：&lt;code&gt;var == *(&amp;amp;var)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;现在，我们应当能理解 pointer.go 的全部内容及其输出：&lt;/p&gt;
&lt;p&gt;示例 4.21 &lt;a href=&#34;examples/chapter_4/pointer.go&#34;&gt;pointer.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func main() {
	var i1 = 5
	fmt.Printf(&amp;#34;An integer: %d, its location in memory: %p\n&amp;#34;, i1, &amp;amp;i1)
	var intP *int
	intP = &amp;amp;i1
	fmt.Printf(&amp;#34;The value at memory location %p is %d\n&amp;#34;, intP, *intP)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;An integer: 5, its location in memory: 0x24f0820
The value at memory location 0x24f0820 is 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以用下图来表示内存使用的情况：&lt;/p&gt;
&lt;img src=&#34;images/4.9_fig4.4.png?raw=true&#34; style=&#34;zoom:67%;&#34; /&gt;
&lt;p&gt;程序 string_pointer.go 为我们展示了指针对 &lt;code&gt;string&lt;/code&gt; 的例子。&lt;/p&gt;
&lt;p&gt;它展示了分配一个新的值给 &lt;code&gt;*p&lt;/code&gt; 并且更改这个变量自己的值（这里是一个字符串）。&lt;/p&gt;
&lt;p&gt;示例 4.22 &lt;a href=&#34;examples/chapter_4/string_pointer.go&#34;&gt;string_pointer.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func main() {
	s := &amp;#34;good bye&amp;#34;
	var p *string = &amp;amp;s
	*p = &amp;#34;ciao&amp;#34;
	fmt.Printf(&amp;#34;Here is the pointer p: %p\n&amp;#34;, p) // prints address
	fmt.Printf(&amp;#34;Here is the string *p: %s\n&amp;#34;, *p) // prints string
	fmt.Printf(&amp;#34;Here is the string s: %s\n&amp;#34;, s) // prints same string
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Here is the pointer p: 0x2540820
Here is the string *p: ciao
Here is the string s: ciao
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过对 &lt;code&gt;*p&lt;/code&gt; 赋另一个值来更改“对象”，这样 &lt;code&gt;s&lt;/code&gt; 也会随之更改。&lt;/p&gt;
&lt;p&gt;内存示意图如下：&lt;/p&gt;
&lt;img src=&#34;images/4.9_fig4.5.png?raw=true&#34; style=&#34;zoom:67%;&#34; /&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你不能获取字面量或常量的地址，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const i = 5
ptr := &amp;amp;i //error: cannot take the address of i
ptr2 := &amp;amp;10 //error: cannot take the address of 10
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;所以说，Go 语言和 C、C++ 以及 D 语言这些低级（系统）语言一样，都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算（所谓的指针算法，如：&lt;code&gt;pointer+2&lt;/code&gt;，移动指针指向字符串的字节数或数组的某个位置）是不被允许的。Go 语言中的指针保证了内存安全，更像是 Java、C# 和 VB.NET 中的引用。&lt;/p&gt;
&lt;p&gt;因此 &lt;code&gt;p++&lt;/code&gt; 在 Go 语言的代码中是不合法的。&lt;/p&gt;
&lt;p&gt;指针的一个高级应用是你可以传递一个变量的引用（如函数的参数），这样不会传递变量的拷贝。指针传递是很廉价的，只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存，或很多变量，或者两者都有，使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中，直到没有任何指针指向它们，所以从它们被创建开始就具有相互独立的生命周期。&lt;/p&gt;
&lt;p&gt;另一方面（虽然不太可能），由于一个指针导致的间接引用（一个进程执行了另一个地址），指针的过度频繁使用也会导致性能下降。&lt;/p&gt;
&lt;p&gt;指针也可以指向另一个指针，并且可以进行任意深度的嵌套，导致你可以有多级的间接引用，但在大多数情况这会使你的代码结构不清晰。&lt;/p&gt;
&lt;p&gt;如我们所见，在大多数情况下 Go 语言可以使程序员轻松创建指针，并且隐藏间接引用，如：自动反向引用。&lt;/p&gt;
&lt;p&gt;对一个空指针的反向引用是不合法的，并且会使程序崩溃：&lt;/p&gt;
&lt;p&gt;示例 4.23 &lt;a href=&#34;examples/chapter_4/testcrash.go&#34;&gt;testcrash.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
func main() {
	var p *int = nil
	*p = 0
}
// in Windows: stops only with: &amp;lt;exit code=&amp;#34;-1073741819&amp;#34; msg=&amp;#34;process crashed&amp;#34;/&amp;gt;
// runtime error: invalid memory address or nil pointer dereference
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;问题 4.2&lt;/strong&gt; 列举 Go 语言中 &lt;code&gt;*&lt;/code&gt; 号的所有用法。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>kill命令</title>
    <link>https://blog.wiseai.cn/post/kill%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:14:28 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/kill%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;kill命令用来删除执行中的程序或工作。kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序，可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或job指令查看。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# kill (选项) (参数)&lt;/code&gt;
选项:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-a：当处理当前进程时，不限制命令名和进程号的对应关系；
-l &amp;lt;信息编号&amp;gt;：若不加&amp;lt;信息编号&amp;gt;选项，则-l参数会列出全部的信息名称；
-p：指定kill 命令只打印相关进程的进程号，而不发送任何信号；
-s &amp;lt;信息名称或编号&amp;gt;：指定要送出的信息；
-u：指定用户。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参数:&lt;/p&gt;
&lt;p&gt;进程或作业识别号：指定要删除的进程或作业。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[root@wiseai ~]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;只有第9种信号(SIGKILL)才可以无条件终止进程，其他信号进程都有权利忽略，下面是常用的信号：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;HUP     1    终端断线
INT     2    中断（同 Ctrl + C）
QUIT    3    退出（同 Ctrl + \）
TERM   15    终止
KILL    9    强制终止
CONT   18    继续（与STOP相反， fg/bg命令）
STOP   19    暂停（同 Ctrl + Z）
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>screen命令</title>
    <link>https://blog.wiseai.cn/post/screen%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:09:03 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/screen%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;h1 id=&#34;一使用&#34;&gt;一、使用&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;创建一个新的窗口
安装完成后，直接敲命令screen就可以启动它。但是这样启动的screen会话没有名字，实践上推荐为每个screen会话取一个名字，方便分辨：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -S david&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;screen启动后，会创建第一个窗口，也就是窗口No. 0，并在其中打开一个系统默认的shell，一般都会是bash。所以你敲入命令screen之后，会立刻又返回到命令提示符，仿佛什么也没有发生似的，其实你已经进入Screen的世界了。当然，也可以在screen命令之后加入你喜欢的参数，使之直接打开你指定的程序，例如：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen vi david.txt
screen创建一个执行vi david.txt的单窗口会话，退出vi 将退出该窗口/会话。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;可以使用快捷键C-a A来为当前窗口重命名&lt;/li&gt;
&lt;li&gt;暂时中断会话，在screen窗口键入C-a d，Screen会给出detached提示&lt;/li&gt;
&lt;li&gt;找到该screen会话：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -ls&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;重新连接会话：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -r 12865&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;将指定的screen作业离线&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -d&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;清除dead 会话&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -wipe&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;语法&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃screen [-AmRvx -ls -wipe][-d &amp;lt;作业名称&amp;gt;][-h &amp;lt;行数&amp;gt;][-r &amp;lt;作业名称&amp;gt;][-s ][-S &amp;lt;作业名称&amp;gt;]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;参数说明&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-A 　将所有的视窗都调整为目前终端机的大小。
-d &amp;lt;作业名称&amp;gt; 　将指定的screen作业离线。
-h &amp;lt;行数&amp;gt; 　指定视窗的缓冲区行数。
-m 　即使目前已在作业中的screen作业，仍强制建立新的screen作业。
-r &amp;lt;作业名称&amp;gt; 　恢复离线的screen作业。
-R 　先试图恢复离线的作业。若找不到离线的作业，即建立新的screen作业。
-s 　指定建立新视窗时，所要执行的shell。
-S &amp;lt;作业名称&amp;gt; 　指定screen作业的名称。
-v 　显示版本信息。
-x 　恢复之前离线的screen作业。
-ls或--list 　显示目前所有的screen作业。
-wipe 　检查目前所有的screen作业，并删除已经无法使用的screen作业。
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;常用screen参数&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt; screen -S yourname -&amp;gt; 新建一个叫yourname的session
&amp;gt; screen -ls -&amp;gt; 列出当前所有的session
&amp;gt; screen -r yourname -&amp;gt; 回到yourname这个session
&amp;gt; screen -d yourname -&amp;gt; 远程detach某个session
&amp;gt; screen -d -r yourname -&amp;gt; 结束当前session并回到yourname这个session
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;在每个screen session 下，所有命令都以 ctrl+a(C-a) 开始。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C-a ? -&amp;gt; 显示所有键绑定信息
C-a c -&amp;gt; 创建一个新的运行shell的窗口并切换到该窗口
C-a n -&amp;gt; Next，切换到下一个 window
C-a p -&amp;gt; Previous，切换到前一个 window
C-a 0..9 -&amp;gt; 切换到第 0..9 个 window
Ctrl+a [Space] -&amp;gt; 由视窗0循序切换到视窗9
C-a C-a -&amp;gt; 在两个最近使用的 window 间切换
C-a x -&amp;gt; 锁住当前的 window，需用用户密码解锁
C-a d -&amp;gt; detach，暂时离开当前session，将目前的 screen session (可能含有多个 windows) 丢到后台执行，并会回到还没进 screen 时的状态，此时在 screen session 里，每个 window 内运行的 process (无论是前台/后台)都在继续执行，即使 logout 也不影响。
C-a z -&amp;gt; 把当前session放到后台执行，用 shell 的 fg 命令则可回去。
C-a w -&amp;gt; 显示所有窗口列表
C-a t -&amp;gt; Time，显示当前时间，和系统的 load
C-a k -&amp;gt; kill window，强行关闭当前的 window
C-a [ -&amp;gt; 进入 copy mode，在 copy mode 下可以回滚、搜索、复制就像用使用 vi 一样
C-b Backward，PageUp
C-f Forward，PageDown
H(大写) High，将光标移至左上角
L Low，将光标移至左下角
0 移到行首
$ 行末
w forward one word，以字为单位往前移
b backward one word，以字为单位往后移
Space 第一次按为标记区起点，第二次按为终点
Esc 结束 copy mode
C-a ] -&amp;gt; Paste，把刚刚在 copy mode 选定的内容贴上
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;screen 高级应用&lt;/li&gt;
&lt;li&gt;会话共享
还有一种比较好玩的会话恢复，可以实现会话共享。假设你在和朋友在不同地点以相同用户登录一台机器，然后你创建一个screen会话，你朋友可以在他的终端上命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃screen -x&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;这个命令会将你朋友的终端Attach到你的Screen会话上，并且你的终端不会被Detach。这样你就可以和朋友共享同一个会话了，如果你们当前又处于同一个窗口，那就相当于坐在同一个显示器前面，你的操作会同步演示给你朋友，你朋友的操作也会同步演示给你。当然，如果你们切换到这个会话的不同窗口中去，那还是可以分别进行不同的操作的。&lt;/li&gt;
&lt;li&gt;会话锁定与解锁
Screen允许使用快捷键C-a s锁定会话。锁定以后，再进行任何输入屏幕都不会再有反应了。但是要注意虽然屏幕上看不到反应，但你的输入都会被Screen中的进程接收到。快捷键C-a q可以解锁一个会话。
也可以使用C-a x锁定会话，不同的是这样锁定之后，会话会被Screen所属用户的密码保护，需要输入密码才能继续访问这个会话。&lt;/li&gt;
&lt;li&gt;发送命令到screen会话
在一个叫做sandy的screen会话中创建一个新窗口，并在其中运行ping命令&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;＃ screen -S sandy -X screen ping 127.0.0.1&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;屏幕分割&lt;/li&gt;
&lt;li&gt;快捷键C-a S将显示器水平分割，Screen 4.00.03版本以后，也支持垂直分屏，快捷键是C-a &amp;lsquo;。分屏以后，可以使用C-a 在各个区块间切换，每一区块上都可以创建窗口并在其中运行进程。&lt;/li&gt;
&lt;li&gt;可以用C-a X快捷键关闭当前焦点所在的屏幕区块，也可以用C-a Q关闭除当前区块之外其他的所有区块。&lt;/li&gt;
&lt;li&gt;C/P模式和操作
screen的另一个很强大的功能就是可以在不同窗口之间进行复制粘贴了。使用快捷键C-a 或者C-a [可以进入copy/paste模式，这个模式下可以像在vi中一样移动光标，并可以使用空格键设置标记。其实在这个模式下有很多类似vi的操作，譬如使用/进行搜索，使用y快速标记一行，使用w快速标记一个单词等。关于C/P模式下的高级操作，其文档的这一部分有比较详细的说明。
一般情况下，可以移动光标到指定位置，按下空格设置一个开头标记，然后移动光标到结尾位置，按下空格设置第二个标记，同时会将两个标记之间的部分储存在copy/paste buffer中，并退出copy/paste模式。在正常模式下，可以使用快捷键C-a ]将储存在buffer中的内容粘贴到当前窗口。&lt;/li&gt;
&lt;li&gt;更多screen功能
同大多数UNIX程序一样，GNU Screen提供了丰富强大的定制功能。你可以在Screen的默认两级配置文件/etc/screenrc和$HOME/.screenrc中指定更多，例如设定screen选项，定制绑定键，设定screen会话自启动窗口，启用多用户模式，定制用户访问权限控制等等。如果你愿意的话，也可以自己指定screen配置文件。
以多用户功能为例，screen默认是以单用户模式运行的，你需要在配置文件中指定multiuser on 来打开多用户模式，通过acl*（acladd,acldel,aclchg&amp;hellip;）命令，你可以灵活配置其他用户访问你的screen会话。更多配置文件内容请参考screen的man页。&lt;/li&gt;
&lt;/ul&gt;

    </description>
    </item>
    
    <item>
    <title>netstat命令</title>
    <link>https://blog.wiseai.cn/post/netstat%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 17:01:28 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/netstat%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;h1 id=&#34;简介&#34;&gt;&lt;strong&gt;简介&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;Netstat 命令用于显示各种网络相关信息，如网络连接，路由表，接口状态 (Interface Statistics)，masquerade 连接，多播成员 (Multicast Memberships) 等等。&lt;/p&gt;
&lt;h1 id=&#34;输出信息含义&#34;&gt;&lt;strong&gt;输出信息含义&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;执行netstat后，其输出结果为&lt;/p&gt;
&lt;pre&gt;Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 2 210.34.6.89:telnet 210.34.6.96:2873 ESTABLISHED
tcp 296 0 210.34.6.89:1165 210.34.6.84:netbios-ssn ESTABLISHED
tcp 0 0 localhost.localdom:9001 localhost.localdom:1162 ESTABLISHED
tcp 0 0 localhost.localdom:1162 localhost.localdom:9001 ESTABLISHED
tcp 0 80 210.34.6.89:1161 210.34.6.10:netbios-ssn CLOSE

Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path
unix 1 [ ] STREAM CONNECTED 16178 @000000dd
unix 1 [ ] STREAM CONNECTED 16176 @000000dc
unix 9 [ ] DGRAM 5292 /dev/log
unix 1 [ ] STREAM CONNECTED 16182 @000000df&lt;/pre&gt;
&lt;p&gt;从整体上看，netstat的输出结果可以分为两个部分：&lt;/p&gt;
&lt;p&gt;一个是Active Internet connections，称为有源TCP连接，其中&amp;quot;Recv-Q&amp;quot;和&amp;quot;Send-Q&amp;quot;指%0A的是接收队列和发送队列。这些数字一般都应该是0。如果不是则表示软件包正在队列中堆积。这种情况只能在非常少的情况见到。&lt;/p&gt;
&lt;p&gt;另一个是Active UNIX domain sockets，称为有源Unix域套接口(和网络套接字一样，但是只能用于本机通信，性能可以提高一倍)。
Proto显示连接使用的协议,RefCnt表示连接到本套接口上的进程号,Types显示套接口的类型,State显示套接口当前的状态,Path表示连接到套接口的其它进程使用的路径名。&lt;/p&gt;
&lt;h1 id=&#34;常见参数&#34;&gt;&lt;strong&gt;常见参数&lt;/strong&gt;&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;-a (all)显示所有选项，默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名，能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服务状态

-p 显示建立相关链接的程序名
-r 显示路由信息，路由表
-e 显示扩展信息，例如uid等
-s 按各个协议进行统计
-c 每隔一个固定时间，执行该netstat命令。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提示：LISTEN和LISTENING的状态只有用-a或者-l才能看到&lt;/p&gt;
&lt;h1 id=&#34;实用命令实例&#34;&gt;&lt;strong&gt;实用命令实例&lt;/strong&gt;&lt;/h1&gt;
&lt;h2 id=&#34;1-列出所有端口-包括监听和未监听的&#34;&gt;&lt;strong&gt;1. 列出所有端口 (包括监听和未监听的)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;列出所有端口 netstat -a&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -a &#39; more
 Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        0      0 localhost:30037         *:*                     LISTEN
 udp        0      0 *:bootpc                *:*

Active UNIX domain sockets (servers and established)
 Proto RefCnt Flags       Type       State         I-Node   Path
 unix  2      [ ACC ]     STREAM     LISTENING     6135     /tmp/.X11-unix/X0
 unix  2      [ ACC ]     STREAM     LISTENING     5140     /var/run/acpid.socket&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;列出所有 tcp 端口 netstat -at&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -at
 Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        0      0 localhost:30037         *:*                     LISTEN
 tcp        0      0 localhost:ipp           *:*                     LISTEN
 tcp        0      0 *:smtp                  *:*                     LISTEN
 tcp6       0      0 localhost:ipp           [::]:*                  LISTEN&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;列出所有 udp 端口 netstat -au&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -au
 Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 udp        0      0 *:bootpc                *:*
 udp        0      0 *:49119                 *:*
 udp        0      0 *:mdns                  *:*&lt;/pre&gt;
&lt;h2 id=&#34;2-列出所有处于监听状态的-sockets&#34;&gt;&lt;strong&gt;2. 列出所有处于监听状态的 Sockets&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;只显示监听端口 netstat -l&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -l
 Active Internet connections (only servers)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        0      0 localhost:ipp           *:*                     LISTEN
 tcp6       0      0 localhost:ipp           [::]:*                  LISTEN
 udp        0      0 *:49119                 *:*&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;只列出所有监听 tcp 端口 netstat -lt&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -lt
 Active Internet connections (only servers)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        0      0 localhost:30037         *:*                     LISTEN
 tcp        0      0 *:smtp                  *:*                     LISTEN
 tcp6       0      0 localhost:ipp           [::]:*                  LISTEN&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;只列出所有监听 udp 端口 netstat -lu&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -lu
 Active Internet connections (only servers)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 udp        0      0 *:49119                 *:*
 udp        0      0 *:mdns                  *:*&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;只列出所有监听 UNIX 端口 netstat -lx&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -lx
 Active UNIX domain sockets (only servers)
 Proto RefCnt Flags       Type       State         I-Node   Path
 unix  2      [ ACC ]     STREAM     LISTENING     6294     private/maildrop
 unix  2      [ ACC ]     STREAM     LISTENING     6203     public/cleanup
 unix  2      [ ACC ]     STREAM     LISTENING     6302     private/ifmail
 unix  2      [ ACC ]     STREAM     LISTENING     6306     private/bsmtp&lt;/pre&gt;
&lt;h2 id=&#34;3-显示每个协议的统计信息&#34;&gt;&lt;strong&gt;3. 显示每个协议的统计信息&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;**  显示所有端口的统计信息 netstat -s**&lt;/p&gt;
&lt;pre&gt;# netstat -s
 Ip: 11150 total packets received 1 with invalid addresses 0 forwarded 0 incoming packets discarded 11149 incoming packets delivered 11635 requests sent out
 Icmp: 0 ICMP messages received 0 input ICMP message failed.
 Tcp: 582 active connections openings 2 failed connection attempts 25 connection resets received
 Udp: 1183 packets received 4 packets to unknown port received.
 .....&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;显示 TCP 或 UDP 端口的统计信息 netstat -st 或 -su&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -st 
# netstat -su&lt;/pre&gt;
&lt;h2 id=&#34;4-在-netstat-输出中显示-pid-和进程名称-netstat--p&#34;&gt;&lt;strong&gt;4. 在 netstat 输出中显示 PID 和进程名称 netstat -p&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;netstat -p 可以与其它开关一起使用，就可以添加 &amp;ldquo;PID/进程名称&amp;rdquo; 到 netstat 输出中，这样 debugging 的时候可以很方便的发现特定端口运行的程序。&lt;/p&gt;
&lt;pre&gt;# netstat -pt
 Active Internet connections (w/o servers)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
 tcp        1      0 ramesh-laptop.loc:47212 192.168.185.75:www        CLOSE_WAIT  2109/firefox
 tcp        0      0 ramesh-laptop.loc:52750 lax:www ESTABLISHED 2109/firefox&lt;/pre&gt;
&lt;h2 id=&#34;5-在-netstat-输出中不显示主机端口和用户名-host-port-or-user&#34;&gt;&lt;strong&gt;5. 在 netstat 输出中不显示主机，端口和用户名 (host, port or user)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;当你不想让主机，端口和用户名显示，使用 netstat -n。将会使用数字代替那些名称。&lt;/p&gt;
&lt;p&gt;同样可以加速输出，因为不用进行比对查询。&lt;/p&gt;
&lt;pre&gt;# netstat -an&lt;/pre&gt;
&lt;p&gt;如果只是不想让这三个名称中的一个被显示，使用以下命令&lt;/p&gt;
&lt;pre&gt;# netsat -a --numeric-ports
# netsat -a --numeric-hosts
# netsat -a --numeric-users&lt;/pre&gt;
&lt;h2 id=&#34;6-持续输出-netstat-信息&#34;&gt;&lt;strong&gt;6. 持续输出 netstat 信息&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;netstat 将每隔一秒输出网络信息。&lt;/p&gt;
&lt;pre&gt;# netstat -c
 Active Internet connections (w/o servers)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State
 tcp        0      0 ramesh-laptop.loc:36130 101-101-181-225.ama:www ESTABLISHED
 tcp        1      1 ramesh-laptop.loc:52564 101.11.169.230:www      CLOSING
 tcp        0      0 ramesh-laptop.loc:43758 server-101-101-43-2:www ESTABLISHED
 tcp        1      1 ramesh-laptop.loc:42367 101.101.34.101:www      CLOSING
 ^C&lt;/pre&gt;
&lt;h2 id=&#34;7-显示系统不支持的地址族-address-families&#34;&gt;&lt;strong&gt;7. 显示系统不支持的地址族 (Address Families)&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;netstat --verbose&lt;/pre&gt;
&lt;p&gt;在输出的末尾，会有如下的信息&lt;/p&gt;
&lt;pre&gt;netstat: no support for `AF IPX&#39; on this system.netstat: no support for `AF AX25&#39; on this system.netstat: no support for `AF X25&#39; on this system.netstat: no support for `AF NETROM&#39; on this system.&lt;/pre&gt;
&lt;h2 id=&#34;8-显示核心路由信息-netstat--r&#34;&gt;&lt;strong&gt;8. 显示核心路由信息 netstat -r&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;# netstat -r
 Kernel IP routing table
 Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface 192.168.1.0     *               255.255.255.0   U         0 0          0 eth2
 link-local      *               255.255.0.0     U         0 0          0 eth2 default         192.168.1.1     0.0.0.0         UG        0 0          0 eth2&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt; 使用 netstat -rn 显示数字格式，不查询主机名称。&lt;/p&gt;
&lt;h2 id=&#34;9-找出程序运行的端口&#34;&gt;&lt;strong&gt;9. 找出程序运行的端口&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;并不是所有的进程都能找到，没有权限的会不显示，使用 root 权限查看所有的信息。&lt;/p&gt;
&lt;pre&gt;# netstat -ap &#39; grep ssh
 tcp        1      0 dev-db:ssh           101.174.100.22:39213        CLOSE_WAIT  -
 tcp        1      0 dev-db:ssh           101.174.100.22:57643        CLOSE_WAIT  -&lt;/pre&gt;
&lt;p&gt;**  找出运行在指定端口的进程**&lt;/p&gt;
&lt;pre&gt;# netstat -an &#39; grep &#39;:80&#39;&lt;/pre&gt;
&lt;h2 id=&#34;10-显示网络接口列表&#34;&gt;&lt;strong&gt;10. 显示网络接口列表&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;# netstat -i
 Kernel Interface table
 Iface   MTU Met   RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
 eth0       1500 0         0      0      0 0             0      0      0      0 BMU
 eth2       1500 0     26196      0      0 0         26883      6      0      0 BMRU
 lo        16436 0         4      0      0 0             4      0      0      0 LRU&lt;/pre&gt;
&lt;p&gt;显示详细信息，像是 ifconfig 使用 netstat -ie:&lt;/p&gt;
&lt;pre&gt;# netstat -ie
 Kernel Interface table
 eth0      Link encap:Ethernet  HWaddr 00:10:40:11:11:11
 UP BROADCAST MULTICAST  MTU:1500  Metric:1
 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
 collisions:0 txqueuelen:1000
 RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
 Memory:f6ae0000-f6b00000&lt;/pre&gt;
&lt;h2 id=&#34;11-ip和tcp分析&#34;&gt;&lt;strong&gt;11. IP和TCP分析&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;查看连接某服务端口最多的的IP地址&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;#netstat -nat &#39; grep &#34;192.168.1.15:22&#34; &#39;awk &#39;{print $5}&#39;&#39;awk -F: &#39;{print $1}&#39;&#39;sort&#39;uniq -c&#39;sort -nr&#39;head -2018 221.136.168.363 154.74.45.2422 78.173.31.2362 62.183.207.982 192.168.1.142 182.48.111.2152 124.193.219.342 119.145.41.22 114.255.41.301 75.102.11.99&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;TCP各种状态列表&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;# netstat -nat &#39;awk &#39;{print $6}&#39;established)
Foreign
LISTEN
TIME_WAIT
ESTABLISHED
TIME_WAIT
SYN_SENT&lt;/pre&gt;
&lt;p&gt;先把状态全都取出来,然后使用uniq -c统计，之后再进行排序。&lt;/p&gt;
&lt;pre&gt;# netstat -nat &#39;awk &#39;{print $6}&#39;&#39;sort&#39;uniq -c143 ESTABLISHED1 FIN_WAIT11 Foreign1 LAST_ACK36 LISTEN6 SYN_SENT113 TIME_WAIT1 established)&lt;/pre&gt;
&lt;p&gt;最后的命令如下:&lt;/p&gt;
&lt;pre&gt;# netstat -nat &#39;awk &#39;{print $6}&#39;&#39;sort&#39;uniq -c&#39;sort -rn&lt;/pre&gt;
&lt;p&gt;分析access.log获得访问前10位的ip地址&lt;/p&gt;
&lt;pre&gt;awk &#39;{print $1}&#39; access.log &#39;sort&#39;uniq -c&#39;sort -nr&#39;head -10&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Debian的包管理工具apt</title>
    <link>https://blog.wiseai.cn/post/debian%E7%9A%84%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7/</link>
    <pubDate>Wed, 06 Jul 2022 16:52:16 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/debian%E7%9A%84%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7/</guid>
    <description>
        &lt;p&gt;清除所有已删除包的残余配置文件：&lt;br&gt;
&lt;code&gt;#dpkg -l |grep ^rc|awk &#39;{print $2}&#39; |xargs dpkg -P&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dpkg -l |grep &amp;quot;^rc&amp;quot;|awk &#39;{print $2}&#39; |xargs apt -y purge&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;说明:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dpkg -l&lt;br&gt;
列出系统中安装的所有包的状态，ii开头的是正常安装的包，rc开头的则是删除但仍留下配置文件的包，其他状态则是有错误的状态，自己执行一次就明白了，输出结果的开头有说明的。&lt;/li&gt;
&lt;li&gt;grep &amp;ldquo;^rc&amp;rdquo;&lt;br&gt;
提取以 rc 开头的包，也就是被删除但仍残留配置文件的包的信息的行。&lt;/li&gt;
&lt;li&gt;awk &amp;lsquo;{print $2}&amp;rsquo;&lt;br&gt;
打印这些包的名字，他们位于 dpkg -l 输出结果的第二个字段，估计有很多人是通过这个命令认识到 awk 的，也有很多人只在这个命令行中才使用 awk，我就是其中之一。&lt;/li&gt;
&lt;li&gt;xargs apt -y purge&lt;br&gt;
把上述输出，也就是要清除配置文件的包的名字放在 apt -y purge 后面，purge命令会清除配置文件，而 -y 参数会自动对后面 apt 命令的提示回答是，这个开关通常是危险的，所以在一般情况下不要轻易使用，而在这里，如果你确定这些包的配置文件的清除是没有问题的的话，可以使用这个开关&lt;/li&gt;
&lt;/ul&gt;

    </description>
    </item>
    
    <item>
    <title>diff、cmp命令</title>
    <link>https://blog.wiseai.cn/post/diffcmp%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 16:46:27 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/diffcmp%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;diff以行为单位对比：&lt;/p&gt;
&lt;pre&gt;$ diff [-bBi] from-file to-file
选项与参数：
from-file ：一个档名，作为原始比对档案的档名；
to-file ：一个档名，作为目的比对档案的档名；
注意，from-file 或to-file 可以- 取代，那个- 代表『Standard input』之意。

-b ：忽略一行当中，仅有多个空白的差异(例如&#34;about me&#34; 与&#34;about me&#34; 视为相同
-B ：忽略空白行的差异。
-i ：忽略大小写的不同。&lt;/pre&gt;
&lt;p&gt;cmp利用『位元组』单位去比对：&lt;/p&gt;
&lt;pre&gt;$ cmp [-l] file1 file2
选项与参数：
-l ：将所有的不同点的位元组处都列出来。因为cmp 预设仅会输出第一个发现的不同点。&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Docker容器设置ssh连接</title>
    <link>https://blog.wiseai.cn/post/docker%E5%AE%B9%E5%99%A8%E8%AE%BE%E7%BD%AEssh%E8%BF%9E%E6%8E%A5/</link>
    <pubDate>Wed, 06 Jul 2022 16:36:58 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/docker%E5%AE%B9%E5%99%A8%E8%AE%BE%E7%BD%AEssh%E8%BF%9E%E6%8E%A5/</guid>
    <description>
        &lt;p&gt;这里以debian容器为例：&lt;/p&gt;
&lt;h2 id=&#34;一进入容器更改更新源及安装openssh-server&#34;&gt;一、进入容器、更改更新源及安装openssh-server&lt;/h2&gt;
&lt;pre class=&#34;editor-colors lang-text&#34;&gt;&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# docker exec -it debian /bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# sed -i -e &#39;s/deb.debian.org/mirrors.aliyun.com/g&#39; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;         &lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;-e &#39;s/security.debian.org/mirrors.aliyun.com/g&#39; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;         /etc/apt/sources.list&lt;/span&gt;&lt;/span&gt;
&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# apt install openssh-server&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;h2 id=&#34;二添加目录修改配置文件&#34;&gt;二、添加目录、修改配置文件&lt;/h2&gt;
&lt;pre class=&#34;editor-colors lang-text&#34;&gt;&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# mkdir -p /run/sshd&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;注：(这个地方看启动服务时的提示信息，运行sshd服务的命令只能使用绝对路径/usr/sbin/sshd -D)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下面修改/etc/ssh/sshd_config
先说下PermitRootLogin：prohibit-password 允许root登陆但不能使用密码登陆yes 允许root以任何方式登陆,我们这里只允许使用密钥无密码登陆：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;editor-colors lang-text&#34;&gt;&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# sed -i &#39;s/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/g&#39; /etc/ssh/sshd_config&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;下面修改/etc/pam.d/sshd&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&#34;editor-colors lang-text&#34;&gt;&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# sed -i &#39;s/session .*required .*pam_loginuid.so/#session required pam_loginuid.so/g&#39; /etc/pam.d/sshd&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;注：这个是一个pam登陆控制，可以参考&lt;a href=&#34;https://blog.wiseai.cn/post/ftp%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E8%AE%BEvsftpd/&#34;&gt;ftp服务器设置&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;三添加ssh服务使服务在容器打开时就启动&#34;&gt;三、添加ssh服务，使服务在容器打开时就启动：&lt;/h2&gt;
&lt;pre class=&#34;editor-colors lang-text&#34;&gt;&lt;span class=&#34;syntax--text syntax--plain&#34;&gt;&lt;span class=&#34;syntax--meta syntax--paragraph syntax--text&#34;&gt;# sed -i &#39;$a /usr/sbin/sshd -D&#39; /etc/bash.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!-- wp:heading {&#34;level&#34;:4} --&gt;
&lt;h4 id=&#34;centos容器中遇到的问题&#34;&gt;centos容器中遇到的问题：&lt;/h4&gt;
&lt;p&gt;显示/etc/ssh/中缺少公钥和私钥：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh-keygen -t rsa -N &amp;#39;&amp;#39; -f /etc/ssh/ sh_host_rsa_key  
$ ssh-keygen -t ed25519 -N &amp;#39;&amp;#39; -f /etc/ssh/ssh_host_ed25519_key  
$ ssh-keygen -t ecdsa -N &amp;#39;&amp;#39; -f /etc/ssh/ssh_host_ecdsa_key
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>profile、bashrc、bash_profile详解</title>
    <link>https://blog.wiseai.cn/post/profilebashrcbash_profile%E8%AF%A6%E8%A7%A3/</link>
    <pubDate>Wed, 06 Jul 2022 16:20:56 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/profilebashrcbash_profile%E8%AF%A6%E8%A7%A3/</guid>
    <description>
        &lt;p&gt;&lt;strong&gt;/etc/profile:&lt;/strong&gt; 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置.&lt;/p&gt;
&lt;p&gt;英文描述为：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases [Go](http://lib.csdn.net/base/go &amp;#34;Go知识库&amp;#34;) in /etc/bashrc

# It&amp;#39;s NOT a good idea to change this file unless you know what you
# are doing. It&amp;#39;s much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;此修改对每个用户都生效。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;/etc/bashrc:&lt;/strong&gt; 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取.&lt;/p&gt;
&lt;p&gt;英文描述为：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# It&amp;#39;s NOT a good idea to change this file unless you know what you
# are doing. It&amp;#39;s much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你想对所有的使用bash的用户修改某个配置并在以后打开的bash都生效的话可以修改这个文件，修改这个文件不用重启，重新打开一个bash即可生效。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;~/.bash_profile:&lt;/strong&gt; 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的 &lt;strong&gt;.bashrc&lt;/strong&gt; 文件.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此文件类似于 &lt;strong&gt;/etc/profile&lt;/strong&gt; ，&lt;strong&gt;/etc/profile&lt;/strong&gt;对所有用户生效，&lt;strong&gt;~/.bash_profile&lt;/strong&gt;只对当前用户生效。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;~/.bashrc:&lt;/strong&gt; 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该文件被读取.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此文件类似于 &lt;strong&gt;/etc/bashrc&lt;/strong&gt;，不需要重启生效，重新打开一个bash即可生效，  /etc/bashrc对所有用户新打开的bash都生效，但 &lt;strong&gt;~/.bashrc&lt;/strong&gt;只对当前用户新打开的bash生效。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;~/.bash_logout:当每次退出系统(退出bash shell)时,执行该文件.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而 &lt;strong&gt;~/.bashrc&lt;/strong&gt; 等中设定的变量(局部)只能继承 &lt;strong&gt;/etc/profile&lt;/strong&gt; 中的变量,他们是&amp;quot;父子&amp;quot;关系.&lt;/p&gt;
&lt;p&gt;~/.bash_profile 是交互式、login 方式进入bash 运行的；
~/ &lt;strong&gt;.bashrc&lt;/strong&gt; 是交互式 non-login 方式进入bash 运行的；
通常二者设置大致相同，所以通常前者会调用后者。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>sed和awk命令</title>
    <link>https://blog.wiseai.cn/post/sed%E5%92%8Cawk%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 16:03:36 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/sed%E5%92%8Cawk%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;&lt;code&gt;# sed [-nefr] [动作]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选项与参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-n ：使用安静(silent)模式。在一般sed 的用法中，所有来自STDIN 的资料一般都会被列出到屏幕上。
      但如果加上-n 参数后，则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e ：直接在指令列模式上进行sed 的动作编辑；
-f ：直接将sed 的动作写在一个档案内， -f filename 则可以执行filename 内的sed 动作；
-r ：sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i ：直接修改读取的档案内容，而不是由屏幕输出。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;动作说明： &lt;code&gt;[n1[,n2]]function&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;n1, n2 ：不见得会存在，一般代表『选择进行动作的行数』，举例来说，如果我的动作
         是需要在10 到20 行之间进行的，则『 10,20[动作行为] 』

function 有底下这些咚咚：
a ：新增， a 的后面可以接字串，而这些字串会在新的一行出现(目前的下一行)～
c ：取代， c 的后面可以接字串，这些字串可以取代n1,n2 之间的行！
d ：删除，因为是删除啊，所以d后面通常不接任何咚咚；
i ：插入， i 的后面可以接字串，而这些字串会在新的一行出现(目前的上一行)；
p ：列印，亦即将某个选择的资料印出。通常p 会与参数sed -n 一起运作～
s ：取代，可以直接进行取代的工作！通常这个s的动作可以搭配正规表示法！
         例如1,20s/old/new/g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例子：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sed &amp;#39;2,5d&amp;#39;  删除2-5行
sed &amp;#39;2a abc&amp;#39;  在第二行后添加abc
sed &amp;#39;2,5c abc&amp;#39; 将2-5行替换为abc
sed -n &amp;#39;2,5p&amp;#39;  打印2-5行
sed &amp;#39;s/word1/word2/g&amp;#39;  将word1替换为word2
sed -i  直接修改文档内容
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;# cat /etc/passwd | sed -e &#39;4d&#39; -e &#39;6c no six line&#39; &amp;gt; passwd.new&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;注：sed后面如果要接超过两个以上的动作时，每个动作前面得加-e才行！&lt;/p&gt;
&lt;pre&gt;$ printf &#39;列印格式&#39;实际内容
选项与参数：
关于格式方面的几个特殊样式：
       \a 警告声音输出
       \b 倒退键(backspace)
       \f 清除屏幕(form feed)
       \n 输出新的一行
       \r 亦即Enter 按键
       \t 水平的[tab] 按键
       \v 垂直的[tab] 按键
       \xNN NN 为两位数的数字，可以转换数字成为字元。
关于C 程式语言内，常见的变数格式
       %ns 那个n 是数字， s 代表string ，亦即多少个字元；
       %ni 那个n 是数字， i 代表integer ，亦即多少整数位数；
       %N.nf 那个n 与N 都是数字， f 代表floating (浮点)，如果有小数位数，
             假设我共要十个位数，但小数点有两位，即为%10.2f 啰！
例子：
printf &#39;%10s %5i %5i %5i %8.2f \n&#39; $(cat printf.txt &#39; grep -v Name)&lt;/pre&gt;
&lt;pre&gt;$ &lt;span class=&#34;term_command&#34;&gt;awk &#39;条件类型1{动作1}条件类型2{动作2} ...&#39; filename&lt;/span&gt;&lt;/pre&gt;
&lt;table class=&#34;news&#34;&gt;
&lt;tbody&gt;
&lt;tr class=&#34;theader&#34;&gt;
&lt;td&gt;变数名称&lt;/td&gt;
&lt;td&gt;代表意义&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;tcenter&#34;&gt;NF&lt;/td&gt;
&lt;td&gt;每一行($0) 拥有的栏位总数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;tcenter&#34;&gt;NR&lt;/td&gt;
&lt;td&gt;目前awk 所处理的是『第几行』资料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&#34;tcenter&#34;&gt;FS&lt;/td&gt;
&lt;td&gt;目前的分隔字元，预设是空白键&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;$ &lt;span class=&#34;term_command&#34;&gt;cat /etc/passwd | awk &#39;BEGIN {FS=&#34;:&#34;} $3 &lt; 10 {print $1 &#34;\t &#34; $3}&#39;&lt;/span&gt; 
这个意思是列出UID&lt;10的所有用户名和UID&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;Name 1st 2nd 3th
VBird 23000 24000 25000
DMTsai 21000 20000 23000
Bird2 43000 42000 41000&lt;/pre&gt;
将这个存为文件a.txt
&lt;pre&gt;$ &lt;span class=&#34;term_command&#34;&gt;cat a.txt | \&lt;/span&gt; 
&gt; &lt;span class=&#34;term_command&#34;&gt;awk &#39;NR==1{printf &#34;%10s %10s %10s %10s %10s\n&#34;,$1,$2,$3,$4,&#34;Total&#34; }&lt;/span&gt; 
&gt; &lt;span class=&#34;term_command&#34;&gt;NR&gt;=2{total = $2 + $3 + $4&lt;/span&gt;          ＃这儿如果要存为一行，需要加；
&gt; &lt;span class=&#34;term_command&#34;&gt;printf &#34;%10s %10d %10d %10d %10.2f\n&#34;, $1, $2, $3, $4, total}&#39;
这样就可以格式化并计算出总成绩
&lt;/span&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>sftp命令</title>
    <link>https://blog.wiseai.cn/post/sftp%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 06 Jul 2022 15:57:22 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/sftp%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;h2 id=&#34;常用方式&#34;&gt;常用方式&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;格式：sftp &amp;lt;host&amp;gt;

通过sftp连接&amp;lt;host&amp;gt;，端口为默认的22，用户为Linux当前登录用户。

格式：sftp -oPort=&amp;lt;port&amp;gt; &amp;lt;host&amp;gt;

通过sftp连接&amp;lt;host&amp;gt;，指定端口&amp;lt;port&amp;gt;，用户为Linux当前登录用户。

格式：sftp &amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;

通过sftp连接&amp;lt;host&amp;gt;，端口为默认的22，指定用户&amp;lt;user&amp;gt;。

格式：sftp -oPort=&amp;lt;port&amp;gt; &amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;

通过sftp连接&amp;lt;host&amp;gt;，端口为&amp;lt;port&amp;gt;，用户为&amp;lt;user&amp;gt;。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;sftp连接成功之后常用操作命令如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;help/? 打印帮助信息。

pwd   查看远程服务器当前目录；

lpwd  查看本地系统的当前目录。

cd &amp;lt;dir&amp;gt;   将远程服务器的当前目录更改为&amp;lt;dir&amp;gt;；

lcd &amp;lt;dir&amp;gt;  将本地系统的当前目录更改为&amp;lt;dir&amp;gt;。

ls 显示远程服务器上当前目录的文件名；

ls -l  显示远程服务器上当前目录的文件详细列表；

ls &amp;lt;pattern&amp;gt; 显示远程服务器上符合指定模式&amp;lt;pattern&amp;gt;的文件名；

ls -l &amp;lt;pattern&amp;gt;  显示远程服务器上符合指定模式&amp;lt;pattern&amp;gt;的文件详细列表。

lls 显示本地系统上当前目录的文件名；

lls的其他参数与ls命令的类似。

get &amp;lt;file&amp;gt; 下载指定文件&amp;lt;file&amp;gt;； -r参数操作目录

get &amp;lt;pattern&amp;gt; 下载符合指定模式&amp;lt;pattern&amp;gt;的文件。-r参数操作目录

put &amp;lt;file&amp;gt; 上传指定文件&amp;lt;file&amp;gt;；-r参数操作目录

put &amp;lt;pattern&amp;gt; 上传符合指定模式&amp;lt;pattern&amp;gt;的文件。-r参数操作目录

progress 切换是否显示文件传输进度。

mkdir &amp;lt;dir&amp;gt; 在远程服务器上创建目录；

lmkdir &amp;lt;dir&amp;gt; 在本地系统上创建目录。

exit/quit/bye 退出sftp。

! 启动一个本地shell。

! &amp;lt;commandline&amp;gt; 执行本地命令行。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其他命令还有：chgrp, chmod, chown, ln, lumask, rename, rm, rmdir, symlink, version。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>ssh免密码登陆</title>
    <link>https://blog.wiseai.cn/post/ssh%E5%85%8D%E5%AF%86%E7%A0%81%E7%99%BB%E9%99%86/</link>
    <pubDate>Wed, 06 Jul 2022 15:49:33 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/ssh%E5%85%8D%E5%AF%86%E7%A0%81%E7%99%BB%E9%99%86/</guid>
    <description>
        &lt;ol&gt;
&lt;li&gt;在client生成公钥和私钥：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;＃ ssh-keygen -t rsa&lt;/pre&gt;
&lt;p&gt;注:这个命令可以参考下&lt;a href=&#34;https://blog.wiseai.cn/post/git/&#34;&gt;git的使用&lt;/a&gt;，rsa也没有问题。&lt;/p&gt;
&lt;p&gt;位置在&lt;code&gt;~/.ssh/&lt;/code&gt;下，分别是id_rsa和id_rsa.pub&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;将client的公钥上传到server：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;＃ ssh-copy-id -p &#39;端口&#39; &#39;用户名@IP或域名&#39;&lt;/pre&gt;
&lt;p&gt;这个时候client的公钥文件内容会追加写入到server的 .ssh/authorized_keys 文件中。&lt;/p&gt;
&lt;p&gt;登陆免密了：&lt;/p&gt;
&lt;pre&gt;＃ ssh -p &#39;端口&#39; &#39;用户名@IP或域名&#39;&lt;/pre&gt;
&lt;p&gt;打完，收功。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Golang生成随机字符串</title>
    <link>https://blog.wiseai.cn/post/golang%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/</link>
    <pubDate>Wed, 06 Jul 2022 08:54:04 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang%E7%94%9F%E6%88%90%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2/</guid>
    <description>
        &lt;p&gt;假如我们要生成一个固定长度的随机字符串，包含大小写字母，没有数字，没有特殊字符串，那么我们怎么做呢？需要怎样优化，才会更简单，更高效？在最终的方案之前，我们看看最常见的写法是怎样的，然后是如何一步步演进到最终的高效率方案的。好吧，先看下最原始的方案。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常见做法(Runes)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func init() {
	rand.Seed(time.Now().UnixNano())
}

var letterRunes = []rune(&amp;#34;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#34;)

func RandStringRunes(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个实现比较简单，二十六字母（大小写），然后随机取数，获得随机字符串。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bytes改进&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们在最开始的时候进行了假设，我们的随机字符串只包含大小写字母，这样的话，我们发现没有必要使用rune类型存储，因为在Golang（Go语言）UTF-8编码下，英文字母和byte字节是一对一的。byte的本质是uint8类型，而rune本质是int32类型。我们改进后的代码如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const letterBytes = &amp;#34;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#34;

func RandStringBytes(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;仔细看上面的代码，我们不光对rune类型进行了改进，还把原来的letter变量变成了常量，这样len(letterBytes)也是一个常量，代码的效率将大大提升。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;余数改进&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们前面的方案都是通过调用rand.Intn()生成的随机字符，这个rand.Intn()其实是委托调用的Rand.Intn(),而Rand.Intn()最终又是调用的Rand.Int31n()实现。相比我们直接调用rand.Int63()来说，rand.Intn()要慢很多。&lt;/p&gt;
&lt;p&gt;所以我们可以把rand.Intn()换成rand.Int63()来提高效率，为了不超过letterBytes的索引范围，我们使用余数来保证。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func RandStringBytesRmndr(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
	}
	return string(b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种方式虽然快，但是有个缺点，就是每个字母的概率可能会不一样，不过52个字母相比1&amp;laquo;63-1是在太小太小，所以在这种情况下，这个缺点可以忽略不计。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Masking 掩码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基于前面的方案，我们可以进一步改进，使用随机数的最低位保证字母的均等分配，也就是掩码的方式。我们现在有52个字母，52用二进制表示就是52==110100b，所以我们可以只使用rand.Int63()返回最低的6位数就可以。为了保证平均分配，如果返回的只大于len(letterBytes)-1，则舍弃不用。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const letterBytes = &amp;#34;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#34;
const (
	letterIdxBits = 6 // 6 bits to represent a letter index
	letterIdxMask = 1&amp;lt;&amp;lt;letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
	)

	func RandStringBytesMask(n int) string {
		b := make([]byte, n)
		for i := 0; i &amp;lt; n; {
			if idx := int(rand.Int63() &amp;amp; letterIdxMask); idx &amp;lt; len(letterBytes) {
				b[i] = letterBytes[idx]
				i++
			}
		}
		return string(b)
	}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;按照作者的推测，在52个字母的情况下，随机到超过范围的可能性(64-52)/64 = 0.19,按上面的代码，如果超过范围会重复生成，重复的10次的概率仅有1e-8。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Masking 掩码改进&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上一步的方案，我们使用rand.Int63()可以生成63个随机位的数，但是我们只用了最低位的6个，有点浪费，因为获取随机数是我们整个代码中最慢的部分。现在我们有52个字母，意味着6位编码字母索引即可满足，所以我们使用rand.Int63()生成的随机数可以被我们使用63/6=10次。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const letterBytes = &amp;#34;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ&amp;#34;
const (
	letterIdxBits = 6 // 6 bits to represent a letter index
	letterIdxMask = 1&amp;lt;&amp;lt;letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
	letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
	)

	func RandStringBytesMaskImpr(n int) string {
		b := make([]byte, n)
		// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
		for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i &amp;gt;= 0; {
			if remain == 0 {
				cache, remain = rand.Int63(), letterIdxMax
			}
			if idx := int(cache &amp;amp; letterIdxMask); idx &amp;lt; len(letterBytes) {
				b[i] = letterBytes[idx]
				i--
			}
			cache &amp;gt;&amp;gt;= letterIdxBits
			remain--
		}

		return string(b)
	}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;把生成的63位的随机数，分成10部分，每一部分都可以被我们使用，这样我们调用rand.Int63()次数将大大降低，进而提升效率。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rand Source 优化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;rand.Rand其实是使用了一个rand.Source作为生成随机数的源，这个rand.Source是个接口，正好有个func Int63() int64 方法。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// A Source represents a source of uniformly-distributed
// pseudo-random int64 values in the range [0, 1&amp;lt;&amp;lt;63).
type Source interface {
Int63() int64
Seed(seed int64)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这正好是我们需要的，也够我们用了。改进后代码如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytesMaskImprSrc(n int) string {
	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i &amp;gt;= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache &amp;amp; letterIdxMask); idx &amp;lt; len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache &amp;gt;&amp;gt;= letterIdxBits
		remain--
	}

	return string(b)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;原来的rand.Int63()是整个rand包全局的，而且支持安全高并发，所以速度比较慢。现在我们自己创建的这个src只有我们自己用，所以效率比较高。
strings.Builder 改进&lt;/p&gt;
&lt;p&gt;这个是G0 1.10 新增的功能，提升字符串拼接的效率，这方面的可以参考我以前写的三篇文章，这里不做过多的介绍了。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzI3MjU4Njk3Ng%3D%3D&amp;amp;chksm=eb3103e0dc468af626e41f136b4652a3fb24527a69db8689c9f691ac94a12a5b6dd6df7b39d3&amp;amp;idx=1&amp;amp;mid=2247484015&amp;amp;scene=21&amp;amp;sn=4fe47b59e7c1595f4d6723c876910247#wechat_redirect&#34;&gt;Go语言字符串高效拼接（一）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzI3MjU4Njk3Ng%3D%3D&amp;amp;chksm=eb31030edc468a18ffce72f8358f5fa3d4eaa05170569cde6615f86afea85e673767fd049fbc&amp;amp;idx=1&amp;amp;mid=2247484033&amp;amp;scene=21&amp;amp;sn=909064f18cb624ff3c2a061f4e3994f4#wechat_redirect&#34;&gt;Go语言字符串高效拼接（二）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s?__biz=MzI3MjU4Njk3Ng%3D%3D&amp;amp;chksm=eb31030adc468a1cf319086310111998a031691334784fd7afdca2afae0eec7656e574d4e89e&amp;amp;idx=1&amp;amp;mid=2247484037&amp;amp;scene=21&amp;amp;sn=653dcecc028b97e16eed393f37a925d2#wechat_redirect&#34;&gt;Go语言字符串高效拼接（三）&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;经过改进后，代码如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func RandStringBytesMaskImprSrcSB(n int) string {
	sb := strings.Builder{}
	sb.Grow(n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i &amp;gt;= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache &amp;amp; letterIdxMask); idx &amp;lt; len(letterBytes) {
			sb.WriteByte(letterBytes[idx])
			i--
		}
		cache &amp;gt;&amp;gt;= letterIdxBits
		remain--
	}

	return sb.String()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;使用unsafe包模拟 strings.Builder&lt;/p&gt;
&lt;p&gt;strings.Builder的原理其实很简单，是内置了一个[]byte存储字符，最终转换为string的时候为了避免拷贝，使用了unsafe包。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// String returns the accumulated string.
func (b *Builder) String() string {
	return *(*string)(unsafe.Pointer(&amp;amp;b.buf))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以上这些我们可以自己来做，看看我们重写后的代码。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func RandStringBytesMaskImprSrcUnsafe(n int) string {
	b := make([]byte, n)
	// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
	for i, cache, remain := n-1, src.Int63(), letterIdxMax; i &amp;gt;= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache &amp;amp; letterIdxMask); idx &amp;lt; len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache &amp;gt;&amp;gt;= letterIdxBits
		remain--
	}

	return *(*string)(unsafe.Pointer(&amp;amp;b))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;效果和使用strings.Builder一样，而且看起来更简洁了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Benchmark 性能测试&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以后，我们通过一步步的改进代码，提升效率，现在我们通过Benchmark测试看下这些方法的效果。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;BenchmarkRunes-4 2000000 723 ns/op 96 B/op 2 allocs/op
BenchmarkBytes-4 3000000 550 ns/op 32 B/op 2 allocs/op
BenchmarkBytesRmndr-4 3000000 438 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMask-4 3000000 534 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImpr-4 10000000 176 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrc-4 10000000 139 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrcSB-4 10000000 134 ns/op 16 B/op 1 allocs/op
BenchmarkBytesMaskImprSrcUnsafe-4 10000000 115 ns/op 16 B/op 1 allocs/op
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;仅仅从rune到byte的改进，我们就获得了&lt;strong&gt;24%的提升，内存占用降低了三分之一&lt;/strong&gt; 。&lt;/p&gt;
&lt;p&gt;使用rand.Int63替换掉原来的rand.Intn，我们又获得了近20%的提升。&lt;/p&gt;
&lt;p&gt;单纯的使用掩码，因为重复获取可用索引的问题，性能下降了 &lt;strong&gt;-22%&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;但是当我们对 Masking 掩码 进行改进，分为10部分缓存的时候，我们获得了3倍的提升。&lt;/p&gt;
&lt;p&gt;使用rand.Source 代替 rand.Rand, 我们再次获得了21%的提升。&lt;/p&gt;
&lt;p&gt;使用strings.Builder,速度提升虽然只有3.5%,但是内存分配降低了50% 。&lt;/p&gt;
&lt;p&gt;最后，通过unsafe包精简重写了strings.Builder的功能，我们又获得了14%的提升。&lt;/p&gt;
&lt;p&gt;最终，RandStringBytesMaskImprSrcUnsafe比RandStringRunes快6.3倍，并且只使用了六分之一的内存和一半的内存分配，我们就完成了任务。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;结束语&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是一篇stackoverflow的文章，有人提问 How to generate a random string of a fixed length in Go? ，icza 做了非常精彩的回答，我把整个翻译下来加以整理分享给大家。&lt;/p&gt;
&lt;p&gt;这是一篇非常棒的文章，它的意义不光是回答这个问题，还有帮助我们建立如何一步步优化的思路以及追求极致的极客精神。&lt;/p&gt;
&lt;p&gt;原文链接：&lt;a href=&#34;https://blog.csdn.net/flysnow_org/java/article/details/103520891&#34;&gt;https://blog.csdn.net/flysnow_org/java/article/details/103520891&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;博客链接：&lt;a href=&#34;https://www.flysnow.org/archives/&#34;&gt;https://www.flysnow.org/archives/&lt;/a&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的时间和日期</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.05-%E6%97%B6%E9%97%B4%E5%92%8C%E6%97%A5%E6%9C%9F/</link>
    <pubDate>Tue, 05 Jul 2022 11:18:29 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.05-%E6%97%B6%E9%97%B4%E5%92%8C%E6%97%A5%E6%9C%9F/</guid>
    <description>
        &lt;h1 id=&#34;48-时间和日期&#34;&gt;4.8 时间和日期&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;time&lt;/code&gt; 包为我们提供了一个数据类型 &lt;code&gt;time.Time&lt;/code&gt;（作为值使用）以及显示和测量时间和日期的功能函数。&lt;/p&gt;
&lt;p&gt;当前时间可以使用 &lt;code&gt;time.Now()&lt;/code&gt; 获取，或者使用 &lt;code&gt;t.Day()&lt;/code&gt;、&lt;code&gt;t.Minute()&lt;/code&gt; 等等来获取时间的一部分；你甚至可以自定义时间格式化字符串，例如： &lt;code&gt;fmt.Printf(&amp;quot;%02d.%02d.%4d\n&amp;quot;, t.Day(), t.Month(), t.Year())&lt;/code&gt; 将会输出 &lt;code&gt;21.07.2011&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Duration&lt;/code&gt; 类型表示两个连续时刻所相差的纳秒数，类型为 &lt;code&gt;int64&lt;/code&gt;。&lt;code&gt;Location&lt;/code&gt; 类型映射某个时区的时间，UTC 表示通用协调世界时间。&lt;/p&gt;
&lt;p&gt;包中的一个预定义函数 &lt;code&gt;func (t Time) Format(layout string) string&lt;/code&gt; 可以根据一个格式化字符串来将一个时间 &lt;code&gt;t&lt;/code&gt; 转换为相应格式的字符串，你可以使用一些预定义的格式，如：&lt;code&gt;time.ANSIC&lt;/code&gt; 或 &lt;code&gt;time.RFC822&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;一般的格式化设计是通过对于一个标准时间的格式化描述来展现的，这听起来很奇怪（&lt;code&gt;02 Jan 2006 15:04&lt;/code&gt; 是 Go 语言的诞生时间且自定义格式化时必须以此时间为基准），但看下面这个例子你就会一目了然：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fmt.Println(t.Format(&amp;#34;02 Jan 2006 15:04&amp;#34;)) 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;21 Jul 2011 10:31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其它有关时间操作的文档请参阅 &lt;a href=&#34;http://golang.org/pkg/time/&#34;&gt;官方文档&lt;/a&gt;（ &lt;strong&gt;译者注：国内用户可访问 &lt;a href=&#34;http://docs.studygolang.com/pkg/time/&#34;&gt;该页面&lt;/a&gt;&lt;/strong&gt; ）。&lt;/p&gt;
&lt;p&gt;示例 4.20 &lt;a href=&#34;examples/chapter_4/time.go&#34;&gt;time.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;time&amp;#34;
)
var week time.Duration
func main() {
	t := time.Now()
	fmt.Println(t) // e.g. Wed Dec 21 09:52:14 +0100 RST 2011
	fmt.Printf(&amp;#34;%02d.%02d.%4d\n&amp;#34;, t.Day(), t.Month(), t.Year())
	// 21.12.2011
	t = time.Now().UTC()
	fmt.Println(t) // Wed Dec 21 08:52:14 +0000 UTC 2011
	fmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011
	// calculating times:
	week = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec
	week_from_now := t.Add(time.Duration(week))
	fmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011
	// formatting times:
	fmt.Println(t.Format(time.RFC822)) // 21 Dec 11 0852 UTC
	fmt.Println(t.Format(time.ANSIC)) // Wed Dec 21 08:56:34 2011
	// The time must be 2006-01-02 15:04:05
	fmt.Println(t.Format(&amp;#34;02 Jan 2006 15:04&amp;#34;)) // 21 Dec 2011 08:52
	s := t.Format(&amp;#34;20060102&amp;#34;)
	fmt.Println(t, &amp;#34;=&amp;gt;&amp;#34;, s)
	// Wed Dec 21 08:52:14 +0000 UTC 2011 =&amp;gt; 20111221
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出的结果已经写在每行 &lt;code&gt;//&lt;/code&gt; 的后面。&lt;/p&gt;
&lt;p&gt;如果你需要在应用程序在经过一定时间或周期执行某项任务（事件处理的特例），则可以使用 &lt;code&gt;time.After()&lt;/code&gt; 或者 &lt;code&gt;time.Ticker&lt;/code&gt;：我们将会在 &lt;a href=&#34;14.5.md&#34;&gt;第 14.5 节&lt;/a&gt; 讨论这些有趣的事情。 另外，&lt;code&gt;time.Sleep(d Duration)&lt;/code&gt; 可以实现对某个进程（实质上是 goroutine）时长为 &lt;code&gt;d&lt;/code&gt; 的暂停。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>U盘启动盘制作</title>
    <link>https://blog.wiseai.cn/post/u%E7%9B%98%E5%90%AF%E5%8A%A8%E7%9B%98%E5%88%B6%E4%BD%9C/</link>
    <pubDate>Tue, 05 Jul 2022 10:08:37 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/u%E7%9B%98%E5%90%AF%E5%8A%A8%E7%9B%98%E5%88%B6%E4%BD%9C/</guid>
    <description>
        &lt;h2 id=&#34;一ventoyhttpswwwventoynetcnindexhtml-制作可启动u盘的开源工具制作可启动u盘的开源工具&#34;&gt;一、&lt;a href=&#34;https://www.ventoy.net/cn/index.html&#34; title=&#34;制作可启动U盘的开源工具&#34;&gt;Ventoy&lt;/a&gt;:制作可启动U盘的开源工具&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/ventoy/Ventoy.git&#34;&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://gitee.com/longpanda/Ventoy.git&#34;&gt;gitee&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;优点&#34;&gt;优点&lt;/h4&gt;
&lt;p&gt;多平台、支持Iso镜像多&lt;/p&gt;
&lt;h4 id=&#34;使用方法&#34;&gt;使用方法&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;在安装包解压后的目录下，打开终端执行 sudo sh VentoyWeb.sh&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开浏览器，直接访问 http://127.0.0.1:24680&lt;/p&gt;
&lt;p&gt;提示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;执行第1步后会在终端上打印出对应的 http 地址。很多系统中都可以按下 Ctrl 键，同时鼠标点击链接即可。无需再手动打开浏览器。&lt;/li&gt;
&lt;li&gt;VentoyWeb.sh 默认情况下监听 127.0.0.1 地址的 24680 端口。此时只能通过本机的浏览器进行访问。&lt;/li&gt;
&lt;li&gt;你也可以像这样 sudo sh VentoyWeb.sh -H 192.168.0.100 -P 8080 指定IP地址和端口号。&lt;/li&gt;
&lt;li&gt;此时你可以通过同网络内的另一台电脑上的浏览器来访问这个界面进行操作。这在有些情况下比较方便。&lt;/li&gt;
&lt;li&gt;比如，你有一台机器里面安装了Linux的系统，但是并没有安装图形界面，只有命令行操作界面。此时你可以在命令行里面执行上述命令，&lt;/li&gt;
&lt;li&gt;然后在另外一台有图形环境的电脑上（比如Windows）通过浏览器访问对应的页面进行操作。只要这两台电脑网络上是联通的即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;二rufushttprufusiezh只支持windows的u盘启动盘制作工具&#34;&gt;二、&lt;a href=&#34;http://rufus.ie/zh/&#34;&gt;rufus&lt;/a&gt;:只支持windows的U盘启动盘制作工具&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/pbatard/rufus&#34;&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;使用方法-1&#34;&gt;使用方法&lt;/h4&gt;
&lt;p&gt;没有特别的，和其它windows程序一样，下一步，下一步就可以了。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>愁</title>
    <link>https://blog.wiseai.cn/post/%E6%84%81/</link>
    <pubDate>Mon, 04 Jul 2022 16:40:53 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E6%84%81/</guid>
    <description>
        &lt;hr&gt;
&lt;div id=&#34;myjj&#34;&gt;
靠在阳台抽烟，
&lt;p&gt;我抽一半，风抽一半，&lt;/p&gt;
&lt;p&gt;我没和风计较，&lt;/p&gt;
&lt;p&gt;因为风也有自己的忧愁。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;斜依栏  吞云雾，&lt;/p&gt;
&lt;p&gt;半入清风半入喉，&lt;/p&gt;
&lt;p&gt;莫问清风何处走，&lt;/p&gt;
&lt;p&gt;清风也识人间愁。&lt;/p&gt;
&lt;/div&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的 Strings 和 strconv 包</title>
    <link>https://blog.wiseai.cn/post/golang/2022.07.01-strings%E5%92%8Cstrconv%E5%8C%85/</link>
    <pubDate>Fri, 01 Jul 2022 10:14:36 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.07.01-strings%E5%92%8Cstrconv%E5%8C%85/</guid>
    <description>
        &lt;h1 id=&#34;47-strings-和-strconv-包&#34;&gt;4.7 strings 和 strconv 包&lt;/h1&gt;
&lt;p&gt;作为一种基本数据结构，每种语言都有一些对于字符串的预定义处理函数。Go 中使用 &lt;code&gt;strings&lt;/code&gt; 包来完成对字符串的主要操作。&lt;/p&gt;
&lt;h2 id=&#34;471-前缀和后缀&#34;&gt;4.7.1 前缀和后缀&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;HasPrefix()&lt;/code&gt; 判断字符串 &lt;code&gt;s&lt;/code&gt; 是否以 &lt;code&gt;prefix&lt;/code&gt; 开头：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.HasPrefix(s, prefix string) bool
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;HasSuffix()&lt;/code&gt; 判断字符串 &lt;code&gt;s&lt;/code&gt; 是否以 &lt;code&gt;suffix&lt;/code&gt; 结尾：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.HasSuffix(s, suffix string) bool
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	var str string = &amp;#34;This is an example of a string&amp;#34;
	fmt.Printf(&amp;#34;T/F? Does the string \&amp;#34;%s\&amp;#34; have prefix %s? &amp;#34;, str, &amp;#34;Th&amp;#34;)
	fmt.Printf(&amp;#34;%t\n&amp;#34;, strings.HasPrefix(str, &amp;#34;Th&amp;#34;))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;T/F? Does the string &amp;quot;This is an example of a string&amp;quot; have prefix Th? true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个例子同样演示了转义字符 &lt;code&gt;\&lt;/code&gt; 和格式化字符串的使用。&lt;/p&gt;
&lt;h2 id=&#34;472-字符串包含关系&#34;&gt;4.7.2 字符串包含关系&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Contains()&lt;/code&gt; 判断字符串 &lt;code&gt;s&lt;/code&gt; 是否包含 &lt;code&gt;substr&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Contains(s, substr string) bool
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;473-判断子字符串或字符在父字符串中出现的位置索引&#34;&gt;4.7.3 判断子字符串或字符在父字符串中出现的位置（索引）&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Index()&lt;/code&gt; 返回字符串 &lt;code&gt;str&lt;/code&gt; 在字符串 &lt;code&gt;s&lt;/code&gt; 中的索引（&lt;code&gt;str&lt;/code&gt; 的第一个字符的索引），&lt;code&gt;-1&lt;/code&gt; 表示字符串 &lt;code&gt;s&lt;/code&gt; 不包含字符串 &lt;code&gt;str&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Index(s, str string) int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;LastIndex()&lt;/code&gt; 返回字符串 &lt;code&gt;str&lt;/code&gt; 在字符串 &lt;code&gt;s&lt;/code&gt; 中最后出现位置的索引（&lt;code&gt;str&lt;/code&gt; 的第一个字符的索引），&lt;code&gt;-1&lt;/code&gt; 表示字符串 &lt;code&gt;s&lt;/code&gt; 不包含字符串 &lt;code&gt;str&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.LastIndex(s, str string) int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果需要查询非 ASCII 编码的字符在父字符串中的位置，建议使用以下函数来对字符进行定位：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.IndexRune(s string, r rune) int
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;注: 原文为 &amp;quot;If ch is a non-ASCII character use strings.IndexRune(s string, ch int) int.&amp;quot;
该方法在最新版本的 Go 中定义为 func IndexRune(s string, r rune) int
实际使用中的第二个参数 rune 可以是 rune 或 int, 例如 strings.IndexRune(&amp;quot;chicken&amp;quot;, 99) 或 strings.IndexRune(&amp;quot;chicken&amp;quot;, rune(&#39;k&#39;))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	var str string = &amp;#34;Hi, I&amp;#39;m Marc, Hi.&amp;#34;
	fmt.Printf(&amp;#34;The position of \&amp;#34;Marc\&amp;#34; is: &amp;#34;)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.Index(str, &amp;#34;Marc&amp;#34;))
	fmt.Printf(&amp;#34;The position of the first instance of \&amp;#34;Hi\&amp;#34; is: &amp;#34;)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.Index(str, &amp;#34;Hi&amp;#34;))
	fmt.Printf(&amp;#34;The position of the last instance of \&amp;#34;Hi\&amp;#34; is: &amp;#34;)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.LastIndex(str, &amp;#34;Hi&amp;#34;))
	fmt.Printf(&amp;#34;The position of \&amp;#34;Burger\&amp;#34; is: &amp;#34;)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.Index(str, &amp;#34;Burger&amp;#34;))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The position of &amp;quot;Marc&amp;quot; is: 8
The position of the first instance of &amp;quot;Hi&amp;quot; is: 0
The position of the last instance of &amp;quot;Hi&amp;quot; is: 14
The position of &amp;quot;Burger&amp;quot; is: -1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;474-字符串替换&#34;&gt;4.7.4 字符串替换&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Replace()&lt;/code&gt; 用于将字符串 &lt;code&gt;str&lt;/code&gt; 中的前 &lt;code&gt;n&lt;/code&gt; 个字符串 &lt;code&gt;old&lt;/code&gt; 替换为字符串 &lt;code&gt;new&lt;/code&gt;，并返回一个新的字符串，如果 &lt;code&gt;n = -1&lt;/code&gt; 则替换所有字符串 &lt;code&gt;old&lt;/code&gt; 为字符串 &lt;code&gt;new&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Replace(str, old, new string, n int) string
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;475-统计字符串出现次数&#34;&gt;4.7.5 统计字符串出现次数&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Count()&lt;/code&gt; 用于计算字符串 &lt;code&gt;str&lt;/code&gt; 在字符串 &lt;code&gt;s&lt;/code&gt; 中出现的非重叠次数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Count(s, str string) int
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	var str string = &amp;#34;Hello, how is it going, Hugo?&amp;#34;
	var manyG = &amp;#34;gggggggggg&amp;#34;
	fmt.Printf(&amp;#34;Number of H&amp;#39;s in %s is: &amp;#34;, str)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.Count(str, &amp;#34;H&amp;#34;))
	fmt.Printf(&amp;#34;Number of double g&amp;#39;s in %s is: &amp;#34;, manyG)
	fmt.Printf(&amp;#34;%d\n&amp;#34;, strings.Count(manyG, &amp;#34;gg&amp;#34;))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Number of H&#39;s in Hello, how is it going, Hugo? is: 2
Number of double g’s in gggggggggg is: 5
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;476-重复字符串&#34;&gt;4.7.6 重复字符串&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Repeat()&lt;/code&gt; 用于重复 &lt;code&gt;count&lt;/code&gt; 次字符串 &lt;code&gt;s&lt;/code&gt; 并返回一个新的字符串：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Repeat(s, count int) string
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	var origS string = &amp;#34;Hi there! &amp;#34;
	var newS string
	newS = strings.Repeat(origS, 3)
	fmt.Printf(&amp;#34;The new repeated string is: %s\n&amp;#34;, newS)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The new repeated string is: Hi there! Hi there! Hi there!
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;477-修改字符串大小写&#34;&gt;4.7.7 修改字符串大小写&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ToLower()&lt;/code&gt; 将字符串中的 Unicode 字符全部转换为相应的小写字符：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.ToLower(s) string
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ToUpper()&lt;/code&gt; 将字符串中的 Unicode 字符全部转换为相应的大写字符：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.ToUpper(s) string
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	var orig string = &amp;#34;Hey, how are you George?&amp;#34;
	var lower string
	var upper string
	fmt.Printf(&amp;#34;The original string is: %s\n&amp;#34;, orig)
	lower = strings.ToLower(orig)
	fmt.Printf(&amp;#34;The lowercase string is: %s\n&amp;#34;, lower)
	upper = strings.ToUpper(orig)
	fmt.Printf(&amp;#34;The uppercase string is: %s\n&amp;#34;, upper)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The original string is: Hey, how are you George?
The lowercase string is: hey, how are you george?
The uppercase string is: HEY, HOW ARE YOU GEORGE?
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;478-修剪字符串&#34;&gt;4.7.8 修剪字符串&lt;/h2&gt;
&lt;p&gt;你可以使用 &lt;code&gt;strings.TrimSpace(s)&lt;/code&gt; 来剔除字符串开头和结尾的空白符号；如果你想要剔除指定字符，则可以使用 &lt;code&gt;strings.Trim(s, &amp;quot;cut&amp;quot;)&lt;/code&gt; 来将开头和结尾的 &lt;code&gt;cut&lt;/code&gt; 去除掉。该函数的第二个参数可以包含任何字符，如果你只想剔除开头或者结尾的字符串，则可以使用 &lt;code&gt;TrimLeft()&lt;/code&gt; 或者 &lt;code&gt;TrimRight()&lt;/code&gt; 来实现。&lt;/p&gt;
&lt;h2 id=&#34;479-分割字符串&#34;&gt;4.7.9 分割字符串&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;strings.Fields(s)&lt;/code&gt; 将会利用 1 个或多个空白符号来作为动态长度的分隔符将字符串分割成若干小块，并返回一个 slice，如果字符串只包含空白符号，则返回一个长度为 0 的 slice。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;strings.Split(s, sep)&lt;/code&gt; 用于自定义分割符号来对指定字符串进行分割，同样返回 slice。&lt;/p&gt;
&lt;p&gt;因为这 2 个函数都会返回 slice，所以习惯使用 for-range 循环来对其进行处理（第 7.3 节）。&lt;/p&gt;
&lt;h2 id=&#34;4710-拼接-slice-到字符串&#34;&gt;4.7.10 拼接 slice 到字符串&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Join()&lt;/code&gt; 用于将元素类型为 string 的 slice 使用分割符号来拼接组成一个字符串：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;strings.Join(sl []string, sep string) string
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strings&amp;#34;
)
func main() {
	str := &amp;#34;The quick brown fox jumps over the lazy dog&amp;#34;
	sl := strings.Fields(str)
	fmt.Printf(&amp;#34;Splitted in slice: %v\n&amp;#34;, sl)
	for _, val := range sl {
		fmt.Printf(&amp;#34;%s - &amp;#34;, val)
	}
	fmt.Println()
	str2 := &amp;#34;GO1|The ABC of Go|25&amp;#34;
	sl2 := strings.Split(str2, &amp;#34;|&amp;#34;)
	fmt.Printf(&amp;#34;Splitted in slice: %v\n&amp;#34;, sl2)
	for _, val := range sl2 {
		fmt.Printf(&amp;#34;%s - &amp;#34;, val)
	}
	fmt.Println()
	str3 := strings.Join(sl2,&amp;#34;;&amp;#34;)
	fmt.Printf(&amp;#34;sl2 joined by ;: %s\n&amp;#34;, str3)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Splitted in slice: [The quick brown fox jumps over the lazy dog]
The - quick - brown - fox - jumps - over - the - lazy - dog -
Splitted in slice: [GO1 The ABC of Go 25]
GO1 - The ABC of Go - 25 -
sl2 joined by ;: GO1;The ABC of Go;25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其它有关字符串操作的文档请参阅 &lt;a href=&#34;http://golang.org/pkg/strings/&#34;&gt;官方文档&lt;/a&gt;（ &lt;strong&gt;译者注：国内用户可访问 &lt;a href=&#34;http://docs.studygolang.com/pkg/strings/&#34;&gt;该页面&lt;/a&gt;&lt;/strong&gt; ）。&lt;/p&gt;
&lt;h2 id=&#34;4711-从字符串中读取内容&#34;&gt;4.7.11 从字符串中读取内容&lt;/h2&gt;
&lt;p&gt;函数 &lt;code&gt;strings.NewReader(str)&lt;/code&gt; 用于生成一个 &lt;code&gt;Reader&lt;/code&gt; 并读取字符串中的内容，然后返回指向该 &lt;code&gt;Reader&lt;/code&gt; 的指针，从其它类型读取内容的函数还有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Read()&lt;/code&gt; 从 &lt;code&gt;[]byte&lt;/code&gt; 中读取内容。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReadByte()&lt;/code&gt; 和 &lt;code&gt;ReadRune()&lt;/code&gt; 从字符串中读取下一个 &lt;code&gt;byte&lt;/code&gt; 或者 &lt;code&gt;rune&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;4712-字符串与其它类型的转换&#34;&gt;4.7.12 字符串与其它类型的转换&lt;/h2&gt;
&lt;p&gt;与字符串相关的类型转换都是通过 &lt;code&gt;strconv&lt;/code&gt; 包实现的。&lt;/p&gt;
&lt;p&gt;该包包含了一些变量用于获取程序运行的操作系统平台下 &lt;code&gt;int&lt;/code&gt; 类型所占的位数，如：&lt;code&gt;strconv.IntSize&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;任何类型 &lt;strong&gt;&lt;code&gt;T&lt;/code&gt;&lt;/strong&gt; 转换为字符串总是成功的。&lt;/p&gt;
&lt;p&gt;针对从数字类型转换到字符串，Go 提供了以下函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strconv.Itoa(i int) string&lt;/code&gt; 返回数字 &lt;code&gt;i&lt;/code&gt; 所表示的字符串类型的十进制数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strconv.FormatFloat(f float64, fmt byte, prec int, bitSize int) string&lt;/code&gt; 将 64 位浮点型的数字转换为字符串，其中 &lt;code&gt;fmt&lt;/code&gt; 表示格式（其值可以是 &lt;code&gt;&#39;b&#39;&lt;/code&gt;、&lt;code&gt;&#39;e&#39;&lt;/code&gt;、&lt;code&gt;&#39;f&#39;&lt;/code&gt; 或 &lt;code&gt;&#39;g&#39;&lt;/code&gt;），&lt;code&gt;prec&lt;/code&gt; 表示精度，&lt;code&gt;bitSize&lt;/code&gt; 则使用 32 表示 &lt;code&gt;float32&lt;/code&gt;，用 64 表示 &lt;code&gt;float64&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;将字符串转换为其它类型 &lt;strong&gt;&lt;code&gt;tp&lt;/code&gt;&lt;/strong&gt; 并不总是可能的，可能会在运行时抛出错误 &lt;code&gt;parsing &amp;quot;…&amp;quot;: invalid argument&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;针对从字符串类型转换为数字类型，Go 提供了以下函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strconv.Atoi(s string) (i int, err error)&lt;/code&gt; 将字符串转换为 &lt;code&gt;int&lt;/code&gt; 型。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strconv.ParseFloat(s string, bitSize int) (f float64, err error)&lt;/code&gt; 将字符串转换为 &lt;code&gt;float64&lt;/code&gt; 型。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;利用多返回值的特性，这些函数会返回 2 个值，第 1 个是转换后的结果（如果转换成功），第 2 个是可能出现的错误，因此，我们一般使用以下形式来进行从字符串到其它类型的转换：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;val, err = strconv.Atoi(s)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在下面这个示例中，我们忽略可能出现的转换错误：&lt;/p&gt;
&lt;p&gt;示例 4.19 &lt;a href=&#34;examples/chapter_4/string_conversion.go&#34;&gt;string_conversion.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;strconv&amp;#34;
)
func main() {
	var orig string = &amp;#34;666&amp;#34;
	var an int
	var newS string
	fmt.Printf(&amp;#34;The size of ints is: %d\n&amp;#34;, strconv.IntSize)	  
	an, _ = strconv.Atoi(orig)
	fmt.Printf(&amp;#34;The integer is: %d\n&amp;#34;, an) 
	an = an + 5
	newS = strconv.Itoa(an)
	fmt.Printf(&amp;#34;The new string is: %s\n&amp;#34;, newS)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;64 位系统：
The size of ints is: 64
32 位系统：
The size of ints is: 32
The integer is: 666
The new string is: 671
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在第 5.1 节，我们将会利用 &lt;code&gt;if&lt;/code&gt; 语句来对可能出现的错误进行分类处理。&lt;/p&gt;
&lt;p&gt;更多有关该包的讨论，请参阅 &lt;a href=&#34;http://golang.org/pkg/strconv/&#34;&gt;官方文档&lt;/a&gt;（ &lt;strong&gt;译者注：国内用户可访问 &lt;a href=&#34;http://docs.studygolang.com/pkg/strconv/&#34;&gt;该页面&lt;/a&gt;&lt;/strong&gt; ）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的字符串</title>
    <link>https://blog.wiseai.cn/post/golang/2022.06.30-%E5%AD%97%E7%AC%A6%E4%B8%B2/</link>
    <pubDate>Thu, 30 Jun 2022 09:37:08 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.06.30-%E5%AD%97%E7%AC%A6%E4%B8%B2/</guid>
    <description>
        &lt;h1 id=&#34;46-字符串&#34;&gt;4.6 字符串&lt;/h1&gt;
&lt;p&gt;字符串是 UTF-8 字符的一个序列（当字符为 ASCII 码时则占用 1 个字节，其它字符根据需要占用 2-4 个字节）。UTF-8 是被广泛使用的编码格式，是文本文件的标准编码，其它包括 XML 和 JSON 在内，也都使用该编码。由于该编码对占用字节长度的不定性，Go 中的字符串里面的字符也可能根据需要占用 1 至 4 个字节，这与其它语言如 C++、Java 或者 Python 不同（Java 始终使用 2 个字节）。Go 这样做的好处是不仅减少了内存和硬盘空间占用，同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。&lt;/p&gt;
&lt;p&gt;字符串是一种值类型，且值不可变，即创建某个文本后你无法再次修改这个文本的内容；更深入地讲，字符串是字节的定长数组。&lt;/p&gt;
&lt;p&gt;Go 支持以下 2 种形式的字面值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;解释字符串：&lt;/p&gt;
&lt;p&gt;该类字符串使用双引号括起来，其中的相关的转义字符将被替换，这些转义字符包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;\n&lt;/code&gt;：换行符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\r&lt;/code&gt;：回车符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\t&lt;/code&gt;：tab 键&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\u&lt;/code&gt; 或 &lt;code&gt;\U&lt;/code&gt;：Unicode 字符&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\\&lt;/code&gt;：反斜杠自身&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;非解释字符串：&lt;/p&gt;
&lt;p&gt;该类字符串使用反引号括起来，支持换行，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  `This is a raw string \n` 中的 `\n\` 会被原样输出。
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和 C/C++不一样，Go 中的字符串是根据长度限定，而非特殊字符 &lt;code&gt;\0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;string&lt;/code&gt; 类型的零值为长度为零的字符串，即空字符串 &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;一般的比较运算符（&lt;code&gt;==&lt;/code&gt;、&lt;code&gt;!=&lt;/code&gt;、&lt;code&gt;&amp;lt;&lt;/code&gt;、&lt;code&gt;&amp;lt;=&lt;/code&gt;、&lt;code&gt;&amp;gt;=&lt;/code&gt;、&lt;code&gt;&amp;gt;&lt;/code&gt;）通过在内存中按字节比较来实现字符串的对比。你可以通过函数 &lt;code&gt;len()&lt;/code&gt; 来获取字符串所占的字节长度，例如：&lt;code&gt;len(str)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;字符串的内容（纯字节）可以通过标准索引法来获取，在中括号 &lt;code&gt;[]&lt;/code&gt; 内写入索引，索引从 0 开始计数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字符串 &lt;code&gt;str&lt;/code&gt; 的第 1 个字节：&lt;code&gt;str[0]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第 &lt;code&gt;i&lt;/code&gt; 个字节：&lt;code&gt;str[i - 1]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最后 1 个字节：&lt;code&gt;str[len(str)-1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要注意的是，这种转换方案只对纯 ASCII 码的字符串有效。&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;strong&gt;注意事项&lt;/strong&gt; 获取字符串中某个字节的地址的行为是非法的，例如：&lt;code&gt;&amp;amp;str[i]&lt;/code&gt;。&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符串拼接符 &lt;code&gt;+&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;两个字符串 &lt;code&gt;s1&lt;/code&gt; 和 &lt;code&gt;s2&lt;/code&gt; 可以通过 &lt;code&gt;s := s1 + s2&lt;/code&gt; 拼接在一起。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;s2&lt;/code&gt; 追加在 &lt;code&gt;s1&lt;/code&gt; 尾部并生成一个新的字符串 &lt;code&gt;s&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可以通过以下方式来对代码中多行的字符串进行拼接：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;str := &amp;#34;Beginning of the string &amp;#34; +
	&amp;#34;second part of the string&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;u&gt;由于编译器行尾自动补全分号的缘故，加号 &lt;code&gt;+&lt;/code&gt; 必须放在第一行。&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;拼接的简写形式 &lt;code&gt;+=&lt;/code&gt; 也可以用于字符串：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;s := &amp;#34;hel&amp;#34; + &amp;#34;lo,&amp;#34;
s += &amp;#34;world!&amp;#34;
fmt.Println(s) //输出 “hello, world!”
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;下面这些后面文章添加了再修改&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在循环中使用加号 &lt;code&gt;+&lt;/code&gt; 拼接字符串并不是最高效的做法，更好的办法是使用函数 &lt;code&gt;strings.Join()&lt;/code&gt;（&lt;a href=&#34;https://blog.wiseai.cn/post/golang/2022.07.01-strings%E5%92%8Cstrconv%E5%8C%85/&#34;&gt;第 4.7.10 节&lt;/a&gt;），有没有更好的办法了？有！使用字节缓冲（&lt;code&gt;bytes.Buffer&lt;/code&gt;）拼接更加给力（&lt;a href=&#34;07.2.md&#34;&gt;第 7.2.6 节&lt;/a&gt;）！&lt;/p&gt;
&lt;p&gt;在&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt;，我们会讲到通过将字符串看作是字节 (&lt;code&gt;byte&lt;/code&gt;) 的切片 (slice) 来实现对其标准索引法的操作。会在&lt;a href=&#34;05.4.md&#34;&gt;第 5.4.1 节&lt;/a&gt; 中讲到的 &lt;code&gt;for&lt;/code&gt; 循环只会根据索引返回字符串中的纯字节，而在&lt;a href=&#34;./05.4.md&#34;&gt;第 5.4.4 节&lt;/a&gt;（以及&lt;a href=&#34;07.6.md&#34;&gt;第 7.6.1 节&lt;/a&gt; 的示例）将会展示如何使用 for-range 循环来实现对 Unicode 字符串的迭代操作。在下一节，我们会学习到许多有关字符串操作的函数和方法，同时 &lt;code&gt;fmt&lt;/code&gt; 包中的 &lt;code&gt;fmt.Sprint(x)&lt;/code&gt; 也可以格式化生成并返回你所需要的字符串（&lt;a href=&#34;04.3.md&#34;&gt;第 4.4.3 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;练习 4.6&lt;/strong&gt; &lt;a href=&#34;exercises/chapter_4/count_characters.go&#34;&gt;count_characters.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;创建一个用于统计字节和字符 (rune) 的程序，并对字符串 &lt;code&gt;asSASA ddd dsjkdsjs dk&lt;/code&gt; 进行分析，然后再分析 &lt;code&gt;asSASA ddd dsjkdsjsこん dk&lt;/code&gt;，最后解释两者不同的原因（提示：使用 &lt;code&gt;unicode/utf8&lt;/code&gt; 包）。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的基本类型和运算符</title>
    <link>https://blog.wiseai.cn/post/golang/2022.06.29-%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%92%8C%E8%BF%90%E7%AE%97%E7%AC%A6/</link>
    <pubDate>Wed, 29 Jun 2022 11:33:14 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.06.29-%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%92%8C%E8%BF%90%E7%AE%97%E7%AC%A6/</guid>
    <description>
        &lt;h1 id=&#34;45-基本类型和运算符&#34;&gt;4.5 基本类型和运算符&lt;/h1&gt;
&lt;p&gt;我们将在这个部分讲解有关布尔型、数字型和字符型的相关知识。&lt;/p&gt;
&lt;p&gt;表达式是一种特定的类型的值，它可以由其它的值以及运算符组合而成。每个类型都定义了可以和自己结合的运算符集合，如果你使用了不在这个集合中的运算符，则会在编译时获得编译错误。&lt;/p&gt;
&lt;p&gt;一元运算符只可以用于一个值的操作（作为后缀），而二元运算符则可以和两个值或者操作数结合（作为中缀）。&lt;/p&gt;
&lt;p&gt;只有两个类型相同的值才可以和二元运算符结合，另外要注意的是，Go 是强类型语言，因此不会进行隐式转换，任何不同类型之间的转换都必须显式说明（第 4.2 节）。Go 不存在像 C 那样的运算符重载，表达式的解析顺序是从左至右。&lt;/p&gt;
&lt;p&gt;你可以在第 4.5.3 节找到有关运算符优先级的相关信息，优先级越高的运算符在条件相同的情况下将被优先执行。但是你可以通过使用括号将其中的表达式括起来，以人为地提升某个表达式的运算优先级。&lt;/p&gt;
&lt;h2 id=&#34;451-布尔类型-bool&#34;&gt;4.5.1 布尔类型 bool&lt;/h2&gt;
&lt;p&gt;一个简单的例子：&lt;code&gt;var b bool = true&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;布尔型的值只可以是常量 true 或者 false。&lt;/p&gt;
&lt;p&gt;两个类型相同的值可以使用相等 &lt;code&gt;==&lt;/code&gt; 或者不等 &lt;code&gt;!=&lt;/code&gt; 运算符来进行比较并获得一个布尔型的值。&lt;/p&gt;
&lt;p&gt;当相等运算符两边的值是完全相同的值的时候会返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;，并且只有在两个的值的类型相同的情况下才可以使用。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var aVar = 10
aVar == 5 -&amp;gt; false
aVar == 10 -&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;当不等运算符两边的值是不同的时候会返回 &lt;code&gt;true&lt;/code&gt;，否则返回 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var aVar = 10
aVar != 5 -&amp;gt; true
aVar != 10 -&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go 对于值之间的比较有非常严格的限制，只有两个类型相同的值才可以进行比较，如果值的类型是接口（interface，&lt;a href=&#34;11.0.md&#34;&gt;第 11 章&lt;/a&gt;），它们也必须都实现了相同的接口。如果其中一个值是常量，那么另外一个值的类型必须和该常量类型相兼容的。如果以上条件都不满足，则其中一个值的类型必须在被转换为和另外一个值的类型相同之后才可以进行比较。&lt;/p&gt;
&lt;p&gt;布尔型的常量和变量也可以通过和逻辑运算符（非 &lt;code&gt;!&lt;/code&gt;、与 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;、或 &lt;code&gt;||&lt;/code&gt;）结合来产生另外一个布尔值，这样的逻辑语句就其本身而言，并不是一个完整的 Go 语句。&lt;/p&gt;
&lt;p&gt;逻辑值可以被用于条件结构中的条件语句（&lt;a href=&#34;05.0.md&#34;&gt;第 5 章&lt;/a&gt;），以便测试某个条件是否满足。另外，与 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;、或 &lt;code&gt;||&lt;/code&gt; 与相等 &lt;code&gt;==&lt;/code&gt; 或不等 &lt;code&gt;!=&lt;/code&gt; 属于二元运算符，而非 &lt;code&gt;!&lt;/code&gt; 属于一元运算符。在接下来的内容中，我们会使用 T 来代表条件符合的语句，用 F 来代表条件不符合的语句。&lt;/p&gt;
&lt;p&gt;Go 语言中包含以下逻辑运算符：&lt;/p&gt;
&lt;p&gt;非运算符：&lt;code&gt;!&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;!T -&amp;gt; false
!F -&amp;gt; true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;非运算符用于取得和布尔值相反的结果。&lt;/p&gt;
&lt;p&gt;与运算符：&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;T &amp;amp;&amp;amp; T -&amp;gt; true
T &amp;amp;&amp;amp; F -&amp;gt; false
F &amp;amp;&amp;amp; T -&amp;gt; false
F &amp;amp;&amp;amp; F -&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;只有当两边的值都为 &lt;code&gt;true&lt;/code&gt; 的时候，和运算符的结果才是 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;或运算符：&lt;code&gt;||&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;T || T -&amp;gt; true
T || F -&amp;gt; true
F || T -&amp;gt; true
F || F -&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;只有当两边的值都为 &lt;code&gt;false&lt;/code&gt; 的时候，或运算符的结果才是 &lt;code&gt;false&lt;/code&gt;，其中任意一边的值为 &lt;code&gt;true&lt;/code&gt; 就能够使得该表达式的结果为 &lt;code&gt;true&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 Go 语言中，&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code&gt;||&lt;/code&gt; 是具有快捷性质的运算符，当运算符左边表达式的值已经能够决定整个表达式的值的时候（&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 左边的值为 &lt;code&gt;false&lt;/code&gt;，&lt;code&gt;||&lt;/code&gt; 左边的值为 &lt;code&gt;true&lt;/code&gt;），运算符右边的表达式将不会被执行。利用这个性质，如果你有多个条件判断，应当将计算过程较为复杂的表达式放在运算符的右侧以减少不必要的运算。&lt;/p&gt;
&lt;p&gt;利用括号同样可以升级某个表达式的运算优先级。&lt;/p&gt;
&lt;p&gt;在格式化输出时，你可以使用 &lt;code&gt;%t&lt;/code&gt; 来表示你要输出的值为布尔型。&lt;/p&gt;
&lt;p&gt;布尔值（以及任何结果为布尔值的表达式）最常用在条件结构的条件语句中，例如：if、for 和 switch 结构（第 5 章）。&lt;/p&gt;
&lt;p&gt;对于布尔值的好的命名能够很好地提升代码的可读性，例如以 &lt;code&gt;is&lt;/code&gt; 或者 &lt;code&gt;Is&lt;/code&gt; 开头的 &lt;code&gt;isSorted&lt;/code&gt;、&lt;code&gt;isFinished&lt;/code&gt;、&lt;code&gt;isVisible&lt;/code&gt;，使用这样的命名能够在阅读代码的获得阅读正常语句一样的良好体验，例如标准库中的 &lt;code&gt;unicode.IsDigit(ch)&lt;/code&gt;（&lt;a href=&#34;04.5.md&#34;&gt;第 4.5.5 节&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id=&#34;452-数字类型&#34;&gt;4.5.2 数字类型&lt;/h2&gt;
&lt;h3 id=&#34;4521-整型-int-和浮点型-float&#34;&gt;4.5.2.1 整型 int 和浮点型 float&lt;/h3&gt;
&lt;p&gt;Go 语言支持整型和浮点型数字，并且原生支持复数，其中位的运算采用补码（详情参见 &lt;a href=&#34;http://en.wikipedia.org/wiki/Two&#39;s_complement&#34;&gt;二的补码&lt;/a&gt; 页面）。&lt;/p&gt;
&lt;p&gt;Go 也有基于架构的类型，例如：&lt;code&gt;int&lt;/code&gt;、&lt;code&gt;uint&lt;/code&gt; 和 &lt;code&gt;uintptr&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这些类型的长度都是根据运行程序所在的操作系统类型所决定的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt; 和 &lt;code&gt;uint&lt;/code&gt; 在 32 位操作系统上，它们均使用 32 位（4 个字节），在 64 位操作系统上，它们均使用 64 位（8 个字节）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uintptr&lt;/code&gt; 的长度被设定为足够存放一个指针即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go 语言中没有 float 类型。（Go语言中只有 &lt;code&gt;float32&lt;/code&gt; 和 &lt;code&gt;float64&lt;/code&gt;）没有 double 类型。&lt;/p&gt;
&lt;p&gt;与操作系统架构无关的类型都有固定的大小，并在类型的名称中就可以看出来：&lt;/p&gt;
&lt;p&gt;整数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;int8&lt;/code&gt;（-128 -&amp;gt; 127）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int16&lt;/code&gt;（-32768 -&amp;gt; 32767）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int32&lt;/code&gt;（-2,147,483,648 -&amp;gt; 2,147,483,647）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int64&lt;/code&gt;（-9,223,372,036,854,775,808 -&amp;gt; 9,223,372,036,854,775,807）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;无符号整数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;uint8&lt;/code&gt;（0 -&amp;gt; 255）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uint16&lt;/code&gt;（0 -&amp;gt; 65,535）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uint32&lt;/code&gt;（0 -&amp;gt; 4,294,967,295）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uint64&lt;/code&gt;（0 -&amp;gt; 18,446,744,073,709,551,615）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;浮点型（IEEE-754 标准）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;float32&lt;/code&gt;（+- 1e-45 -&amp;gt; +- 3.4 * 1e38）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;float64&lt;/code&gt;（+- 5 * 1e-324 -&amp;gt; 107 * 1e308）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;int&lt;/code&gt; 型是计算最快的一种类型。&lt;/p&gt;
&lt;p&gt;整型的零值为 &lt;code&gt;0&lt;/code&gt;，浮点型的零值为 &lt;code&gt;0.0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;float32&lt;/code&gt; 精确到小数点后 7 位，&lt;code&gt;float64&lt;/code&gt; 精确到小数点后 15 位。由于精确度的缘故，你在使用 &lt;code&gt;==&lt;/code&gt; 或者 &lt;code&gt;!=&lt;/code&gt; 来比较浮点数时应当非常小心。你最好在正式使用前测试对于精确度要求较高的运算。&lt;/p&gt;
&lt;p&gt;你应该尽可能地使用 &lt;code&gt;float64&lt;/code&gt;，因为 &lt;code&gt;math&lt;/code&gt; 包中所有有关数学运算的函数都会要求接收这个类型。&lt;/p&gt;
&lt;p&gt;你可以通过增加前缀 0 来表示 8 进制数（如：077），增加前缀 0x 来表示 16 进制数（如：&lt;code&gt;0xFF&lt;/code&gt;），以及使用 &lt;code&gt;e&lt;/code&gt; 来表示 10 的连乘（如： 1e3 = 1000，或者 6.022e23 = 6.022 x 1e23）。&lt;/p&gt;
&lt;p&gt;你可以使用 &lt;code&gt;a := uint64(0)&lt;/code&gt; 来同时完成类型转换和赋值操作，这样 &lt;code&gt;a&lt;/code&gt; 的类型就是 &lt;code&gt;uint64&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Go 中不允许不同类型之间的混合使用，但是对于常量的类型限制非常少，因此允许常量之间的混合使用，下面这个程序很好地解释了这个现象（该程序无法通过编译）：&lt;/p&gt;
&lt;p&gt;示例 4.8 &lt;a href=&#34;examples/chapter_4/type_mixing.go&#34;&gt;type_mixing.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

func main() {
	var a int
	var b int32
	a = 15
	b = a + a	 // 编译错误
	b = b + 5    // 因为 5 是常量，所以可以通过编译
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你尝试编译该程序，则将得到编译错误 &lt;code&gt;cannot use a + a (type int) as type int32 in assignment&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;同样地，&lt;code&gt;int16&lt;/code&gt;  也不能够被隐式转换为 &lt;code&gt;int32&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;下面这个程序展示了通过显式转换来避免这个问题（&lt;a href=&#34;04.2.md&#34;&gt;第 4.2 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;示例 4.9 &lt;a href=&#34;examples/chapter_4/casting.go&#34;&gt;casting.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;

func main() {
	var n int16 = 34
	var m int32
	// compiler error: cannot use n (type int16) as type int32 in assignment
	//m = n
	m = int32(n)

	fmt.Printf(&amp;#34;32 bit int is: %d\n&amp;#34;, m)
	fmt.Printf(&amp;#34;16 bit int is: %d\n&amp;#34;, n)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;32 bit int is: 34
16 bit int is: 34
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;格式化说明符&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在格式化字符串里，&lt;code&gt;%d&lt;/code&gt; 用于格式化整数（&lt;code&gt;%x&lt;/code&gt; 和 &lt;code&gt;%X&lt;/code&gt; 用于格式化 16 进制表示的数字），&lt;code&gt;%g&lt;/code&gt; 用于格式化浮点型（&lt;code&gt;%f&lt;/code&gt; 输出浮点数，&lt;code&gt;%e&lt;/code&gt; 输出科学计数表示法），&lt;code&gt;%0nd&lt;/code&gt; 用于规定输出长度为 n 的整数，其中开头的数字 0 是必须的。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%n.mg&lt;/code&gt; 用于表示数字 n 并精确到小数点后 m 位，除了使用 g 之外，还可以使用 e 或者 f，例如：使用格式化字符串 &lt;code&gt;%5.2e&lt;/code&gt; 来输出 3.4 的结果为 &lt;code&gt;3.40e+00&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;数字值转换&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当进行类似 &lt;code&gt;a32bitInt = int32(a32Float)&lt;/code&gt; 的转换时，小数点后的数字将被丢弃。这种情况一般发生当从取值范围较大的类型转换为取值范围较小的类型时，或者你可以写一个专门用于处理类型转换的函数来确保没有发生精度的丢失。下面这个例子展示如何安全地从 &lt;code&gt;int&lt;/code&gt; 型转换为 &lt;code&gt;int8&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Uint8FromInt(n int) (uint8, error) {
	if 0 &amp;lt;= n &amp;amp;&amp;amp; n &amp;lt;= math.MaxUint8 { // conversion is safe
		return uint8(n), nil
	}
	return 0, fmt.Errorf(&amp;#34;%d is out of the uint8 range&amp;#34;, n)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或者安全地从 &lt;code&gt;float64&lt;/code&gt; 转换为 &lt;code&gt;int&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func IntFromFloat64(x float64) int {
	if math.MinInt32 &amp;lt;= x &amp;amp;&amp;amp; x &amp;lt;= math.MaxInt32 { // x lies in the integer range
		whole, fraction := math.Modf(x)
		if fraction &amp;gt;= 0.5 {
			whole++
		}
		return int(whole)
	}
	panic(fmt.Sprintf(&amp;#34;%g is out of the int32 range&amp;#34;, x))
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不过如果你实际存的数字超出你要转换到的类型的取值范围的话，则会引发 &lt;code&gt;panic&lt;/code&gt;（&lt;a href=&#34;./13.2.md&#34;&gt;第 13.2 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题 4.1&lt;/strong&gt; &lt;code&gt;int&lt;/code&gt; 和 &lt;code&gt;int64&lt;/code&gt; 是相同的类型吗？&lt;/p&gt;
&lt;h3 id=&#34;4522-复数&#34;&gt;4.5.2.2 复数&lt;/h3&gt;
&lt;p&gt;Go 拥有以下复数类型：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;complex64 (32 位实数和虚数)
complex128 (64 位实数和虚数)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;复数使用 &lt;code&gt;re+imI&lt;/code&gt; 来表示，其中 &lt;code&gt;re&lt;/code&gt; 代表实数部分，&lt;code&gt;im&lt;/code&gt; 代表虚数部分，&lt;code&gt;I&lt;/code&gt; 代表根号负 1。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var c1 complex64 = 5 + 10i
fmt.Printf(&amp;#34;The value is: %v&amp;#34;, c1)
// 输出： 5 + 10i
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果 &lt;code&gt;re&lt;/code&gt; 和 &lt;code&gt;im&lt;/code&gt; 的类型均为 &lt;code&gt;float32&lt;/code&gt;，那么类型为 &lt;code&gt;complex64&lt;/code&gt; 的复数 &lt;code&gt;c&lt;/code&gt; 可以通过以下方式来获得：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;c = complex(re, im)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;函数 &lt;code&gt;real(c)&lt;/code&gt; 和 &lt;code&gt;imag(c)&lt;/code&gt; 可以分别获得相应的实数和虚数部分。&lt;/p&gt;
&lt;p&gt;在使用格式化说明符时，可以使用 &lt;code&gt;%v&lt;/code&gt; 来表示复数，但当你希望只表示其中的一个部分的时候需要使用 &lt;code&gt;%f&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;复数支持和其它数字类型一样的运算。当你使用等号 &lt;code&gt;==&lt;/code&gt; 或者不等号 &lt;code&gt;!=&lt;/code&gt; 对复数进行比较运算时，注意对精确度的把握。&lt;code&gt;cmath&lt;/code&gt; 包中包含了一些操作复数的公共方法。如果你对内存的要求不是特别高，最好使用 &lt;code&gt;complex128&lt;/code&gt; 作为计算类型，因为相关函数都使用这个类型的参数。&lt;/p&gt;
&lt;h3 id=&#34;4523-位运算&#34;&gt;4.5.2.3 位运算&lt;/h3&gt;
&lt;p&gt;位运算只能用于整数类型的变量，且需当它们拥有等长位模式时。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%b&lt;/code&gt; 是用于表示位的格式化标识符。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二元运算符&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;按位与 &lt;code&gt;&amp;amp;&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;对应位置上的值经过和运算结果，具体参见和运算符（第 4.5.1 节），并将 T (true) 替换为 &lt;code&gt;1&lt;/code&gt;，将 F (false) 替换为 &lt;code&gt;0&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1 &amp;amp; 1 -&amp;gt; 1
  1 &amp;amp; 0 -&amp;gt; 0
  0 &amp;amp; 1 -&amp;gt; 0
  0 &amp;amp; 0 -&amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;按位或 &lt;code&gt;|&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;对应位置上的值经过或运算结果，具体参见或运算符（第 4.5.1 节），并将 T (true) 替换为 &lt;code&gt;1&lt;/code&gt;，将 F (false) 替换为 &lt;code&gt;0&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1 | 1 -&amp;gt; 1
  1 | 0 -&amp;gt; 1
  0 | 1 -&amp;gt; 1
  0 | 0 -&amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;按位异或 &lt;code&gt;^&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;对应位置上的值根据以下规则组合：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1 ^ 1 -&amp;gt; 0
  1 ^ 0 -&amp;gt; 1
  0 ^ 1 -&amp;gt; 1
  0 ^ 0 -&amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;位清除 &lt;code&gt;&amp;amp;^&lt;/code&gt;：将指定位置上的值设置为 &lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func main() {
	var x uint8 = 15
	var y uint8 = 4
	fmt.Printf(&amp;#34;%08b\n&amp;#34;, x &amp;amp;^ y);  // 00001011
}
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;一元运算符&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;按位补足 &lt;code&gt;^&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;该运算符与异或运算符一同使用，即 &lt;code&gt;m^x&lt;/code&gt;，对于无符号 &lt;code&gt;x&lt;/code&gt; 使用 “全部位设置为 1” 的规则，对于有符号 &lt;code&gt;x&lt;/code&gt; 时使用 &lt;code&gt;m=-1&lt;/code&gt;。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  ^10 = -01 ^ 10 = -11
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;位左移 &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用法：&lt;code&gt;bitP &amp;lt;&amp;lt; n&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;bitP&lt;/code&gt; 的位向左移动 &lt;code&gt;n&lt;/code&gt; 位，右侧空白部分使用 0 填充；如果 &lt;code&gt;n&lt;/code&gt; 等于 2，则结果是 2 的相应倍数，即 2 的 &lt;code&gt;n&lt;/code&gt; 次方。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  1 &amp;lt;&amp;lt; 10 // 等于 1 KB
  1 &amp;lt;&amp;lt; 20 // 等于 1 MB
  1 &amp;lt;&amp;lt; 30 // 等于 1 GB
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;位右移 &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用法：&lt;code&gt;bitP &amp;gt;&amp;gt; n&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bitP&lt;/code&gt; 的位向右移动 &lt;code&gt;n&lt;/code&gt; 位，左侧空白部分使用 0 填充；如果 &lt;code&gt;n&lt;/code&gt; 等于 2，则结果是当前值除以 2 的 n 次方。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当希望把结果赋值给第一个操作数时，可以简写为 &lt;code&gt;a &amp;lt;&amp;lt;= 2&lt;/code&gt; 或者 &lt;code&gt;b ^= a &amp;amp; 0xffffffff&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;位左移常见实现存储单位的用例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用位左移与 &lt;code&gt;iota&lt;/code&gt; 计数配合可优雅地实现存储单位的常量枚举：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type ByteSize float64
const (
	_ = iota // 通过赋值给空白标识符来忽略值
	KB ByteSize = 1&amp;lt;&amp;lt;(10*iota)
	MB
	GB
	TB
	PB
	EB
	ZB
	YB
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;在通讯中使用位左移表示标识的用例&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type BitFlag int
const (
	Active BitFlag = 1 &amp;lt;&amp;lt; iota // 1 &amp;lt;&amp;lt; 0 == 1
	Send // 1 &amp;lt;&amp;lt; 1 == 2
	Receive // 1 &amp;lt;&amp;lt; 2 == 4
)

flag := Active | Send // == 3
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;4524-逻辑运算符&#34;&gt;4.5.2.4 逻辑运算符&lt;/h3&gt;
&lt;p&gt;Go 中拥有以下逻辑运算符：&lt;code&gt;==&lt;/code&gt;、&lt;code&gt;!=&lt;/code&gt;（第 4.5.1 节）、&lt;code&gt;&amp;lt;&lt;/code&gt;、&lt;code&gt;&amp;lt;=&lt;/code&gt;、&lt;code&gt;&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;gt;=&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;它们之所以被称为逻辑运算符是因为它们的运算结果总是为布尔值 &lt;code&gt;bool&lt;/code&gt;。例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;b3 := 10 &amp;gt; 5 // b3 is true
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;4525-算术运算符&#34;&gt;4.5.2.5 算术运算符&lt;/h3&gt;
&lt;p&gt;常见可用于整数和浮点数的二元运算符有 &lt;code&gt;+&lt;/code&gt;、&lt;code&gt;-&lt;/code&gt;、&lt;code&gt;*&lt;/code&gt; 和 &lt;code&gt;/&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;（相对于一般规则而言，Go 在进行字符串拼接时允许使用对运算符 &lt;code&gt;+&lt;/code&gt; 的重载，但 Go 本身不允许开发者进行自定义的运算符重载）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/&lt;/code&gt; 对于整数运算而言，结果依旧为整数，例如：&lt;code&gt;9 / 4 -&amp;gt; 2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;取余运算符只能作用于整数：&lt;code&gt;9 % 4 -&amp;gt; 1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;整数除以 0 可能导致程序崩溃，将会导致运行时的恐慌状态（如果除以 0 的行为在编译时就能被捕捉到，则会引发编译错误）；&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt; 将会详细讲解如何正确地处理此类情况。&lt;/p&gt;
&lt;p&gt;浮点数除以 &lt;code&gt;0.0&lt;/code&gt; 会返回一个无穷尽的结果，使用 &lt;code&gt;+Inf&lt;/code&gt; 表示。&lt;/p&gt;
&lt;p&gt;你可以将语句 &lt;code&gt;b = b + a&lt;/code&gt; 简写为 &lt;code&gt;b += a&lt;/code&gt;，同样的写法也可用于 &lt;code&gt;-=&lt;/code&gt;、&lt;code&gt;*=&lt;/code&gt;、&lt;code&gt;/=&lt;/code&gt;、&lt;code&gt;%=&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;对于整数和浮点数，你可以使用一元运算符 &lt;code&gt;++&lt;/code&gt;（递增）和 &lt;code&gt;--&lt;/code&gt;（递减），但&lt;u&gt;只能用于后缀&lt;/u&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i++ -&amp;gt; i += 1 -&amp;gt; i = i + 1
i-- -&amp;gt; i -= 1 -&amp;gt; i = i - 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时，&lt;u&gt;带有 &lt;code&gt;++&lt;/code&gt; 和 &lt;code&gt;--&lt;/code&gt; 的只能作为语句，而非表达式&lt;/u&gt;，因此 &lt;code&gt;n = i++&lt;/code&gt; 这种写法是无效的，其它像 &lt;code&gt;f(i++)&lt;/code&gt; 或者 &lt;code&gt;a[i]=b[i++]&lt;/code&gt; 这些可以用于 C、C++ 和 Java 中的写法在 Go 中也是不允许的。&lt;/p&gt;
&lt;p&gt;在运算时 &lt;strong&gt;溢出&lt;/strong&gt; 不会产生错误，Go 会简单地将超出位数抛弃。如果你需要范围无限大的整数或者有理数（意味着只被限制于计算机内存），你可以使用标准库中的 &lt;code&gt;big&lt;/code&gt; 包，该包提供了类似 &lt;code&gt;big.Int&lt;/code&gt; 和 &lt;code&gt;big.Rat&lt;/code&gt; 这样的类型（&lt;a href=&#34;09.4.md&#34;&gt;第 9.4 节&lt;/a&gt;）。&lt;/p&gt;
&lt;h3 id=&#34;4526-随机数&#34;&gt;4.5.2.6 随机数&lt;/h3&gt;
&lt;p&gt;一些像游戏或者统计学类的应用需要用到随机数。&lt;code&gt;rand&lt;/code&gt; 包实现了伪随机数的生成。&lt;/p&gt;
&lt;p&gt;下面的代码演示了如何生成 10 个非负随机数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
	&amp;#34;math/rand&amp;#34;
	&amp;#34;time&amp;#34;
)

func main() {
	for i := 0; i &amp;lt; 10; i++ {
		a := rand.Int()
		fmt.Printf(&amp;#34;%d / &amp;#34;, a)
	}
	for i := 0; i &amp;lt; 5; i++ {
		r := rand.Intn(8)
		fmt.Printf(&amp;#34;%d / &amp;#34;, r)
	}
	fmt.Println()
	timens := int64(time.Now().Nanosecond())
	rand.Seed(timens)
	for i := 0; i &amp;lt; 10; i++ {
		fmt.Printf(&amp;#34;%2.2f / &amp;#34;, 100*rand.Float32())
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;可能的输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;816681689 / 1325201247 / 623951027 / 478285186 / 1654146165 /
1951252986 / 2029250107 / 762911244 / 1372544545 / 591415086 / / 3 / 0 / 6 / 4 / 2 /22.10
/ 65.77 / 65.89 / 16.85 / 75.56 / 46.90 / 55.24 / 55.95 / 25.58 / 70.61 /
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数 &lt;code&gt;rand.Float32&lt;/code&gt; 和 &lt;code&gt;rand.Float64&lt;/code&gt; 返回介于 $[0.0, 1.0)$ 之间的伪随机数，其中包括 &lt;code&gt;0.0&lt;/code&gt; 但不包括 &lt;code&gt;1.0&lt;/code&gt;。函数 &lt;code&gt;rand.Intn&lt;/code&gt; 返回介于 $[0, n)$ 之间的伪随机数。&lt;/p&gt;
&lt;p&gt;你可以使用 &lt;code&gt;rand.Seed(value)&lt;/code&gt; 函数来提供伪随机数的生成种子，一般情况下都会使用当前时间的纳秒级数字（第 4.8 节）。&lt;/p&gt;
&lt;h2 id=&#34;453-运算符与优先级&#34;&gt;4.5.3 运算符与优先级&lt;/h2&gt;
&lt;p&gt;有些运算符拥有较高的优先级，二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级，由上至下代表优先级由高到低：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;优先级 	运算符
 7 		^ !
 6 		* / % &amp;lt;&amp;lt; &amp;gt;&amp;gt; &amp;amp; &amp;amp;^
 5 		+ - | ^
 4 		== != &amp;lt; &amp;lt;= &amp;gt;= &amp;gt;
 3 		&amp;lt;-
 2 		&amp;amp;&amp;amp;
 1 		||
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，你可以通过使用括号来临时提升某个表达式的整体运算优先级。&lt;/p&gt;
&lt;h2 id=&#34;454-类型别名&#34;&gt;4.5.4 类型别名&lt;/h2&gt;
&lt;p&gt;当你在使用某个类型时，你可以给它起另一个名字，然后你就可以在你的代码中使用新的名字（用于简化名称或解决名称冲突）。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;type TZ int&lt;/code&gt; 中，&lt;code&gt;TZ&lt;/code&gt; 就是 &lt;code&gt;int&lt;/code&gt; 类型的新名称（用于表示程序中的时区），然后就可以使用 &lt;code&gt;TZ&lt;/code&gt; 来操作 &lt;code&gt;int&lt;/code&gt; 类型的数据。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;

type TZ int

func main() {
	var a, b TZ = 3, 4
	c := a + b
	fmt.Printf(&amp;#34;c has the value: %d&amp;#34;, c) // 输出：c has the value: 7
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;实际上，类型别名得到的新类型并非和原类型完全相同，新类型不会拥有原类型所附带的方法（&lt;a href=&#34;./10.0.md&#34;&gt;第 10 章&lt;/a&gt;）；&lt;code&gt;TZ&lt;/code&gt; 可以自定义一个方法用来输出更加人性化的时区信息。&lt;/p&gt;
&lt;h2 id=&#34;455-字符类型&#34;&gt;4.5.5 字符类型&lt;/h2&gt;
&lt;p&gt;严格来说，这并不是 Go 语言的一个类型，字符只是整数的特殊用例。&lt;code&gt;byte&lt;/code&gt; 类型是 &lt;code&gt;uint8&lt;/code&gt; 的别名，对于只占用 1 个字节的传统 ASCII 编码的字符来说，完全没有问题。例如：&lt;code&gt;var ch byte = &#39;A&#39;&lt;/code&gt;；字符使用单引号括起来。&lt;/p&gt;
&lt;p&gt;在 ASCII 码表中，&lt;code&gt;&#39;A&#39;&lt;/code&gt; 的值是 &lt;code&gt;65&lt;/code&gt;，而使用 16 进制表示则为 &lt;code&gt;41&lt;/code&gt;，所以下面的写法是等效的：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var ch byte = 65 或 var ch byte = &amp;#39;\x41&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;（&lt;code&gt;\x&lt;/code&gt; 总是紧跟着长度为 2 的 16 进制数）&lt;/p&gt;
&lt;p&gt;另外一种可能的写法是 &lt;code&gt;\&lt;/code&gt; 后面紧跟着长度为 3 的 8 进制数，例如：&lt;code&gt;\377&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;不过 Go 同样支持 Unicode（UTF-8），因此字符同样称为 Unicode 代码点或者 runes，并在内存中使用 &lt;code&gt;int&lt;/code&gt; 来表示。在文档中，一般使用格式 &lt;code&gt;U+hhhh&lt;/code&gt; 来表示，其中 &lt;code&gt;h&lt;/code&gt; 表示一个 16 进制数。其实 &lt;code&gt;rune&lt;/code&gt; 也是 Go 当中的一个类型，并且是 &lt;code&gt;int32&lt;/code&gt; 的别名。&lt;/p&gt;
&lt;p&gt;在书写 Unicode 字符时，需要在 16 进制数之前加上前缀 &lt;code&gt;\u&lt;/code&gt; 或者 &lt;code&gt;\U&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;因为 Unicode 至少占用 2 个字节，所以我们使用 &lt;code&gt;int16&lt;/code&gt; 或者 &lt;code&gt;int&lt;/code&gt; 类型来表示。如果需要使用到 4 字节，则会加上 &lt;code&gt;\U&lt;/code&gt; 前缀；前缀 &lt;code&gt;\u&lt;/code&gt; 则总是紧跟着长度为 4 的 16 进制数，前缀 &lt;code&gt;\U&lt;/code&gt; 紧跟着长度为 8 的 16 进制数。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var ch int = &amp;#39;\u0041&amp;#39;
var ch2 int = &amp;#39;\u03B2&amp;#39;
var ch3 int = &amp;#39;\U00101234&amp;#39;
fmt.Printf(&amp;#34;%d - %d - %d\n&amp;#34;, ch, ch2, ch3) // integer
fmt.Printf(&amp;#34;%c - %c - %c\n&amp;#34;, ch, ch2, ch3) // character
fmt.Printf(&amp;#34;%X - %X - %X\n&amp;#34;, ch, ch2, ch3) // UTF-8 bytes
fmt.Printf(&amp;#34;%U - %U - %U&amp;#34;, ch, ch2, ch3) // UTF-8 code point
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;65 - 946 - 1053236
A - β - r
41 - 3B2 - 101234
U+0041 - U+03B2 - U+101234
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;格式化说明符 &lt;code&gt;%c&lt;/code&gt; 用于表示字符；当和字符配合使用时，&lt;code&gt;%v&lt;/code&gt; 或 &lt;code&gt;%d&lt;/code&gt; 会输出用于表示该字符的整数；&lt;code&gt;%U&lt;/code&gt; 输出格式为 &lt;code&gt;U+hhhh&lt;/code&gt; 的字符串（另一个示例见&lt;a href=&#34;./05.4.md&#34;&gt;第 5.4.4 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;包 &lt;code&gt;unicode&lt;/code&gt; 包含了一些针对测试字符的非常有用的函数（其中 &lt;code&gt;ch&lt;/code&gt; 代表字符）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;判断是否为字母：&lt;code&gt;unicode.IsLetter(ch)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;判断是否为数字：&lt;code&gt;unicode.IsDigit(ch)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;判断是否为空白符号：&lt;code&gt;unicode.IsSpace(ch)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些函数返回单个布尔值。包 &lt;code&gt;utf8&lt;/code&gt; 拥有更多与 &lt;code&gt;rune&lt;/code&gt; 类型相关的函数。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Go程序的变量</title>
    <link>https://blog.wiseai.cn/post/golang/2022.06.27-%E5%8F%98%E9%87%8F/</link>
    <pubDate>Mon, 27 Jun 2022 11:46:14 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.06.27-%E5%8F%98%E9%87%8F/</guid>
    <description>
        &lt;h1 id=&#34;44-变量&#34;&gt;4.4 变量&lt;/h1&gt;
&lt;h2 id=&#34;441-简介&#34;&gt;4.4.1 简介&lt;/h2&gt;
&lt;p&gt;声明变量的一般形式是使用 &lt;code&gt;var&lt;/code&gt; 关键字：&lt;code&gt;var identifier type&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;需要注意的是，Go 和许多编程语言不同，它在声明变量时将变量的类型放在变量的名称之后。Go 为什么要选择这么做呢？&lt;/p&gt;
&lt;p&gt;首先，它是为了避免像 C 语言中那样含糊不清的声明形式，例如：&lt;code&gt;int* a, b;&lt;/code&gt;。在这个例子中，只有 &lt;code&gt;a&lt;/code&gt; 是指针而 &lt;code&gt;b&lt;/code&gt; 不是。如果你想要这两个变量都是指针，则需要将它们分开书写（你可以在 &lt;a href=&#34;http://blog.golang.org/2010/07/gos-declaration-syntax.html&#34;&gt;Go 语言的声明语法&lt;/a&gt; 页面找到有关于这个话题的更多讨论）。&lt;/p&gt;
&lt;p&gt;而在 Go 中，则可以很轻松地将它们都声明为指针类型：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a, b *int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其次，这种语法能够按照从左至右的顺序阅读，使得代码更加容易理解。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a int
var b bool
var str string
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你也可以改写成这种形式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var (
	a int
	b bool
	str string
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种因式分解关键字的写法一般用于声明全局变量。&lt;/p&gt;
&lt;p&gt;当一个变量被声明之后，系统自动赋予它该类型的零值：&lt;code&gt;int&lt;/code&gt; 为 &lt;code&gt;0&lt;/code&gt;，&lt;code&gt;float32(64)&lt;/code&gt; 为 &lt;code&gt;0.0&lt;/code&gt;，bool 为 &lt;code&gt;false&lt;/code&gt;，&lt;code&gt;string&lt;/code&gt; 为空字符串，指针为 &lt;code&gt;nil&lt;/code&gt;。记住，所有的内存在 Go 中都是经过初始化的。&lt;/p&gt;
&lt;p&gt;变量的命名规则遵循骆驼命名法，即首个单词小写，每个新单词的首字母大写，例如：&lt;code&gt;numShips&lt;/code&gt; 和 &lt;code&gt;startDate&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但如果你的全局变量希望能够被外部包所使用，则需要将首个单词的首字母也大写（第 4.2 节：可见性规则）。&lt;/p&gt;
&lt;p&gt;一个变量（常量、类型或函数）在程序中都有一定的作用范围，称之为作用域。如果一个变量在函数体外声明，则被认为是全局变量，可以在整个包甚至外部包（被导出后）使用，不管你声明在哪个源文件里或在哪个源文件里调用该变量。&lt;/p&gt;
&lt;p&gt;在函数体内声明的变量称之为局部变量，它们的作用域只在函数体内，参数和返回值变量也是局部变量。在 &lt;a href=&#34;05.0.md&#34;&gt;第 5 章&lt;/a&gt;，我们将会学习到像 &lt;code&gt;if&lt;/code&gt; 和 &lt;code&gt;for&lt;/code&gt; 这些控制结构，而在这些结构中声明的变量的作用域只在相应的代码块内。一般情况下，局部变量的作用域可以通过代码块（用大括号括起来的部分）判断。&lt;/p&gt;
&lt;p&gt;尽管变量的标识符必须是唯一的，但你可以在某个代码块的内层代码块中使用相同名称的变量，则此时外部的同名变量将会暂时隐藏（结束内部代码块的执行后隐藏的外部同名变量又会出现，而内部同名变量则被释放），你任何的操作都只会影响内部代码块的局部变量。&lt;/p&gt;
&lt;p&gt;变量可以编译期间就被赋值，赋值给变量使用运算符等号 &lt;code&gt;=&lt;/code&gt;，当然你也可以在运行时对变量进行赋值操作。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a = 15
b = false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一般情况下，当变量a和变量b之间类型相同时，才能进行如 &lt;code&gt;a = b&lt;/code&gt; 的赋值。&lt;/p&gt;
&lt;p&gt;声明与赋值（初始化）语句也可以组合起来。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var identifier [type] = value
var a int = 15
var i = 5
var b bool = false
var str string = &amp;#34;Go says hello to the world!&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但是 Go 编译器的智商已经高到可以根据变量的值来自动推断其类型，这有点像 Ruby 和 Python 这类动态语言，只不过它们是在运行时进行推断，而 Go 是在编译时就已经完成推断过程。因此，你还可以使用下面的这些形式来声明及初始化变量：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a = 15
var b = false
var str = &amp;#34;Go says hello to the world!&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var (
	a = 15
	b = false
	str = &amp;#34;Go says hello to the world!&amp;#34;
	numShips = 50
	city string
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;不过自动推断类型并不是任何时候都适用的，当你想要给变量的类型并不是自动推断出的某种类型时，你还是需要显式指定变量的类型，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var n int64 = 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然而，&lt;code&gt;var a&lt;/code&gt; 这种语法是不正确的，因为编译器没有任何可以用于自动推断类型的依据。变量的类型也可以在运行时实现自动推断，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var (
	HOME = os.Getenv(&amp;#34;HOME&amp;#34;)
	USER = os.Getenv(&amp;#34;USER&amp;#34;)
	GOROOT = os.Getenv(&amp;#34;GOROOT&amp;#34;)
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种写法主要用于声明包级别的全局变量，当你在函数体内声明局部变量时，应使用简短声明语法 &lt;code&gt;:=&lt;/code&gt;，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a := 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;下面这个例子展示了如何通过 &lt;code&gt;runtime&lt;/code&gt; 包在运行时获取所在的操作系统类型，以及如何通过 &lt;code&gt;os&lt;/code&gt; 包中的函数 &lt;code&gt;os.Getenv()&lt;/code&gt; 来获取环境变量中的值，并保存到 &lt;code&gt;string&lt;/code&gt; 类型的局部变量 &lt;code&gt;path&lt;/code&gt; 中。&lt;/p&gt;
&lt;p&gt;示例 4.5 &lt;a href=&#34;examples/chapter_4/goos.go&#34;&gt;goos.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
	&amp;#34;fmt&amp;#34;
   &amp;#34;runtime&amp;#34;
	&amp;#34;os&amp;#34;
)
func main() {
	var goos string = runtime.GOOS
	fmt.Printf(&amp;#34;The operating system is: %s\n&amp;#34;, goos)
	path := os.Getenv(&amp;#34;PATH&amp;#34;)
	fmt.Printf(&amp;#34;Path is %s\n&amp;#34;, path)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你在 Windows 下运行这段代码，则会输出 &lt;code&gt;The operating system is: windows&lt;/code&gt; 以及相应的环境变量的值；如果你在 Linux 下运行这段代码，则会输出 &lt;code&gt;The operating system is: linux&lt;/code&gt; 以及相应的的环境变量的值。&lt;/p&gt;
&lt;p&gt;这里用到了 &lt;code&gt;Printf&lt;/code&gt; 的格式化输出的功能（&lt;a href=&#34;./04.4.md&#34;&gt;第 4.4.3 节&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id=&#34;442-值类型和引用类型&#34;&gt;4.4.2 值类型和引用类型&lt;/h2&gt;
&lt;p&gt;程序中所用到的内存在计算机中使用一堆箱子来表示（这也是人们在讲解它的时候的画法），这些箱子被称为“字”。根据不同的处理器以及操作系统类型，所有的字都具有 32 位（4 字节）或 64 位（8 字节）的相同长度；所有的字都使用相关的内存地址来进行表示（以十六进制数表示）。&lt;/p&gt;
&lt;p&gt;所有像 &lt;code&gt;int&lt;/code&gt;、&lt;code&gt;float&lt;/code&gt;、&lt;code&gt;bool&lt;/code&gt; 和 &lt;code&gt;string&lt;/code&gt; 这些基本类型都属于值类型，使用这些类型的变量直接指向存在内存中的值：&lt;/p&gt;
&lt;img src=&#34;http://static.wiseai.cn/img/4.4.2_fig4.1.jpg&#34; style=&#34;zoom:67%;&#34; /&gt;
&lt;p&gt;另外，像数组（&lt;a href=&#34;./07.0.md&#34;&gt;第 7 章&lt;/a&gt;）和结构（&lt;a href=&#34;./10.0md&#34;&gt;第 10 章&lt;/a&gt;）这些复合类型也是值类型。&lt;/p&gt;
&lt;p&gt;当使用等号 &lt;code&gt;=&lt;/code&gt; 将一个变量的值赋值给另一个变量时，如：&lt;code&gt;j = i&lt;/code&gt;，实际上是在内存中将 &lt;code&gt;i&lt;/code&gt; 的值进行了拷贝：&lt;/p&gt;
&lt;img src=&#34;http://static.wiseai.cn/img/4.4.2_fig4.2.jpg&#34; style=&#34;zoom: 67%;&#34; /&gt;
&lt;p&gt;你可以通过 &lt;code&gt;&amp;amp;i&lt;/code&gt; 来获取变量 &lt;code&gt;i&lt;/code&gt; 的内存地址（&lt;a href=&#34;./04.9.md&#34;&gt;第 4.9 节&lt;/a&gt;），例如：&lt;code&gt;0xf840000040&lt;/code&gt;（每次的地址都可能不一样）。值类型的变量的值存储在栈中。&lt;/p&gt;
&lt;p&gt;内存地址会根据机器的不同而有所不同，甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局，并且位置分配也可能不同。&lt;/p&gt;
&lt;p&gt;更复杂的数据通常会需要使用多个字，这些数据一般使用引用类型保存。&lt;/p&gt;
&lt;p&gt;一个引用类型的变量 &lt;code&gt;r1&lt;/code&gt; 存储的是 &lt;code&gt;r1&lt;/code&gt; 的值所在的内存地址（数字），或内存地址中第一个字所在的位置。&lt;/p&gt;
&lt;img src=&#34;http://static.wiseai.cn/img/4.4.2_fig4.3.jpg&#34; style=&#34;zoom:67%;&#34; /&gt;
&lt;p&gt;这个内存地址被称之为指针（你可以从上图中很清晰地看到，&lt;a href=&#34;./04.9.md&#34;&gt;第 4.9 节&lt;/a&gt; 将会详细说明），这个指针实际上也被存在另外的某一个字中。&lt;/p&gt;
&lt;p&gt;同一个引用类型的指针指向的多个字可以是在连续的内存地址中（内存布局是连续的），这也是计算效率最高的一种存储形式；也可以将这些字分散存放在内存中，每个字都指示了下一个字所在的内存地址。&lt;/p&gt;
&lt;p&gt;当使用赋值语句 &lt;code&gt;r2 = r1&lt;/code&gt; 时，只有引用（地址）被复制。&lt;/p&gt;
&lt;p&gt;如果 &lt;code&gt;r1&lt;/code&gt; 的值被改变了，那么这个值的所有引用都会指向被修改后的内容，在这个例子中，&lt;code&gt;r2&lt;/code&gt; 也会受到影响。&lt;/p&gt;
&lt;p&gt;在 Go 语言中，指针（&lt;a href=&#34;./04.9.md&#34;&gt;第 4.9 节&lt;/a&gt;）属于引用类型，其它的引用类型还包括 slices（&lt;a href=&#34;07.0.md&#34;&gt;第 7 章&lt;/a&gt;），maps（&lt;a href=&#34;08.0.md&#34;&gt;第 8 章&lt;/a&gt;）和 channel（&lt;a href=&#34;13.0.md&#34;&gt;第 13 章&lt;/a&gt;）。被引用的变量会存储在堆中，以便进行垃圾回收，且比栈拥有更大的内存空间。&lt;/p&gt;
&lt;h2 id=&#34;443-打印&#34;&gt;4.4.3 打印&lt;/h2&gt;
&lt;p&gt;函数 &lt;code&gt;Printf&lt;/code&gt; 可以在 &lt;code&gt;fmt&lt;/code&gt; 包外部使用，这是因为它以大写字母 P 开头，该函数主要用于打印输出到控制台。通常使用的格式化字符串作为第一个参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Printf(format string, list of variables to be printed)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在示例 4.5 中，格式化字符串为：&lt;code&gt;&amp;quot;The operating system is: %s\n&amp;quot;&lt;/code&gt;。
这个格式化字符串可以含有一个或多个的格式化标识符，例如：&lt;code&gt;%..&lt;/code&gt;，其中 &lt;code&gt;..&lt;/code&gt; 可以被不同类型所对应的标识符替换，如 &lt;code&gt;%s&lt;/code&gt; 代表字符串标识符、&lt;code&gt;%v&lt;/code&gt; 代表使用类型的默认输出格式的标识符。这些标识符所对应的值从格式化字符串后的第一个逗号开始按照相同顺序添加，如果参数超过 1 个则同样需要使用逗号分隔。使用这些占位符可以很好地控制格式化输出的文本。
函数 &lt;code&gt;fmt.Sprintf&lt;/code&gt; 与 &lt;code&gt;Printf&lt;/code&gt; 的作用是完全相同的，不过前者将格式化后的字符串以返回值的形式返回给调用者，因此你可以在程序中使用包含变量的字符串，具体例子可以参见示例 15.4 &lt;a href=&#34;examples/chapter_15/simple_tcp_server.go&#34;&gt;simple_tcp_server.go&lt;/a&gt;。
函数 &lt;code&gt;fmt.Print&lt;/code&gt; 和 &lt;code&gt;fmt.Println&lt;/code&gt; 会自动使用格式化标识符 &lt;code&gt;%v&lt;/code&gt; 对字符串进行格式化，两者都会在每个参数之间自动增加空格，而后者还会在字符串的最后加上一个换行符。例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fmt.Print(&amp;#34;Hello:&amp;#34;, 23)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;将输出：&lt;code&gt;Hello: 23&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;444-简短形式使用--赋值操作符&#34;&gt;4.4.4 简短形式，使用 := 赋值操作符&lt;/h2&gt;
&lt;p&gt;我们知道可以在变量的初始化时省略变量的类型而由系统自动推断，而这个时候再在 Example 4.4.1 的最后一个声明语句写上 &lt;code&gt;var&lt;/code&gt; 关键字就显得有些多余了，因此我们可以将它们简写为 &lt;code&gt;a := 50&lt;/code&gt; 或 &lt;code&gt;b := false&lt;/code&gt;。
&lt;code&gt;a&lt;/code&gt; 和 &lt;code&gt;b&lt;/code&gt; 的类型（&lt;code&gt;int&lt;/code&gt; 和 &lt;code&gt;bool&lt;/code&gt;）将由编译器自动推断。
这是使用变量的首选形式，但是它只能被用在函数体内，而不可以用于全局变量的声明与赋值。使用操作符 &lt;code&gt;:=&lt;/code&gt; 可以高效地创建一个新的变量，称之为初始化声明。
&lt;strong&gt;注意事项&lt;/strong&gt;
如果在相同的代码块中，我们不可以再次对于相同名称的变量使用初始化声明，例如：&lt;code&gt;a := 20&lt;/code&gt; 就是不被允许的，编译器会提示错误 &lt;code&gt;no new variables on left side of :=&lt;/code&gt;，但是 &lt;code&gt;a = 20&lt;/code&gt; 是可以的，因为这是给相同的变量赋予一个新的值。
如果你在定义变量 &lt;code&gt;a&lt;/code&gt; 之前使用它，则会得到编译错误 &lt;code&gt;undefined: a&lt;/code&gt;。
如果你声明了一个局部变量却没有在相同的代码块中使用它，同样会得到编译错误，例如下面这个例子当中的变量 &lt;code&gt;a&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func main() {
   var a string = &amp;#34;abc&amp;#34;
   fmt.Println(&amp;#34;hello, world&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;尝试编译这段代码将得到错误 &lt;code&gt;a declared and not used&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;此外，单纯地给 &lt;code&gt;a&lt;/code&gt; 赋值也是不够的，这个值必须被使用，所以使用 &lt;code&gt;fmt.Println(&amp;quot;hello, world&amp;quot;, a)&lt;/code&gt; 会移除错误。&lt;/p&gt;
&lt;p&gt;但是全局变量是允许声明但不使用。&lt;/p&gt;
&lt;p&gt;其他的简短形式为：&lt;/p&gt;
&lt;p&gt;同一类型的多个变量可以声明在同一行，如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a, b, c int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(这是将类型写在标识符后面的一个重要原因)&lt;/p&gt;
&lt;p&gt;多变量可以在同一行进行赋值，如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a, b, c = 5, 7, &amp;#34;abc&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面这行假设了变量 &lt;code&gt;a&lt;/code&gt;，&lt;code&gt;b&lt;/code&gt; 和 &lt;code&gt;c&lt;/code&gt; 都已经被声明，否则的话应该这样使用：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a, b, c := 5, 7, &amp;#34;abc&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;右边的这些值以相同的顺序赋值给左边的变量，所以 &lt;code&gt;a&lt;/code&gt; 的值是 &lt;code&gt;5&lt;/code&gt;， &lt;code&gt;b&lt;/code&gt; 的值是 &lt;code&gt;7&lt;/code&gt;，&lt;code&gt;c&lt;/code&gt; 的值是 &lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这被称为 &lt;strong&gt;并行&lt;/strong&gt; 或 &lt;strong&gt;同时&lt;/strong&gt; 赋值。&lt;/p&gt;
&lt;p&gt;如果你想要交换两个变量的值，则可以简单地使用 &lt;code&gt;a, b = b, a&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;(在 Go 语言中，这样省去了使用交换函数的必要)&lt;/p&gt;
&lt;p&gt;空白标识符 &lt;code&gt;_&lt;/code&gt; 也被用于抛弃值，如值 &lt;code&gt;5&lt;/code&gt; 在：&lt;code&gt;_, b = 5, 7&lt;/code&gt; 中被抛弃。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;_&lt;/code&gt; 实际上是一个只写变量，你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量，但有时你并不需要使用从一个函数得到的所有返回值。&lt;/p&gt;
&lt;p&gt;并行赋值也被用于当一个函数返回多个返回值时，比如这里的 &lt;code&gt;val&lt;/code&gt; 和错误 &lt;code&gt;err&lt;/code&gt; 是通过调用 &lt;code&gt;Func1&lt;/code&gt; 函数同时得到：&lt;code&gt;val, err = Func1(var1)&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;445-init-函数&#34;&gt;4.4.5 init 函数&lt;/h2&gt;
&lt;p&gt;变量除了可以在全局声明中初始化，也可以在 &lt;code&gt;init()&lt;/code&gt; 函数中初始化。这是一类非常特殊的函数，它不能够被人为调用，而是在每个包完成初始化后自动执行，并且执行优先级比 &lt;code&gt;main()&lt;/code&gt; 函数高。&lt;/p&gt;
&lt;p&gt;每个源文件可以包含多个 &lt;code&gt;init()&lt;/code&gt; 函数，同一个源文件中的 &lt;code&gt;init()&lt;/code&gt; 函数会按照从上到下的顺序执行，如果一个包有多个源文件包含 &lt;code&gt;init()&lt;/code&gt; 函数的话，则官方鼓励但不保证以文件名的顺序调用。初始化总是以单线程并且按照包的依赖关系顺序执行。&lt;/p&gt;
&lt;p&gt;一个可能的用途是在开始执行程序之前对数据进行检验或修复，以保证程序状态的正确性。&lt;/p&gt;
&lt;p&gt;示例 4.6 &lt;a href=&#34;examples/chapter_4/init.go&#34;&gt;init.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package trans
import &amp;#34;math&amp;#34;
var Pi float64
func init() {
   Pi = 4 * math.Atan(1) // init() function computes Pi
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在它的 &lt;code&gt;init()&lt;/code&gt; 函数中计算变量 &lt;code&gt;Pi&lt;/code&gt; 的初始值。&lt;/p&gt;
&lt;p&gt;示例 4.7 &lt;a href=&#34;examples/chapter_4/user_init.go&#34;&gt;user_init.go&lt;/a&gt; 中导入了包 &lt;code&gt;trans&lt;/code&gt;（需要 &lt;code&gt;init.go&lt;/code&gt; 目录为 &lt;code&gt;./trans/init.go&lt;/code&gt; ）并且使用到了变量 &lt;code&gt;Pi&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
   &amp;#34;fmt&amp;#34;
   &amp;#34;./trans&amp;#34;
)
var twoPi = 2 * trans.Pi
func main() {
   fmt.Printf(&amp;#34;2*Pi = %g\n&amp;#34;, twoPi) // 2*Pi = 6.283185307179586
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;init()&lt;/code&gt; 函数也经常被用在当一个程序开始之前调用后台执行的 goroutine，如下面这个例子当中的 &lt;code&gt;backend()&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func init() {
   // setup preparations
   go backend()
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;练习&lt;/strong&gt; 推断以下程序的输出，并解释你的答案，然后编译并执行它们。&lt;/p&gt;
&lt;p&gt;练习 4.1 &lt;a href=&#34;examples/chapter_4/local_scope.go&#34;&gt;local_scope.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
var a = &amp;#34;G&amp;#34;
func main() {
   n()
   m()
   n()
}
func n() { print(a) }
func m() {
   a := &amp;#34;O&amp;#34;
   print(a)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;练习 4.2 &lt;a href=&#34;examples/chapter_4/global_scope.go&#34;&gt;global_scope.go&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
var a = &amp;#34;G&amp;#34;
func main() {
   n()
   m()
   n()
}
func n() {
   print(a)
}
func m() {
   a = &amp;#34;O&amp;#34;
   print(a)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;练习 4.3 &lt;a href=&#34;examples/chapter_4/function_calls_function.go&#34;&gt;function_calls_function.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
var a string
func main() {
   a = &amp;#34;G&amp;#34;
   print(a)
   f1()
}
func f1() {
   a := &amp;#34;O&amp;#34;
   print(a)
   f2()
}
func f2() {
   print(a)
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的常量</title>
    <link>https://blog.wiseai.cn/post/golang/2022.06.27-%E5%B8%B8%E9%87%8F/</link>
    <pubDate>Mon, 27 Jun 2022 11:36:06 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.06.27-%E5%B8%B8%E9%87%8F/</guid>
    <description>
        &lt;h1 id=&#34;43-常量&#34;&gt;4.3 常量&lt;/h1&gt;
&lt;p&gt;常量使用关键字 &lt;code&gt;const&lt;/code&gt; 定义，用于存储不会改变的数据。&lt;/p&gt;
&lt;p&gt;存储在常量中的数据类型只可以是布尔型、数字型（整数型、浮点型和复数）和字符串型。&lt;/p&gt;
&lt;p&gt;常量的定义格式：&lt;code&gt;const identifier [type] = value&lt;/code&gt;，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const Pi = 3.14159
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在 Go 语言中，你可以省略类型说明符 &lt;code&gt;[type]&lt;/code&gt;，因为编译器可以根据变量的值来推断其类型。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显式类型定义： &lt;code&gt;const b string = &amp;quot;abc&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;隐式类型定义： &lt;code&gt;const b = &amp;quot;abc&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个没有指定类型的常量被使用时，会根据其使用环境而推断出它所需要具备的类型。换句话说，未定义类型的常量会在必要时刻根据上下文来获得相关类型。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var n int
f(n + 5) // 无类型的数字型常量 “5” 它的类型在这里变成了 int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;常量的值必须是能够在编译时就能够确定的；你可以在其赋值表达式中涉及计算过程，但是所有用于计算的值必须在编译期间就能获得。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正确的做法：&lt;code&gt;const c1 = 2/3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;错误的做法：&lt;code&gt;const c2 = getNumber()&lt;/code&gt; // 引发构建错误: &lt;code&gt;getNumber() used as value&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;因为在编译期间自定义函数均属于未知，因此无法用于常量的赋值，但内置函数可以使用，如：&lt;code&gt;len()&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;数字型的常量是没有大小和符号的，并且可以使用任何精度而不会导致溢出：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const Ln2 = 0.693147180559945309417232121458\
			176568075500134360255254120680009
const Log2E = 1/Ln2 // this is a precise reciprocal
const Billion = 1e9 // float constant
const hardEight = (1 &amp;lt;&amp;lt; 100) &amp;gt;&amp;gt; 97
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;根据上面的例子我们可以看到，反斜杠 &lt;code&gt;\&lt;/code&gt; 可以在常量表达式中作为多行的连接符使用。&lt;/p&gt;
&lt;p&gt;与各种类型的数字型变量相比，你无需担心常量之间的类型转换问题，因为它们都是非常理想的数字。&lt;/p&gt;
&lt;p&gt;不过需要注意的是，当常量赋值给一个精度过小的数字型变量时，可能会因为无法正确表达常量所代表的数值而导致溢出，这会在编译期间就引发错误。另外，常量也允许使用并行赋值的形式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const beef, two, c = &amp;#34;eat&amp;#34;, 2, &amp;#34;veg&amp;#34;
const Monday, Tuesday, Wednesday, Thursday, Friday, Saturday = 1, 2, 3, 4, 5, 6
const (
	Monday, Tuesday, Wednesday = 1, 2, 3
	Thursday, Friday, Saturday = 4, 5, 6
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;常量还可以用作枚举：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (
	Unknown = 0
	Female = 1
	Male = 2
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;现在，数字 &lt;code&gt;0&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 分别代表未知性别、女性和男性。这些枚举值可以用于测试某个变量或常量的实际值，比如使用 switch/case 结构（&lt;a href=&#34;./05.3.md&#34;&gt;第 5.3 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;在这个例子中，&lt;code&gt;iota&lt;/code&gt; 可以被用作枚举值：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;const (
	a = iota
	b = iota
	c = iota
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;第一个 &lt;code&gt;iota&lt;/code&gt; 等于 0，每当 &lt;code&gt;iota&lt;/code&gt; 在新的一行被使用时，它的值都会自动加 1，并且没有赋值的常量默认会应用上一行的赋值表达式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// 赋值一个常量时，之后没赋值的常量都会应用上一行的赋值表达式
const (
	a = iota  // a = 0
	b         // b = 1
	c         // c = 2
	d = 5     // d = 5   
	e         // e = 5
)
// 赋值两个常量，iota 只会增长一次，而不会因为使用了两次就增长两次
const (
	Apple, Banana = iota + 1, iota + 2 // Apple=1 Banana=2
	Cherimoya, Durian                  // Cherimoya=2 Durian=3
	Elderberry, Fig                    // Elderberry=3, Fig=4
)
// 使用 iota 结合 位运算 表示资源状态的使用案例
const (
	Open = 1 &amp;lt;&amp;lt; iota  // 0001
	Close             // 0010
	Pending           // 0100
)
const (
	_           = iota             // 使用 _ 忽略不需要的 iota
	KB = 1 &amp;lt;&amp;lt; (10 * iota)          // 1 &amp;lt;&amp;lt; (10*1)
	MB                             // 1 &amp;lt;&amp;lt; (10*2)
	GB                             // 1 &amp;lt;&amp;lt; (10*3)
	TB                             // 1 &amp;lt;&amp;lt; (10*4)
	PB                             // 1 &amp;lt;&amp;lt; (10*5)
	EB                             // 1 &amp;lt;&amp;lt; (10*6)
	ZB                             // 1 &amp;lt;&amp;lt; (10*7)
	YB                             // 1 &amp;lt;&amp;lt; (10*8)
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;（ &lt;strong&gt;译者注：关于 &lt;code&gt;iota&lt;/code&gt; 的使用涉及到非常复杂多样的情况，这里作者解释的并不清晰，因为很难对 &lt;code&gt;iota&lt;/code&gt; 的用法进行直观的文字描述。如希望进一步了解，请观看视频教程 &lt;a href=&#34;https://github.com/Unknwon/go-fundamental-programming&#34;&gt;《Go编程基础》&lt;/a&gt; &lt;a href=&#34;https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture4.md&#34;&gt;第四课：常量与运算符&lt;/a&gt;&lt;/strong&gt; ）&lt;/p&gt;
&lt;p&gt;&lt;code&gt;iota&lt;/code&gt; 也可以用在表达式中，如：&lt;code&gt;iota + 50&lt;/code&gt;。在每遇到一个新的常量块或单个常量声明时， &lt;code&gt;iota&lt;/code&gt; 都会重置为 0（ &lt;strong&gt;简单地讲，每遇到一次 const 关键字，&lt;code&gt;iota&lt;/code&gt; 就重置为 0&lt;/strong&gt; ）。&lt;/p&gt;
&lt;p&gt;当然，常量之所以为常量就是恒定不变的量，因此我们无法在程序运行过程中修改它的值；如果你在代码中试图修改常量的值则会引发编译错误。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>大数的阶乘</title>
    <link>https://blog.wiseai.cn/post/%E5%A4%A7%E6%95%B0%E7%9A%84%E9%98%B6%E4%B9%98/</link>
    <pubDate>Mon, 27 Jun 2022 10:38:51 +0800</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E5%A4%A7%E6%95%B0%E7%9A%84%E9%98%B6%E4%B9%98/</guid>
    <description>
        &lt;p&gt;直接上代码:&lt;/p&gt;
&lt;h2 id=&#34;c语言&#34;&gt;C语言:&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;time.h&amp;gt;

//给计算结果一个数组，并指定大小为4096
//第一次写程序使用的字符串保存大数，但是有个问题，需要char到int来回转换，影响计算性能。
//所以使用数组来保存数据，方便计算
int jieguo[40960] = {0};

//数组长度
int len = 0;

//fac为计算阶乘的函数，计算结果存储在jieguo[4096]数组中
int fac(int n)
{
	int v = 0;
	int f = 0;

	//初始化jieguo数组和数组长度
	jieguo[0] = 1;
	len = 1;

	for(int i = 1; i &amp;lt;= n; i++)
	{
		for(int j = len - 1; j &amp;gt;= 0; j--)
		{
			v = i * jieguo[j];
			jieguo[j] = v % 10;
			v = v / 10;

			f = j + 1;

			//如果v大于0，说明还要进位，所以将进位的数加上原来的数，判断进位后存储。
			//为了提高效率，使用for进行循环
			for(; v &amp;gt; 0 &amp;amp;&amp;amp; f &amp;lt; len; f++)
			{
				v = v + jieguo[f];
				jieguo[f] = v % 10;
				v = v / 10;
			}
			
			//如果这时f等于数组的长度len并且v还大于0，说明需要增加jieguo数组的长度
			//为了提高效率，仍然使用for进行循环
			for(; v &amp;gt; 0;)
			{
				jieguo[len] = v % 10;
				v = v / 10;
				len++;
				
				if(len &amp;gt; 40960 - 2)
				{
					//这个时候说明数组长度不够，需要增加jieguo后面的数值
					//计算100的阶乘的数组长度为158，计算1000的阶乘的数组长度为2568。
					//可以根据实际需要进行设置。
					return 1;
				}
			}
		}		
	}
	return 0;
}

//下面写主函数
int main(void)
{
	int num;
	printf(&amp;#34;请输入需要计算阶乘的数字:&amp;#34;);
	scanf(&amp;#34;%d&amp;#34;, &amp;amp;num);
	
	//计算阶乘结果
	int r = fac(num);
	//如果返回1,则说明数组长度不够，需要增加长度
	if(r == 1)
	{
		printf(&amp;#34;计算结果太大，请增加数组长度！\n&amp;#34;);
		return 0;
	}
	printf(&amp;#34;%d的阶乘为：&amp;#34;, num);
	for(int i = len - 1; i &amp;gt;= 0; i--)
	{
		printf(&amp;#34;%d&amp;#34;, jieguo[i]);
	}
	printf(&amp;#34;\n&amp;#34;);
	printf(&amp;#34;%d\n&amp;#34;, len);
	printf(&amp;#34;运行时间:%.2lf秒\n&amp;#34;, (double)clock() / CLOCKS_PER_SEC);
	printf(&amp;#34;\n&amp;#34;);
	return 0;	
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;golang&#34;&gt;golang:&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main

import &amp;#34;fmt&amp;#34;
import &amp;#34;time&amp;#34;

const m int = 40960

func fac(n int) ([m]int, int) {
	var jieguo [m]int
	var len int
	var v, f = 0, 0

	jieguo[0] = 1
	len = 1
	
	for i := 1; i &amp;lt;= n; i++{
		for j := len - 1; j &amp;gt;= 0; j--{
			v = i * jieguo[j]
			jieguo[j] = v % 10
			v = v / 10 
			
			f = j + 1

			for ; v &amp;gt; 0 &amp;amp;&amp;amp; f &amp;lt; len; f++ {
				v = v + jieguo[f]
				jieguo[f] = v % 10
				v = v / 10
			}

			for ; v &amp;gt; 0; len++ {
				jieguo[len] = v % 10
				v = v / 10
				
				if len &amp;gt; m - 2 {
					fmt.Println(&amp;#34;error!&amp;#34;)
					return jieguo, len
				}
			}
		}
	}
	return jieguo, len
}

func main() {
	a := 10000
	startTime := time.Now()
	f, len := fac(a)
	fmt.Printf(&amp;#34;%d的阶乘为：&amp;#34;, a)
	for i := len - 1; i &amp;gt;= 0; i-- {
		fmt.Print(f[i])
	}
	fmt.Print(&amp;#34;\n&amp;#34;)
	fmt.Println(len)
	
	t := time.Since(startTime)
    fmt.Println(t.Seconds())
}
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Go程序的基本结构和要素</title>
    <link>https://blog.wiseai.cn/post/golang/2022.06.22-go%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84%E5%92%8C%E8%A6%81%E7%B4%A0/</link>
    <pubDate>Wed, 22 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang/2022.06.22-go%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84%E5%92%8C%E8%A6%81%E7%B4%A0/</guid>
    <description>
        &lt;h1 id=&#34;42-go-程序的基本结构和要素&#34;&gt;4.2 Go 程序的基本结构和要素&lt;/h1&gt;
&lt;p&gt;示例 hello_world.go&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34;
func main() {
	fmt.Println(&amp;#34;hello, world&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;421-包的概念导入与可见性&#34;&gt;4.2.1 包的概念、导入与可见性&lt;/h2&gt;
&lt;p&gt;包是结构化代码的一种方式：每个程序都由包（通常简称为 pkg）的概念组成，可以使用自身的包或者从其它包中导入内容。&lt;/p&gt;
&lt;p&gt;如同其它一些编程语言中的类库或命名空间的概念，每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 &lt;code&gt;.go&lt;/code&gt; 为扩展名的源文件组成，因此文件名和包名一般来说都是不相同的。&lt;/p&gt;
&lt;p&gt;&lt;u&gt;你必须在源文件中非注释的第一行指明这个文件属于哪个包&lt;/u&gt;，如：&lt;code&gt;package main&lt;/code&gt;。&lt;code&gt;package main&lt;/code&gt; 表示一个可独立执行的程序，&lt;u&gt;每个 Go 应用程序都包含一个名为 &lt;code&gt;main&lt;/code&gt; 的包&lt;/u&gt;。&lt;/p&gt;
&lt;p&gt;一个应用程序可以包含不同的包，而且即使你只使用 main 包也不必把所有的代码都写在一个巨大的文件里：你可以用一些较小的文件，并且在每个文件非注释的第一行都使用 &lt;code&gt;package main&lt;/code&gt; 来指明这些文件都属于 &lt;code&gt;main&lt;/code&gt; 包。如果你打算编译包名不是为 main 的源文件，如 &lt;code&gt;pack1&lt;/code&gt;，编译后产生的对象文件将会是 &lt;code&gt;pack1.a&lt;/code&gt; 而不是可执行程序。另外要注意的是，&lt;u&gt;所有的包名都应该使用小写字母&lt;/u&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标准库&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Go 的安装文件里包含了一些可以直接使用的包，即标准库。在 Windows 下，标准库的位置在 Go 根目录下的子目录 &lt;code&gt;pkg\windows_386&lt;/code&gt; 中；在 Linux 下，标准库在 Go 根目录下的子目录 &lt;code&gt;pkg\linux_amd64&lt;/code&gt; 中（如果是安装的是 32 位，则在 &lt;code&gt;linux_386&lt;/code&gt; 目录中）。一般情况下，标准包会存放在 &lt;code&gt;$GOROOT/pkg/$GOOS_$GOARCH/&lt;/code&gt; 目录下。&lt;/p&gt;
&lt;p&gt;Go 的标准库包含了大量的包（如：&lt;code&gt;fmt&lt;/code&gt; 和 &lt;code&gt;os&lt;/code&gt;），但是你也可以创建自己的包（&lt;a href=&#34;.%5C09.0.md&#34;&gt;第 9 章&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;如果想要构建一个程序，则包和包内的文件都必须以正确的顺序进行编译。包的依赖关系决定了其构建顺序。&lt;/p&gt;
&lt;p&gt;属于同一个包的源文件必须全部被一起编译，一个包即是编译时的一个单元，因此根据惯例，每个目录都只包含一个包。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果对一个包进行更改或重新编译，所有引用了这个包的客户端程序都必须全部重新编译。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Go 中的包模型采用了显式依赖关系的机制来达到快速编译的目的，编译器会从后缀名为 &lt;code&gt;.o&lt;/code&gt; 的对象文件（需要且只需要这个文件）中提取传递依赖类型的信息。&lt;/p&gt;
&lt;p&gt;如果 &lt;code&gt;A.go&lt;/code&gt; 依赖 &lt;code&gt;B.go&lt;/code&gt;，而 &lt;code&gt;B.go&lt;/code&gt; 又依赖 &lt;code&gt;C.go&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编译 &lt;code&gt;C.go&lt;/code&gt;, &lt;code&gt;B.go&lt;/code&gt;, 然后是 &lt;code&gt;A.go&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;为了编译 &lt;code&gt;A.go&lt;/code&gt;, 编译器读取的是 &lt;code&gt;B.o&lt;/code&gt; 而不是 &lt;code&gt;C.o&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种机制对于编译大型的项目时可以显著地提升编译速度。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;每一段代码只会被编译一次&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个 Go 程序是通过 &lt;code&gt;import&lt;/code&gt; 关键字将一组包链接在一起。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;import &amp;quot;fmt&amp;quot;&lt;/code&gt; 告诉 Go 编译器这个程序需要使用 &lt;code&gt;fmt&lt;/code&gt; 包（的函数，或其他元素），&lt;code&gt;fmt&lt;/code&gt; 包实现了格式化 IO（输入/输出）的函数。包名被封闭在半角双引号 &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; 中。如果你打算从已编译的包中导入并加载公开声明的方法，不需要插入已编译包的源代码。&lt;/p&gt;
&lt;p&gt;如果需要多个包，它们可以被分别导入：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import &amp;#34;fmt&amp;#34;
import &amp;#34;os&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import &amp;#34;fmt&amp;#34;; import &amp;#34;os&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但是还有更短且更优雅的方法（被称为因式分解关键字，该方法同样适用于 &lt;code&gt;const&lt;/code&gt;、&lt;code&gt;var&lt;/code&gt; 和 &lt;code&gt;type&lt;/code&gt; 的声明或定义）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import (
   &amp;#34;fmt&amp;#34;
   &amp;#34;os&amp;#34;
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;它甚至还可以更短的形式，但使用 gofmt 后将会被强制换行：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;import (&amp;#34;fmt&amp;#34;; &amp;#34;os&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;当你导入多个包时，最好按照字母顺序排列包名，这样做更加清晰易读。&lt;/p&gt;
&lt;p&gt;如果包名不是以 &lt;code&gt;.&lt;/code&gt; 或 &lt;code&gt;/&lt;/code&gt; 开头，如 &lt;code&gt;&amp;quot;fmt&amp;quot;&lt;/code&gt; 或者 &lt;code&gt;&amp;quot;container/list&amp;quot;&lt;/code&gt;，则 Go 会在全局文件进行查找；如果包名以 &lt;code&gt;./&lt;/code&gt; 开头，则 Go 会在相对目录中查找；如果包名以 &lt;code&gt;/&lt;/code&gt; 开头（在 Windows 下也可以这样使用），则会在系统的绝对路径中查找。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;译者注：以相对路径在GOPATH下导入包会产生报错信息&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;报错信息：local import &amp;ldquo;./XXX&amp;rdquo; in non-local package&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;引用：&lt;a href=&#34;https://golang.org/cmd/go/#hdr-Relative_import_paths&#34;&gt;Go programs cannot use relative import paths within a work space.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;注解：在GOPATH外可以以相对路径的形式执行go build（go install 不可以）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;导入包即等同于包含了这个包的所有的代码对象。&lt;/p&gt;
&lt;p&gt;除了符号 &lt;code&gt;_&lt;/code&gt;，包中所有代码对象的标识符必须是唯一的，以避免名称冲突。但是相同的标识符可以在不同的包中使用，因为可以使用包名来区分它们。&lt;/p&gt;
&lt;p&gt;包通过下面这个被编译器强制执行的规则来决定是否将自身的代码对象暴露给外部文件：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可见性规则&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当标识符（包括常量、变量、类型、函数名、结构字段等等）以一个大写字母开头，如：Group1，那么使用这种形式的标识符的对象就可以被外部包的代码所使用（客户端程序需要先导入这个包），这被称为导出（像面向对象语言中的 public）；标识符如果以小写字母开头，则对包外是不可见的，但是它们在整个包的内部是可见并且可用的（像面向对象语言中的 private ）。&lt;/p&gt;
&lt;p&gt;（大写字母可以使用任何 Unicode 编码的字符，比如希腊文，不仅仅是 ASCII 码中的大写字母）。&lt;/p&gt;
&lt;p&gt;因此，在导入一个外部包后，能够且只能够访问该包中导出的对象。&lt;/p&gt;
&lt;p&gt;假设在包 &lt;code&gt;pack1&lt;/code&gt; 中我们有一个变量或函数叫做 &lt;code&gt;Thing&lt;/code&gt;（以 T 开头，所以它能够被导出），那么在当前包中导入 &lt;code&gt;pack1&lt;/code&gt; 包，&lt;code&gt;Thing&lt;/code&gt; 就可以像面向对象语言那样使用点标记来调用：&lt;code&gt;pack1.Thing&lt;/code&gt;（pack1 在这里是不可以省略的）。&lt;/p&gt;
&lt;p&gt;因此包也可以作为命名空间使用，帮助避免命名冲突（名称冲突）：两个包中的同名变量的区别在于它们的包名，例如 &lt;code&gt;pack1.Thing&lt;/code&gt; 和 &lt;code&gt;pack2.Thing&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可以通过使用包的别名来解决包名之间的名称冲突，或者说根据你的个人喜好对包名进行重新设置，如：&lt;code&gt;import fm &amp;quot;fmt&amp;quot;&lt;/code&gt;。下面的代码展示了如何使用包的别名：&lt;/p&gt;
&lt;p&gt;示例 4.2 &lt;a href=&#34;examples/chapter_4/alias.go&#34;&gt;alias.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import fm &amp;#34;fmt&amp;#34; // alias3
func main() {
   fm.Println(&amp;#34;hello, world&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你导入了一个包却没有使用它，则会在构建程序时引发错误，如 &lt;code&gt;imported and not used: os&lt;/code&gt;，这正是遵循了 Go 的格言：“没有不必要的代码！”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;包的分级声明和初始化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可以在使用 &lt;code&gt;import&lt;/code&gt; 导入包之后定义或声明 0 个或多个常量 (const)、变量 (var) 和类型 (type)，这些对象的作用域都是全局的（在本包范围内），所以可以被本包中所有的函数调用，然后声明一个或多个函数 (func)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;特别注意:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;导入本地包，一定要初始化mod，&lt;code&gt;go mod init&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;导入时写到目录，查看go.mod，module后的为根目录（这个说法不准确，也不知道怎么说）&lt;/p&gt;
&lt;p&gt;package名和目录名可以不同，调用时按包名调用&lt;/p&gt;
&lt;p&gt;跨包调用时注意可见性，首字母大写时才可以跨包调用
&lt;/u&gt;&lt;/p&gt;
&lt;h2 id=&#34;422-函数&#34;&gt;4.2.2 函数&lt;/h2&gt;
&lt;p&gt;这是定义一个函数最简单的格式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func functionName()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你可以在括号 &lt;code&gt;()&lt;/code&gt; 中写入 0 个或多个函数的参数（使用逗号 &lt;code&gt;,&lt;/code&gt; 分隔），每个参数的名称后面必须紧跟着该参数的类型。
&lt;u&gt;&lt;code&gt;main()&lt;/code&gt; 函数是每一个可执行程序所必须包含的，一般来说都是在启动后第一个执行的函数（如果有 &lt;code&gt;init()&lt;/code&gt; 函数则会先执行该函数）&lt;/u&gt;。如果你的 &lt;code&gt;main&lt;/code&gt; 包的源代码没有包含 &lt;code&gt;main()&lt;/code&gt; 函数，则会引发构建错误 &lt;code&gt;undefined: main.main&lt;/code&gt;。&lt;code&gt;main()&lt;/code&gt; 函数既没有参数，也没有返回类型（与 C 家族中的其它语言恰好相反）。如果你不小心为 &lt;code&gt;main()&lt;/code&gt; 函数添加了参数或者返回类型，将会引发构建错误：
func main must have no arguments and no return values results.
在程序开始执行并完成初始化后，第一个调用（程序的入口点）的函数是 &lt;code&gt;main.main()&lt;/code&gt;（如：C 语言），该函数一旦返回就表示程序已成功执行并立即退出。
函数里的代码（函数体）使用大括号 &lt;code&gt;{}&lt;/code&gt; 括起来。
左大括号 &lt;code&gt;{&lt;/code&gt; 必须与方法的声明放在同一行，这是编译器的强制规定，否则你在使用 gofmt 时就会出现错误提示：
&lt;code&gt;build-error: syntax error: unexpected semicolon or newline before {&lt;/code&gt;
（这是因为编译器会产生 &lt;code&gt;func main() ;&lt;/code&gt; 这样的结果，很明显这是错误的）
&lt;strong&gt;Go 语言虽然看起来不使用分号作为语句的结束，但实际上这一过程是由编译器自动完成，因此才会引发像上面这样的错误&lt;/strong&gt;
右大括号 &lt;code&gt;}&lt;/code&gt; 需要被放在紧接着函数体的下一行。如果你的函数非常简短，你也可以将它们放在同一行：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func Sum(a, b int) int { return a + b }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;对于大括号 &lt;code&gt;{}&lt;/code&gt; 的使用规则在任何时候都是相同的（如：&lt;code&gt;if&lt;/code&gt; 语句等）。
因此符合规范的函数一般写成如下的形式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func functionName(parameter_list) (return_value_list) {
   …
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parameter_list&lt;/code&gt; 的形式为 &lt;code&gt;(param1 type1, param2 type2, …)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;return_value_list&lt;/code&gt; 的形式为 &lt;code&gt;(ret1 type1, ret2 type2, …)&lt;/code&gt;
只有当某个函数需要被外部包调用的时候才使用大写字母开头，并遵循 Pascal 命名法；否则就遵循骆驼命名法，即第一个单词的首字母小写，其余单词的首字母大写。
下面这一行调用了 &lt;code&gt;fmt&lt;/code&gt; 包中的 &lt;code&gt;Println&lt;/code&gt; 函数，可以将字符串输出到控制台，并在最后自动增加换行字符 &lt;code&gt;\n&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;fmt.Println（&amp;#34;hello, world&amp;#34;）
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;使用 &lt;code&gt;fmt.Print(&amp;quot;hello, world\n&amp;quot;)&lt;/code&gt; 可以得到相同的结果。
&lt;code&gt;Print&lt;/code&gt; 和 &lt;code&gt;Println&lt;/code&gt; 这两个函数也支持使用变量，如：&lt;code&gt;fmt.Println(arr)&lt;/code&gt;。如果没有特别指定，它们会以默认的打印格式将变量 &lt;code&gt;arr&lt;/code&gt; 输出到控制台。
单纯地打印一个字符串或变量甚至可以使用预定义的方法来实现，如：&lt;code&gt;print&lt;/code&gt;、&lt;code&gt;println：print(&amp;quot;ABC&amp;quot;)&lt;/code&gt;、&lt;code&gt;println(&amp;quot;ABC&amp;quot;)&lt;/code&gt;、&lt;code&gt;println(i)&lt;/code&gt;（带一个变量 &lt;code&gt;i&lt;/code&gt;）。
这些函数只可以用于调试阶段，在部署程序的时候务必将它们替换成 &lt;code&gt;fmt&lt;/code&gt; 中的相关函数。
当被调用函数的代码执行到结束符 &lt;code&gt;}&lt;/code&gt; 或返回语句时就会返回，然后程序继续执行调用该函数之后的代码。
程序正常退出的代码为 &lt;code&gt;0&lt;/code&gt; 即 &lt;code&gt;Program exited with code 0&lt;/code&gt;；如果程序因为异常而被终止，则会返回非零值，如：&lt;code&gt;1&lt;/code&gt;。这个数值可以用来测试是否成功执行一个程序。&lt;/p&gt;
&lt;h2 id=&#34;423-注释&#34;&gt;4.2.3 注释&lt;/h2&gt;
&lt;p&gt;示例 4.2 &lt;a href=&#34;examples/chapter_4/hello_world2.go&#34;&gt;hello_world2.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import &amp;#34;fmt&amp;#34; // Package implementing formatted I/O.
func main() {
   fmt.Printf(&amp;#34;Καλημέρα κόσμε; or こんにちは 世界\n&amp;#34;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;上面这个例子通过打印 &lt;code&gt;Καλημέρα κόσμε; or こんにちは 世界&lt;/code&gt; 展示了如何在 Go 中使用国际化字符，以及如何使用注释。&lt;/p&gt;
&lt;p&gt;注释不会被编译，但可以通过 godoc 来使用（&lt;a href=&#34;03.6.md&#34;&gt;第 3.6 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;单行注释是最常见的注释形式，你可以在任何地方使用以 &lt;code&gt;//&lt;/code&gt; 开头的单行注释。多行注释也叫块注释，均已以 &lt;code&gt;/*&lt;/code&gt; 开头，并以 &lt;code&gt;*/&lt;/code&gt; 结尾，且不可以嵌套使用，多行注释一般用于包的文档描述或注释成块的代码片段。&lt;/p&gt;
&lt;p&gt;每一个包应该有相关注释，在 &lt;code&gt;package&lt;/code&gt; 语句之前的块注释将被默认认为是这个包的文档说明，其中应该提供一些相关信息并对整体功能做简要的介绍。一个包可以分散在多个文件中，但是只需要在其中一个进行注释说明即可。当开发人员需要了解包的一些情况时，自然会用 godoc 来显示包的文档说明，在首行的简要注释之后可以用成段的注释来进行更详细的说明，而不必拥挤在一起。另外，在多段注释之间应以空行分隔加以区分。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// Package superman implements methods for saving the world.
//
// Experience has shown that a small number of procedures can prove
// helpful when attempting to save the world.
package superman
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。如果这种注释（称为文档注释）出现在函数前面，例如函数 Abcd，则要以 &lt;code&gt;&amp;quot;Abcd...&amp;quot;&lt;/code&gt; 作为开头。&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// enterOrbit causes Superman to fly into low Earth orbit, a position
// that presents several possibilities for planet salvation.
func enterOrbit() error {
   ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;godoc 工具（&lt;a href=&#34;03.6.md&#34;&gt;第 3.6 节&lt;/a&gt;）会收集这些注释并产生一个技术文档。&lt;/p&gt;
&lt;h2 id=&#34;424-类型&#34;&gt;4.2.4 类型&lt;/h2&gt;
&lt;p&gt;变量（或常量）包含数据，这些数据可以有不同的数据类型，简称类型。使用 &lt;code&gt;var&lt;/code&gt; 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。&lt;/p&gt;
&lt;p&gt;类型可以是基本类型，如：&lt;code&gt;int&lt;/code&gt;、&lt;code&gt;float&lt;/code&gt;、&lt;code&gt;bool&lt;/code&gt;、&lt;code&gt;string&lt;/code&gt;；结构化的（复合的），如：&lt;code&gt;struct&lt;/code&gt;、&lt;code&gt;array&lt;/code&gt;、切片 (slice)、&lt;code&gt;map&lt;/code&gt;、通道 (channel)；只描述类型的行为的，如：&lt;code&gt;interface&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;结构化的类型没有真正的值，它使用 &lt;code&gt;nil&lt;/code&gt; 作为默认值（在 Objective-C 中是 nil，在 Java 中是 null，在 C 和 C++ 中是 NULL 或 0）。值得注意的是，Go 语言中不存在类型继承。&lt;/p&gt;
&lt;p&gt;函数也可以是一个确定的类型，就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func FunctionName (a typea, b typeb) typeFunc
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;你可以在函数体中的某处返回使用类型为 &lt;code&gt;typeFunc&lt;/code&gt; 的变量 &lt;code&gt;var&lt;/code&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;return var
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;一个函数可以拥有多返回值，返回类型之间需要使用逗号分割，并使用小括号 &lt;code&gt;()&lt;/code&gt; 将它们括起来，如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;func FunctionName (a typea, b typeb) (t1 type1, t2 type2)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;示例： 函数 &lt;code&gt;Atoi()&lt;/code&gt;（&lt;a href=&#34;04.7.md&#34;&gt;第 4.7 节&lt;/a&gt;）：&lt;code&gt;func Atoi(s string) (i int, err error)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;返回的形式：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;return var1, var2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这种多返回值一般用于判断某个函数是否执行成功 (true/false) 或与其它返回值一同返回错误消息（详见之后的并行赋值）。&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;type&lt;/code&gt; 关键字可以定义你自己的类型，你可能想要定义一个结构体（&lt;a href=&#34;10.0.md&#34;&gt;第 10 章&lt;/a&gt;），但是也可以定义一个已经存在的类型的别名，如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type IZ int
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;这里并不是真正意义上的别名，因为使用这种方法定义之后的类型可以拥有更多的特性，且在类型转换时必须显式转换。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;然后我们可以使用下面的方式声明变量：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a IZ = 5
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这里我们可以看到 &lt;code&gt;int&lt;/code&gt; 是变量 &lt;code&gt;a&lt;/code&gt; 的底层类型，这也使得它们之间存在相互转换的可能（&lt;a href=&#34;04.2.md&#34;&gt;第 4.2.6 节&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;如果你有多个类型需要定义，可以使用因式分解关键字的方式，例如：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;type (
   IZ int
   FZ float64
   STR string
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个值都必须在经过编译后属于某个类型（编译器必须能够推断出所有值的类型），因为 Go 语言是一种静态类型语言。&lt;/p&gt;
&lt;h2 id=&#34;425-go-程序的一般结构&#34;&gt;4.2.5 Go 程序的一般结构&lt;/h2&gt;
&lt;p&gt;下面的程序可以被顺利编译但什么都做不了，不过这很好地展示了一个 Go 程序的首选结构。这种结构并没有被强制要求，编译器也不关心 &lt;code&gt;main()&lt;/code&gt; 函数在前还是变量的声明在前，但使用统一的结构能够在从上至下阅读 Go 代码时有更好的体验。&lt;/p&gt;
&lt;p&gt;所有的结构将在这一章或接下来的章节中进一步地解释说明，但总体思路如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在完成包的 &lt;code&gt;import&lt;/code&gt; 之后，开始对常量、变量和类型的定义或声明。&lt;/li&gt;
&lt;li&gt;如果存在 &lt;code&gt;init()&lt;/code&gt; 函数的话，则对该函数进行定义（这是一个特殊的函数，每个含有该函数的包都会首先执行这个函数）。&lt;/li&gt;
&lt;li&gt;如果当前包是 &lt;code&gt;main&lt;/code&gt; 包，则定义 &lt;code&gt;main()&lt;/code&gt; 函数。&lt;/li&gt;
&lt;li&gt;然后定义其余的函数，首先是类型的方法，接着是按照 &lt;code&gt;main()&lt;/code&gt; 函数中先后调用的顺序来定义相关函数，如果有很多函数，则可以按照字母顺序来进行排序。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例 4.4 &lt;a href=&#34;examples/chapter_4/gotemplate.go&#34;&gt;gotemplate.go&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;package main
import (
   &amp;#34;fmt&amp;#34;
)
const c = &amp;#34;C&amp;#34;
var v int = 5
type T struct{}
func init() { // initialization of package
}
func main() {
   var a int
   Func1()
   // ...
   fmt.Println(a)
}
func (t T) Method1() {
   //...
}
func Func1() { // exported function Func1
   //...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Go 程序的执行（程序启动）顺序如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;按顺序导入所有被 &lt;code&gt;main&lt;/code&gt; 包引用的其它包，然后在每个包中执行如下流程：&lt;/li&gt;
&lt;li&gt;如果该包又导入了其它的包，则从第一步开始递归执行，但是每个包只会被导入一次。&lt;/li&gt;
&lt;li&gt;然后以相反的顺序在每个包中初始化常量和变量，如果该包含有 &lt;code&gt;init()&lt;/code&gt; 函数的话，则调用该函数。&lt;/li&gt;
&lt;li&gt;在完成这一切之后，&lt;code&gt;main&lt;/code&gt; 也执行同样的过程，最后调用 &lt;code&gt;main()&lt;/code&gt; 函数开始执行程序。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;426-类型转换&#34;&gt;4.2.6 类型转换&lt;/h2&gt;
&lt;p&gt;在必要以及可行的情况下，一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换，因此所有的转换都必须显式说明，就像调用一个函数一样（类型在这里的作用可以看作是一种函数）：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;valueOfTypeB = typeB(valueOfTypeA)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;类型 B 的值 = 类型 B(类型 A 的值)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;a := 5.0
b := int(a)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但这只能在定义正确的情况下转换成功，例如从一个取值范围较小的类型转换到一个取值范围较大的类型（例如将 &lt;code&gt;int16&lt;/code&gt; 转换为 &lt;code&gt;int32&lt;/code&gt;）。当从一个取值范围较大的转换到取值范围较小的类型时（例如将 &lt;code&gt;int32&lt;/code&gt; 转换为 &lt;code&gt;int16&lt;/code&gt; 或将 &lt;code&gt;float32&lt;/code&gt; 转换为 &lt;code&gt;int&lt;/code&gt;），会发生精度丢失（截断）的情况。当编译器捕捉到非法的类型转换时会引发编译时错误，否则将引发运行时错误。&lt;/p&gt;
&lt;p&gt;具有相同底层类型的变量之间可以相互转换：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;var a IZ = 5
c := int(a)
d := IZ(c)
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;427-go-命名规范&#34;&gt;4.2.7 Go 命名规范&lt;/h2&gt;
&lt;p&gt;干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。Go 语言中对象的命名也应该是简洁且有意义的。像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码的可读性。名称不需要指出自己所属的包，因为在调用的时候会使用包名作为限定符。返回某个对象的函数或方法的名称一般都是使用名词，没有 &lt;code&gt;Get...&lt;/code&gt; 之类的字符，如果是用于修改某个对象，则使用 &lt;code&gt;SetName()&lt;/code&gt;。有必须要的话可以使用大小写混合的方式，如 &lt;code&gt;MixedCaps()&lt;/code&gt; 或 &lt;code&gt;mixedCaps()&lt;/code&gt;，而不是使用下划线来分割多个名称。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux中firefox支持flash</title>
    <link>https://blog.wiseai.cn/post/linux%E4%B8%ADfirefox%E6%94%AF%E6%8C%81flash/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E4%B8%ADfirefox%E6%94%AF%E6%8C%81flash/</guid>
    <description>
        &lt;h2 id=&#34;这个有点旧了参考下&#34;&gt;这个有点旧了，参考下&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进入adobe中下载flash&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解压文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cp ./libflashplayer.so /home/wiseai/.mozilla/plugins/&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;chmod 755 /home/wiseai/.mozilla/plugins/libflashplayer.so&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;搞定&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux远程连接软件</title>
    <link>https://blog.wiseai.cn/post/linux%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5%E8%BD%AF%E4%BB%B6/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5%E8%BD%AF%E4%BB%B6/</guid>
    <description>
        &lt;p&gt;远程桌面客户端:Remmina Remote Desktop Client&lt;/p&gt;
&lt;p&gt;安装：apt install remmina&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>NetworkManager网络管理相关问题</title>
    <link>https://blog.wiseai.cn/post/networkmanager%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/networkmanager%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/</guid>
    <description>
        &lt;ol&gt;
&lt;li&gt;相关配置文件位置&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;/etc/dbus-1/system.d/NetworkManager.conf
/etc/dbus-1/system.d/nm-avahi-autoipd.conf
/etc/dbus-1/system.d/nm-dhcp-client.conf
/etc/dbus-1/system.d/nm-dispatcher.conf
/etc/dbus-1/system.d/nm-system-settings.conf
/etc/rc.d/init.d/NetworkManager&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;/etc/dbus-1/system.d/nm-applet.conf&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不使用Network Manager 管理某些网络设备
默认情况下，NetworkManager 管理除 lo（环回）设备以外的所有网络设备，因为一些情况需要将设备设置为 unmanaged，也就是不使用NetworkManager管理这些设备。
&lt;ul&gt;
&lt;li&gt;查看设备状态
&lt;code&gt;# nmcli device status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 /etc/NetworkManager/NetworkManager.conf 配置文件的[main] 层级下启用插件 keyfile。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[main]
plugins=keyfile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个一般都启用了。
- 创建 /etc/NetworkManager/conf.d/unmanaged-devices.conf 配置文件（这个文件名没有要求，自己容易识别就可以了），包含以下内容：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[keyfile]
unmanaged-devices=interface-name:wlan*
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;以分号隔开；&lt;/li&gt;
&lt;li&gt;可以使用通配符来匹配接口；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;interface-name:eth*,except:interface-name:eth0;&lt;/code&gt;表示：除了 eth0，其他以 eth 开头的接口全部 unmanaged；&lt;/li&gt;
&lt;li&gt;可以通过 mac 地址来排除接口，&lt;code&gt;mac:66:77:88:99:00:aa&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;- 重启服务
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;# systemctl restart network-manager.service&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>php5-fpm.sock failed</title>
    <link>https://blog.wiseai.cn/post/php5-fpm.sock-failed/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/php5-fpm.sock-failed/</guid>
    <description>
        &lt;p&gt;/etc/php5/fpm/pool.d/www.conf&lt;/p&gt;
&lt;p&gt;里面找到这样一段代码：&lt;/p&gt;
&lt;p&gt;listen = 127.0.0.1:9000&lt;/p&gt;
&lt;p&gt;在这上面代码的下面添加一行：&lt;/p&gt;
&lt;p&gt;listen = /var/run/php5-fpm.sock&lt;/p&gt;
&lt;p&gt;保存后启动php5-fpm&lt;/p&gt;
&lt;p&gt;/etc/init.d/php5-fpm restart&lt;/p&gt;
&lt;p&gt;这时就可以正常访问了&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>php截取中文字符串乱码问题</title>
    <link>https://blog.wiseai.cn/post/php%E6%88%AA%E5%8F%96%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/php%E6%88%AA%E5%8F%96%E4%B8%AD%E6%96%87%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98/</guid>
    <description>
        &lt;p&gt;php截取字符串有两个函数:&lt;/p&gt;
&lt;p&gt;一个是substr()，这个用来截取全字母的字符串，截取UTF-8的汉字就会出错；&lt;/p&gt;
&lt;p&gt;另一个是mb_substr()，这个用来截取汉字，但一定要注明编码方式：mb_substr(&amp;lsquo;我的祖国是中国&amp;rsquo;, 0, 4, &amp;lsquo;utf-8&amp;rsquo;);&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>samba服务器配置和使用</title>
    <link>https://blog.wiseai.cn/post/samba%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%85%8D%E7%BD%AE%E5%92%8C%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/samba%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%85%8D%E7%BD%AE%E5%92%8C%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;p&gt;安装：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#dnf install samba&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;端口：&lt;/p&gt;
&lt;p&gt;管理工作组：UDP137，138&lt;/p&gt;
&lt;p&gt;共享数据：TCP139，445（不一定存在）&lt;/p&gt;
&lt;p&gt;启动服务：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl start  smb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl enable smb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;配置文件（/etc/samba/smb.conf）:&lt;/p&gt;
&lt;p&gt;1.先设定好服务器整体环境方面的参数&lt;/p&gt;
&lt;p&gt;[global]&lt;/p&gt;
&lt;p&gt;config file = /etc/samba/conf/smb.conf.%m&lt;/p&gt;
&lt;p&gt;说明：config file可以让你使用另一个配置文件来覆盖缺省的配置文件。如果文件 不存在，则该项无效。这个参数很有用，可以使得samba配置更灵活，可以让一台samba服务器模拟多台不同配置的服务器。比如，你想让PC1（主机 名）这台电脑在访问Samba Server时使用它自己的配置文件，那么先在/etc/samba/host/下为PC1配置一个名为smb.conf.pc1的文件，然后在 smb.conf中加入：config file = /etc/samba/conf/smb.conf.%m。这样当PC1请求连接Samba Server时，smb.conf.%m就被替换成smb.conf.pc1。这样，对于PC1来说，它所使用的Samba服务就是由 smb.conf.pc1定义的，而其他机器访问Samba Server则还是应用smb.conf。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#用户组

workgroup = mygroup

#欢迎信息

server string = Samba Server Version %v

#加密

security = user

#使用TDB 数据库格式

passdb backend = tdbsam

#不加载打印机

load printers = no

#共享名

[homes]

#说明

comment = Home Directories

#除了使用者自己外,不可被其他人浏览

browseable = no

#挂载后可擦写此分享

writable = yes

#建立档案的权限为 664

create mode = 0664

#建立目录的权限为 775

directory mode = 0775

#指定允许访问该共享资源的用户&amp;amp;&amp;amp;多个用户或者组中间用逗号隔开，如果要加入一个组就用&amp;#34;@组名&amp;#34;表示,%s表示代替前一个[]里的内容

valid users = %S

valid users = MYDOMAIN\%S

[share]

comment = Public Stuff

#实际的 Linux 上面的目录位置

path = /home/samba

#用来指定该共享的管理员（对该共享具有完全控制权限）

admin users = admin

#指定允许访问该共享资源的用户或者组

valid users = share,@share

#指定不允许访问该共享资源的用户

invalid users = guest

#除了使用者自己外,不可被其他人浏览

browseable = no

#指定可以在该共享下写入文件的用户

write list = share,@share

#指定该共享是否允许guest账户访问

public = no

#指定该共享路径是否可写

writable = yes

#可以在该共享下写入文件的用户和组

write list = share,@share
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;用 testparm 查阅 smb.conf 的语法设定正确性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;3.建立目录&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#mkdir /home/project&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#chmod 2770 /home/project&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;最后改变目录用户或者组属性&lt;/p&gt;
&lt;p&gt;4.设定可使用 Samba 的用户账号与密码&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#useradd -M -s /sbin/nologin -d /dev/null -G user1 share&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;使用 pdbedit 指令功能&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pdbedit -L [-vw]        &amp;lt;==单纯的察看帐户信息&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pdbedit -a&#39;-r&#39;-x -u 账号       &amp;lt;==新增/修改/删除账号&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pdbedit -a -m -u 机器账号       &amp;lt;==与 PDC 有关的机器码&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选项与参数:&lt;/p&gt;
&lt;p&gt;-L :列出目前在数据库当中的账号与 UID 等相关信息;&lt;/p&gt;
&lt;p&gt;-v :需要搭配 -L 来执行,可列出更多的讯息,包括家目录等数据;&lt;/p&gt;
&lt;p&gt;-w :需要搭配 -L 来执行,使用旧版的 smbpasswd 格式来显示数据;&lt;/p&gt;
&lt;p&gt;-a :新增一个可使用 Samba 的账号,后面的账号需要在 /etc/passwd 内存在者;&lt;/p&gt;
&lt;p&gt;-r :修改一个账号的相关信息,需搭配很多特殊参数,请 man pdbedit;&lt;/p&gt;
&lt;p&gt;-x :删除一个可使用 Samba 的账号,可先用 -L 找到账号后再删除;&lt;/p&gt;
&lt;p&gt;-m :后面接的是机器的代码 (machine account),与 domain model 有关!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# pdbedit -a -u share&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改密码:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#smbpasswd share&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;5.查看分享资源&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#smbclient -L [//主机或 IP] [-U 使用者账号]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;6.挂载&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#mount -t cifs //127.0.0.1/share /mnt -o username=share,password=123456&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;7.smbstatus:观察 SAMBA 的状态&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# smbstatus [-pS] [-u username]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选项与参数:&lt;/p&gt;
&lt;p&gt;-p :列出已经使用 SAMBA 联机的程序 PID ;&lt;/p&gt;
&lt;p&gt;-S :列出已经被使用的资源共享状态;&lt;/p&gt;
&lt;p&gt;-u :只列出某个用户相关的分享数据&lt;/p&gt;
&lt;p&gt;8.SELinux设置&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#允许共享Home目录&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setsebool -P samba_enable_home_dirs on&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;#更改SELinux用户&lt;/p&gt;
&lt;p&gt;&lt;code&gt;chcon -t samba_share_t /path/to/directory&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>ssh加密算法的启用禁用方法</title>
    <link>https://blog.wiseai.cn/post/ssh%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95%E7%9A%84%E5%90%AF%E7%94%A8%E7%A6%81%E7%94%A8%E6%96%B9%E6%B3%95/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/ssh%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95%E7%9A%84%E5%90%AF%E7%94%A8%E7%A6%81%E7%94%A8%E6%96%B9%E6%B3%95/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;“手机ES文件浏览器”不能连接sftp的问题解决方法
首先，说说为什么学习这个东西，在使用“手机ES文件浏览器”这个软件的时候，出现了不能连接的情况，在网上查询后发现是KexAlgorithms 密钥交换算法的问题，在/etc/ssh/sshd_config后添加:
&lt;code&gt;KexAlgorithms +diffie-hellman-group14-sha1&lt;/code&gt;
后问题解决。&lt;/li&gt;
&lt;li&gt;出现no matching host key type found.Their offer: ssh-rsa,ssh-dss这个问题时,在/etc/ssh/sshd_config后添加：
&lt;code&gt;HostKeyAlgorithms ssh-rsa&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;配置位置etcsshsshd_config&#34;&gt;配置位置：/etc/ssh/sshd_config&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Ciphers
Ciphers 指定 ssh 使用的加密算法。多个加密算法之间使用逗号分隔。当 Ciphers 的值以 + 字符开始时，指定的加密算法将附加到默认集合，不影响默认集合中的其它算法。当 Ciphers 的值以 ‘-’ 字符开始时，指定的加密算法将会从默认集合中移除，不影响默认集合中的其它项目。
比如，在 sshd_config 文件的最后添加如下行：
&lt;code&gt;Ciphers +3des-cbc,aes128-cbc&lt;/code&gt;
这样就添加了两个加密算法。
&lt;code&gt;Ciphers -3des-cbc,aes128-cbc&lt;/code&gt;
这样就删除了两个加密算法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MACs
MACs 选项指定可用的 MAC（消息认证代码）算法，用于数据完整性保护。配置方法与 Ciphers 一致。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;KexAlgorithms
KexAlgorithms 选项指定可用的密钥交换算法。配置方法与 Ciphers 一致。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PubkeyAcceptedKeyTypes
PubkeyAcceptedKeyTypes 指定公钥认证允许的密钥类型。配置方法与 Ciphers 一致。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看 ssh 支持的不同类别的加密算法
Ciphers:&lt;code&gt; ssh -Q cipher&lt;/code&gt;
MACs:&lt;code&gt; ssh -Q mac&lt;/code&gt;
KexAlgorithms:&lt;code&gt; ssh -Q kex&lt;/code&gt;
PubkeyAcceptedKeyTypes:&lt;code&gt; ssh -Q key&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

    </description>
    </item>
    
    <item>
    <title>vi和vim使用</title>
    <link>https://blog.wiseai.cn/post/vi%E5%92%8Cvim%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/vi%E5%92%8Cvim%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h4 id=&#34;三种模式分别是一般指令模式编辑模式与指令列命令模式&#34;&gt;三种模式，分别是『一般指令模式』、『编辑模式』与『指令列命令模式』。&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;一般指令模式(command mode)
以vi 打开一个档案就直接进入一般指令模式了(这是预设的模式，也简称为一般模式)。在这个模式中， 你可以使用『上下左右』按键来移动游标，你可以使用『删除字元』或『删除整列』来处理档案内容， 也可以使用『复制、贴上』来处理你的文件资料。
编辑模式(insert mode)
在一般指令模式中可以进行删除、复制、贴上等等的动作，但是却无法编辑文件内容的！要等到你按下『i, I, o, O, a, A, r, R』等任何一个字母之后才会进入编辑模式。注意了！通常在Linux中，按下这些按键时，在画面的左下方会出现『INSERT或REPLACE』的字样，此时才可以进行编辑。而如果要回到一般指令模式时，则必须要按下『Esc』这个按键即可退出编辑模式。
指令列命令模式(command-line mode)
在一般模式当中，输入『 : / ?』三个中的任何一个按钮，就可以将游标移动到最底下那一列。在这个模式当中，可以提供你『搜寻资料』的动作，而读取、存档、大量取代字元、离开vi 、显示行号等等的动作则是在此模式中达成的！
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;按键说明&#34;&gt;按键说明&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;一般指令模式可用的按键说明，游标移动、复制贴上、搜寻取代等
屏幕『向下』移动半页: [Ctrl] + [d]
屏幕『向上』移动半页: [Ctrl] + [u]
游标向右移动这一列的n个字符: n空格键
移动到这个档案的第n行#: nG
游标向下移动n行#:n回车键
搜寻与取代
在第n1与n2列之间寻找word1这个字串，并将该字串取代为word2
#:n1,n2s/word1/word2/g
从第一列到最后一列寻找word1字串，并将该字串取代为word2
#:1,$s/word1/word2/g
从第一列到最后一列寻找word1字串，并将该字串取代为word2,且在取代前显示提示字元给使用者确认(confirm)是否需要取代
#:1,$s/word1/word2/gc
删除、复制与粘贴
删除游标所在到第一行的所有资料#:d1G
删除游标所在到最后一列的所有资料#:dG
删除游标所在处，到该行的最后一个字符#:d$
删除游标所在处，到该行的最前面一个字符#:d0
将游标所在行与下一行的资料结合成同一列#:J
复原前一个动作#:u
重做上一个动作#:[Ctrl]+r
重复前一个动作#:.
区块选择的按键意义
用长方形的方式选择区块#:[Ctrl]+v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;多档案编辑&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vim /etc/hosts /etc/resolv.conf
编辑下一个档案::n
编辑上一个档案::N
列出目前这个vim的开启的所有档案::files
多视窗功能

在指令列模式输入『:sp {filename}』  
『[ctrl]+w+↑』[ctrl]+w+j及『[ctrl]+w+↓』[ctrl]+w+k在两个视窗之间移动&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>下载命令wget</title>
    <link>https://blog.wiseai.cn/post/%E4%B8%8B%E8%BD%BD%E5%91%BD%E4%BB%A4wget/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E4%B8%8B%E8%BD%BD%E5%91%BD%E4%BB%A4wget/</guid>
    <description>
        &lt;p&gt;选项:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-b 后台下载
-O 文件名为***（大写o）
-i 下载文件中的URL
-t 设置重试次数（0为无限次）
-c 断点续传
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其它参数：wget –help&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>使用caddy搭建webdav服务器</title>
    <link>https://blog.wiseai.cn/post/%E4%BD%BF%E7%94%A8caddy%E6%90%AD%E5%BB%BAwebdav%E6%9C%8D%E5%8A%A1%E5%99%A8/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E4%BD%BF%E7%94%A8caddy%E6%90%AD%E5%BB%BAwebdav%E6%9C%8D%E5%8A%A1%E5%99%A8/</guid>
    <description>
        &lt;ol&gt;
&lt;li&gt;首先下载或者编译包含Webdav插件的Caddy&lt;/li&gt;
&lt;li&gt;生成密码（设置123456789的密码）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# caddy hash-password --plaintext 123456789&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;JDJhJDE0JHpGUDNaL1Q5UEV2dWZxd1BCYWdGUS56WkdZbHJDZG8ybURERWYyRzlzLzd0TTM2akYxNFVh&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;编辑Caddyfile&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    {
            http_port 80
            https_port 443
            order webdav before file_server
    }
    
    :6666 {
            root * /home/xjc/software/caddy/
            encode zstd gzip
            basicauth {
	           #用户名   上面命令生成的密码
                    admin JDJhJDE0JHpGUDNaL1Q5UEV2dWZxd1BCYWdGUS56WkdZbHJDZG8ybURERWYyRzlzLzd0TTM2akYxNFVh
            }
            route {
                    rewrite /webdav /webdav/
                    webdav /webdav/* {
                            prefix /webdav
                    }
            }
    }
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;运行服务&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# caddy run -config Caddyfile -watch&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# caddy start&lt;/code&gt;后台运行服务&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# caddy stop&lt;/code&gt;停止服务&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;挂载webdav网盘&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# sudo mount.davfs http://127.0.0.1:6666/webdav /file/to/path/ -o uid=uos,gid=uos&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>使用OpenSSL生成证书</title>
    <link>https://blog.wiseai.cn/post/%E4%BD%BF%E7%94%A8openssl%E7%94%9F%E6%88%90%E8%AF%81%E4%B9%A6/</link>
    <pubDate>Mon, 20 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E4%BD%BF%E7%94%A8openssl%E7%94%9F%E6%88%90%E8%AF%81%E4%B9%A6/</guid>
    <description>
        &lt;p&gt;&lt;code&gt;openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out cert.pem -keyout key.pem&lt;/code&gt;&lt;br&gt;
这个命令就可以生成了,下面的是网上的,可以参考.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下载安装OpenSSL，进入/bin/下面，执行命令（把ssl目录下的openssl.cnf 拷贝到bin目录下）&lt;/p&gt;
&lt;p&gt;1.首先要生成服务器端的私钥(key文件):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
openssl genrsa -des3 -out server.key 1024

[root@airwaySSL openssl]# cd ssl/

[root@airwaySSL ssl]# pwd

/home/openssl/ssl

[root@airwaySSL ssl]# ls

certs  man  misc  openssl.cnf  private  server.csr  server.key
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;运行时会提示输入密码,此密码用于加密key文件(参数des3便是指加密算法,当然也可以选用其他你认为安全的算法.),以后每当需读取此文件(通过openssl提供的命令或API)都需输入口令.如果觉得不方便,也可以去除这个口令,但一定要采取其他的保护措施!&lt;/p&gt;
&lt;p&gt;去除key文件口令的命令:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
openssl rsa -in server.key -out server.key
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.openssl req -new -key server.key -out server.csr -config openssl.cnf&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
[root@airwaySSL bin]# openssl req -new -key server.key -out server.csr -config openssl.cnf

Enter pass phrase for server.key:12345

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter &amp;#39;.&amp;#39;, the field will be left blank.
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
Country Name (2 letter code) [AU]:CN

State or Province Name (full name) [Some-State]:china

Locality Name (eg, city) []:wuhan

Organization Name (eg, company) [Internet Widgits Pty Ltd]:airway

Organizational Unit Name (eg, section) []:airway

Common Name (eg, YOUR name) []:airway

Email Address []:

Please enter the following &amp;#39;extra&amp;#39; attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:

生成Certificate Signing Request（CSR）,生成的csr文件交给CA签名后形成服务端自己的证书.屏幕上将有提示,依照其指示一步一步输入要求的个人信息即可.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;3.对客户端也作同样的命令生成key及csr文件:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
openssl genrsa -des3 -out client.key 1024

Generating RSA private key, 1024 bit long modulus

...........++++++

..++++++

e is 65537 (0x10001)

Enter pass phrase for client.key:12345

Verifying - Enter pass phrase for client.key:12345

openssl req -new -key client.key -out client.csr -config openssl.cnf

[root@airwaySSL bin]# openssl req -new -key client.key -out client.csr -config openssl.cnf

Enter pass phrase for client.key:12345

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter &amp;#39;.&amp;#39;, the field will be left blank.
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
Country Name (2 letter code) [AU]:cn

State or Province Name (full name) [Some-State]:china

Locality Name (eg, city) []:wuhan

Organization Name (eg, company) [Internet Widgits Pty Ltd]:airway

Organizational Unit Name (eg, section) []:airway

Common Name (eg, YOUR name) []:airway

Email Address []:

Please enter the following &amp;#39;extra&amp;#39; attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;4.CSR文件必须有CA的签名才可形成证书.可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
openssl req -new -x509 -keyout ca.key -out ca.crt -config openssl.cnf

[root@airwaySSL bin]# openssl req -new -x509 -keyout ca.key -out ca.crt -config openssl.cnf

Generating a 1024 bit RSA private key

...++++++

...................++++++

writing new private key to &amp;#39;ca.key&amp;#39;

Enter PEM pass phrase:12345

Verifying - Enter PEM pass phrase:

-----

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter &amp;#39;.&amp;#39;, the field will be left blank.

-----

Country Name (2 letter code) [AU]:CN

State or Province Name (full name) [Some-State]:china

Locality Name (eg, city) []:wuhan

Organization Name (eg, company) [Internet Widgits Pty Ltd]:airway

Organizational Unit Name (eg, section) []:airway

Common Name (eg, YOUR name) []:airway

Email Address []:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;在继续下面操作前，将openssl.conf文件打开，查看其dir路径将其修改为dir = /home/openssl/bin/demoCA /，否则下面的步骤会提示路径无法找到。&lt;/p&gt;
&lt;p&gt;自己手动创建一个CA目录结构：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;
[weigw@TEST bin]$ mkdir ./demoCA

[weigw@TEST bin]$ mkdir
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Raspberry Pi 4 boot EEPROM</title>
    <link>https://blog.wiseai.cn/post/raspberry-pi-4-boot-eeprom/</link>
    <pubDate>Sun, 19 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/raspberry-pi-4-boot-eeprom/</guid>
    <description>
        &lt;h1 id=&#34;raspberry-pi-4-boot-eeprom&#34;&gt;Raspberry Pi 4 boot EEPROM&lt;/h1&gt;
&lt;p&gt;官方文档：https://www.raspberrypi.org/documentation/hardware/raspberrypi/booteeprom.md&lt;/p&gt;
&lt;h1 id=&#34;升级&#34;&gt;升级&lt;/h1&gt;
&lt;h2 id=&#34;updating-from-raspberry-pi-os&#34;&gt;Updating from Raspberry Pi OS&lt;/h2&gt;
&lt;p&gt;Bootloader updates are instigated during a normal apt update, apt full-upgrade cycle, this means you will get new features and bug fixes during your normal updates.&lt;/p&gt;
&lt;p&gt;Bootloader updates are performed by rpi-eeprom-update service provided by the rpi-eeprom package. This service runs at boot and updates the bootloader at the next reboot if a new production release is available. The service automatically migrates the current boot settings to the new bootloader release.&lt;/p&gt;
&lt;p&gt;To update your system, including the bootloader:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt update
sudo apt full-upgrade
sudo reboot
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也就是说在更新系统的时候同时更新eeprom&lt;/p&gt;
&lt;h2 id=&#34;manually-checking-if-an-update-is-available&#34;&gt;Manually checking if an update is available&lt;/h2&gt;
&lt;p&gt;提前说一句，手动检查更新，必须更新rpi-eeprom这个软件包，单独更新可以使用安装命令。&lt;/p&gt;
&lt;p&gt;Running the rpi-eeprom-update command with no parameters indicates whether an update is required. An update is required if the version of the most recent file in the firmware directory (normally /lib/firmware/raspberrypi/bootloader/critical) is newer than that reported by the current bootloader. The images under /lib/firmware/raspberrypi/bootloader are part of the rpi-eeprom package and are only updated via apt upgrade.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo rpi-eeprom-update&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If an update is available, you can install it using:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo rpi-eeprom-update -a&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo reboot&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Reading the current EEPROM version&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vcgencmd bootloader_version&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;firmware-release-status&#34;&gt;Firmware release status&lt;/h2&gt;
&lt;p&gt;The firmware release status corresponds to a particular subdirectory of bootloader firmware images (/lib/firmware/raspberrypi/bootloader/&amp;hellip;), and can be changed to select a different release stream. By default, Raspberry Pi OS only selects critical updates (security fixes or major hardware compatiblity changes) since most users do not use alternate boot modes (TFTP, USB etc)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;critical - Default - rarely updated&lt;/li&gt;
&lt;li&gt;stable - Updated when new/advanced features have been successfully beta tested.&lt;/li&gt;
&lt;li&gt;beta - New or experimental features are tested here first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since the release status string is just a subdirectory name then it&amp;rsquo;s possible to create your own release streams e.g. a pinned release or custom network boot configuration.&lt;/p&gt;
&lt;p&gt;Changing the firmware release
You can change which release stream is to be used during an update by editing the /etc/default/rpi-eeprom-update file and changing the FIRMWARE_RELEASE_STATUS entry to the appropriate stream.&lt;/p&gt;
&lt;p&gt;For more information about the rpi-eeprom-update configuration file please run &lt;code&gt;rpi-eeprom-update -h&lt;/code&gt;.&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>更改ubuntu源</title>
    <link>https://blog.wiseai.cn/post/%E6%9B%B4%E6%94%B9ubuntu%E6%BA%90/</link>
    <pubDate>Sun, 19 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E6%9B%B4%E6%94%B9ubuntu%E6%BA%90/</guid>
    <description>
        &lt;p&gt;&lt;code&gt;sed -i &#39;s#http://archive.ubuntu.com/ubuntu/#mirror://mirrors.ubuntu.com/mirrors.txt#&#39; /etc/apt/sources.list&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>locate命令</title>
    <link>https://blog.wiseai.cn/post/locate%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Sat, 18 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/locate%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;h5 id=&#34;locate命令用于查找符合条件的文档他会去保存文档和目录名称的数据库内查找合乎范本样式条件的文档或目录&#34;&gt;locate命令用于查找符合条件的文档，他会去保存文档和目录名称的数据库内，查找合乎范本样式条件的文档或目录。&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;# locate [-d ][--help][--version][查找内容文本]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;参数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-b, --basename         仅匹配路径名的基本名称，**也就是说文件名包含的才显示，目录不显示**。
-c, --count            只输出找到的数量
-d, --database DBPATH  使用 DBPATH 指定的数据库，而不是默认数据库 /var/lib/mlocate/mlocate.db
-e, --existing         仅打印当前现有文件的条目
-1                     如果是 1则启动安全模式。在安全模式下，使用者不会看到权限无法看到的档案。这会始速度减慢，因为 locate 必须至实际的档案系统中取得档案的权限资料。
-0, --null             在输出上带有NUL的单独条目
-S, --statistics       不搜索条目，打印有关每个数据库的统计信息
-P, --nofollow, -H     检查文件存在时不要遵循尾随的符号链接
-l, --limit, -n LIMIT  将输出（或计数）限制为LIMIT个条目
-n                     至多显示 n个输出。
-m, --mmap             被忽略，为了向后兼容
-r, --regexp REGEXP    使用基本正则表达式
--regex                使用扩展正则表达式
-q, --quiet            安静模式，不会显示任何错误讯息
-s, --stdio            被忽略，为了向后兼容
-o                     指定资料库存的名称。
-h, --help             显示帮助
-i, --ignore-case      忽略大小写
-V, --version          显示版本信息 	
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;locate 的数据库在 /var/lib/slocate/slocate.db 中，一般是系统自己维护，也可以手工升级数据库 ，命令为：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# updatedb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;默认情况下 updatedb 每天执行一次。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>整站或网页下载软件httrack</title>
    <link>https://blog.wiseai.cn/post/%E6%95%B4%E7%AB%99%E6%88%96%E7%BD%91%E9%A1%B5%E4%B8%8B%E8%BD%BD%E8%BD%AF%E4%BB%B6httrack/</link>
    <pubDate>Fri, 17 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E6%95%B4%E7%AB%99%E6%88%96%E7%BD%91%E9%A1%B5%E4%B8%8B%E8%BD%BD%E8%BD%AF%E4%BB%B6httrack/</guid>
    <description>
        &lt;p&gt;首先安装httrack:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;＃dnf install httrack&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;使用说明：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;＃httrack&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;输入项目名&lt;/p&gt;
&lt;p&gt;输入保存目录&lt;/p&gt;
&lt;p&gt;输入需要保存的网站URL或者网页URL&lt;/p&gt;
&lt;p&gt;其它的默认就好&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>搭建apt本地源</title>
    <link>https://blog.wiseai.cn/post/%E6%90%AD%E5%BB%BAapt%E6%9C%AC%E5%9C%B0%E6%BA%90/</link>
    <pubDate>Thu, 16 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E6%90%AD%E5%BB%BAapt%E6%9C%AC%E5%9C%B0%E6%BA%90/</guid>
    <description>
        &lt;h2 id=&#34;一安装apt-mirror命令&#34;&gt;一、安装apt-mirror命令&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;# apt install apt-mirror&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;二-修改apt-mirror配置文件&#34;&gt;二、 修改apt-mirror配置文件&lt;/h2&gt;
&lt;p&gt;首先， 确定自己系统的版本 ，root权限执行：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# lsb_release -a&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我的系统是ubuntu的bionic，从阿里云下载源，一般有5个目录，前三个常用，后两个是预发布软件，不常用，我一般用前三个&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然后，编辑apt-mirror的配置文件/etc/apt/mirror.list&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;### 配置镜像下载目录
set base_path /var/spool/apt-mirror
# 架构配置，i386/amd64，默认的话会下载跟本机相同的架构的源
set defaultarch amd64
# 下载线程数
set nthreads 20
set _tilde 0
# 阿里云的源（这里没有添加deb-src的源码源）
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse

# 后面还可以添加其它版本的源

# 清理不使用的软件包
clean http://mirrors.aliyun.com/ubuntu
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;三开始同步&#34;&gt;三、开始同步&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;# apt-mirror&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;注意：同步的文件必须在Linux文件系统下，所有人为_apt；否则出错。&lt;/p&gt;
&lt;h2 id=&#34;四-客户端配置&#34;&gt;四、 客户端配置&lt;/h2&gt;
&lt;p&gt;编辑/etc/apt/source.list&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;deb [arch=amd64] http://[host]:[port]/ubuntu/ bionic main restricted universe multiverse 
deb [arch=amd64] http://[host]:[port]/ubuntu/ bionic-security main restricted universe multiverse 
deb [arch=amd64] http://[host]:[port]/ubuntu/ bionic-updates main restricted universe multiverse 
deb [arch=amd64] http://[host]:[port]/ubuntu/ bionic-proposed main restricted universe multiverse 
deb [arch=amd64] http://[host]:[port]/ubuntu/ bionic-backports main restricted universe multiverse
或

deb [arch=amd64] file:///你的目录/ubuntu/ bionic main restricted universe multiverse 
deb [arch=amd64] file:///你的目录/ubuntu/ bionic-security main restricted universe multiverse 
deb [arch=amd64] file:///你的目录/ubuntu/ bionic-updates main restricted universe multiverse 
deb [arch=amd64] file:///你的目录/ubuntu/ bionic-proposed main restricted universe multiverse 
deb [arch=amd64] file:///你的目录/ubuntu/ bionic-backports main restricted universe multiverse
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>yum时出现error的解决办法</title>
    <link>https://blog.wiseai.cn/post/yum%E6%97%B6%E5%87%BA%E7%8E%B0error%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
    <pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/yum%E6%97%B6%E5%87%BA%E7%8E%B0error%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
    <description>
        &lt;h3 id=&#34;错误内容&#34;&gt;错误内容：&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Unable to detect release version (use ‘–releasever’ to specify release version)

error: rpmdb: damaged header #173 retrieved — skipping.
error: rpmdbNextIterator: skipping h# 173 blob size(55540): BAD, 8 + 16 * il(78) + dl(54284)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;解决办法&#34;&gt;解决办法：&lt;/h3&gt;
&lt;p&gt;首先：删除/var/lib/下的rpm文件夹&lt;/p&gt;
&lt;p&gt;之后：执行命令&lt;code&gt;rpm --rebuilddb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;最后：安装一个提供版本号的包&lt;code&gt;yum --releasever 8 install centos-release&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;--releasever 8&lt;/code&gt;是指定版本号，我的是centos8，所以这么写。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>树莓派蓝牙相关问题</title>
    <link>https://blog.wiseai.cn/post/%E6%A0%91%E8%8E%93%E6%B4%BE%E8%93%9D%E7%89%99%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/</link>
    <pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E6%A0%91%E8%8E%93%E6%B4%BE%E8%93%9D%E7%89%99%E7%9B%B8%E5%85%B3%E9%97%AE%E9%A2%98/</guid>
    <description>
        &lt;p&gt;&lt;strong&gt;因系统更新快，这里的操作仅供参考!!&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;升级安装蓝牙相关软件包&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# sudo apt-get install pi-bluetooth bluez bluez-firmware blueman&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;添加pi用户到蓝牙组&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# sudo usermod -G bluetooth -a pi&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;重启&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# sudo reboot&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;使用命令打开蓝牙&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pi@raspberrypi:/ $ sudo bluetoothctl
[bluetooth]#list
Controller B8:27:EB:3D:15:E9 raspberrypi [default]
[bluetooth]# scan on
Discovery started
[CHG] Device 5F:FC:4E:B0:EA:59 RSSI: -97
[NEW] Device 42:29:9A:3B:75:E6 42-29-9A-3B-75-E6
[bluetooth]# devices
Device 30:21:4F:54:4D:8E Newmine
Device 72:D7:02:62:CE:A0 72-D7-02-62-CE-A0
Device 5F:FC:4E:B0:EA:59 5F-FC-4E-B0-EA-59
Device E4:A7:C5:C8:55:F5 HUAWEI Mate 10 Pro
Device 42:29:9A:3B:75:E6 42-29-9A-3B-75-E6
[bluetooth]# pair 30:21:4F:54:4D:8E
Attempting to pair with 30:21:4F:54:4D:8E
Failed to pair: org.bluez.Error.AlreadyExists(已配对成功过，如果是初次配对是显示成功的信息）
[bluetooth]# trust 30:21:4F:54:4D:8E
Changing 30:21:4F:54:4D:8E trust succeeded
[bluetooth]# connect 30:21:4F:54:4D:8E
Attempting to connect to 30:21:4F:54:4D:8E
[CHG] Device 30:21:4F:54:4D:8E Connected: yes
Connection successful
[CHG] Device 5F:FC:4E:B0:EA:59 RSSI: -86
[CHG] Device 30:21:4F:54:4D:8E ServicesResolved: yes
[Newmine]#quit
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>MarkDown在线编辑器</title>
    <link>https://blog.wiseai.cn/post/markdown%E5%9C%A8%E7%BA%BF%E7%BC%96%E8%BE%91%E5%99%A8/</link>
    <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/markdown%E5%9C%A8%E7%BA%BF%E7%BC%96%E8%BE%91%E5%99%A8/</guid>
    <description>
        &lt;p&gt;使用开源项目&lt;a href=&#34;https://github.com/pandao/editor.md.git&#34; title=&#34;editor.md&#34;&gt;editor.md&lt;/a&gt;我的gitee仓库下也有。&lt;/p&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;# npm install editor.md&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;使用&#34;&gt;使用&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&amp;#34;utf-8&amp;#34;&amp;gt;
        &amp;lt;link rel=&amp;#34;stylesheet&amp;#34; href=&amp;#34;editor.md/css/editormd.min.css&amp;#34; /&amp;gt;
        &amp;lt;title&amp;gt;Markdown 编辑在线器&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    &amp;lt;div id=&amp;#34;text&amp;#34; style=&amp;#34;width: 100%;&amp;#34;&amp;gt;
        &amp;lt;div id=&amp;#34;editor&amp;#34; style=&amp;#34;height: 100%;&amp;#34;&amp;gt;
            &amp;lt;!-- Tips: Editor.md can auto append a `&amp;lt;textarea&amp;gt;` tag --&amp;gt;
            &amp;lt;textarea style=&amp;#34;display:none;&amp;#34; placeholder=&amp;#34;欢迎使用&amp;#34;&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;script src=&amp;#34;jquery.min.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;#34;editor.md/editormd.min.js&amp;#34;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&amp;#34;text/javascript&amp;#34;&amp;gt;
        // 设置div与窗口高度一致
        window.onload = function () {
            var h = window.innerHeight - 25 ; //获取当前窗口的高度; 
            document.getElementById(&amp;#34;text&amp;#34;).style.height = h + &amp;#34;px&amp;#34;; //设置div的高度
        }
        // 设置textarea参数
        $(function() {
            var h = window.innerHeight - 25
            var editor = editormd(&amp;#34;editor&amp;#34;, {
                // width: &amp;#34;100%&amp;#34;,
                height: h,
                saveHTMLToTextarea   : true,
                // markdown: &amp;#34;xxxx&amp;#34;,     // dynamic set Markdown text
                path : &amp;#34;editor.md/lib/&amp;#34;  // Autoload modules mode, codemirror, marked... dependents libs path
            });
        });
    &amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Nginx出现问题的解决办法</title>
    <link>https://blog.wiseai.cn/post/nginx%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
    <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/nginx%E5%87%BA%E7%8E%B0%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
    <description>
        &lt;p&gt;最近遇到Permission denied这个问题，经过排查，是目录权限的问题，server的root目录必须要有写权限才行，只有读权限还是会出现Permission denied的问题，并且要给nginx的执行用户，比如默认的www-data用户。
待续&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>sublime使用</title>
    <link>https://blog.wiseai.cn/post/sublime%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/sublime%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h2 id=&#34;安装package-control&#34;&gt;安装Package Control&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Tools-&amp;gt;Install Package Control  选项，即可调出 Package Control&lt;/li&gt;
&lt;li&gt;“Tools-&amp;gt;Command Palette&amp;hellip;”选项，快捷键Ctrl+Shift +P&lt;/li&gt;
&lt;li&gt;输入 ipc ,点击 Install Package Control，即可调出 Package Control&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;设置中文&#34;&gt;设置中文&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Preferences-&amp;gt;Package Control，快捷键Ctrl+Shift +P&lt;/li&gt;
&lt;li&gt;选项弹出命令行输入框，输入ip，点击“install Package”&lt;/li&gt;
&lt;li&gt;弹出命令行输入框，输入clz，点击“ChineseLocalizations”&lt;/li&gt;
&lt;li&gt;“帮助-&amp;gt;Language ”切换语言。&lt;/li&gt;
&lt;/ol&gt;

    </description>
    </item>
    
    <item>
    <title>systemd的服务管理程序systemctl</title>
    <link>https://blog.wiseai.cn/post/systemd%E7%9A%84%E6%9C%8D%E5%8A%A1%E7%AE%A1%E7%90%86%E7%A8%8B%E5%BA%8Fsystemctl/</link>
    <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/systemd%E7%9A%84%E6%9C%8D%E5%8A%A1%E7%AE%A1%E7%90%86%E7%A8%8B%E5%BA%8Fsystemctl/</guid>
    <description>
        &lt;p&gt;systemctl融合service和chkconfig的功能于一体,可以使用它永久性或只在当前会话中启用/禁用服务。&lt;/p&gt;
&lt;h2 id=&#34;一启动关闭启用禁用服务&#34;&gt;一、启动/关闭、启用/禁用服务&lt;/h2&gt;
&lt;p&gt;运行一个服务：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl start firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;关闭一个服务：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl stop firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;重启一个服务:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl restart firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;显示一个服务（无论运行与否）的状态:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl status firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在开机时启用一个服务：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl enable firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在开机时禁用一个服务:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl disable firewalld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;检查一个服务是否是开机启用：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl is-enabled firewalld&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;二改变运行级别&#34;&gt;二、改变运行级别&lt;/h2&gt;
&lt;p&gt;切换到运行级别3或者5：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl isolate multi-user.target&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl isolate graphical.target&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;改变默认运行级别3或者5：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target或#systemctl set-default multi-user.target&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#ln -sf /lib/systemd/system/graphical.target /etc/systemd/system/default.target或#systemctl set-default graphical.target&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查看当下运行级别:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#systemctl list-units --type=target&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;三改变默认-getty-数目&#34;&gt;三、改变默认 getty 数目&lt;/h2&gt;
&lt;p&gt;添加一个新的 getty ：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#ln -sf /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@tty9.service
#systemctl daemon-reload
#systemctl start getty@tty9.service`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;删除一个 getty ：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#rm /etc/systemd/system/getty.target.wants/getty@tty9.service
#systemctl daemon-reload
#systemctl stop getty@tty9.service
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;四读懂配置文件&#34;&gt;四、读懂配置文件&lt;/h2&gt;
&lt;p&gt;一个服务怎么启动，完全由它的配置文件决定。下面就来看，配置文件有些什么内容。&lt;/p&gt;
&lt;p&gt;前面说过，配置文件主要放在&lt;code&gt;/usr/lib/systemd/system&lt;/code&gt;目录，也可能在&lt;code&gt;/etc/systemd/system&lt;/code&gt;目录。找到配置文件以后，使用文本编辑器打开即可。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemctl cat&lt;/code&gt;命令可以用来查看配置文件，下面以&lt;code&gt;sshd.service&lt;/code&gt;文件为例，它的作用是启动一个 SSH 服务器，供其他用户以 SSH 方式登录。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ systemctl cat sshd.service&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D &amp;lt;span class=&amp;quot;hljs-variable&amp;quot;&amp;gt;$OPTIONS
ExecReload=/bin/kill -HUP &amp;lt;span class=&amp;quot;hljs-variable&amp;quot;&amp;gt;$MAINPID
Type=simple
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以看到，配置文件分成几个区块，每个区块包含若干条键值对。&lt;/p&gt;
&lt;p&gt;下面依次解释每个区块的内容。&lt;/p&gt;
&lt;h2 id=&#34;五-unit-区块启动顺序与依赖关系&#34;&gt;五、 [Unit] 区块：启动顺序与依赖关系。&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Unit&lt;/code&gt;区块的&lt;code&gt;Description&lt;/code&gt;字段给出当前服务的简单描述，&lt;code&gt;Documentation&lt;/code&gt;字段给出文档位置。&lt;/p&gt;
&lt;p&gt;接下来的设置是启动顺序和依赖关系，这个比较重要。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;After&lt;/code&gt;字段：表示如果&lt;code&gt;network.target&lt;/code&gt;或&lt;code&gt;sshd-keygen.service&lt;/code&gt;需要启动，那么&lt;code&gt;sshd.service&lt;/code&gt;应该在它们之后启动。
相应地，还有一个&lt;code&gt;Before&lt;/code&gt;字段，定义&lt;code&gt;sshd.service&lt;/code&gt;应该在哪些服务之前启动。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意，&lt;code&gt;After&lt;/code&gt;和&lt;code&gt;Before&lt;/code&gt;字段只涉及启动顺序，不涉及依赖关系。&lt;/p&gt;
&lt;p&gt;举例来说，某 Web 应用需要 postgresql 数据库储存数据。在配置文件中，它只定义要在 postgresql 之后启动，而没有定义依赖 postgresql 。上线后，由于某种原因，postgresql 需要重新启动，在停止服务期间，该 Web 应用就会无法建立数据库连接。&lt;/p&gt;
&lt;p&gt;设置依赖关系，需要使用&lt;code&gt;Wants&lt;/code&gt;字段和&lt;code&gt;Requires&lt;/code&gt;字段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Wants&lt;/code&gt;字段：表示&lt;code&gt;sshd.service&lt;/code&gt;与&lt;code&gt;sshd-keygen.service&lt;/code&gt;之间存在&amp;quot;弱依赖&amp;quot;关系，即如果&amp;quot;sshd-keygen.service&amp;quot;启动失败或停止运行，不影响&lt;code&gt;sshd.service&lt;/code&gt;继续执行。
&lt;code&gt;Requires&lt;/code&gt;字段则表示&amp;quot;强依赖&amp;quot;关系，即如果该服务启动失败或异常退出，那么&lt;code&gt;sshd.service&lt;/code&gt;也必须退出。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意，&lt;code&gt;Wants&lt;/code&gt;字段与&lt;code&gt;Requires&lt;/code&gt;字段只涉及依赖关系，与启动顺序无关，默认情况下是同时启动的。&lt;/p&gt;
&lt;h2 id=&#34;六service-区块启动行为&#34;&gt;六、[Service] 区块：启动行为&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Service&lt;/code&gt;区块定义如何启动当前服务。&lt;/p&gt;
&lt;h3 id=&#34;61-启动命令&#34;&gt;6.1 启动命令&lt;/h3&gt;
&lt;p&gt;许多软件都有自己的环境参数文件，该文件可以用&lt;code&gt;EnvironmentFile&lt;/code&gt;字段读取。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;EnvironmentFile&lt;/code&gt;字段：指定当前服务的环境参数文件。该文件内部的&lt;code&gt;key=value&lt;/code&gt;键值对，可以用&lt;code&gt;$key&lt;/code&gt;的形式，在当前配置文件中获取。
上面的例子中，sshd 的环境参数文件是&lt;code&gt;/etc/sysconfig/sshd&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;配置文件里面最重要的字段是&lt;code&gt;ExecStart&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ExecStart&lt;/code&gt;字段：定义启动进程时执行的命令。
上面的例子中，启动&lt;code&gt;sshd&lt;/code&gt;，执行的命令是&lt;code&gt;/usr/sbin/sshd -D $OPTIONS&lt;/code&gt;，其中的变量&lt;code&gt;$OPTIONS&lt;/code&gt;就来自&lt;code&gt;EnvironmentFile&lt;/code&gt;字段指定的环境参数文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;与之作用相似的，还有如下这些字段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ExecReload&lt;/code&gt;字段：重启服务时执行的命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecStop&lt;/code&gt;字段：停止服务时执行的命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecStartPre&lt;/code&gt;字段：启动服务之前执行的命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecStartPost&lt;/code&gt;字段：启动服务之后执行的命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecStopPost&lt;/code&gt;字段：停止服务之后执行的命令
请看下面的例子。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;[Service]
ExecStart=/bin/echo execstart1
ExecStart=
ExecStart=/bin/echo execstart2
ExecStartPost=/bin/echo post1
ExecStartPost=/bin/echo post2
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面这个配置文件，第二行&lt;code&gt;ExecStart&lt;/code&gt;设为空值，等于取消了第一行的设置，运行结果如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;span class=&#34;hljs-attribute&#34;&gt;execstart2
post1
post2
所有的启动设置之前，都可以加上一个连词号（&lt;code&gt;-&lt;/code&gt;），表示&amp;quot;抑制错误&amp;quot;，即发生错误的时候，不影响其他命令的执行。比如，&lt;code&gt;EnvironmentFile=-/etc/sysconfig/sshd&lt;/code&gt;（注意等号后面的那个连词号），就表示即使&lt;code&gt;/etc/sysconfig/sshd&lt;/code&gt;文件不存在，也不会抛出错误。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;62-启动类型&#34;&gt;6.2 启动类型&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Type&lt;/code&gt;字段定义启动类型。它可以设置的值如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;simple（默认值）：&lt;code&gt;ExecStart&lt;/code&gt;字段启动的进程为主进程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;forking：&lt;code&gt;ExecStart&lt;/code&gt;字段将以&lt;code&gt;fork()&lt;/code&gt;方式启动，此时父进程将会退出，子进程将成为主进程&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;oneshot：类似于&lt;code&gt;simple&lt;/code&gt;，但只执行一次，Systemd 会等它执行完，才启动其他服务&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dbus：类似于&lt;code&gt;simple&lt;/code&gt;，但会等待 D-Bus 信号后启动&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;notify：类似于&lt;code&gt;simple&lt;/code&gt;，启动结束后会发出通知信号，然后 Systemd 再启动其他服务&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;idle：类似于&lt;code&gt;simple&lt;/code&gt;，但是要等到其他任务都执行完，才会启动该服务。一种使用场合是为让该服务的输出，不与其他服务的输出相混合
下面是一个&lt;code&gt;oneshot&lt;/code&gt;的例子，笔记本电脑启动时，要把触摸板关掉，配置文件可以这样写。&lt;/p&gt;
&lt;p&gt;[Unit]
Description=Switch-off Touchpad&lt;/p&gt;
&lt;p&gt;[Service]
Type=oneshot
ExecStart=/usr/bin/touchpad-off&lt;/p&gt;
&lt;p&gt;[Install]
WantedBy=multi-user.target
上面的配置文件，启动类型设为&lt;code&gt;oneshot&lt;/code&gt;，就表明这个服务只要运行一次就够了，不需要长期运行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果关闭以后，将来某个时候还想打开，配置文件修改如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Switch-off Touchpad

[Service]
Type=oneshot
ExecStart=/usr/bin/touchpad-off start
ExecStop=/usr/bin/touchpad-off stop
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面配置文件中，&lt;code&gt;RemainAfterExit&lt;/code&gt;字段设为&lt;code&gt;yes&lt;/code&gt;，表示进程退出以后，服务仍然保持执行。这样的话，一旦使用&lt;code&gt;systemctl stop&lt;/code&gt;命令停止服务，&lt;code&gt;ExecStop&lt;/code&gt;指定的命令就会执行，从而重新开启触摸板。&lt;/p&gt;
&lt;h3 id=&#34;63-重启行为&#34;&gt;6.3 重启行为&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Service&lt;/code&gt;区块有一些字段，定义了重启行为。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;KillMode&lt;/code&gt;字段：定义 Systemd 如何停止 sshd 服务。
上面这个例子中，将&lt;code&gt;KillMode&lt;/code&gt;设为&lt;code&gt;process&lt;/code&gt;，表示只停止主进程，不停止任何sshd 子进程，即子进程打开的 SSH session 仍然保持连接。这个设置不太常见，但对 sshd 很重要，否则你停止服务的时候，会连自己打开的 SSH session 一起杀掉。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;KillMode&lt;/code&gt;字段可以设置的值如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;control-group（默认值）：当前控制组里面的所有子进程，都会被杀掉&lt;/li&gt;
&lt;li&gt;process：只杀主进程&lt;/li&gt;
&lt;li&gt;mixed：主进程将收到 SIGTERM 信号，子进程收到 SIGKILL 信号&lt;/li&gt;
&lt;li&gt;none：没有进程会被杀掉，只是执行服务的 stop 命令。
接下来是&lt;code&gt;Restart&lt;/code&gt;字段。
&lt;code&gt;Restart&lt;/code&gt;字段：定义了 sshd 退出后，Systemd 的重启方式。
上面的例子中，&lt;code&gt;Restart&lt;/code&gt;设为&lt;code&gt;on-failure&lt;/code&gt;，表示任何意外的失败，就将重启sshd。如果 sshd 正常停止（比如执行&lt;code&gt;systemctl stop&lt;/code&gt;命令），它就不会重启。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Restart&lt;/code&gt;字段可以设置的值如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;no（默认值）：退出后不会重启&lt;/li&gt;
&lt;li&gt;on-success：只有正常退出时（退出状态码为0），才会重启&lt;/li&gt;
&lt;li&gt;on-failure：非正常退出时（退出状态码非0），包括被信号终止和超时，才会重启&lt;/li&gt;
&lt;li&gt;on-abnormal：只有被信号终止和超时，才会重启&lt;/li&gt;
&lt;li&gt;on-abort：只有在收到没有捕捉到的信号终止时，才会重启&lt;/li&gt;
&lt;li&gt;on-watchdog：超时退出，才会重启&lt;/li&gt;
&lt;li&gt;always：不管是什么退出原因，总是重启
对于守护进程，推荐设为&lt;code&gt;on-failure&lt;/code&gt;。对于那些允许发生错误退出的服务，可以设为&lt;code&gt;on-abnormal&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;最后是&lt;code&gt;RestartSec&lt;/code&gt;字段。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;RestartSec&lt;/code&gt;字段：表示 Systemd 重启服务之前，需要等待的秒数。上面的例子设为等待42秒。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;七install-区块&#34;&gt;七、[Install] 区块&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Install&lt;/code&gt;区块，定义如何安装这个配置文件，即怎样做到开机启动。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;WantedBy&lt;/code&gt;字段：表示该服务所在的 Target。
&lt;code&gt;Target&lt;/code&gt;的含义是服务组，表示一组服务。&lt;code&gt;WantedBy=multi-user.target&lt;/code&gt;指的是，sshd 所在的 Target 是&lt;code&gt;multi-user.target&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个设置非常重要，因为执行&lt;code&gt;systemctl enable sshd.service&lt;/code&gt;命令时，&lt;code&gt;sshd.service&lt;/code&gt;的一个符号链接，就会放在&lt;code&gt;/etc/systemd/system&lt;/code&gt;目录下面的&lt;code&gt;multi-user.target.wants&lt;/code&gt;子目录之中。&lt;/p&gt;
&lt;p&gt;Systemd 有默认的启动 Target。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ systemctl get-default
multi-user.target
上面的结果表示，默认的启动 Target 是&lt;code&gt;multi-user.target&lt;/code&gt;。在这个组里的所有服务，都将开机启动。这就是为什么&lt;code&gt;systemctl enable&lt;/code&gt;命令能设置开机启动的原因。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 Target 的时候，&lt;code&gt;systemctl list-dependencies&lt;/code&gt;命令和&lt;code&gt;systemctl isolate&lt;/code&gt;命令也很有用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id=&#34;查看-multi-usertarget-包含的所有服务&#34;&gt;查看 multi-user.target 包含的所有服务&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ systemctl list-dependencies multi-user.target

# 切换到另一个 target
# shutdown.target 就是关机状态
$ sudo systemctl isolate shutdown.target
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;一般来说，常用的 Target 有两个：一个是&lt;code&gt;multi-user.target&lt;/code&gt;，表示多用户命令行状态；另一个是&lt;code&gt;graphical.target&lt;/code&gt;，表示图形用户状态，它依赖于&lt;code&gt;multi-user.target&lt;/code&gt;。官方文档有一张非常清晰的 &lt;a href=&#34;https://www.freedesktop.org/software/systemd/man/bootup.html#System%20Manager%20Bootup&#34;&gt;Target 依赖关系图&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;八target-的配置文件&#34;&gt;八、Target 的配置文件&lt;/h2&gt;
&lt;p&gt;Target 也有自己的配置文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ systemctl cat multi-user.target&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;注意，Target 配置文件里面没有启动命令。&lt;/p&gt;
&lt;p&gt;上面输出结果中，主要字段含义如下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Requires&lt;/code&gt;字段：要求&lt;code&gt;basic.target&lt;/code&gt;一起运行。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Conflicts&lt;/code&gt;字段：冲突字段。如果&lt;code&gt;rescue.service&lt;/code&gt;或&lt;code&gt;rescue.target&lt;/code&gt;正在运行，&lt;code&gt;multi-user.target&lt;/code&gt;就不能运行，反之亦然。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;After&lt;/code&gt;：表示&lt;code&gt;multi-user.target&lt;/code&gt;在&lt;code&gt;basic.target&lt;/code&gt; 、 &lt;code&gt;rescue.service&lt;/code&gt;、 &lt;code&gt;rescue.target&lt;/code&gt;之后启动，如果它们有启动的话。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AllowIsolate&lt;/code&gt;：允许使用&lt;code&gt;systemctl isolate&lt;/code&gt;命令切换到&lt;code&gt;multi-user.target&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;九修改配置文件后重启&#34;&gt;九、修改配置文件后重启&lt;/h2&gt;
&lt;p&gt;修改配置文件以后，需要重新加载配置文件，然后重新启动相关服务。&lt;/p&gt;
&lt;blockquote&gt;
&lt;h1 id=&#34;重新加载配置文件&#34;&gt;重新加载配置文件&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl daemon-reload&amp;gt; 
# 重启相关服务
$ sudo systemctl restart foobar
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;

    </description>
    </item>
    
    <item>
    <title>玩玩LFS-从源码构建Linux系统</title>
    <link>https://blog.wiseai.cn/post/%E7%8E%A9%E7%8E%A9lfs-%E4%BB%8E%E6%BA%90%E7%A0%81%E6%9E%84%E5%BB%BAlinux%E7%B3%BB%E7%BB%9F/</link>
    <pubDate>Fri, 10 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/%E7%8E%A9%E7%8E%A9lfs-%E4%BB%8E%E6%BA%90%E7%A0%81%E6%9E%84%E5%BB%BAlinux%E7%B3%BB%E7%BB%9F/</guid>
    <description>
        &lt;p&gt;官方网站：http://www.linuxfromscratch.org/&lt;/p&gt;
&lt;p&gt;LFS Book 翻译项目:https://github.com/LCTT/LFS-BOOK&lt;/p&gt;
&lt;p&gt;Linux From Scratch （简体中文版） Version 8.3-systemd&lt;/p&gt;
&lt;p&gt;LFS：Linux From Scratch是主要书籍，是从中衍生所有其他项目的基础。&lt;/p&gt;
&lt;p&gt;BLFS：Beyond Linux From Scratch可帮助您将已完成的LFS安装扩展到更加个性化和可用的系统。&lt;/p&gt;
&lt;p&gt;ALFS：Automated Linux From Scratch提供了自动化和管理LFS和BLFS构建的工具。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux下合并文件</title>
    <link>https://blog.wiseai.cn/post/linux%E4%B8%8B%E5%90%88%E5%B9%B6%E6%96%87%E4%BB%B6/</link>
    <pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E4%B8%8B%E5%90%88%E5%B9%B6%E6%96%87%E4%BB%B6/</guid>
    <description>
        &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/bin/bash

for ((i=0; i&amp;lt;700; i++))
do 
cat $i.ts &amp;gt;&amp;gt; all.ts
done
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;某视频APP下载位置：手机存储/Android/data/com.tencent.qqlive/files/videos_***/&lt;/p&gt;
&lt;p&gt;在这个目录下找下载的视频文件(以.hls结束的），找找就好了。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux中修改limits值</title>
    <link>https://blog.wiseai.cn/post/linux%E4%B8%AD%E4%BF%AE%E6%94%B9limits%E5%80%BC/</link>
    <pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E4%B8%AD%E4%BF%AE%E6%94%B9limits%E5%80%BC/</guid>
    <description>
        &lt;p&gt;在/etc/security/limits.conf 最后增加:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;* soft nofile 65535&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;* hard nofile 65535&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改ulimit值&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux中的搜索</title>
    <link>https://blog.wiseai.cn/post/linux%E4%B8%AD%E7%9A%84%E6%90%9C%E7%B4%A2/</link>
    <pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E4%B8%AD%E7%9A%84%E6%90%9C%E7%B4%A2/</guid>
    <description>
        &lt;p&gt;一、whereis&lt;/p&gt;
&lt;p&gt;二、which&lt;/p&gt;
&lt;p&gt;三、locate&lt;/p&gt;
&lt;p&gt;安装：mlocate&lt;/p&gt;
&lt;p&gt;之后更新数据库：updatedb&lt;/p&gt;
&lt;p&gt;四、find&lt;/p&gt;
&lt;p&gt;五、catfish&lt;/p&gt;
&lt;p&gt;一款Linux桌面图形软件，可以在桌面图形化的搜索文件。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux中缺少运行库的解决办法</title>
    <link>https://blog.wiseai.cn/post/linux%E4%B8%AD%E7%BC%BA%E5%B0%91%E8%BF%90%E8%A1%8C%E5%BA%93%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
    <pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E4%B8%AD%E7%BC%BA%E5%B0%91%E8%BF%90%E8%A1%8C%E5%BA%93%E7%9A%84%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
    <description>
        &lt;ol&gt;
&lt;li&gt;编辑 vi /etc/ld.so.conf&lt;/li&gt;
&lt;li&gt;root权限执行 /sbin/ldconfig -v命令&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样 ldd 才能找到这个库&lt;/p&gt;
&lt;p&gt;/etc/ld.so.conf:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;这个文件记录了编译时使用的动态链接库的路径。
默认情况下，编译器只会使用/lib和/usr/lib这两个目录下的库文件
如果你安装了某些库，没有指定 –prefix=/usr 这样lib库就装到了/usr/local下，而又没有在/etc/ld.so.conf中添加/usr/local/lib，就会报错了
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ldconfig是个什么东东吧 ：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;它是一个程序，通常它位于/sbin下，是root用户使用的东东。具体作用及用法可以man ldconfig查到
简单的说，它的作用就是将/etc/ld.so.conf列出的路径下的库文件 缓存到/etc/ld.so.cache 以供使用
因此当安装完一些库文件，(例如刚安装好glib)，或者修改ld.so.conf增加新的库路径后，需要运行一下/sbin/ldconfig
使所有的库文件都被缓存到ld.so.cache中，如果没做，即使库文件明明就在/usr/lib下的，也是不会被使用的，结果
编译过程中抱错，缺少xxx库。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上尝试后发现还是不行，添加 /usr/lib/x86_64-linux-gnu 有许多QT库文件&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>mariadb和mysql命令</title>
    <link>https://blog.wiseai.cn/post/mariadb%E5%92%8Cmysql%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/mariadb%E5%92%8Cmysql%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;错误：ERROR 1045 (28000): Access denied for user &amp;lsquo;root&amp;rsquo;@&amp;rsquo;localhost&amp;rsquo; (using password: NO)&lt;/p&gt;
&lt;p&gt;1.首先停掉数据库服务:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# systemctl stop mariadb.service&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;2.使用mysqld_safe来启动mysqld服务器:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysqld_safe --user=mysql --skip-grant-tables --skip-networking&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;3.无密码登录:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysql -u root mysql&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;命令行登录：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysql -h 127.0.0.1 -u root -p -P 3306&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;新建用户：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# create user &#39;newuser&#39;@&#39;localhost&#39; identified by &#39;123456&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# insert into mysql.user(user,host,password) values(&#39;newuser&#39;,&#39;localhost&#39;,password(&#39;123456&#39;));&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除用户：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# DROP USER &#39;newuser&#39;@&#39;localhost&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;授权:&lt;/p&gt;
&lt;p&gt;注：you password获取方法：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# select password(&#39;你的明文密码&#39;);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所有权限&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# grant all privileges on *.* to &#39;newuser&#39;@&#39;localhost&#39; identified by password &#39;you password&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;部分权限&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# grant insert,update,delete,select on *.* to &#39;newuser&#39;@&#39;localhost&#39;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;给予test数据库所有权限：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# grant all on test.* to &#39;newuser&#39;@&#39;localhost&#39;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;**注意：**需要执行 &lt;strong&gt;FLUSH PRIVILEGES&lt;/strong&gt; 语句重新载入授权表。&lt;/p&gt;
&lt;p&gt;查看队列：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# show processlist;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# show full processlist;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;创建数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# CREATE DATABASE IF NOT EXISTS test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;选择数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# USE test;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ALTER DATABASE test DEFAULT CHARACTER SET gb2312 DEFAULT COLLATE gb2312_chinese_ci;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# DROP DATABASE IF EXISTS test;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查看数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# SHOW DATABASES;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;创建表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `blog_users`;（如果存在，删除表再添加）
CREATE TABLE `blog_users` (
`ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_login` varchar(60) NOT NULL DEFAULT &#39;&#39;,
`user_pass` varchar(255) NOT NULL DEFAULT &#39;&#39;,
`user_nicename` varchar(50) NOT NULL DEFAULT &#39;&#39;,
`user_email` varchar(100) NOT NULL DEFAULT &#39;&#39;,
`user_url` varchar(100) NOT NULL DEFAULT &#39;&#39;,
`user_registered` datetime NOT NULL DEFAULT &#39;0000-00-00 00:00:00&#39;,
`user_activation_key` varchar(255) NOT NULL DEFAULT &#39;&#39;,
`user_status` int(11) NOT NULL DEFAULT &#39;0&#39;,
`display_name` varchar(250) NOT NULL DEFAULT &#39;&#39;,
PRIMARY KEY (`ID`),
KEY `user_login_key` (`user_login`),
KEY `user_nicename` (`user_nicename`),
KEY `user_email` (`user_email`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看表：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# SHOW TABLES;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查看表结构：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# DESC test；&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查看表如何创建：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# show create table user \G;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除表：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# DROP TABLE test;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除表中的所有数据并自增长数据从1开始：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# TRUNCATE TABLE 表名;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;插入数据：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;insert into &#39;blog_users&#39; (&#39;ID&#39;, &#39;user_login&#39;, &#39;user_pass&#39;) values (&#39;1&#39;,&#39;admin&#39;,&#39;$P$BnOWIsBwuIA7Hf3gZh9fVuY6sWkKmt1&#39;); &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# INSERT INTO test set title = &#39;这个是title&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除数据：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# DELETE FROM test where title = &#39;这个是title&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;修改数据或更新数据：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# UPDATE test set title = &#39;这个是title&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查询：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# SELECT id, title FROM test where title = &#39;这个是title&#39;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;备份数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysqldump -uroot -p123456 数据库 &amp;gt; /tmp/数据库.sql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;恢复数据库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysql -uroot -p123456 数据库 &amp;lt; /tmp/数据库.sql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;备份表：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysqldump -uroot -p 数据库 表 &amp;gt; /tmp/表.sql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;恢复表：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysql -uroot -p 数据库 &amp;lt; /tmp/表.sql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;备份所有库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysqldump -uroot -p -A &amp;gt; /tmp/123.sql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;只备份表结构：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# mysqldump -uroot -p -d 数据库 &amp;gt; /tmp/数据库.sql&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux屏录软件</title>
    <link>https://blog.wiseai.cn/post/linux%E5%B1%8F%E5%BD%95%E8%BD%AF%E4%BB%B6/</link>
    <pubDate>Tue, 31 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E5%B1%8F%E5%BD%95%E8%BD%AF%E4%BB%B6/</guid>
    <description>
        &lt;h3 id=&#34;一simplescreenrecorder&#34;&gt;一、SimpleScreenRecorder&lt;/h3&gt;
&lt;p&gt;可以记录其他的应用程序和你的屏幕上运行的游戏。这是一个简单但功能强大，功能丰富的屏幕记录软件，包括一个易于使用的界面。
只支持Linux系统，包含在rpmfusion中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# yum install simplescreenrecorder&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# apt install simplescreenrecorder&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其它安装方式：https://www.maartenbaert.be/simplescreenrecorder/&lt;/p&gt;
&lt;p&gt;它的一些显著特点包括:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;基于QT的简单GUI
可以记录整个屏幕或它的一部分	
从OpenGL的应用程序直接记录
良好的视频和音频同步
有助于减少慢速机的视频帧速率
支持暂停和恢复功能
显示了了在记录过程期间的统计
支持录制过程中预览
默认设置已经很好，不需要进行过多设置
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;二-obs-open-broadcaster-software&#34;&gt;二、 OBS (Open Broadcaster Software)&lt;/h3&gt;
&lt;p&gt;OBS 是一个免费、开源和跨平台的视频记录和流媒体应用程序,它可以工作在Linux、Windows和Mac OS X。
Windows需要从官网下载（https://obsproject.com/），Linux系统包含在rpmfusion中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;＃ yum install obs-studio&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# apt install ffmpeg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# add-apt-repository ppa:obsproject/obs-studio&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# apt update&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# apt install obs-studio&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;它有几个强大的功能和显著的功能包括:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;支持使用H264和AAC编码
支持英特尔QSV和NVENC
支持无限数量的场景和输入源
输出文件使用MP4 或FLV 格式
允许在记录会话中访问网络摄像头，采集卡等
高度可扩展的插件,开发人员可以使用api编写自己的插件&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>adduser&amp;addgroup添加用户&amp;用户组</title>
    <link>https://blog.wiseai.cn/post/adduseraddgroup%E6%B7%BB%E5%8A%A0%E7%94%A8%E6%88%B7%E7%94%A8%E6%88%B7%E7%BB%84/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/adduseraddgroup%E6%B7%BB%E5%8A%A0%E7%94%A8%E6%88%B7%E7%94%A8%E6%88%B7%E7%BB%84/</guid>
    <description>
        &lt;ul&gt;
&lt;li&gt;添加一个普通用户&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# adduser [--home 主目录] [--shell SHELL] [--no-create-home] [--uid ID] [--firstuid ID] [--lastuid ID] [--gecos GECOS] [--ingroup 用户组 | --gid ID][--disabled-password] [--disabled-login] [--add_extra_groups] 用户名&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加一个系统用户&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# adduser --system [--home 主目录] [--shell SHELL] [--no-create-home] [--uid ID] [--gecos GECOS] [--group | --ingroup 用户组 | --gid ID] [--disabled-password] [--disabled-login] [--add_extra_groups] 用户名&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加一个用户组&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# adduser --group [--gid ID] 用户组名&lt;/code&gt;
&lt;code&gt;# addgroup [--gid ID] 用户组名&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加一个系统用户组&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# addgroup --system [--gid ID] 用户组名&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将一个已存在的用户添加至一个已存在的用户组&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# adduser 用户名 用户组名&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;常规设置：&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--quiet | -q          不在标准输出中给出进度信息
  --force-badname       允许用户名不匹配：
                        NAME_REGEX[_SYSTEM] 配置变量
  --help | -h           给出本命令用法
  --version | -v        版本号和版权信息
  --conf | -c 文件      使用文件中的配置
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>docker学习笔记</title>
    <link>https://blog.wiseai.cn/post/docker%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/docker%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid>
    <description>
        &lt;p&gt;现在docker分企业版和社区版，如果自己用的话，还是社区版吧！！！&lt;/p&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装：&lt;/h2&gt;
&lt;p&gt;centos7：&lt;/p&gt;
&lt;p&gt;1.卸载旧版本&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ yum remove docker \
             docker-client \
             docker-client-latest \
             docker-common \
             docker-latest \
             docker-latest-logrotate \
             docker-logrotate \
             docker-selinux \
             docker-engine-selinux \
             docker-engine
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.安装依赖包：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ yum install -y yum-utils device-mapper-persistent-data lvm2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;3.安装docker官方库：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;4.安装最新稳定版本docker：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ yum install docker-ce&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果提示接受GPG密钥，请验证指纹是否匹配&lt;code&gt;060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35&lt;/code&gt;，如果匹配 ，则接受它。&lt;/p&gt;
&lt;p&gt;ubuntu:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    $ sudo apt-get update
    $ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
    $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg &amp;#39;sudo pt-key add -&amp;#39;
    $ sudo add-apt-repository &amp;#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable&amp;#34;
    $ sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;其它发行版本的请参考：&lt;a href=&#34;https://docs.docker.com/install/linux/docker-ce/ubuntu/#os-requirements&#34;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;配置：&lt;/p&gt;
&lt;p&gt;1.Linux更改Docker运行根目录&lt;/p&gt;
&lt;p&gt;Docker 的配置文件可以设置大部分的后台进程参数，在各个操作系统中的存放位置不一致:&lt;/p&gt;
&lt;p&gt;在 ubuntu 中的位置是：/etc/default/docker&lt;/p&gt;
&lt;p&gt;在 centos 中的位置是：/etc/sysconfig/docker&lt;/p&gt;
&lt;p&gt;只需要更改 vim /etc/sysconfig/docker 成&lt;/p&gt;
&lt;p&gt;为OPTIONS选项添加参数：&amp;ndash;graph=/home/docker &amp;ndash;icc=false &amp;ndash;graph=/your/dir&lt;/p&gt;
&lt;p&gt;&amp;ndash;icc=false禁用容器内部通信，为安全建议添加，内部通信使用link&lt;/p&gt;
&lt;p&gt;&amp;ndash;graph=/your/dir 自定义运行目录&lt;/p&gt;
&lt;p&gt;或者使用软件链接：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ln -sv /home/docker/ /var/lib/docker&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;搞定！&lt;/p&gt;
&lt;p&gt;2.iptables设置：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# iptables -A INPUT -s 172.17.0.0/16 -d 172.17.0.0/16 -j ACCEPT&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;查找镜像：docker search centos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载镜像：docker pull centos&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;列出本地镜像：docker images&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除镜像：docker rmi 镜像名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行容器：
&lt;code&gt;# docker run -it --name 容器名 --link 链接容器名:别名 -v 本地绝对路径:容器路径  -p ip:本地端口:容器端口/udp --restart=always （docker服务启动后容器自动启动，no 不启动；on-failure 容器退出状态非0时重启）-e MYSQL_ROOT_PASSWORD=&#39;passwd&#39;(设置环境变量） --dns=自定义的DNS -d(后台运行) 镜像 运行程序(这里的程序可以是/bin/下的程序，可以将shell放到bin下)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;列出运行容器：docker container ls&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;列出所有容器：docker container ls -a&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行容器：docker start 容器名（-i参数进入命令行）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入运行容器：docker exec -it 容器名 /bin/bash&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;停止容器：docker stop 容器名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;kill容器：docker kill 容器名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;删除容器：docker rm 容器名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看容器信息：docker inspect 容器名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;查看容器log：docker logs 容器名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用已有容器修改或建立新的镜像：docker commit -m &amp;ldquo;说明信息&amp;rdquo; -a &amp;ldquo;用户信息&amp;rdquo; 容器名 centos7/nginx-php:v2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;镜像存出：docker save -o ubuntu_14.04.tar ubuntu:14.04或docker save &amp;gt; ubuntu_14.04.tar ubuntu:14.04&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;镜像载入：docker load -i ubuntu_14.04.tar或docker load &amp;lt; ubuntu_14.04.tar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;容器导出：docker export ubuntu &amp;gt; ubuntu.tar&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;容器导入：docker import ubuntu.tar test/ubuntu
注意：
容器导入后就成镜像了，如果容器有运行的命令，在run的时候也一定要加上命令，否则不能成功创建。
镜像就不存在这个问题&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从主机复制到容器：docker cp host_path containerID:container_path&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从容器复制到主机：docker cp containerID:container_path host_path&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后添加&amp;quot;运行命令&amp;quot;的shell里，如果没有驻留服务的话，一定要在最后一行写bash，否则无法启动。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;docker网络设置&#34;&gt;docker网络设置：&lt;/h2&gt;
&lt;p&gt;docker安装后，默认会创建三种网络类型，bridge、host和none，可通过如下命令查看：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# docker network ls&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;bridge：网络桥接&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;默认情况下启动、创建容器都是用该模式，所以每次docker容器重启时会按照顺序获取对应IP地址，这就导致容器每次重启，IP都发生变化，这种类型下无法设置固定IP&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;none：无指定网络&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;启动容器时，可以通过-network=none，docker容器不会分配局域网ip&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;host：主机网络&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;docker容器的网络会附属在主机上，两者是互通的。这种类型下可以设置固定IP&lt;/p&gt;
&lt;h3 id=&#34;创建固定ip容器&#34;&gt;创建固定ip容器：&lt;/h3&gt;
&lt;h4 id=&#34;1创建自定义网络类型并且指定网段&#34;&gt;1.创建自定义网络类型，并且指定网段&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;# docker network create --subnet=192.168.168.0/30 mynet&lt;/code&gt;
通过docker network ls可以查看到网络类型中多了一个mynet&lt;/p&gt;
&lt;h4 id=&#34;2使用新的网络类型创建并启动容器&#34;&gt;2.使用新的网络类型创建并启动容器&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;# docker run -it --name test --net mynet --ip 192.168.168.2 centos /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;存在问题&#34;&gt;存在问题：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;docker-storage-setup不能启动的问题。
编辑/etc/sysconfig/docker-storage-setup文件
添加STORAGE_DRIVER=&amp;ldquo;overlay&amp;rdquo;&lt;/li&gt;
&lt;li&gt;容器启动自动运行
编辑/etc/bashrc文件
添加运行命令在文件最后&lt;/li&gt;
&lt;/ul&gt;

    </description>
    </item>
    
    <item>
    <title>efi和grub2相关配置文件及命令</title>
    <link>https://blog.wiseai.cn/post/efi%E5%92%8Cgrub2%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%8F%8A%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/efi%E5%92%8Cgrub2%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E5%8F%8A%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;h3 id=&#34;一配置文件目录&#34;&gt;一、配置文件目录&lt;/h3&gt;
&lt;p&gt;/etc/grub2/目录下
/etc/default/grub&lt;/p&gt;
&lt;h3 id=&#34;二更新引导项目&#34;&gt;二、更新引导项目&lt;/h3&gt;
&lt;p&gt;根据配置文件目录下的顺序更新启动项，命令：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# grub2-mkconfig -o /boot/grub2/grub.cfg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;-o 选项为输出到那个文件&lt;/p&gt;
&lt;p&gt;efi下的/boot/efi/EFI/centos/grub.cfg文件一般调用/boot/grub2/grub.cfg这个文件&lt;/p&gt;
&lt;p&gt;类debian发行版，一般用这个命令：
&lt;code&gt;# update-grub2&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;三更换默认启动项&#34;&gt;三、更换默认启动项&lt;/h3&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# grub2-set-default 2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;一般从0开始&lt;/p&gt;
&lt;h3 id=&#34;四安装&#34;&gt;四、安装&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# grub-install --efi-directory=/boot/efi/ --boot-directory=/boot/ --removable /dev/sda&lt;/code&gt;
几个需要说明的地方：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;--efi-directory=/boot/efi/&lt;/code&gt;这个位置的问题，如果efi分区是单独挂载的，就直接写这个目录就可以了，不用加efi，它会在这个目录下建立EFI的目录，写入相关文件。
&lt;code&gt;--boot-directory=/boot/&lt;/code&gt;这个不用加grub2，它会自己生成grub2目录，写入相关文件
&lt;code&gt;--removable&lt;/code&gt;如果是移动设备，一定要加这个选项&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;五常用命令&#34;&gt;五、常用命令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;启动常用的grub命令&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root (hd0,2)          #设置某块分区为根目录
kernel /vmlinuz       #加载linux kernel
initrd /initrd.gz     #加载驱动，或者虚拟rootfs
boot                  #启动加载的kernel
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;查找文件
&lt;code&gt;find --set-root --ignore-floppies --ignore-cd /usr/bin/cp   #查找/usr/bin/cp文件，并将分区设为根目录，忽略软盘和cd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;加载iso&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root (hdx,x)                    #设置根目录
map --mem /xx.iso (0xff)        #映射iso文件到模拟cdrom 0xff
map --hook                      #映射钩子
chainloader (0xff)              #链接到映射的CDROM
boot                            #启动
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;grub命名规则，(hd32)为第一仿真cd/dvd设备，等同于0xa0，(hd33)为二仿真cd/dvd设备，0xff为最后一个。grub中将0xa0以上都认做cdrom。
&lt;code&gt;map --mem&lt;/code&gt; 用于仿真不连续的存放的iso文件，不加则必须连续存放，碎片整理后再存放iso一般才会连续。
另外grub不支持大的iso，如果iso不支持则提示:inviladorupsupported executableformat。&lt;/p&gt;
&lt;h3 id=&#34;六添加efi启动项&#34;&gt;六、添加efi启动项&lt;/h3&gt;
&lt;p&gt;在安装程序不完善的时候，有时候会存在没有启动项的问题，这个时候就要用efibootmgr管理UEFI启动项。
&lt;code&gt;# efibootmgr&lt;/code&gt;
这个命令会显示所有已经添加的启动项，BootOrder后会显示启动顺序。
&lt;code&gt;# efibootmgr -c -w -L &amp;quot;BootOptionName&amp;quot; -d /dev/sda -p 1 -l \\EFI\\UOS\\grubx64.efi&lt;/code&gt;
BootOptionName是你启动项的名字，修改为自己的
-d修改那个硬盘
-p分区位置，默认为1
-l是启动efi文件的路径，注意是\而不是//
这样就添加了EFI启动项，刚添加的启动项的顺序排第一个
&lt;code&gt;# efibootmgr -b 0013 -B&lt;/code&gt;
删除编号为0013的启动项
&lt;code&gt;# efibootmgr -o 0012,0010,000f&lt;/code&gt;
修改启动顺序。
这个命令没有认真研究，有空再说吧。&lt;/p&gt;
&lt;h3 id=&#34;七需要注意的地方&#34;&gt;七、需要注意的地方&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;grub背景图片的问题，最近用gimp做了个图片设为grub2启动背景图片，grub2提示错误，最后发现是图片的问题。&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>filebrowser的安装和使用</title>
    <link>https://blog.wiseai.cn/post/filebrowser%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/filebrowser%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8/</guid>
    <description>
        &lt;h2 id=&#34;编译安装&#34;&gt;编译安装&lt;/h2&gt;
&lt;p&gt;可以自己编译，必须安装golang v1.16以上和nodejs，最好是最新版本，否则会出现一些问题。&lt;/p&gt;
&lt;p&gt;编译最新版本出现一个问题,&amp;ldquo;opensslErrorStack: [ &amp;rsquo;error:03000086:digital envelope routines::initialization error&amp;rsquo; ]&amp;quot;,出现这个错误是因为 node.js V17以上的版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严格的限制，解决办法是&lt;code&gt;# export NODE_OPTIONS=--openssl-legacy-provider&lt;/code&gt;后再编译。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git clone https://github.com/filebrowser/filebrowser
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注：如果有gitee的可以改，速度快，可以直接下载对应版本&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ make help
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注：编译帮助&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ make build
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注：开始编译，完成后在当前目录下filebrowser&lt;/p&gt;
&lt;h2 id=&#34;使用简介&#34;&gt;使用简介：&lt;/h2&gt;
&lt;p&gt;直接输入命令：
&lt;code&gt;./filebrowser&lt;/code&gt;
会在当前目录下生成数据库文件，并开启服务&lt;/p&gt;
&lt;p&gt;关闭服务后，输入以下命令进行设置：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser config cat&lt;/code&gt;查看配置文件，这个配置是写入数据库里的&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser config set --address 0.0.0.0&lt;/code&gt;打开所有端口&lt;/p&gt;
&lt;p&gt;这里的&lt;code&gt;config set --address&lt;/code&gt;可以从配置文件里看，想设置那个就把address换成那个&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  config set --port 80&lt;/code&gt;设置端口&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  config set --locale zh-cn&lt;/code&gt;设置语言&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  config set --log /your/path/filebrowser.log&lt;/code&gt;设置日志&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  config set --root /your/path/&lt;/code&gt;设置根目录&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  users add root password --perm.admin&lt;/code&gt;这个是添加用户，其中的root和password分别是用户名和密码&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser -d /you/path/filebrowser.db config init&lt;/code&gt;这里的-d是指定数据库文件，config init是初始化，这个命令应该一开始就执行，但是什么参数都不带的话，会自己生成。&lt;/p&gt;
&lt;p&gt;设置https访问：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser  config set --port 443&lt;/code&gt;设置https端口&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser/filebrowser config set -t &amp;quot;/path/to/***.pem&amp;quot;&lt;/code&gt;设置证书&lt;/p&gt;
&lt;p&gt;&lt;code&gt;./filebrowser/filebrowser config set -k &amp;quot;/path/to/***.key&amp;quot;&lt;/code&gt;设置key&lt;/p&gt;
&lt;p&gt;设置系统服务：位置&lt;code&gt;/lib/systemd/system/filebrowser.service&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-[Unit]&#34; data-lang=&#34;[Unit]&#34;&gt;Description=File Browser
After=network.target

[Service]
ExecStart=/usr/local/bin/filebrowser -d /your/path/filebrowser.db

[Install]
WantedBy=multi-user.target```
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>FTP服务器架设（vsftpd）</title>
    <link>https://blog.wiseai.cn/post/ftp%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E8%AE%BEvsftpd/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/ftp%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E8%AE%BEvsftpd/</guid>
    <description>
        &lt;h3 id=&#34;一安装vsftpd及相关组件&#34;&gt;一、安装vsftpd及相关组件：&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# apt install vsftpd db-util&lt;/code&gt;&lt;br&gt;
注意：db-util如果不存在，那就找db5.3-util，所以用&lt;code&gt;# apt list db*&lt;/code&gt;查找下再安装。&lt;/p&gt;
&lt;h3 id=&#34;二修改ftp相关帐户&#34;&gt;二、修改FTP相关帐户：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;vsftpd服务的宿主用户&lt;br&gt;
vsftpd默认只能使用root用户运行。使用非root用户运行，需要在配置文件里设置run_as_launching_user=YES。
官方强烈不推荐使用这种方式启动，会带来安全问题，并且会导致无法使用chroot技术来限制文件访问。这一步现在可能不需要了，先放着。&lt;br&gt;
&lt;code&gt;# useradd vsftpd -s /sbin/nologin&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;vsftpd的虚拟宿主用户&lt;br&gt;
&lt;code&gt;# useradd ftp -d /var/ftp/ -s /sbin/nologin&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# chown -R ftp:ftp /var/ftp/&lt;/code&gt;&lt;br&gt;
vsftpd的虚拟用户并不是系统用户，也就是说这些FTP的用户在系统中是不存在的。他们的总体权限其实是集中寄托在一个在系统中的某一个用户身上的，所谓vsftpd的虚拟宿主用户，就是这样一个支持着所有虚拟用户的宿主用户。由于他支撑了FTP的所有虚拟的用户，那么他本身的权限将会影响着这些虚拟的用户，因此，处于安全性的考虑，也要非分注意对该用户的权限的控制，该用户也绝对没有登陆系统的必要，这里也设定他为不能登陆系统的用户。ftp用户在安装vsftpd的时候就已经添加了，我们只需要将它家目录改为你需要的位置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;三vsftpdconf基本配置&#34;&gt;三、vsftpd.conf基本配置：&lt;/h3&gt;
&lt;p&gt;配置 /etc/vsftpd/vsftpd.conf文件，这里我们只写有变动的地方，其它的保持默认。&lt;br&gt;
首先，生成证书：&lt;br&gt;
&lt;code&gt;# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ftp.key -out ftp.pem&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#ssl加密传输
rsa_cert_file=/etc/ssl/certs/ftp.pem
rsa_private_key_file=/etc/ssl/ftp.key
ssl_enable=YES

#被动ftp
#listen_port=56880
#pasv_min_port=30000
#pasv_max_port=35000

#主动ftp，这个只需要打开20和21两个端口，在负载比较小的时候，不想开多个端口的时候，可以使用
pasv_enable=NO

#这个地方一定要注意，这里的user_list里的用户是可以登陆的用户
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd/user_list

tcp_wrappers=YES

chroot_local_user=YES
chroot_list_file=/etc/vsftpd/chroot_list

#虚拟用户
guest_enable=YES
guest_username=ftp
virtual_use_local_privs=YES
pam_service_name=vsftpd

#独立用户设置
user_config_dir=/etc/vsftpd/vsftpd_user_conf
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;四生成vsftpd虚拟用户数据库文件&#34;&gt;四、生成vsftpd虚拟用户数据库文件：&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;建立虚拟用户名单文件：&lt;br&gt;
&lt;code&gt;# vim /etc/vsftpd/ftpuser.txt&lt;/code&gt;&lt;br&gt;
内容如下：
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ftp1
1234
ftp2
5678
&lt;/code&gt;&lt;/pre&gt;格式很简单：“一行用户名，一行密码！”。&lt;/li&gt;
&lt;li&gt;生成虚拟用户数据文件：&lt;br&gt;
&lt;code&gt;# db_load -T -t hash -f /etc/vsftpd/ftpuser.txt /etc/vsftpd/vsftpd_login.db  //注意5.3，安装那个版本就用那个版本号&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# chmod 600 /etc/vsftpd/vsftpd_login.db  //修改文件权限&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;五配置pam验证文件&#34;&gt;五、配置PAM验证文件：&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# vim /etc/pam.d/vsftpd&lt;/code&gt;
将以下内容加入到文件最前面（在后面加入无效）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：pam_userdb.so这个文件可以搜索下位置写入，不同系统的文件位置不同&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;本地用户登陆&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auth    required        pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed

# Note: vsftpd handles anonymous logins on its own. Do not enable pam_ftp.so.

# Standard pam includes
@include common-account
@include common-session
@include common-auth

auth	required        pam_shells.so
#auth    required        pam_nologin.so
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ftpusers的原位置在/etc/，移动ftpusers到/etc/vsftpd/，这里的ftpusers，是限制本地用户登陆的文件，在这个文件里的用户不能登陆。&lt;br&gt;
pam_shells.so和pam_nologin.so两个配置，如果你的用户是可以登陆系统的用pam_shells.so，如果是不能登陆系统的用pam_nologin.so&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;虚拟用户登陆&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auth    required        /usr/lib/aarch64-linux-gnu/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
account required        /usr/lib/aarch64-linux-gnu/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
#auth    required        pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
auth    required        pam_nologin.so
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;经过测试，这样写也行：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;auth    required        pam_userdb.so db=/etc/vsftpd/vsftpd_login
account required        pam_userdb.so db=/etc/vsftpd/vsftpd_login
#auth    required        pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
auth    required        pam_nologin.so
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;br&gt;
第三行的auth一定要注释掉，否则虚拟用户不能登陆。&lt;br&gt;
上一步建立的数据库 vsftpd_login 在此处被使用，建立的虚拟用户将采用PAM进行验证，这是通过/etc/vsftpd/vsftpd.conf文件中的语句pam_service_name=vsftpd来启用的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;六vsftpd虚拟用户的独立配置&#34;&gt;六、vsftpd虚拟用户的独立配置：&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# mkdir -p /etc/vsftpd/vsftpd_user_conf&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# vim /etc/vsftpd/vsftpd_user_conf/（用户名）&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;配置如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;allow_writeable_chroot=YES
write_enable=YES
local_root=/var/ftp/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;七vsftpd服务器之间的站点对传&#34;&gt;七、vsftpd服务器之间的站点对传：&lt;/h3&gt;
&lt;p&gt;有时候可能需要开启vsftpd服务器之间的站点对传功能，只需在主配置文件 /etc/vsftpd/vsftpd.conf 里加入如下参数即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pasv_promiscuous=YES
port_promiscuous=YES
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明：
port_promiscuous=YES|NO
默认值为NO。为YES时，取消PORT安全检查。该检查确保外出的数据只能连接到客户端上。小心打开此选项。&lt;/p&gt;
&lt;p&gt;pasv_promiscuous=YES|NO
默认值为NO。为YES时，将关闭PASV模式的安全检查。该检查确保数据连接和控制连接是来自同一个IP地址。小心打开此选项。此选项唯一合理的用法是存在于由安全隧道方案构成的组织中。
由于取消了数据包的安全检查，允许数据流向非客户端，所以站点对传成功。&lt;/p&gt;
&lt;h3 id=&#34;八etcvsftpd目录下的文件有&#34;&gt;八、/etc/vsftpd/目录下的文件有：&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;chroot_list 
ftpuser.txt		//这个文件在生成数据库文件后删除 
ftpusers		//虚拟用户登陆的话这个文件就没用了  
user_list  
vsftpd_login.db  
vsftpd_user_conf  //这个是目录,下面是各个用户的配置文件
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;配置修改完成后重启vsftpd服务生效&#34;&gt;配置修改完成后，重启vsftpd服务生效&lt;/h3&gt;
&lt;p&gt;配置文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/vsftpd/vsftpd.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;严格来说,整个 vsftpd 的配置文件就只有这个档案!这个档案的设定是以 bash的变量设定相同的方式来处理的, 也就是『参数=设定值』来设定的,注意, 等号两边不能有空白喔!至于详细的 vsftpd.conf 可以使用 『 man 5 vsftpd.conf 』来详查。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/pam.d/vsftpd&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个是 vsftpd 使用 PAM 模块时的相关配置文件。主要用来作为身份认证之用,还有一些用户身份的抵挡功能, 也是透过这个档案来达成的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/vsftpd/ftpusers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与上一个档案有关系,也就是 PAM 模块 (/etc/pam.d/vsftpd) 所指定的那个无法登入的用户配置文件! 这个档案的设定很简单,你只要将『不想让他登入FTP 的账号』写入这个档案即可。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/vsftpd/user_list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个档案是否能够生效与 vsftpd.conf 内的两个参数有关,分别是『 userlist_enable, userlist_deny 』。 如果说 /etc/vsftpd/ftpusers 是PAM 模块的抵挡设定项目,那么这个 /etc/vsftpd/user_list 则是 vsftpd 自定义的抵挡项目。事实上这个档案与 /etc/vsftpd/ftpusers 几乎一模一样, 在预设的情况下,你可以将不希望可登入 vsftpd 的账号写入这里。不过这个档案的功能会依据 vsftpd.conf 配置文件内的 serlist_deny={YES/NO} 而不同。&lt;br&gt;
userlist_deny只有在userlist_enable为YES时为生效，userlist_deny为NO时为白名单，为YES时为黑名单。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/vsftpd/chroot_list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个档案预设是不存在的,所以你必须要手动自行建立。这个档案的主要功能是可以将某些账号的使用者 chroot 在他们的家目录下!但这个档案要生效与vsftpd.conf 内的『 chroot_list_enable, chroot_list_file 』两个参数有关。如果你想要将某些实体用户限制在他们的家目录下而不许到其他目录去,可以启动这个设定项目。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/usr/sbin/vsftpd&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是 vsftpd 的主要执行档，vsftpd 只有这一个执行档。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/var/ftp/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个是 vsftpd 的预设匿名者登入的根目录，其实与 ftp 这个账号的家目录有关。local_root也可指定根目录，特别注意目录权限。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制用户是否允许切换到上级目录&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在默认配置下，本地用户登入FTP后可以使用cd命令切换到其他目录，这样会对系统带来安全隐患。可以通过以下三条配置文件来控制用户切换目录.&lt;/p&gt;
&lt;p&gt;chroot_list_enable=YES/NO（NO）&lt;/p&gt;
&lt;p&gt;设置是否启用chroot_list_file配置项指定的用户列表文件。默认值为NO。&lt;/p&gt;
&lt;p&gt;chroot_list_file=/etc/vsftpd.chroot_list&lt;/p&gt;
&lt;p&gt;用于指定用户列表文件，该文件用于控制哪些用户可以切换到用户家目录的上级目录。&lt;/p&gt;
&lt;p&gt;chroot_local_user=YES/NO（NO）&lt;/p&gt;
&lt;p&gt;用于指定用户列表文件中的用户是否允许切换到上级目录。默认值为NO。&lt;/p&gt;
&lt;p&gt;通过搭配能实现以下几种效果：&lt;/p&gt;
&lt;p&gt;①当chroot_list_enable=YES，chroot_local_user=YES时，在/etc/vsftpd.chroot_list文件中列出的用户，可以切换到其他目录；未在文件中列出的用户，不能切换到其他目录。&lt;/p&gt;
&lt;p&gt;②当chroot_list_enable=YES，chroot_local_user=NO时，在/etc/vsftpd.chroot_list文件中列出的用户，不能切换到其他目录；未在文件中列出的用户，可以切换到其他目录。&lt;/p&gt;
&lt;p&gt;③当chroot_list_enable=NO，chroot_local_user=YES时，所有的用户均不能切换到其他目录。&lt;/p&gt;
&lt;p&gt;④当chroot_list_enable=NO，chroot_local_user=NO时，所有的用户均可以切换到其他目录。&lt;/p&gt;
&lt;p&gt;[chroot_local_user=YES/NO ,特别注意当等于YES时，因为FTP不能切换目录，有些FTP客户端会在FTP目标目录里再新建一个目标目录，如 upload/upload  造成应用调试困扰]&lt;/p&gt;
&lt;h3 id=&#34;后面是一些网上收集的资料&#34;&gt;后面是一些网上收集的资料：&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.wiseai.cn/post/vsftpd.png&#34; alt=&#34;vsftpd配置&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;安装&#34;&gt;安装：&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;# dnf install vsftpd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;**FTP主动模式：客户端从一个任意的非特权端口N（N&amp;gt;1024）连接到FTP服务器的port 21命令端口。然后客户端开始监听端口N+1，**并发送FTP命令&amp;quot;port N+1&amp;quot;到FTP服务器。接着服务器会从它自己的数据端口（20）连接到客户端指定的数据端口（N+1）。&lt;/p&gt;
&lt;p&gt;**FTP被动模式：客户端从一个任意的非特权端口N（N&amp;gt;1024）连接到FTP服务器的port 21命令端口。然后客户端开始监听端口N+1，**同时客户端提交 PASV命令。服务器会开启一个任意的非特权端口（P &amp;gt;1024），并发送PORT P命令给客户端。然后客户端发起从本地端口N+1到服务器的端口P的连接用来传送数据。&lt;/p&gt;
&lt;p&gt;端口：
主动模式：TCP 21（指令），20（数据）端口&lt;/p&gt;
&lt;p&gt;被动模式：TCP 21（指令），大于1024端口传输数据（可在配置文件中指定范围）&lt;/p&gt;
&lt;p&gt;生成证书：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -key out ftp.key -out ftp.pem&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;配置文件：&lt;/p&gt;
&lt;p&gt;/etc/vsftpd/vsftpd.conf&lt;/p&gt;
&lt;p&gt;严格来说,整个 vsftpd 的配置文件就只有这个档案!这个档案的设定是以 bash的变量设定相同的方式来处理的, 也就是『参数=设定值』来设定的,注意, 等号两边不能有空白喔!至于详细的 vsftpd.conf 可以使用 『 man 5 vsftpd.conf 』来详查。&lt;/p&gt;
&lt;p&gt;/etc/pam.d/vsftpd&lt;/p&gt;
&lt;p&gt;这个是 vsftpd 使用 PAM 模块时的相关配置文件。主要用来作为身份认证之用,还有一些用户身份的抵挡功能, 也是透过这个档案来达成的。&lt;/p&gt;
&lt;p&gt;/etc/vsftpd/ftpusers&lt;/p&gt;
&lt;p&gt;与上一个档案有关系,也就是 PAM 模块 (/etc/pam.d/vsftpd) 所指定的那个无法登入的用户配置文件啊! 这个档案的设定很简单,你只要将『不想让他登入FTP 的账号』写入这个档案即可。&lt;/p&gt;
&lt;p&gt;/etc/vsftpd/user_list&lt;/p&gt;
&lt;p&gt;这个档案是否能够生效与 vsftpd.conf 内的两个参数有关,分别是『 userlist_enable, userlist_deny 』。 如果说 /etc/vsftpd/ftpusers 是PAM 模块的抵挡设定项目,那么这个 /etc/vsftpd/user_list 则是 vsftpd 自定义的抵挡项目。事实上这个档案与 /etc/vsftpd/ftpusers 几乎一模一样, 在预设的情况下,你可以将不希望可登入 vsftpd 的账号写入这里。不过这个档案的功能会依据 vsftpd.conf 配置文件内的 serlist_deny={YES/NO} 而不同。&lt;/p&gt;
&lt;p&gt;/etc/vsftpd/chroot_list&lt;/p&gt;
&lt;p&gt;这个档案预设是不存在的,所以你必须要手动自行建立。这个档案的主要功能是可以将某些账号的使用者 chroot 在他们的家目录下!但这个档案要生效与vsftpd.conf 内的『 chroot_list_enable, chroot_list_file 』两个参数有关。如果你想要将某些实体用户限制在他们的家目录下而不许到其他目录去,可以启动这个设定项目。&lt;/p&gt;
&lt;p&gt;/usr/sbin/vsftpd&lt;/p&gt;
&lt;p&gt;这就是 vsftpd 的主要执行档，vsftpd 只有这一个执行档。&lt;/p&gt;
&lt;p&gt;/var/ftp/&lt;/p&gt;
&lt;p&gt;这个是 vsftpd 的预设匿名者登入的根目录，其实与 ftp 这个账号的家目录有关。&lt;/p&gt;
&lt;p&gt;!!服务器环境设定&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;### 使用本地时间

use_localtime=yes

dirmessage_enable=yes

xferlog_enable=yes

connect_from_port_20=yes

xferlog_std_format=yes

listen=yes

pam_service_name=vsftpd

tcp_wrappers=yes

### 欢迎信息

banner_file=/etc/vsftpd/welcome.txt

### 限制带宽单位Bytes/秒

local_max_rate=100000000

### 限制最大同时在线人数

max_clients=100

max_per_ip=100

### 数据流传输10分钟停止传输

data_connection_timeout=600

### 发呆超过 10 分钟就断线

idle_session_timeout=600

write_enable=yes

userlist_enable=yes

userlist_deny=no

### user_list文件必须建立

userlist_file=/etc/vsftpd/user_list

### 为了避免一个安全漏洞，从 vsftpd 2.3.5 开始，chroot 目录必须不可写。

chroot_local_user=yes

chroot_list_enable=yes

### chroot_list必须建立，空文件都可以

chroot_list_file=/etc/vsftpd/chroot_list

### 被动式端口范围设定

pasv_min_port=65500

pasv_max_port=65535

### 设定上传文件权限

local_umask=002

### anonymous设定，设定上传目录拥有者为ftp

anonymous_enable=yes

no_anon_password=yes

anon_max_rate=100000000

anon_other_write_enable=yes

anon_mkdir_write_enable=yes

anon_upload_enable=yes

anon_root=/var/vsftpd/

### 下两行的作用是修改anonymous上传文件的拥有者为daemon,所以anonymous上传的文件是不能下载的，只有修改权限后才能下载

chown_uploads=yes

chown_username=daemon

### 针对实体账号的设定

local_enable=yes

### 针对 SSL 所加入的特别参数。

### 启动 SSL 的支持

ssl_enable=YES

### 但是不允许匿名者使用 SSL 喔

allow_anon_ssl=NO

### 强制实体用户数据传输加密

force_local_data_ssl=YES

### 登入时的帐密也加密

force_local_logins_ssl=YES

### 支持 TLS 方式即可,底下不用启动

ssl_tlsv1=YES

ssl_sslv2=NO

ssl_sslv3=NO

### 预设 RSA 加密的凭证档案所在

rsa_cert_file=/etc/vsftpd/vsftpd.pem
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;vsftpd配置文件详解&#34;&gt;vsftpd配置文件详解&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;默认配置：允许匿名用户和本地用户登陆。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anonymous_enable=YES&lt;/li&gt;
&lt;li&gt;local_enable=YES&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;匿名用户使用的登陆名为ftp或anonymous，口令为空；匿名用户不能离开匿名 用户家目录/var/ftp,且只能下载不能上传。
本地用户的登录名为本地用户名，口令为此本地用户的口令；本地用户可以在自己家目录中进行读写操作；本地用户可以离开自家目录切换至有权限访问的其他目录，并在权限允许的情况下进行上传/下载。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;write_enable=YES&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;写在文件/etc/vsftpd.ftpusers中的本地用户禁止登陆。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置文件格式：vsftpd.conf 的内容非常单纯，每一行即为一项设定。若是空白行或是开头为#的一行，将会被忽略。内容的格式只有一种，如下所示&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;option=value&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要注意的是，等号两边不能加空白。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;匿名用户（anonymous）设置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anonymous_enable=YES/NO（YES）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;控制是否允许匿名用户登入，YES 为允许匿名登入，NO 为不允许。默认值为YES。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;write_enable=YES/NO（YES）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;是否允许登陆用户有写权限。属于全局设置，默认值为YES。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no_anon_password=YES/NO（NO）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;若是启动这项功能，则使用匿名登入时，不会询问密码。默认值为NO。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ftp_username=ftp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;定义匿名登入的使用者名称。默认值为ftp。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anon_root=/var/ftp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用匿名登入时，所登入的目录。默认值为/var/ftp。注意ftp目录不能是777的权限属性，即匿名用户的家目录不能有777的权限。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anon_upload_enable=YES/NO（NO）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果设为YES，则允许匿名登入者有上传文件（非目录）的权限，只有在write_enable=YES时，此项才有效。当然，匿名用户必须要有对上层目录的写入权。默认值为NO。
anon_world_readable_only=YES/NO（YES）
如果设为YES，则允许匿名登入者下载可阅读的档案（可以下载到本机阅读，不能直接在FTP服务器中打开阅读）。默认值为YES。
anon_mkdir_write_enable=YES/NO（NO）
如果设为YES，则允许匿名登入者有新增目录的权限，只有在write_enable=YES时，此项才有效。当然，匿名用户必须要有对上层目录的写入权。默认值为NO。
anon_other_write_enable=YES/NO（NO）
如 果设为YES，则允许匿名登入者更多于上传或者建立目录之外的权限，譬如删除或者重命名。（如果anon_upload_enable=NO，则匿名用户 不能上传文件，但可以删除或者重命名已经存在的文件；如果anon_mkdir_write_enable=NO，则匿名用户不能上传或者新建文件夹，但 可以删除或者重命名已经存在的文件夹。）默认值为NO。
chown_uploads=YES/NO（NO）
设置是否改变匿名用户上传文件（非目录）的属主。默认值为NO。
chown_username=username
设置匿名用户上传文件（非目录）的属主名。建议不要设置为root。
anon_umask=077
设置匿名登入者新增或上传档案时的umask 值。默认值为077，则新建档案的对应权限为700。
deny_email_enable=YES/NO（NO）
若是启动这项功能，则必须提供一个档案/etc/vsftpd/banner_emails，内容为email address。若是使用匿名登入，则会要求输入email address，若输入的email address 在此档案内，则不允许进入。默认值为NO。
banned_email_file=/etc/vsftpd/banner_emails
此文件用来输入email address，只有在deny_email_enable=YES时，才会使用到此档案。若是使用匿名登入，则会要求输入email address，若输入的email address 在此档案内，则不允许进入。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;4.本地用户设置
local_enable=YES/NO（YES）
控制是否允许本地用户登入，YES 为允许本地用户登入，NO为不允许。默认值为YES。
local_root=/home/username
当本地用户登入时，将被更换到定义的目录下。默认值为各用户的家目录。
write_enable=YES/NO（YES）
是否允许登陆用户有写权限。属于全局设置，默认值为YES。
local_umask=022
本地用户新增档案时的umask 值。默认值为077。
file_open_mode=0755
本地用户上传档案后的档案权限，与chmod 所使用的数值相同。默认值为0666。&lt;/p&gt;
&lt;p&gt;5.欢迎语设置
dirmessage_enable=YES/NO（YES）
如果启动这个选项，那么使用者第一次进入一个目录时，会检查该目录下是否有.message这个档案，如果有，则会出现此档案的内容，通常这个档案会放置欢迎话语，或是对该目录的说明。默认值为开启。
message_file=.message
设置目录消息文件，可将要显示的信息写入该文件。默认值为.message。
banner_file=/etc/vsftpd/banner
当使用者登入时，会显示此设定所在的档案内容，通常为欢迎话语或是说明。默认值为无。如果欢迎信息较多，则使用该配置项。
ftpd_banner=Welcome to BOB&amp;rsquo;s FTP server
这里用来定义欢迎话语的字符串，banner_file是档案的形式，而ftpd_banner 则是字符串的形式。预设为无。&lt;/p&gt;
&lt;p&gt;6.控制用户是否允许切换到上级目录
在默认配置下，本地用户登入FTP后可以使用cd命令切换到其他目录，这样会对系统带来安全隐患。可以通过以下三条配置文件来控制用户切换目录。
chroot_list_enable=YES/NO（NO）
设置是否启用chroot_list_file配置项指定的用户列表文件。默认值为NO。
chroot_list_file=/etc/vsftpd.chroot_list
用于指定用户列表文件，该文件用于控制哪些用户可以切换到用户家目录的上级目录。
chroot_local_user=YES/NO（NO）
用于指定用户列表文件中的用户是否允许切换到上级目录。默认值为NO。
通过搭配能实现以下几种效果：
①当chroot_list_enable=YES，chroot_local_user=YES时，在/etc/vsftpd.chroot_list文件中列出的用户，可以切换到其他目录；未在文件中列出的用户，不能切换到其他目录。
②当chroot_list_enable=YES，chroot_local_user=NO时，在/etc/vsftpd.chroot_list文件中列出的用户，不能切换到其他目录；未在文件中列出的用户，可以切换到其他目录。
③当chroot_list_enable=NO，chroot_local_user=YES时，所有的用户均不能切换到其他目录。
④当chroot_list_enable=NO，chroot_local_user=NO时，所有的用户均可以切换到其他目录。
[chroot_local_user=YES/NO ,特别注意当等于YES时，因为FTP不能切换目录，有些FTP客户端会在FTP目标目录里再新建一个目标目录，如 upload/upload  造成应用调试困扰]
7.数据传输模式设置
FTP在传输数据时，可以使用二进制方式，也可以使用ASCII模式来上传或下载数据。
ascii_upload_enable=YES/NO（NO）
设置是否启用ASCII 模式上传数据。默认值为NO。
ascii_download_enable=YES/NO（NO）
设置是否启用ASCII 模式下载数据。默认值为NO。&lt;/p&gt;
&lt;p&gt;8.访问控制设置
两种控制方式：一种控制主机访问，另一种控制用户访问。
①控制主机访问：
tcp_wrappers=YES/NO（YES）
设 置vsftpd是否与tcp wrapper相结合来进行主机的访问控制。默认值为YES。如果启用，则vsftpd服务器会检查/etc/hosts.allow 和/etc/hosts.deny 中的设置，来决定请求连接的主机，是否允许访问该FTP服务器。这两个文件可以起到简易的防火墙功能。
比如：若要仅允许192.168.0.1—192.168.0.254的用户可以连接FTP服务器，则在/etc/hosts.allow文件中添加以下内容：
vsftpd:192.168.0. :allow
all:all :deny
②控制用户访问：
对于用户的访问控制可以通过/etc目录下的vsftpd.user_list和ftpusers文件来实现。
userlist_file=/etc/vsftpd.user_list
控制用户访问FTP的文件，里面写着用户名称。一个用户名称一行。
userlist_enable=YES/NO（NO）
是否启用vsftpd.user_list文件。
userlist_deny=YES/NO（YES）
决定vsftpd.user_list文件中的用户是否能够访问FTP服务器。若设置为YES，则vsftpd.user_list文件中的用户不允许访问FTP，若设置为NO，则只有vsftpd.user_list文件中的用户才能访问FTP。
/etc /vsftpd/ftpusers文件专门用于定义不允许访问FTP服务器的用户列表（注意:如果 userlist_enable=YES,userlist_deny=NO,此时如果在vsftpd.user_list和ftpusers中都有某个 用户时，那么这个用户是不能够访问FTP的，即ftpusers的优先级要高）。默认情况下vsftpd.user_list和ftpusers，这两个 文件已经预设置了一些不允许访问FTP服务器的系统内部账户。如果系统没有这两个文件，那么新建这两个文件，将用户添加进去即可。&lt;/p&gt;
&lt;p&gt;9.访问速率设置
anon_max_rate=0
设置匿名登入者使用的最大传输速度，单位为B/s，0 表示不限制速度。默认值为0。
local_max_rate=0
本地用户使用的最大传输速度，单位为B/s，0 表示不限制速度。预设值为0。&lt;/p&gt;
&lt;p&gt;10.超时时间设置
accept_timeout=60
设置建立FTP连接的超时时间，单位为秒。默认值为60。
connect_timeout=60
PORT 方式下建立数据连接的超时时间，单位为秒。默认值为60。
data_connection_timeout=120
设置建立FTP数据连接的超时时间，单位为秒。默认值为120。
idle_session_timeout=300
设置多长时间不对FTP服务器进行任何操作，则断开该FTP连接，单位为秒。默认值为300 。&lt;/p&gt;
&lt;p&gt;11.日志文件设置
xferlog_enable= YES/NO（YES）
是否启用上传/下载日志记录。如果启用，则上传与下载的信息将被完整纪录在xferlog_file 所定义的档案中。预设为开启。
xferlog_file=/var/log/vsftpd.log
设置日志文件名和路径，默认值为/var/log/vsftpd.log。
xferlog_std_format=YES/NO（NO）
如果启用，则日志文件将会写成xferlog的标准格式，如同wu-ftpd 一般。默认值为关闭。
log_ftp_protocol=YES|NO（NO）
如果启用此选项，所有的FTP请求和响应都会被记录到日志中，默认日志文件在/var/log/vsftpd.log。启用此选项时，xferlog_std_format不能被激活。这个选项有助于调试。默认值为NO。&lt;/p&gt;
&lt;p&gt;12.定义用户配置文件
在vsftpd中，可以通过定义用户配置文件来实现不同的用户使用不同的配置。
user_config_dir=/etc/vsftpd/userconf
设置用户配置文件所在的目录。当设置了该配置项后，用户登陆服务器后，系统就会到/etc/vsftpd/userconf目录下，读取与当前用户名相同的文件，并根据文件中的配置命令，对当前用户进行更进一步的配置。
例 如：定义user_config_dir=/etc/vsftpd/userconf，且主机上有使用者 test1,test2，那么我们就在user_config_dir 的目录新增文件名为test1和test2两个文件。若是test1 登入，则会读取user_config_dir 下的test1 这个档案内的设定。默认值为无。利用用户配置文件，可以实现对不同用户进行访问速度的控制，在各用户配置文件中定义local_max_rate=XX， 即可。&lt;/p&gt;
&lt;p&gt;13.FTP的工作方式与端口设置
FTP有两种工作方式：PORT FTP（主动模式）和PASV FTP（被动模式）
listen_port=21
设置FTP服务器建立连接所监听的端口，默认值为21。
connect_from_port_20=YES/NO
指定FTP使用20端口进行数据传输，默认值为YES。
ftp_data_port=20
设置在PORT方式下，FTP数据连接使用的端口，默认值为20。
pasv_enable=YES/NO（YES）
若设置为YES，则使用PASV工作模式；若设置为NO，则使用PORT模式。默认值为YES，即使用PASV工作模式。
pasv_max_port=0
在PASV工作模式下，数据连接可以使用的端口范围的最大端口，0 表示任意端口。默认值为0。
pasv_min_port=0
在PASV工作模式下，数据连接可以使用的端口范围的最小端口，0 表示任意端口。默认值为0。&lt;/p&gt;
&lt;p&gt;14.与连接相关的设置
listen=YES/NO（YES）
设 置vsftpd服务器是否以standalone模式运行。以standalone模式运行是一种较好的方式，此时listen必须设置为YES，此为默 认值。建议不要更改，有很多与服务器运行相关的配置命令，需要在此模式下才有效。若设置为NO，则vsftpd不是以独立的服务运行，要受到xinetd 服务的管控，功能上会受到限制。
max_clients=0
设置vsftpd允许的最大连接数，默认值为0，表示不受限制。若设置为100时，则同时允许有100个连接，超出的将被拒绝。只有在standalone模式运行才有效。
max_per_ip=0
设置每个IP允许与FTP服务器同时建立连接的数目。默认值为0，表示不受限制。只有在standalone模式运行才有效。
listen_address=IP地址
设置FTP服务器在指定的IP地址上侦听用户的FTP请求。若不设置，则对服务器绑定的所有IP地址进行侦听。只有在standalone模式运行才有效。
setproctitle_enable=YES/NO（NO）
设置每个与FTP服务器的连接，是否以不同的进程表现出来。默认值为NO，此时使用ps aux |grep ftp只会有一个vsftpd的进程。若设置为YES，则每个连接都会有一个vsftpd的进程。&lt;/p&gt;
&lt;p&gt;15.虚拟用户设置
虚拟用户使用PAM认证方式。
pam_service_name=vsftpd
设置PAM使用的名称，默认值为/etc/pam.d/vsftpd。
guest_enable= YES/NO（NO）
启用虚拟用户。默认值为NO。
guest_username=ftp
这里用来映射虚拟用户。默认值为ftp。
virtual_use_local_privs=YES/NO（NO）
当该参数激活（YES）时，虚拟用户使用与本地用户相同的权限。当此参数关闭（NO）时，虚拟用户使用与匿名用户相同的权限。默认情况下此参数是关闭的（NO）。&lt;/p&gt;
&lt;p&gt;16.其他设置
text_userdb_names= YES/NO（NO）
设置在执行ls –la之类的命令时，是显示UID、GID还是显示出具体的用户名和组名。默认值为NO，即以UID和GID方式显示。若希望显示用户名和组名，则设置为YES。
ls_recurse_enable=YES/NO（NO）
若是启用此功能，则允许登入者使用ls –R（可以查看当前目录下子目录中的文件）这个指令。默认值为NO。
hide_ids=YES/NO（NO）
如果启用此功能，所有档案的拥有者与群组都为ftp，也就是使用者登入使用ls -al之类的指令，所看到的档案拥有者跟群组均为ftp。默认值为关闭。
download_enable=YES/NO（YES）
如果设置为NO，所有的文件都不能下载到本地，文件夹不受影响。默认值为YES。&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>git的使用</title>
    <link>https://blog.wiseai.cn/post/git/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/git/</guid>
    <description>
        &lt;h2 id=&#34;添加ssh公钥&#34;&gt;添加ssh公钥：&lt;/h2&gt;
&lt;p&gt;查看&lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt;文件，如果有公钥直接添加，没有的话使用这个命令：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ssh-keygen -t ed25519 -C &amp;#34;xxxxx@xxxxx.com&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;三次回车后，再查看&lt;code&gt;~/.ssh/id_rsa.pub&lt;/code&gt;文件，添加到gitee设置。&lt;/p&gt;
&lt;h2 id=&#34;创建版本库&#34;&gt;创建版本库：&lt;/h2&gt;
&lt;p&gt;不建议直接生成，可以在gitee网站下建立仓库，之后clone到本地，直接使用下面的命令操作就可以了。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git init
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这个命令可以把当前目录变成git管理的仓库。
远程库如果是空的，我们第一次推送master分支时，加上了-u参数，Git不但会把本地的master分支内容推送的远程新的master分支，还会把本地的master分支和远程的master分支关联起来，在以后的推送或者拉取时就可以简化命令。&lt;/p&gt;
&lt;h2 id=&#34;添加远程仓库&#34;&gt;添加远程仓库：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git remote add gitee git@gitee.com:wiseai/wiseai.git
$ git remote add github git@github.com:wiseai/wiseai.git
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;查看远程仓库信息&#34;&gt;查看远程仓库信息：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git remote -v
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;删除远程仓库&#34;&gt;删除远程仓库：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git remote rm github
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;推送到github或gitee&#34;&gt;推送到GitHub或Gitee:&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git push gitee master
$ git push github master
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;该操作在后面内容同步后再推送。&lt;/p&gt;
&lt;h2 id=&#34;设置邮件和用户名&#34;&gt;设置邮件和用户名：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git config --global user.email &amp;#34;you@example.com&amp;#34;
$ git config --global user.name &amp;#34;Your Name&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;全局设置使用&amp;ndash;global参数，如果仅在本仓库设置身份标识，则省略 &amp;ndash;global 参数。&lt;/p&gt;
&lt;h2 id=&#34;查看git的修改状态&#34;&gt;查看git的修改状态：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git status
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;查看具体修改内容&#34;&gt;查看具体修改内容：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git diff readme.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;添加修改文件&#34;&gt;添加修改文件：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git add readme.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也可以提交文件，但是空目录不显示&lt;/p&gt;
&lt;h2 id=&#34;提交文件修改&#34;&gt;提交文件修改：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git commit -m &amp;#34;修改说明&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;该操作提交所有add的文件和目录&lt;/p&gt;
&lt;h2 id=&#34;丢弃没有add的修改&#34;&gt;丢弃没有add的修改：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git checkout -- readme.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;丢弃已经add文件的修改&#34;&gt;丢弃已经add文件的修改：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git reset HEAD readme.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;删除文件&#34;&gt;删除文件：&lt;/h2&gt;
&lt;p&gt;先从本地删除文件，之后&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git rm test.txt
$ git commit -m &amp;#34;修改说明&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;文件已删除。恢复使用：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git checkout -- test.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;将远程仓库克隆到本地&#34;&gt;将远程仓库克隆到本地：&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ git clone git@gitee.com:wiseai/wiseai.git
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;克隆可以使用Https或者ssh等，具体可以看网站。
使用ssh必有添加公钥才行。&lt;/p&gt;
&lt;h2 id=&#34;仓库&#34;&gt;仓库&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 在当前目录新建一个Git代码库
$ git init

# 新建一个目录，将其初始化为Git代码库
$ git init [project-name]

# 下载一个项目和它的整个代码历史
$ git clone [url]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;配置&#34;&gt;配置&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 显示当前的Git配置
$ git config --list

# 编辑Git配置文件
$ git config -e [--global]

# 设置提交代码时的用户信息
$ git config [--global] user.name &amp;quot;[name]&amp;quot;
$ git config [--global] user.email &amp;quot;[email address]&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;增加删除文件&#34;&gt;增加/删除文件&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 添加指定文件到暂存区
$ git add [file1] [file2] ...

# 添加指定目录到暂存区，包括子目录
$ git add [dir]

# 添加当前目录的所有文件到暂存区
$ git add .

# 添加每个变化前，都会要求确认
# 对于同一个文件的多处变化，可以实现分次提交
$ git add -p

# 删除工作区文件，并且将这次删除放入暂存区
$ git rm [file1] [file2] ...

# 停止追踪指定文件，但该文件会保留在工作区
$ git rm --cached [file]

# 改名文件，并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;代码提交&#34;&gt;代码提交&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 提交暂存区到仓库区
$ git commit -m [message]

# 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] ... -m [message]

# 提交工作区自上次commit之后的变化，直接到仓库区
$ git commit -a

# 提交时显示所有diff信息
$ git commit -v

# 使用一次新的commit，替代上一次提交
# 如果代码没有任何新变化，则用来改写上一次commit的提交信息
$ git commit --amend -m [message]

# 重做上一次commit，并包括指定文件的新变化
$ git commit --amend [file1] [file2] ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;分支&#34;&gt;分支&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 列出所有本地分支
$ git branch

# 列出所有远程分支
$ git branch -r

# 列出所有本地分支和远程分支
$ git branch -a

# 新建一个分支，但依然停留在当前分支
$ git branch [branch-name]

# 新建一个分支，并切换到该分支
$ git checkout -b [branch]

# 新建一个分支，指向指定commit
$ git branch [branch] [commit]

# 新建一个分支，与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]

# 切换到指定分支，并更新工作区
$ git checkout [branch-name]

# 切换到上一个分支
$ git checkout -

# 建立追踪关系，在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支
$ git merge [branch]

# 选择一个commit，合并进当前分支
$ git cherry-pick [commit]

# 删除分支
$ git branch -d [branch-name]

# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;标签&#34;&gt;标签&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 列出所有tag
$ git tag

# 新建一个tag在当前commit
$ git tag [tag]

# 新建一个tag在指定commit
$ git tag [tag] [commit]

# 删除本地tag
$ git tag -d [tag]

# 删除远程tag
$ git push origin :refs/tags/[tagName]

# 查看tag信息
$ git show [tag]

# 提交指定tag
$ git push [remote] [tag]

# 提交所有tag
$ git push [remote] --tags

# 新建一个分支，指向某个tag
$ git checkout -b [branch] [tag]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;查看信息&#34;&gt;查看信息&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 显示有变更的文件
$ git status

# 显示当前分支的版本历史
$ git log

# 显示commit历史，以及每次commit发生变更的文件
$ git log --stat

# 搜索提交历史，根据关键词
$ git log -S [keyword]

# 显示某个commit之后的所有变动，每个commit占据一行
$ git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动，其&amp;quot;提交说明&amp;quot;必须符合搜索条件
$ git log [tag] HEAD --grep feature

# 显示某个文件的版本历史，包括文件改名
$ git log --follow [file]
$ git whatchanged [file]

# 显示指定文件相关的每一次diff
$ git log -p [file]

# 显示过去5次提交
$ git log -5 --pretty --oneline

# 显示所有提交过的用户，按提交次数排序
$ git shortlog -sn

# 显示指定文件是什么人在什么时间修改过
$ git blame [file]

# 显示暂存区和工作区的差异
$ git diff

# 显示暂存区和上一个commit的差异
$ git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异
$ git diff HEAD

# 显示两次提交之间的差异
$ git diff [first-branch]...[second-branch]

# 显示今天你写了多少行代码
$ git diff --shortstat &amp;quot;@{0 day ago}&amp;quot;

# 显示某次提交的元数据和内容变化
$ git show [commit]

# 显示某次提交发生变化的文件
$ git show --name-only [commit]

# 显示某次提交时，某个文件的内容
$ git show [commit]:[filename]

# 显示当前分支的最近几次提交
$ git reflog
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;远程同步&#34;&gt;远程同步&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 下载远程仓库的所有变动
$ git fetch [remote]

# 显示所有远程仓库
$ git remote -v

# 显示某个远程仓库的信息
$ git remote show [remote]

# 增加一个新的远程仓库，并命名
$ git remote add [shortname] [url]

# 取回远程仓库的变化，并与本地分支合并
$ git pull [remote] [branch]

# 上传本地指定分支到远程仓库
$ git push [remote] [branch]

# 强行推送当前分支到远程仓库，即使有冲突
$ git push [remote] --force

# 推送所有分支到远程仓库
$ git push [remote] --all
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;撤销&#34;&gt;撤销&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 恢复暂存区的指定文件到工作区
$ git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
$ git checkout .

# 重置暂存区的指定文件，与上一次commit保持一致，但工作区不变
$ git reset [file]

# 重置暂存区与工作区，与上一次commit保持一致
$ git reset --hard

# 重置当前分支的指针为指定commit，同时重置暂存区，但工作区不变
$ git reset [commit]

# 重置当前分支的HEAD为指定commit，同时重置暂存区和工作区，与指定commit一致
$ git reset --hard [commit]

# 重置当前HEAD为指定commit，但保持暂存区和工作区不变
$ git reset --keep [commit]

# 新建一个commit，用来撤销指定commit
# 后者的所有变化都将被前者抵消，并且应用到当前分支
$ git revert [commit]

暂时将未提交的变化移除，稍后再移入
$ git stash
$ git stash pop
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;其他&#34;&gt;其他&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 生成一个可供发布的压缩包
$ git archive
&lt;/code&gt;&lt;/pre&gt;

    </description>
    </item>
    
    <item>
    <title>golang和nodejs环境配置</title>
    <link>https://blog.wiseai.cn/post/golang%E5%92%8Cnodejs%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang%E5%92%8Cnodejs%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/</guid>
    <description>
        &lt;ol&gt;
&lt;li&gt;配置golang&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;下载文件：&lt;a href=&#34;https://golang.google.cn/dl/&#34; title=&#34;golang官网下载&#34;&gt;golang官网下载&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;解压文件至/usr/local/目录下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# rm -rf /usr/local/go &amp;amp;&amp;amp; tar -C /usr/local -xzf go1.17.6.linux-amd64.tar.gz&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;（第一个命令是删除golang的残余文件）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建立软链接至/usr/bin/目录下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# ln -s /usr/local/go/bin/go /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ln -s /usr/local/go/bin/gofmt /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改为国内源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# go env -w GO111MODULE=auto &lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# go env -w GOPROXY=https://goproxy.cn,direct&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查看配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# go env&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;配置nodejs&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;下载文件：&lt;a href=&#34;https://nodejs.org/zh-cn/&#34; title=&#34;nodejs官网&#34;&gt;nodejs官网&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;中文网站：&lt;a href=&#34;http://nodejs.cn/download/&#34;&gt;nodejs中文网&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;解压文件至/usr/local/目录下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# rm -rf /usr/local/node &amp;amp;&amp;amp; tar -C /usr/local -xzf node-v16.13.2-linux-arm64.tar&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;（第一个命令是删除nodejs的残余文件）
&lt;code&gt;# npm install yarn&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建立软链接至/usr/bin/目录下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# ln -s /usr/local/node-v16.13.2-linux-arm64/bin/node /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ln -s /usr/local/node-v16.13.2-linux-arm64/bin/npm /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ln -s /usr/local/node-v16.13.2-linux-arm64/bin/npx /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改为国内源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# npm config set registry https://registry.npmmirror.com&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查看配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# npm config get&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;go mod&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在项目目录下，要执行两个命令自动生成相关文件&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# go mod init&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# go mod tidy&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>golang的build命令编译</title>
    <link>https://blog.wiseai.cn/post/golang%E7%9A%84build%E5%91%BD%E4%BB%A4/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/golang%E7%9A%84build%E5%91%BD%E4%BB%A4/</guid>
    <description>
        &lt;p&gt;&lt;code&gt;go build&lt;/code&gt;，是我们非常常用的命令，它可以启动编译，把我们的包和相关的依赖编译成一个可执行的文件。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;usage: go build [-o output] [-i] [build flags] [packages]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go build&lt;/code&gt;的使用比较简洁，所有的参数都可以忽略，直到只有&lt;code&gt;go build&lt;/code&gt;，这个时候意味着使用当前目录进行编译，下面的几条命令是等价的：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go build

go build .

go build hello.go
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以上这三种写法，都是使用当前目录编译的意思。因为我们忽略了&lt;code&gt;packages&lt;/code&gt;,所以自然就使用当前目录进行编译了。从这里我们也可以推测出，&lt;code&gt;go build&lt;/code&gt;本质上需要的是一个路径，让编译器可以找到哪些需要编译的go文件。&lt;code&gt;packages&lt;/code&gt;其实是一个相对路径，是相对于我们定义的&lt;code&gt;GOROOT&lt;/code&gt;和&lt;code&gt;GOPATH&lt;/code&gt;这两个环境变量的，所以有了&lt;code&gt;packages&lt;/code&gt;这个参数后，&lt;code&gt;go build&lt;/code&gt;就可以知道哪些需要编译的go文件了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go build flysnow.org/tools&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这种方式是指定包的方式，这样会明确地编译我们这个包。当然我们也可以使用通配符&lt;/p&gt;
&lt;p&gt;&lt;code&gt;go build flysnow.org/tools/...&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3个点表示匹配所有字符串，这样&lt;code&gt;go build&lt;/code&gt;就会编译tools目录下的所有包。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;讲到&lt;code&gt;go build&lt;/code&gt;编译，不能不提跨平台编译，Go提供了编译链工具，可以让我们在任何一个开发平台上，编译出其他平台的可执行文件。&lt;/p&gt;
&lt;p&gt;默认情况下，都是根据我们当前的机器生成的可执行文件，比如你的是Linux 64位，就会生成Linux 64位下的可执行文件，比如我的Mac；可以使用go env查看编译环境,以下截取重要的部分。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;go env
GOARCH=&amp;#34;amd64&amp;#34;
GOEXE=&amp;#34;&amp;#34;
GOHOSTARCH=&amp;#34;amd64&amp;#34;
GOHOSTOS=&amp;#34;darwin&amp;#34;
GOOS=&amp;#34;darwin&amp;#34;
GOROOT=&amp;#34;/usr/local/go&amp;#34;
GOTOOLDIR=&amp;#34;/usr/local/go/pkg/tool/darwin_amd64&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意里面两个重要的环境变量GOOS和GOARCH,其中GOOS指的是目标操作系统，它的可用值为：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;darwin&lt;/li&gt;
&lt;li&gt;freebsd&lt;/li&gt;
&lt;li&gt;linux&lt;/li&gt;
&lt;li&gt;windows&lt;/li&gt;
&lt;li&gt;android&lt;/li&gt;
&lt;li&gt;dragonfly&lt;/li&gt;
&lt;li&gt;netbsd&lt;/li&gt;
&lt;li&gt;openbsd&lt;/li&gt;
&lt;li&gt;plan9&lt;/li&gt;
&lt;li&gt;solaris&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;一共支持10种操作系统。GOARCH指的是目标处理器的架构，目前支持的有：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;arm&lt;/li&gt;
&lt;li&gt;arm64&lt;/li&gt;
&lt;li&gt;386&lt;/li&gt;
&lt;li&gt;amd64&lt;/li&gt;
&lt;li&gt;ppc64&lt;/li&gt;
&lt;li&gt;ppc64le&lt;/li&gt;
&lt;li&gt;mips64&lt;/li&gt;
&lt;li&gt;mips64le&lt;/li&gt;
&lt;li&gt;s390x&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一共支持9种处理器的架构&lt;/p&gt;
&lt;p&gt;如果我们要生成不同平台架构的可执行程序，只要改变这两个环境变量就可以了，比如要生成Linux 64位的程序，命令如下：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GOOS=linux GOARCH=amd64 go build flysnow.org/hello&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;前面两个赋值，是更改环境变量，这样的好处是只针对本次运行有效，不会更改我们默认的配置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-ldflags参数的使用和gdb调试&lt;/strong&gt;&lt;br&gt;
设置编译参数-ldflags &amp;ldquo;-w -s&amp;rdquo;&lt;br&gt;
其中-w为去掉调试信息（无法使用gdb调试），-s为去掉符号表&lt;br&gt;
&lt;code&gt;# go build -ldflags &amp;quot;-w -s&amp;quot; ./hello.go&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# gdb ./main&lt;/code&gt;&lt;br&gt;
这样就可以进行gdb调试。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;golang删除二进制文件中的源码路径信息 -trimpath&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;# env CGO_ENABLED=0 go build -trimpath -ldflags &amp;quot;-s -w&amp;quot; -o bin/frps ./cmd/frps&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查询所有build用法使用这个命令&lt;br&gt;
&lt;code&gt;go help build&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CGO_ENABLED 环境变量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;系统默认为1，可以使用&lt;code&gt;go env&lt;/code&gt;命令查看。&lt;/p&gt;
&lt;p&gt;当CGO_ENABLED=1，进行编译时会将文件中引用libc的库（比如常用的net包），以动态链接的方式生成目标文件。&lt;/p&gt;
&lt;p&gt;当CGO_ENABLED=0，进行编译时则会把在目标文件中未定义的符号（外部函数）一起链接到可执行文件中。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# CGO_ENABLED=1 go build hello.go&lt;/code&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>GPIO相关知识</title>
    <link>https://blog.wiseai.cn/post/gpio%E7%9B%B8%E5%85%B3/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/gpio%E7%9B%B8%E5%85%B3/</guid>
    <description>
        &lt;h2 id=&#34;linux查看gpio状态&#34;&gt;linux查看gpio状态&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cat /sys/kernel/debug/gpio&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;有个blog里有这个方法，去试试：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mkdir /tmp/debug
mount -t debugfs debugfs /tmp/debug
cat /tmp/debug/gpio
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;gpio操作的一些说明&#34;&gt;GPIO操作的一些说明&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;首先，看看系统中有没有“/sys/class/gpio”这个文件夹。如果没有请在编译内核的时候加入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Device Drivers-&amp;gt; GPIO Support -&amp;gt;/sys/class/gpio/… (sysfs interface)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/sys/class/gpio 的使用说明：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;gpio_operation 通过/sys/文件接口操作IO端口 GPIO到文件系统的映射&lt;/p&gt;
&lt;p&gt;◇  控制GPIO的目录位于/sys/class/gpio&lt;/p&gt;
&lt;p&gt;◇  /sys/class/gpio/export文件用于通知系统需要导出控制的GPIO引脚编号&lt;/p&gt;
&lt;p&gt;◇  /sys/class/gpio/unexport 用于通知系统取消导出&lt;/p&gt;
&lt;p&gt;◇  /sys/class/gpio/gpiochipX目录保存系统中GPIO寄存器的信息，包括每个寄存器控制引脚的起始编号base，寄存器名称，引脚总数 导出一个引脚的操作步骤&lt;/p&gt;
&lt;p&gt;◇  首先计算此引脚编号，引脚编号 = 控制引脚的寄存器基数 + 控制引脚寄存器位数&lt;/p&gt;
&lt;p&gt;◇  向/sys/class/gpio/export写入此编号，比如12号引脚，在shell中可以通过以下命令实现，&lt;code&gt;echo 12 &amp;gt; export&lt;/code&gt;命令成功后生成/sys/class/gpio/gpio12目录，如果没有出现相应的目录，说明此引脚不可导出&lt;/p&gt;
&lt;p&gt;◇  direction文件，定义输入输入方向，可以通过下面命令定义为输出。direction接受的参数：in, out, high, low。high/low同时设置方向为输出，并将value设置为相应的1/0&lt;/p&gt;
&lt;p&gt;◇  value文件是端口的数值，为1或0&lt;/p&gt;
&lt;h2 id=&#34;例子&#34;&gt;例子：&lt;/h2&gt;
&lt;p&gt;进入/sys/class/gpio/文件夹：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将gpio7重定向用户定义设备，生成gpio7目录&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# echo 7 &amp;gt; export&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;进入gpio7目录并查看文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;# cd gpio7&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# ls&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;direction设置引脚方向，输入还是输出&lt;/p&gt;
&lt;p&gt;value设置引脚状态，高电平还是低电平&lt;/p&gt;
&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;输入状态&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;设置引脚状态为输入状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# echo in &amp;gt; direction&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查看引脚高低电平&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# cat value &lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;输出状态&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;设置引脚状态为输出状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# echo out &amp;gt; direction&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设置输出高电平&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# echo 1 &amp;gt; value&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设置输出低电平&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;# echo 0 &amp;gt; value&lt;/code&gt;&lt;/p&gt;
&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;注销&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;测试完毕之后返回/sys/class/gpio/目录，并将gpio注销&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# cd /sys/class/gpio/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# echo 7 &amp;gt; /sys/class/gpio/unexport&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这篇博客很不错：https://zhuanlan.zhihu.com/p/66660750
&lt;a href=&#34;https://blog.csdn.net/k1ang/article/details/107117077&#34;&gt;https://blog.csdn.net/k1ang/article/details/107117077&lt;/a&gt;&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux安装hp打印机驱动</title>
    <link>https://blog.wiseai.cn/post/linux%E5%AE%89%E8%A3%85hp%E6%89%93%E5%8D%B0%E6%9C%BA%E9%A9%B1%E5%8A%A8/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E5%AE%89%E8%A3%85hp%E6%89%93%E5%8D%B0%E6%9C%BA%E9%A9%B1%E5%8A%A8/</guid>
    <description>
        &lt;h3 id=&#34;一安装hplip&#34;&gt;一、安装hplip&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;# dnf install hplip&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;或者官网下载&lt;/p&gt;
&lt;p&gt;安装hplip-gui,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# dnf install hplip-gui&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;二运行hp-plugin安装插件&#34;&gt;二、运行hp-plugin安装插件&lt;/h3&gt;
&lt;p&gt;或者下载hplip-3.17.10-plugin.run文件，版本和hplip相同，网址为：http://www.openprinting.org/download/printdriver/auxfiles/HP/plugins/&lt;/p&gt;
&lt;p&gt;安装时会验证文件，不用理会，直接安装就行。&lt;/p&gt;
&lt;p&gt;好了！&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Linux开机启动的方法</title>
    <link>https://blog.wiseai.cn/post/linux%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E7%9A%84%E6%96%B9%E6%B3%95/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8%E7%9A%84%E6%96%B9%E6%B3%95/</guid>
    <description>
        &lt;h2 id=&#34;一配置rclocal文件实现开机启动&#34;&gt;一、配置rc.local文件实现开机启动：&lt;/h2&gt;
&lt;p&gt;首先，建立一个文件：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo touch /etc/rc.local&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;给予可执行权限：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo chmod +x /etc/rc.local&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在文件写入以下内容：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/bin/sh

/path/to/gohttpserver -r /path/to/ --port 8080 --upload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;以上两个/path/to/，一个是文件所在目录，一个是工作目录，都使用绝对路径。
完成！！！&lt;/p&gt;
&lt;h2 id=&#34;二第二种方法以后有时间再补充&#34;&gt;二、第二种方法以后有时间再补充。&lt;/h2&gt;

    </description>
    </item>
    
    <item>
    <title>Linux更换网卡后网络服务不能启动的问题解决办法</title>
    <link>https://blog.wiseai.cn/post/linux%E6%9B%B4%E6%8D%A2%E7%BD%91%E5%8D%A1%E5%90%8E%E7%BD%91%E7%BB%9C%E6%9C%8D%E5%8A%A1%E4%B8%8D%E8%83%BD%E5%90%AF%E5%8A%A8%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/linux%E6%9B%B4%E6%8D%A2%E7%BD%91%E5%8D%A1%E5%90%8E%E7%BD%91%E7%BB%9C%E6%9C%8D%E5%8A%A1%E4%B8%8D%E8%83%BD%E5%90%AF%E5%8A%A8%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E5%8A%9E%E6%B3%95/</guid>
    <description>
        &lt;p&gt;1、迁移以后，会存在其中一个网卡无法启动（eth0 or eth1）&lt;/p&gt;
&lt;p&gt;[root@ ~]# ifup eth0&lt;/p&gt;
&lt;p&gt;WARNING: Deprecated config file /etc/modprobe.conf, all config files belong into /etc/modprobe.d/.&lt;/p&gt;
&lt;p&gt;WARNING: Deprecated config file /etc/modprobe.conf, all config files belong into /etc/modprobe.d/.&lt;/p&gt;
&lt;p&gt;Device eth0 does not seem to be present, delaying initialization.&lt;/p&gt;
&lt;p&gt;2、删除文件&lt;/p&gt;
&lt;p&gt;[root@ ~]# mv /etc/udev/rules.d/70-persistent-net.rules /etc/udev/rules.d/70-persistent-net.rules.bak&lt;/p&gt;
&lt;p&gt;3、注释掉文件ifcfg-eth0及ifcfg-eth01中MAC地址（HWADDR）&lt;/p&gt;
&lt;p&gt;[root@ ~]# cat /etc/sysconfig/network-scripts/ifcfg-eth0&lt;/p&gt;
&lt;p&gt;DEVICE=eth0&lt;/p&gt;
&lt;p&gt;BOOTPROTO=static&lt;/p&gt;
&lt;p&gt;#HWADDR=&amp;ldquo;XX:XX:XX:XX:XX:XX&amp;rdquo;&lt;/p&gt;
&lt;p&gt;ONBOOT=yes&lt;/p&gt;
&lt;p&gt;TYPE=Ethernet&lt;/p&gt;
&lt;p&gt;4、重启服务器（重启网卡不好使）&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>通过iptables设置防火墙</title>
    <link>https://blog.wiseai.cn/post/iptables%E8%AE%BE%E7%BD%AEsh/</link>
    <pubDate>Mon, 30 May 2022 00:00:00 +0000</pubDate>
    <author>wiseai</author>
    <guid>https://blog.wiseai.cn/post/iptables%E8%AE%BE%E7%BD%AEsh/</guid>
    <description>
        &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    #!/bin/bash

    PATH=/sbin:/bin:/usr/sbin:/usr/bin

    #设置网卡
    wk=&amp;#34;eth1&amp;#34;

    # 1. 清除规则
    iptables -F
    iptables -X
    iptables -Z

    # 2. 设定政策
    iptables -P INPUT DROP
    iptables -P OUTPUT ACCEPT
    iptables -P FORWARD ACCEPT

    # 3~5. 制订各项规则
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A INPUT -i $wk -m state --state RELATED,ESTABLISHED -j ACCEPT

    #samba服务
    iptables -A INPUT -i $wk -p tcp --dport 139 -j ACCEPT
    iptables -A INPUT -i $wk -p tcp --dport 445 -j ACCEPT
    iptables -A INPUT -i $wk -p udp --dport 137:138 -j ACCEPT

    #vsftp服务
    iptables -A INPUT -i $wk -p tcp --dport 21 -j ACCEPT
    iptables -A INPUT -i $wk -p tcp --dport 1024:65535 -j ACCEPT
    #iptables -A INPUT -i $wk -s 192.168.1.0/24 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;1.查看防火墙规则&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;# iptables [-t tables] [-L] [-nv] &lt;/code&gt;
选项与参数：
-t ：后面接table ，例如nat或filter，若省略此项目，则使用预设的filter
-L ：列出目前的table的规则
-n ：不进行IP 与HOSTNAME 的反查，显示速度会快很多！
-v ：列出更多的相关信息&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.清除规则&lt;/strong&gt;
&lt;code&gt;# iptables [-t tables] [-FXZ] &lt;/code&gt;
选项与参数：
-F ：清除所有的已订定的规则；
-X ：杀掉所有使用者&amp;quot;自订&amp;quot; 的chain；
-Z ：将所有的chain 的计数与流量统计都归零&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.定义预设政策(policy)&lt;/strong&gt;
&lt;code&gt;# iptables [-t nat] -P [INPUT,OUTPUT,FORWARD] [ACCEPT,DROP] &lt;/code&gt;
选项与参数：
-P ：定义政策( Policy )。注意，这个P为大写啊！
ACCEPT ：该封包可接受
DROP ：该封包直接丢弃，不会让client 端知道为何被丢弃。&lt;/p&gt;
&lt;p&gt;范例：将本机的INPUT设定为DROP ，其他设定为ACCEPT&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# iptables -P INPUT DROP 
# iptables -P OUTPUT ACCEPT 
# iptables -P FORWARD ACCEPT 
# iptables -t nat -P PREROUTING ACCEPT
# iptables -t nat -P POSTROUTING ACCEPT
# iptables -t nat -P INPUT ACCEPT
# iptables -t nat -P OUTPUT ACCEPT
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;nat表不用于过滤，所以不能设置为DROP&lt;/strong&gt;
&lt;strong&gt;4.定义规则&lt;/strong&gt;
&lt;code&gt;# iptables [-AI链名] [-io网络界面] [-p协议] [-s来源IP/网域] [-sport 端口范围] [-d目标IP/网域] [-dport 端口范围] -j [ACCEPT&#39;DROP&#39;REJECT&#39;LOG] &lt;/code&gt;
选项与参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-AI链名：规则的&amp;#34;插入&amp;#34;或&amp;#34;增加&amp;#34;
-A：新增加一条规则，该规则增加在原本规则的最后面。例如原本已经有四条规则，使用-A就可以加上第五条规则！
-I：插入一条规则。如果没有指定此规则的顺序，预设是插入变成第一条规则。
例如原本有四条规则，使用-I则该规则变成第一条，而原本四条变成2~5号
链：有INPUT，OUTPUT，FORWARD等，此链名称又与-io有关，请看底下。
-io网卡：设定封包进出的规范
-i：封包所进入的那个网卡，例如eth0，lo等网卡。需与INPUT链配合；
-o：封包所传出的那个网卡，需与OUTPUT链配合；
-p协议：设定此规则适用于哪种封包格式
主要的封包格式有：tcp，udp，icmp及all。
-s来源IP/网域：设定此规则之封包的来源项目，可指定单纯的IP或包括网域，例如：
IP :192.168.0.100
网域：192.168.0.0/24，192.168.0.0/255.255.255.0均可。
若规范为『不许』时，则加上！即可，例如：
-s！192.168.100.0/24表示不许192.168.100.0/24封包来源；
-d目标IP/网域：同-s，只不过这里指的是目标的IP或网域。
--sport 来源端口范围。例如 1024:65535 
--dport 目标端口范围
-j：后面接动作，主要的动作有接受（ACCEPT）、丢弃（DROP）、拒绝（REJECT）及记录（LOG） 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;# iptables -A INPUT [-m state] [--state状态]  &lt;/code&gt;
选项与参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-m：一些iptables的外挂模块，主要常见的有：  
          state：状态模块  
          mac：网络卡硬件地址（hardware address）  
--state：一些封包的状态，主要有：  
         INVALID：无效的封包，例如数据破损的封包状态  
        ESTABLISHED：已经联机成功的联机状态；  
        NEW：想要新建立联机的封包状态；  
       RELATED：这个最常用！表示这个封包是与我们主机发送出去的封包有关  
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例：只要已建立或相关封包就予以通过，只要是不合法封包就丢弃 &lt;br&gt;
&lt;code&gt;# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT  &lt;/code&gt;
&lt;code&gt;# iptables -A INPUT -m state --state INVALID -j DROP&lt;/code&gt;
对MAC地址为aa:bb:cc:dd:ee:ff主机开放其连接
&lt;code&gt;# iptables -A INPUT -m mac --mac-source aa:bb:cc:dd :ee:ff -j ACCEPT &lt;/code&gt;
选项与参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--mac-source ：就是来源主机的MAC
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;ICMP 封包规则：针对是否回应ping 来设计&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常我们会把ICMP type 8 (echo request)去掉而已，让远端主机不知道我们是否存在，也不会接受ping的回应。
&lt;code&gt;# iptables -A INPUT [-p icmp] [--icmp-type类型] -j ACCEPT &lt;/code&gt;
选项与参数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--icmp-type ：后面必须要接ICMP 的封包类型，也可以使用代号，
              例如8 代表echo request 的意思。
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;例：让0,3,4,11,12,14,16,18的ICMP type可以进入本机：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#!/bin/bash 
icmp_type=&amp;#34;0 3 4 11 12 14 16 18&amp;#34; 
for typeicmp in $icmp_type 
do 
    iptables -A INPUT -i eth0 -p icmp --icmp-type $typeicmp -j ACCEPT 
done
&lt;/code&gt;&lt;/pre&gt;
    </description>
    </item>
    
    <item>
    <title>Footnote test</title>
    <link>https://blog.wiseai.cn/post/footnote/</link>
    <pubDate>Mon, 31 May 2021 00:00:00 +0000</pubDate>
    
    <guid>https://blog.wiseai.cn/post/footnote/</guid>
    <description>
        &lt;p&gt;Lorem ipsum dolor sit amet&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; consectetur adipisicing elit. Nemo tempora eum cumque neque voluptatum, odit ipsum consequatur animi.&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Nemo tempora eum cumque neque voluptatum, odit ipsum&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; consequatur animi.&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Nemo tempora eum cumque neque voluptatum, odit ipsum consequatur animi.&lt;/p&gt;
&lt;p&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Nemo tempora eum cumque neque voluptatum, odit ipsum consequatur animi.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Test Footnote&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Test Footnote2&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

    </description>
    </item>
    
    <item>
    <title>Katex support</title>
    <link>https://blog.wiseai.cn/post/test-katex/</link>
    <pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/test-katex/</guid>
    <description>
        &lt;p&gt;The following&lt;/p&gt;
&lt;p&gt;$$ \int_{a}^{b} x^2 dx $$&lt;/p&gt;
&lt;p&gt;Is an integral&lt;/p&gt;
&lt;p&gt;$$ \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } $$&lt;/p&gt;
&lt;p&gt;Enable Katex in the config file by setting the &lt;code&gt;katex&lt;/code&gt; param to &lt;code&gt;true&lt;/code&gt;. This will import the necessary Katex CSS/JS.&lt;/p&gt;
&lt;p&gt;See the online reference of &lt;a href=&#34;https://katex.org/docs/supported.html&#34;&gt;supported TeX functions&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-latex&#34; data-lang=&#34;latex&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Inline math: &lt;span class=&#34;s&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\varphi&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\dfrac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\sqrt&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;6180339887&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;… &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Inline math: $ \varphi = \dfrac{1+\sqrt5}{2}= 1.6180339887… $&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-latex&#34; data-lang=&#34;latex&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Block math:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;$$&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\varphi&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\cdots&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} } } &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Block math:&lt;/p&gt;
&lt;p&gt;$$ \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } $$&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>MathJax support</title>
    <link>https://blog.wiseai.cn/post/test-mathjax/</link>
    <pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/test-mathjax/</guid>
    <description>
        &lt;p&gt;The following&lt;/p&gt;
&lt;p&gt;$$ \int_{a}^{b} x^2 dx $$&lt;/p&gt;
&lt;p&gt;Is an integral&lt;/p&gt;
&lt;p&gt;$$ \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } $$&lt;/p&gt;
&lt;p&gt;Enable MathJax in the config file by setting the &lt;code&gt;mathjax&lt;/code&gt; param to &lt;code&gt;true&lt;/code&gt;. This will import the necessary MathJax CSS/JS.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-latex&#34; data-lang=&#34;latex&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Inline math: &lt;span class=&#34;s&#34;&gt;$&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\varphi&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\dfrac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\sqrt&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;}{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;6180339887&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;… &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Inline math: $ \varphi = \dfrac{1+\sqrt5}{2}= 1.6180339887… $&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-latex&#34; data-lang=&#34;latex&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Block math:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;$$&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\varphi&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\frac&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} {&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;\cdots&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;} } } &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$$&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Block math:&lt;/p&gt;
&lt;p&gt;$$ \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } $$&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Markdown Syntax Guide</title>
    <link>https://blog.wiseai.cn/post/markdown-syntax/</link>
    <pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/markdown-syntax/</guid>
    <description>
        &lt;p&gt;This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.&lt;/p&gt;
&lt;h2 id=&#34;headings&#34;&gt;Headings&lt;/h2&gt;
&lt;p&gt;The following HTML &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;—&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt; elements represent six levels of section headings. &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; is the highest section level while &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt; is the lowest.&lt;/p&gt;
&lt;h1 id=&#34;h1&#34;&gt;H1&lt;/h1&gt;
&lt;h2 id=&#34;h2&#34;&gt;H2&lt;/h2&gt;
&lt;h3 id=&#34;h3&#34;&gt;H3&lt;/h3&gt;
&lt;h4 id=&#34;h4&#34;&gt;H4&lt;/h4&gt;
&lt;h5 id=&#34;h5&#34;&gt;H5&lt;/h5&gt;
&lt;h6 id=&#34;h6&#34;&gt;H6&lt;/h6&gt;
&lt;h2 id=&#34;paragraph&#34;&gt;Paragraph&lt;/h2&gt;
&lt;p&gt;Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.&lt;/p&gt;
&lt;p&gt;Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.&lt;/p&gt;
&lt;h2 id=&#34;blockquotes&#34;&gt;Blockquotes&lt;/h2&gt;
&lt;p&gt;The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a &lt;code&gt;footer&lt;/code&gt; or &lt;code&gt;cite&lt;/code&gt; element, and optionally with in-line changes such as annotations and abbreviations.&lt;/p&gt;
&lt;h4 id=&#34;blockquote-without-attribution&#34;&gt;Blockquote without attribution&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Tiam, ad mint andaepu dandae nostion secatur sequo quae.
&lt;strong&gt;Note&lt;/strong&gt; that you can use &lt;em&gt;Markdown syntax&lt;/em&gt; within a blockquote.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;blockquote-with-attribution&#34;&gt;Blockquote with attribution&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t communicate by sharing memory, share memory by communicating.&lt;br&gt;
— &lt;cite&gt;Rob Pike&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;tables&#34;&gt;Tables&lt;/h2&gt;
&lt;p&gt;Tables aren&amp;rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Age&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&#34;inline-markdown-within-tables&#34;&gt;Inline Markdown within tables&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Italics&lt;/th&gt;
&lt;th&gt;Bold&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;italics&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;bold&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;code&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;code-blocks&#34;&gt;Code Blocks&lt;/h2&gt;
&lt;h4 id=&#34;code-block-with-backticks&#34;&gt;Code block with backticks&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;lang&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;charset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Example HTML5 Document&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Test&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;code-block-indented-with-four-spaces&#34;&gt;Code block indented with four spaces&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;
  &amp;lt;title&amp;gt;Example HTML5 Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;p&amp;gt;Test&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;code-block-with-hugos-internal-highlight-shortcode&#34;&gt;Code block with Hugo&amp;rsquo;s internal highlight shortcode&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;lang&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;meta&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;charset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Example HTML5 Document&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Test&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;list-types&#34;&gt;List Types&lt;/h2&gt;
&lt;h4 id=&#34;ordered-list&#34;&gt;Ordered List&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;First item&lt;/li&gt;
&lt;li&gt;Second item&lt;/li&gt;
&lt;li&gt;Third item&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;unordered-list&#34;&gt;Unordered List&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;List item&lt;/li&gt;
&lt;li&gt;Another item&lt;/li&gt;
&lt;li&gt;And another item&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;nested-list&#34;&gt;Nested list&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Fruit
&lt;ul&gt;
&lt;li&gt;Apple&lt;/li&gt;
&lt;li&gt;Orange&lt;/li&gt;
&lt;li&gt;Banana&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dairy
&lt;ul&gt;
&lt;li&gt;Milk&lt;/li&gt;
&lt;li&gt;Cheese&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;other-elements--abbr-sub-sup-kbd-mark&#34;&gt;Other Elements — abbr, sub, sup, kbd, mark&lt;/h2&gt;
&lt;p&gt;&lt;abbr title=&#34;Graphics Interchange Format&#34;&gt;GIF&lt;/abbr&gt; is a bitmap image format.&lt;/p&gt;
&lt;p&gt;H&lt;sub&gt;2&lt;/sub&gt;O&lt;/p&gt;
&lt;p&gt;X&lt;sup&gt;n&lt;/sup&gt; + Y&lt;sup&gt;n&lt;/sup&gt; = Z&lt;sup&gt;n&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Press &lt;kbd&gt;&lt;kbd&gt;CTRL&lt;/kbd&gt;+&lt;kbd&gt;ALT&lt;/kbd&gt;+&lt;kbd&gt;Delete&lt;/kbd&gt;&lt;/kbd&gt; to end the session.&lt;/p&gt;
&lt;p&gt;Most &lt;mark&gt;salamanders&lt;/mark&gt; are nocturnal, and hunt for insects, worms, and other small creatures.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;The above quote is excerpted from Rob Pike&amp;rsquo;s &lt;a href=&#34;https://www.youtube.com/watch?v=PAAkCSZUG1c&#34;&gt;talk&lt;/a&gt; during Gopherfest, November 18, 2015.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
    </description>
    </item>
    
    <item>
    <title>Example Lazy Load Image</title>
    <link>https://blog.wiseai.cn/post/example-lazy-load-image/</link>
    <pubDate>Sat, 09 Mar 2019 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/example-lazy-load-image/</guid>
    <description>
        &lt;h2 id=&#34;nya-nya-nyan-meow-meow-mama&#34;&gt;Nya nya nyan meow meow mama&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=1327&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;p&gt;More napping, more napping all the napping is exhausting stretch out on bed you are a captive audience while sitting on the toilet, pet me slap the dog because cats rule bleghbleghvomit my furball really tie the room together always hungry. Humans,humans, humans oh how much they love us felines we are the center of attention they feed, they clean miaow then turn around and show you my bum. Cats secretly make all the worlds muffins slap owner&amp;rsquo;s face at 5am until human fills food dish, milk the cow hunt by meowing loudly at 5am next to human slave food dispenser throwup on your pillow. Get scared by doggo also cucumerro .&lt;/p&gt;
&lt;h2 id=&#34;cat-is-meow-meow&#34;&gt;Cat is meow meow&lt;/h2&gt;
&lt;p&gt;Sees bird in air, breaks into cage and attacks creature when in doubt, wash spend six hours per day washing, but still have a crusty butthole yet lick sellotape tickle my belly at your own peril i will pester for food when you&amp;rsquo;re in the kitchen even if it&amp;rsquo;s salad find box a little too small and curl up with fur hanging out.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1501820488136-72669149e0d4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=750&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;p&gt;Claw at curtains stretch and yawn nibble on tuna ignore human bite human hand. Under the bed mice yet funny little cat chirrup noise shaking upright tail when standing next to you but white cat sleeps on a black shirt for eat an easter feather as if it were a bird then burp victoriously.&lt;/p&gt;
&lt;h2 id=&#34;has-closed-eyes-but-still-sees-you-present-belly&#34;&gt;Has closed eyes but still sees you present belly&lt;/h2&gt;
&lt;p&gt;scratch hand when stroked for is good you understand your place in my world get scared by sudden appearance of cucumber. What the heck just happened, something feels fishy chew master&amp;rsquo;s slippers yet brown cats with pink ears bite the neighbor&amp;rsquo;s bratty kid cereal boxes make for five star accommodation but i like to spend my days sleeping and eating fishes that my human fished for me we live on a luxurious yacht, sailing proudly under the sun, i like to walk on the deck, watching the horizon, dreaming of a good bowl of milk. Lounge in doorway put butt in owner&amp;rsquo;s face, or ptracy destroy house in 5 seconds. Mrow no, you can&amp;rsquo;t close the door, i haven&amp;rsquo;t decided whether or not i wanna go out is good you understand your place in my world.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1511044568932-338cba0ad803?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=750&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;p&gt;Brown cats with pink ears shred all toilet paper and spread around the house being gorgeous with belly side up. Cats go for world domination the best thing in the universe is a cardboard box cats are cute so meow all night having their mate disturbing sleeping humans. Nya nya nyan annoy owner until he gives you food say meow repeatedly until belly rubs, feels good eat the fat cats food but meowing non stop for food. Pet right here, no not there, here, no fool, right here that other cat smells funny you should really give me all the treats because i smell the best and omg you finally got the right spot and i love you right now see brother cat receive pets, attack out of jealousy. Headbutt owner&amp;rsquo;s knee love blinks and purr purr purr purr yawn for stand in front of the computer screen, or mew mew for human is washing you why halp oh the horror flee scratch hiss bite.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1494256997604-768d1f608cac?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=801&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;cats-making-all-the-muffins&#34;&gt;Cats making all the muffins&lt;/h2&gt;
&lt;p&gt;Cats making all the muffins asdflkjaertvlkjasntvkjn (sits on keyboard) so the dog smells bad but cough hairball on conveniently placed pants and show belly but loved it, hated it, loved it, hated it catch mouse and gave it as a present. Give me attention or face the wrath of my claws meow all night for love me! and love you, then bite you or mesmerizing birds. Lick human with sandpaper tongue. Murf pratt ungow ungow scratch the box sit in box and to pet a cat, rub its belly, endure blood and agony, quietly weep, keep rubbing belly wake up human for food at 4am or eat owner&amp;rsquo;s food trip owner up in kitchen i want food. Curl up and sleep on the freshly laundered towels paw at your fat belly, steal mom&amp;rsquo;s crouton while she is in the bathroom yet nyan nyan goes the cat, scraaaaape scraaaape goes the walls when the cat murders them with its claws milk the cow suddenly go on wild-eyed crazy rampage toy mouse squeak roll over.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1519052537078-e6302a4968d4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=750&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;p&gt;Hunt by meowing loudly at 5am next to human slave food dispenser hate dog reward the chosen human with a slow blink. Cat dog hate mouse eat string barf pillow no baths hate everything miaow then turn around and show you my bum love fish, and kitty scratches couch bad kitty steal the warm chair right after you get up kitty poochy munch on tasty moths. Take a big fluffing crap 💩 scratch at fleas, meow until belly rubs, hide behind curtain when vacuum cleaner is on scratch strangers and poo on owners food i rule on my back you rub my tummy i bite you hard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://images.unsplash.com/photo-1489084917528-a57e68a79a1e?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;amp;ixlib=rb-1.2.1&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;w=750&amp;amp;q=80&#34; alt=&#34;Cute CAT&#34;&gt;&lt;/p&gt;
&lt;p&gt;Thanks by cats.&lt;/p&gt;

    </description>
    </item>
    
    <item>
    <title>Placeholder Text</title>
    <link>https://blog.wiseai.cn/post/placeholder-text/</link>
    <pubDate>Sat, 09 Mar 2019 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/placeholder-text/</guid>
    <description>
        &lt;p&gt;Lorem est tota propiore conpellat pectoribus de pectora summo.&lt;/p&gt;
&lt;p&gt;Redit teque digerit hominumque toris verebor lumina non cervice subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Exierant elisi ambit vivere dedere&lt;/li&gt;
&lt;li&gt;Duce pollice&lt;/li&gt;
&lt;li&gt;Eris modo&lt;/li&gt;
&lt;li&gt;Spargitque ferrea quos palude&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria tractus malis.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Comas hunc haec pietate fetum procerum dixit&lt;/li&gt;
&lt;li&gt;Post torum vates letum Tiresia&lt;/li&gt;
&lt;li&gt;Flumen querellas&lt;/li&gt;
&lt;li&gt;Arcanaque montibus omnes&lt;/li&gt;
&lt;li&gt;Quidem et&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;vagus-elidunt&#34;&gt;Vagus elidunt&lt;/h1&gt;
&lt;p&gt;&lt;svg class=&#34;canon&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; overflow=&#34;visible&#34; viewBox=&#34;0 0 496 373&#34; height=&#34;373&#34; width=&#34;496&#34;&gt;&lt;g fill=&#34;none&#34;&gt;&lt;path stroke=&#34;#000&#34; stroke-width=&#34;.75&#34; d=&#34;M.599 372.348L495.263 1.206M.312.633l494.95 370.853M.312 372.633L247.643.92M248.502.92l246.76 370.566M330.828 123.869V1.134M330.396 1.134L165.104 124.515&#34;&gt;&lt;/path&gt;&lt;path stroke=&#34;#ED1C24&#34; stroke-width=&#34;.75&#34; d=&#34;M275.73 41.616h166.224v249.05H275.73zM54.478 41.616h166.225v249.052H54.478z&#34;&gt;&lt;/path&gt;&lt;path stroke=&#34;#000&#34; stroke-width=&#34;.75&#34; d=&#34;M.479.375h495v372h-495zM247.979.875v372&#34;&gt;&lt;/path&gt;&lt;ellipse cx=&#34;498.729&#34; cy=&#34;177.625&#34; rx=&#34;.75&#34; ry=&#34;1.25&#34;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&#34;247.229&#34; cy=&#34;377.375&#34; rx=&#34;.75&#34; ry=&#34;1.25&#34;&gt;&lt;/ellipse&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Canons_of_page_construction#Van_de_Graaf_canon&#34;&gt;The Van de Graaf Canon&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;mane-refeci-capiebant-unda-mulcebat&#34;&gt;Mane refeci capiebant unda mulcebat&lt;/h2&gt;
&lt;p&gt;Victa caducifer, malo vulnere contra dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. &lt;strong&gt;Faces illo pepulere&lt;/strong&gt; tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.&lt;/p&gt;
&lt;p&gt;Iubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae vulnus haerentia iuste et exercebat, sui et.&lt;/p&gt;
&lt;p&gt;Eurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel &lt;strong&gt;mitis temploque&lt;/strong&gt; vocatus, inque alis, &lt;em&gt;oculos nomen&lt;/em&gt; non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem Propoetides &lt;strong&gt;parte&lt;/strong&gt;.&lt;/p&gt;
    </description>
    </item>
    
    <item>
    <title>Emoji Support</title>
    <link>https://blog.wiseai.cn/post/emoji-support/</link>
    <pubDate>Tue, 05 Mar 2019 00:00:00 +0000</pubDate>
    <author>Hugo Authors</author>
    <guid>https://blog.wiseai.cn/post/emoji-support/</guid>
    <description>
        &lt;p&gt;Emoji can be enabled in a Hugo project in a number of ways.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://gohugo.io/functions/emojify/&#34;&gt;&lt;code&gt;emojify&lt;/code&gt;&lt;/a&gt; function can be called directly in templates or &lt;a href=&#34;https://gohugo.io/templates/shortcode-templates/#inline-shortcodes&#34;&gt;Inline Shortcodes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To enable emoji globally, set &lt;code&gt;enableEmoji&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in your site&amp;rsquo;s &lt;a href=&#34;https://gohugo.io/getting-started/configuration/&#34;&gt;configuration&lt;/a&gt; and then you can type emoji shorthand codes directly in content files; e.g.&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;nowrap&#34;&gt;&lt;span class=&#34;emojify&#34;&gt;🙈&lt;/span&gt; &lt;code&gt;:see_no_evil:&lt;/code&gt;&lt;/span&gt;  &lt;span class=&#34;nowrap&#34;&gt;&lt;span class=&#34;emojify&#34;&gt;🙉&lt;/span&gt; &lt;code&gt;:hear_no_evil:&lt;/code&gt;&lt;/span&gt;  &lt;span class=&#34;nowrap&#34;&gt;&lt;span class=&#34;emojify&#34;&gt;🙊&lt;/span&gt; &lt;code&gt;:speak_no_evil:&lt;/code&gt;&lt;/span&gt;&lt;/p&gt;
&lt;br&gt;
&lt;p&gt;The &lt;a href=&#34;http://www.emoji-cheat-sheet.com/&#34;&gt;Emoji cheat sheet&lt;/a&gt; is a useful reference for emoji shorthand codes.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.emoji {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
    </description>
    </item>
    
  </channel>
</rss>
