diff --git a/about/index.html b/about/index.html index d10b660d4..772c14493 100644 --- a/about/index.html +++ b/about/index.html @@ -586,7 +586,7 @@ type: 'pie', radius: '50%', color: ['#6772e5', '#ff9e0f', '#fa755a', '#3ecf8e', '#82d3f4', '#ab47bc', '#525f7f', '#f51c47', '#26A69A'], - data: [{"name":"运维","value":3},{"name":"Linux","value":5},{"name":"AI","value":2},{"name":"数据库","value":1},{"name":"编程语言","value":5},{"name":"git","value":1},{"name":"前端开发","value":3},{"name":"小知识","value":2},{"name":"硬件","value":1},{"name":"工具","value":2},{"name":"设计模式","value":1},{"name":"计算机基础知识","value":1},{"name":"算法","value":1},{"name":"并行计算","value":1},{"name":"生活","value":1},{"name":"思考","value":1},{"name":"金融","value":1},{"name":"rnn","value":1},{"name":"web","value":1},{"name":"操作系统","value":1}], + data: [{"name":"算法","value":1},{"name":"数据库","value":1},{"name":"运维","value":3},{"name":"git","value":1},{"name":"Linux","value":5},{"name":"AI","value":2},{"name":"前端开发","value":3},{"name":"编程语言","value":5},{"name":"并行计算","value":1},{"name":"工具","value":2},{"name":"生活","value":1},{"name":"金融","value":1},{"name":"思考","value":1},{"name":"小知识","value":2},{"name":"硬件","value":1},{"name":"设计模式","value":1},{"name":"计算机基础知识","value":1},{"name":"rnn","value":1},{"name":"web","value":1},{"name":"操作系统","value":1}], itemStyle: { emphasis: { shadowBlur: 10, @@ -610,7 +610,7 @@ xAxis: [ { type: 'category', - data: ["运维","Linux","深度学习代码问题","JavaScript","git","持续集成","Vue","React","Json","VM"] + data: ["运维","Linux","深度学习代码问题","JavaScript","动态规划","数据结构","git","Vue","React","Json"] } ], yAxis: [ @@ -793,74 +793,68 @@
touch
命令用于修改文件或者目录的时间属性,包括存取时间和更改时间,若文件不存在,系统会建立一个新的文件。
某个文件夹下,有很多文件名形如:checkpoint{epoch}{loss}.pth;其中epoch和loss是变量;我现在希望把epoch小于400的删除
+ls checkpoint_*.pth | awk -F'_' '{if ($2 < 400) print $0}' | xargs rm
+touch
命令用于修改文件或者目录的时间属性,包括存取时间和更改时间,若文件不存在,系统会建立一个新的文件。
创建一个空白文件,如果文件已经存在,它将更改文件的访问时间。
touch /tmp/file.txt
创建多个文件。
@@ -651,7 +653,7 @@touch -m /tmp/file.txt && stat /tmp/file.txt
同时修改访问时间和修改时间并设置一个特定的访问与修改时间。
touch -am -t 202007010000.00 /tmp/file.txt && stat /tmp/file.txt
-cat
命令属于文件管理,用于连接文件并打印到标准输出设备上,cat
经常用来显示文件的内容,注意,当文件较大时,文本在屏幕上迅速闪过,会出现滚屏现象,此时往往看不清所显示的内容,为了控制滚屏,可以按Ctrl+S
键停止滚屏,按Ctrl+Q
键可以恢复滚屏,此外可以用more
等命令进行读文件并分页显示。
cat
命令属于文件管理,用于连接文件并打印到标准输出设备上,cat
经常用来显示文件的内容,注意,当文件较大时,文本在屏幕上迅速闪过,会出现滚屏现象,此时往往看不清所显示的内容,为了控制滚屏,可以按Ctrl+S
键停止滚屏,按Ctrl+Q
键可以恢复滚屏,此外可以用more
等命令进行读文件并分页显示。
使用cat
命令创建一个文件,输入文件信息后按Ctrl+D
输出EOF
标识后结束输入。
cat > file.txt
输出file.txt
文件中的内容。
cat /dev/null > file2.txt
将file.txt
与file2.txt
文件内容合并输出到file3.txt
。
cat file.txt file2.txt > file3.txt
+cat textfile1 > textfile2 //使用“>” 重定向后 文件 中原本的内容会被覆盖
+
+cat textfile1 >> textfile2 //">>" 代表 将输出的内容已追加的方式重定向到文件
+
+cat 原单词concatenate(用途是连接文件或标准输入并打印。)
cat 命令用于将所有文件内容打印到屏幕上。
语法:
cat 文件
服务相关命令使用systemctl,之前的版本是service
systemctl (stop/restart/start) (服务)
@@ -838,12 +846,6 @@
systemctl restart nginx-
文件内容覆盖/追加内容(cat命令)
-cat textfile1 > textfile2 //使用“>” 重定向后 文件 中原本的内容会被覆盖 - -cat textfile1 >> textfile2 //">>" 代表 将输出的内容已追加的方式重定向到文件 -
cat 原单词concatenate(用途是连接文件或标准输入并打印。)
-
cat 命令用于将所有文件内容打印到屏幕上。
语法:cat 文件
linux中的&& 和 &,| 和 ||
@@ -949,14 +951,14 @@在linux中,&和&&,|和||介绍如下:
& 表示任务在后台执行,如要在后台运行redis-server,则有 redis-server &
&& 表示前一条命令执行成功时,才执行后一条命令 ,如 echo ‘1‘ && echo ‘2’
| 表示管道,上一条命令的输出,作为下一条命令参数,如 echo ‘yes’ | wc -l
|| 表示上一条命令执行失败后,才执行下一条命令,如 cat nofile || echo “fail”- - 运维 - - Linux + + 运维 + + ssh @@ -1140,13 +1142,13 @@
你的赏识是我前进的动力
上一篇- +- + - Golang + Shell 命令@@ -1163,8 +1165,8 @@你的赏识是我前进的动力
- - 编程语言 + + Linux @@ -1174,8 +1176,16 @@你的赏识是我前进的动力
@@ -1189,13 +1199,13 @@你的赏识是我前进的动力
下一篇- +- + - Linux知识点+问题 + Golang@@ -1212,8 +1222,8 @@你的赏识是我前进的动力
- - Linux + + 编程语言 @@ -1223,28 +1233,8 @@你的赏识是我前进的动力
diff --git a/posts/51872/index.html b/posts/51872/index.html index 3104bf0e5..3b1021515 100644 --- a/posts/51872/index.html +++ b/posts/51872/index.html @@ -1616,13 +1616,13 @@你的赏识是我前进的动力
下一篇- +- + - Jenkins知识点 + 零散知识@@ -1639,8 +1639,8 @@你的赏识是我前进的动力
- - 运维 + + 小知识 @@ -1650,16 +1650,44 @@你的赏识是我前进的动力
- - 运维 + + IDE - - 持续集成 + + 编辑器 - - Jenkins + + vscode + + + + 照片模糊处理 + + + + win10 + + + + 专利 + + + + bat + + + + 博客 + + + + 带宽 + + + + 数学diff --git a/posts/53165/index.html b/posts/53165/index.html index 6f614bb1d..d7c05b71f 100644 --- a/posts/53165/index.html +++ b/posts/53165/index.html @@ -2095,13 +2095,13 @@你的赏识是我前进的动力
上一篇- +- + - 工具使用技巧集合 + 计算机基础知识@@ -2118,8 +2118,16 @@你的赏识是我前进的动力
- - 工具 + + 计算机基础知识 + + + + web + + + + 操作系统 @@ -2129,32 +2137,40 @@你的赏识是我前进的动力
- - office + + VM + + + + nginx + + + + 编译 - - IDE + + 环境变量 - - 编辑器 + + Cookie - - 快捷键 + + Session - - vscode + + 密码学 - - visio + + 硬件知识 - - postman + + 线程、进程diff --git a/posts/54220/index.html b/posts/54220/index.html index 688e41a7e..f6abb8317 100644 --- a/posts/54220/index.html +++ b/posts/54220/index.html @@ -433,14 +433,14 @@Linux知识点+问题
- - 运维 - - Linux + + 运维 + + 持续集成 @@ -797,14 +797,14 @@- - 运维 - - Linux + + 运维 + + 持续集成 @@ -996,13 +996,13 @@
你的赏识是我前进的动力
上一篇- +- + - Linux常用命令 + 代理设置@@ -1019,8 +1019,8 @@你的赏识是我前进的动力
- - Linux + + 小知识 @@ -1030,20 +1030,20 @@你的赏识是我前进的动力
@@ -1057,13 +1057,13 @@你的赏识是我前进的动力
下一篇- +- + - 代理设置 + 计算机基础知识@@ -1080,8 +1080,16 @@你的赏识是我前进的动力
- - 小知识 + + 计算机基础知识 + + + + web + + + + 操作系统 @@ -1091,20 +1099,40 @@你的赏识是我前进的动力
- - git + + VM - - npm + + nginx - - yarn + + 编译 - - 梯子 + + 环境变量 + + + + Cookie + + + + Session + + + + 密码学 + + + + 硬件知识 + + + + 线程、进程diff --git a/posts/56435/index.html b/posts/56435/index.html index 391ab1a8a..eba1427a9 100644 --- a/posts/56435/index.html +++ b/posts/56435/index.html @@ -2612,13 +2612,13 @@你的赏识是我前进的动力
上一篇- +- + - Shell 命令 + Linux常用命令@@ -2646,16 +2646,20 @@你的赏识是我前进的动力
@@ -2669,13 +2673,13 @@你的赏识是我前进的动力
下一篇- +- + - Linux常用命令 + 工具使用技巧集合@@ -2692,8 +2696,8 @@你的赏识是我前进的动力
- - Linux + + 工具 @@ -2703,20 +2707,32 @@你的赏识是我前进的动力
- - 运维 + + office - - Linux + + IDE - - ssh + + 编辑器 - - Vim + + 快捷键 + + + + vscode + + + + visio + + + + postmandiff --git a/posts/57899/index.html b/posts/57899/index.html index 09fd5b321..fb115c28e 100644 --- a/posts/57899/index.html +++ b/posts/57899/index.html @@ -1319,14 +1319,14 @@你的赏识是我前进的动力
- - 运维 - - Linux + + 运维 + + git diff --git a/posts/58712/index.html b/posts/58712/index.html index 74e95969c..ea1ddc41c 100644 --- a/posts/58712/index.html +++ b/posts/58712/index.html @@ -888,13 +888,13 @@你的赏识是我前进的动力
上一篇- +- + - Linux知识点+问题 + 工具使用技巧集合@@ -911,8 +911,8 @@你的赏识是我前进的动力
- - Linux + + 工具 @@ -922,28 +922,32 @@你的赏识是我前进的动力
- - 运维 + + office - - Linux + + IDE - - 持续集成 + + 编辑器 - - gcc + + 快捷键 - - Linux权限用法 + + vscode - - make&cmake + + visio + + + + postman@@ -957,13 +961,13 @@你的赏识是我前进的动力
下一篇- +- + - 计算机基础知识 + Linux知识点+问题@@ -980,16 +984,8 @@你的赏识是我前进的动力
- - 计算机基础知识 - - - - web - - - - 操作系统 + + Linux @@ -999,40 +995,28 @@你的赏识是我前进的动力
- - VM - - - - nginx - - - - 编译 - - - - 环境变量 + + Linux - - Cookie + + 运维 - - Session + + 持续集成 - - 密码学 + + gcc - - 硬件知识 + + Linux权限用法 - - 线程、进程 + + make&cmakediff --git a/posts/62688/index.html b/posts/62688/index.html index 42f076659..601d670e1 100644 --- a/posts/62688/index.html +++ b/posts/62688/index.html @@ -439,16 +439,16 @@Mysql
- - 运维 + + 数据库 Linux - - 数据库 + + 运维 @@ -929,16 +929,16 @@- - 运维 + + 数据库 Linux - - 数据库 + + 运维 @@ -1120,13 +1120,13 @@
你的赏识是我前进的动力
上一篇- +- + - Docker + 牛客网@@ -1137,14 +1137,14 @@你的赏识是我前进的动力
- 2022-03-06 + 2022-04-22 - - 运维 + + 编程语言 @@ -1154,12 +1154,8 @@你的赏识是我前进的动力
@@ -1173,13 +1169,13 @@你的赏识是我前进的动力
下一篇- +- + - Shell 命令 + Docker@@ -1196,8 +1192,8 @@diff --git a/posts/7909/index.html b/posts/7909/index.html index 216cd2574..e7aa17638 100644 --- a/posts/7909/index.html +++ b/posts/7909/index.html @@ -1275,13 +1275,13 @@你的赏识是我前进的动力
- - Linux + + 运维 @@ -1211,12 +1207,8 @@你的赏识是我前进的动力
运维 - - Linux - - - - sed + + Docker你的赏识是我前进的动力
上一篇- +- + - 代理设置 + Linux知识点+问题@@ -1298,8 +1298,8 @@你的赏识是我前进的动力
- - 小知识 + + Linux @@ -1309,20 +1309,28 @@你的赏识是我前进的动力
- - git + + Linux - - npm + + 运维 - - yarn + + 持续集成 - - 梯子 + + gcc + + + + Linux权限用法 + + + + make&cmake@@ -1336,13 +1344,13 @@你的赏识是我前进的动力
下一篇- +- + - 工具使用技巧集合 + Java知识@@ -1353,14 +1361,14 @@你的赏识是我前进的动力
- 2022-03-06 + 2022-02-28 - - 工具 + + 编程语言 @@ -1370,32 +1378,20 @@你的赏识是我前进的动力
- - office - - - - IDE - - - - 编辑器 - - - - 快捷键 + + 动态规划 - - vscode + + 数据结构 - - visio + + Java - - postman + + SpringBootdiff --git a/search.xml b/search.xml index 031f607c5..f56256a4d 100644 --- a/search.xml +++ b/search.xml @@ -238,10 +238,10 @@- @@ -473,26 +473,30 @@运维 -Linux +运维 +Slurm - @@ -500,16 +504,16 @@Docker - -/posts/40991/ +Mysql + +/posts/62688/ -docker下载 +Docker安装
1)卸载旧版本
yum list installed | grep docker 列出当前所有docker的包
yum -y remove docker的包名称 卸载docker包
rm -rf /var/lib/docker 删除docker的所有镜像和容器
2)安装必要的软件包
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
3)设置下载的镜像仓库
sudo yum-config-manager \ —add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
如果下载失败,采用阿里源和清华源。(详见菜鸟教程)
4)列出需要安装的版本列表
yum list docker-ce —showduplicates | sort -r
5)安装指定版本(这里使用18.0.1版本)
sudo yum install docker-ce-18.06.1.ce
6)查看版本
docker -v
7)启动Docker
sudo systemctl start docker 启动
sudo systemctl enable docker 设置开机启动yum list docker-ce --showduplicates | sort -r
列出版本列表,看看是否下载成功添加镜像地址:
vi /etc/docker/daemon.json //没有这个文件也无妨,直接创建就好
这个地址的来源:
访问阿里云这个网址,要先登录:镜像加速页面docker启动失败
错误1:
如图所示:
原因:当时在 /etc/docker/daemon.json 添加了一行,但是忘了在第一行后面添加 “逗号”,加上就好。
错误2:
docker启动失败,有一个可能就是包(jar,war)有问题。可以先单独检测下包是否可以运行。如果包不能运行,就是代码有问题。
如果代码逻辑没有问题,甚至没有改动。可能要注意格式问题,比如少敲或多敲空格这种,这种错误往往看不出来,或者编译器没有明显的错误提示(idea对于这个问题就是标黄,但有时候又不影响,很容易不注意)镜像、仓库的关系
docker可以把服务和需要的库一起打包
- 拉取镜像
- Docker build
- 镜像->容器 docker run
- 容器->镜像 docker commit。
上面这图说明了三个得到镜像的途径
1. 从docker官方仓库拉取。
2. 使用dockerfile构建。
3. 从已有的镜像构建容器,进入容器里面(docker -it 容器名称 /bin/bash),做修改,再提交(docker commit)。//这里面 /bin/bash 是进入容器内部,交互运行,和前面的-it也有关系。可以做什么?
比如我拉取一个centos7镜像,进入里面把私有云那一堆东西全部装好。下次我就可以直接拿来用,把服务扔上去就好。
这个过程或者我可以用dockerfile来实现。docker基本操作
换源
- 编辑/etc/docker/daemon.json文件(没有该文件就创建),中加下面参数(注意json串的格式):
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com", "https://registry.docker-cn.com"]}
- 重启docker服务
systemctl restart docker
如果使用的是docker desktop,那就鼠标右键点击图案,来重启
镜像命令
docker search 镜像名称docker pull 镜像名 docker pull 镜像名:tagdocker images //查看本地所有镜像//提交镜像docker commit 容器id 镜像:版本号//删除镜像docker rmi -f 镜像名称 ##删除多个 其镜像ID或镜像用用空格隔开即可 docker rmi -f 镜像名/镜像ID 镜像名/镜像ID 镜像名/镜像ID##删除全部镜像 -a 意思为显示全部, -q 意思为只显示IDdocker rmi -f $(docker images -aq)//强制删除镜像docker image rm 镜像名称/镜像ID//保存镜像//将我们的镜像 保存为tar 压缩文件 这样方便镜像转移和保存 ,然后 可以在任何一台安装了docker的服务器上 加载这//个镜像docker save 镜像名/镜像ID -o 镜像保存在哪个位置与名字
容器命令
容器是由镜像创建而来。容器是Docker运行应用的载体,每个应用都分别运行在Docker的每个 容器中
docker run -i 镜像名称:标签 //运行容器(默认是前台运行) docker ps //查看运行的容器 docker ps -a //查询所有容器
实际命令分析:
docker run -d -it --hostname phdev -p 90:80 -p 8899:8899 -v /sys/fs/cgroup:/sys/fs/cgroup --privileged “镜像名字” /usr/sbin/init//-d 容器以后台方式运行,不会直接进入到容器里面//hostname 指定主机名称;//-p 是端口映射。90:80 访问服务器90端口就是访问容器80端口//-v 是挂载到指定目录// privileged /usr/sbin/init 是给容器赋予更高的权限
//以shell方式进入到一个已经运行的容器当中,和上一条命令搭配起来使用docker exec -it “容器id” /bin/bash
传输文件命令
- 传输文件命令——本地传到docker
docker cp “文件” 容器id:“路径”
- 从宿主机拷贝文件到容器中:
docker cp <path-on-host> <container-id-or-name>:<path-in-container>
docker cp sample.txt abcd1234:/tmp/
登录镜像仓库
docker login registry.cn-beijing.aliyuncs.com
然后依次输入用户名和密码
推送本地镜像到仓库
在登录后,先要把本地镜像给打上标签,和仓库对应起来。
sudo docker tag pytorch:v3 registry.cn-beijing.aliyuncs.com/tianchi_cfd1_rematch/pytorch:v3
然后再推送
sudo docker push registry.cn-beijing.aliyuncs.com/tianchi_cfd1_rematch/pytorch:v3
导出/导入镜像
方法1:save
//导出docker save imagesID > /存放位置/打包文件名.tar//导入docker load < 打包文件名.tar
方法2:export
//导出docker export 容器名 > /位置/打包名.tar//导入docker import < 打包名.tar
区别:
(1).export导出的镜像文件大小 小于 save保存的镜像(2).export 导出(import导入)是根据容器拿到的镜像,再导入时会丢失镜像所有的历史,所以无法进行回滚操作(docker tag );而save保存(load加载)的镜像,没有丢失镜像的历史,可以回滚到之前的层(layer)。(查看方式:docker images —tree)
删除镜像或容器
原文链接
方法一:删除所有未运行的容器(已经运行的删除不了,未运行的就一起被删除了)docker rm $(docker ps -a -q)
方法二:根据容器的状态,删除Exited状态的容器
docker rm $(docker ps -qf status=exited)
方法三:docker 1.13版本以后,可以使用 docker system 或 docker container命令清理容器。
//docker container prune 删除已停用容器docker container prune//删除关闭的容器、无用的数据卷和网络,以及dangling镜像docker system prune //命令清理得更加彻底,可以将没有容器使用Docker的镜像都删掉docker system prune -a
查看docker信息
docker info
可以看到docker的根路径是 /var/lib/docker
查看docker根路径的磁盘占用率df -Th /var/lib/docker
WSL2 安装docker
方法1:安装Docker Desktop
直接搜索,然后安装。
一下两张图片标出的地方需要勾选如果要重启的话,需要在界面图标处右键,选择重启
方法2:使用便捷腳本安裝 Docker
$ curl -fsSL https://get.docker.com -o get-docker.sh$ sudo sh get-docker.sh$ sudo service docker start
測試 docker run 可以順利進行耶!
]]>Mysql变量定义与赋值 局部变量
变量声明
declare a int default value 0;
变量赋值
//法一set a=10;//法二select user_name into uname from t_user where id = 2;
用户变量
使用set或select直接赋值,变量名以 @ 开头.
变量赋值
SET @a=1,@b=2;//法二select @变量名:=变量值select @变量名:=字段名 from table where ... limit 1;
系统变量
MySQL理论知识
事务并发异常
SQL 标准共定义了 3 种并发异常,这三种异常分别是脏读(Dirty Read)、不可重复读(Nnrepeatable Read)和幻读(Phantom Read)。
- 脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
脏读最大的问题就是可能会读到不存在的数据。
- 不可重复读
不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。
事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读
幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。
举例:
假设有张用户表,这张表的 id 是主键。表中一开始有4条数据。我们再来看下出现 幻读 的场景
这里是在RR级别下研究(可重复读),因为 RU / RC 下还会存在脏读、不可重复读,故我们就以 RR 级别来研究 幻读,排除其他干扰。
1、事务A,查询是否存在 id=5 的记录,没有则插入,这是我们期望的正常业务逻辑。
2、这个时候 事务B 新增的一条 id=5 的记录,并提交事务。
3、事务A,再去查询 id=5 的时候,发现还是没有记录(因为这里是在RR级别下研究(可重复读),所以读到依然没有数据)
4、事务A,插入一条 id=5 的数据。
最终 事务A 提交事务,发现报错了。这就很奇怪,查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。这才是所有的幻读。
事务隔离级别
上面的隔离级别由上往下,级别依次会提高,但消耗的性能也会依次提高。我们总结一下四种隔离级别:
- 读未提交:允许读未提交数据,可能会发生脏读、不可重复读和幻读异常;
- 读已提交:只能读已经提交的数据,避免了脏读,但可能会出现不可重复读和幻读;
- 可重复读:即能保证在一个事务中多次读取,数据一致,但可能会出现幻读;
- 可串行化:最高的隔离级别,串行的执行事务,可以避免 3 种异常,但性能耗损最高。
数据类型
MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。
数值类型
日期和时间
字符串类型
Mysql语句
查看表结构
desc tabl_name;
创建数据表
CREATE TABLE table_name (column_name column_type);
实例1:
CREATE TABLE IF NOT EXISTS `runoob_tbl`( `runoob_id` INT UNSIGNED AUTO_INCREMENT, `runoob_title` VARCHAR(100) NOT NULL, `runoob_author` VARCHAR(40) NOT NULL, `submission_date` DATE, PRIMARY KEY ( `runoob_id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;
如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
ENGINE 设置存储引擎,CHARSET 设置编码。实例2:
int(10)的意思是假设有一个变量名为id,它的能显示的宽度能显示10位。在使用id时,假如我给id输入10,那么mysql会默认给你存储0000000010。当你输入的数据不足10位时,会自动帮你补全位数。假如我设计的id字段是int(20),那么我在给id输入10时,mysql会自动补全18个0,补到20位为止。int(M)的作用于int的范围明显是无关的,int(M)只是用来显示数据的宽度,我们能看到的宽度。当字段被设计为int类型,那么它的范围就已经被写死了(看上面的1.1节的内容),与M无关。
以上面这个为例,有几个注意事项:
1.表名和字段名外面的符号 ` 不是单引号,而是英文输入法状态下的反单引号,也就是键盘左上角 esc 按键下面的那一个 ~ 按键。
2.comment 后面的字段用单引号括起来注意:MySQL命令终止符为分号 ; 。
查看数据表
法一:
DESCRIBE 表名;
或简写成:
DESC 表名;
法二:
SHOW CREATE TABLE 表名;
在 SHOW CREATE TABLE 语句的结尾处(分号前面)添加\g或者\G参数可以改变展示形式。
插入数据
1.插入一行
INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );
注意:这个是添加一行数据,不是添加一列。添加一列要增加新的字段。
INSERT INTO teacher (name,age,id_number) VALUES ('秦小贤',18,'42011720200604088X');
注意:表名后面的字段没有引号,插入的数据,如果是字符串,要加引号
2.插入多行
INSERT INTO teacher(name,age,id_number)VALUES('王小花',19,'42011720200604077X'),('张晓丽',18,'42011720200604099X'),('刘美丽',20,'42011720200604020X'),('吴帅',21,'42011720200604022X'),('张平',22,'42011720200604033X')
3.插入一列
前提是这一列已经建好(通过ALTER),如果不加where,那是这个字段(一列)全部更新。
UPDATE table_name SET field1=new-value1, field2=new-value2[WHERE Clause]
修改表名和字段(增、删、改)
删除、添加、修改字段
//删除字段i//如果数据表中只剩余一个字段则无法使用DROP来删除字段。mysql> ALTER TABLE testalter_tbl DROP i;
添加字段:
mysql> ALTER TABLE testalter_tbl ADD i INT;
如果你需要指定新增字段的位置,可以使用MySQL提供的关键字 FIRST (设定位第一列), AFTER 字段名(设定位于某个字段之后)。
尝试以下 ALTER TABLE 语句, 在执行成功后,使用 SHOW COLUMNS 查看表结构的变化:
ALTER TABLE testalter_tbl DROP i;ALTER TABLE testalter_tbl ADD i INT FIRST;ALTER TABLE testalter_tbl DROP i;ALTER TABLE testalter_tbl ADD i INT AFTER c;
指定位置插入字段
//添加到第一个alter table 表名 add column 字段名 varchar(255) FIRST;//添加到指定字段后,记得加上符号,那个符号是esc按键下面的alter table person_param add column `module_name` VARCHAR(20) after `product_id`
修改字段类型及名称
删除的几种情况
- drop table table_name
删除表全部数据和表结构,立刻释放磁盘空间,不管是 Innodb 和 MyISAM;- truncate table table_name
删除表全部数据,保留表结构,立刻释放磁盘空间 ,不管是 Innodb 和 MyISAM;- delete from table_name
删除表全部数据,表结构不变,对于 MyISAM 会立刻释放磁盘空间,InnoDB 不会释放磁盘空间;- delete from table_name where xxx
带条件的删除,表结构不变,不管是 innodb 还是 MyISAM 都不会释放磁盘空间;- delete 操作以后,使用 optimize table table_name,会立刻释放磁盘空间,不管是 innodb 还是 myisam
delete from student where T_name = "张三";optimize table student;
- delete from 表以后虽然未释放磁盘空间,但是下次插入数据的时候,仍然可以使用这部分空间。
创建用户
默认用户为root,但是在Linux和mysql中,可以认为root用户就是各自系统的皇帝,对其它用户的数据有生杀大权,所以最好创建其它的用户来执行。
1.先登录mysql -uroot -p
2.创建一个只能在mysql服务器所在主机使用的用户,此处为localuser
create user '用户名'@'localhost' identified by '用户的密码';
localuser可以在mysql服务器所在主机正常使用
3.在另外一台主机登陆时,会报错
本机登陆:mysql -ulcocaluser -p远程登陆:mysql -h mysql服务器ip -ulocaluser -p
4、创建一个只能由特定远程主机使用的帐户,此处为limituser。
limituser只能在指定的主机使用。
create user 'limituser'@'远程主机ip' identified by '123';
本机登陆:mysql -ulcocaluser -p
远程登陆:mysql -h mysql服务器ip -ulocaluser -p
5、创建一个可以在本地和远程都可以登陆的用户,此处为unlimituser。
对,就是在创建用户时,host使用通配符%
create user 'unlimituser'@'%' identified by '123';
unlimituser用户服务器主机和远程主机登陆
6.删除用户
drop user 'mysqluser'@'host'
创建外键
创建表时增加外键
首先创建第一张被关联表Vendors商品供应商表。
-- 供应商列表CREATE TABLE Vendors (-- 供应商ID:主键列,自增长vend_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '供应商ID',-- 供应商名:可变字符,非空vend_name VARCHAR (30) NOT NULL COMMENT '供应商名',-- 供应商地址vend_address VARCHAR (100) NOT NULL COMMENT '地址',-- 供应商城市vend_city VARCHAR (20) NOT NULL COMMENT '城市',-- 供应商州vend_state VARCHAR (20) NOT NULL COMMENT '州',-- 供应商邮编vend_zip VARCHAR (20) NOT NULL COMMENT '邮编',-- 供应商国家vend_country VARCHAR (20) NOT NULL COMMENT '国家');
然后创建第二张关联表Products产品表。
-- 产品目录表CREATE TABLE Products (-- 产品ID:主键列,自增长prod_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,-- 供应商ID:外键vend_id INT NOT NULL COMMENT '供应商ID',-- 产品名prod_name VARCHAR (30) NOT NULL COMMENT '产品名',-- 产品价格prod_price DOUBLE NOT NULL COMMENT '产品价格',-- 产品描述prod_desc VARCHAR (100) COMMENT '产品描述',FOREIGN KEY (vend_id) REFERENCES Vendors (vend_id));
已存在表增加外键
首先删除刚才两张表所创建的外键。
然后通过下面指令对已经存在的表增加外键。语法如下:-- 已存在表增加外键//(主键字段)和 (外键字段)没有加括号会报错ALTER TABLE ZDZ ADD FOREIGN KEY (sd) REFERENCES Ws_para (snum);
连表查询
SELECT * from `products` a RIGHT JOIN `person_param` b ON a.product_id=b.product_id WHERE a.product_id=338;
连表查的第一步就是两个表要关连起来,在上面的代码就是 ON 后面的a.product_id=b.product_id
复制表数据,修改后再插入
INSERT INTO basic_param (origin_name,present_name,version,project_name) SELECT origin_name,present_name,(28),'xlh' FROM basic_param
mysql在linux上快速导入sql文件
mysql导入sql文件,如果文件稍大一些,速度会非常慢,以下脚本可以借鉴:
#!/bin/bashmysql -u root -pSBcaiyong@PASSword123 <<EOFset global innodb_flush_log_at_trx_commit=0;set global sync_binlog=0;use phenglei;source /home/yskj/lgf/db/xlh_dynpara/lgf.sql;set global innodb_flush_log_at_trx_commit=1;set global sync_binlog=1;EOFecho"数据库改变完成"
Mysql常见问题
mysql官网下载老版本
进入后依次选择:DOWNLOADS(下载)——>Community(社区)——MySQL Community Downloads
进入后往下拉,如下图选择Looking for previous GA versions(寻找以前的GA版本)centos安装mysql
本地下载mysql,但是xftp上传太慢,暂未找到解决的办法,所以尝试下面这个方法。
1 下载并安装MySQL官方的 Yum Repository
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
使用上面的命令就直接下载了安装用的Yum Repository,大概25KB的样子,然后就可以直接yum安装了。
yum -y install mysql57-community-release-el7-10.noarch.rpm
之后就开始安装MySQL服务器。
yum -y install mysql-community-server
这步可能会花些时间,安装完成后就会覆盖掉之前的mariadb。
至此MySQL就安装完成了,然后是对MySQL的一些设置。2 MySQL数据库设置
systemctl start mysqld.service // 首先启动MySQLsystemctl status mysqld.service //查看MySQL运行状态
此时MySQL已经开始正常运行,不过要想进入MySQL还得先找出此时root用户的密码,通过如下命令可以在日志文件中找出密码:grep "password" /var/log/mysqld.log
如下命令进入数据库:
mysql -u root -p
输入初始密码,此时不能做任何事情,因为MySQL默认必须修改密码之后才能操作数据库:
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';//密码需要设置复杂点,不然会报错。原因是因为MySQL有密码设置的规范,具体是与validate_password_policy的值有关:
3.密码修改
上面那一步修改是必须的,否则没法二次修改密码先进入mysql
查看 mysql 初始的密码策略
SHOW VARIABLES LIKE 'validate_password%';
首先需要设置密码的验证强度等级,设置 validate_password_policy 的全局参数为 LOW 即可set global validate_password_policy=LOW;
当前密码长度为 8 ,如果不介意的话就不用修改了,按照通用的来讲,设置为 6 位的密码,设置 validate_password_length 的全局参数为 6 即可
set global validate_password_length=6;
现在可以为 mysql 设置简单密码了,只要满足六位的长度即可
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
注:在默认密码的长度最小值为 4 ,由 大/小写字母各一个 + 阿拉伯数字一个 + 特殊字符一个, 只要设置密码的长度小于 3
,都将自动设值为 4 ,如下图:
关于 mysql 密码策略相关参数;
1)、validate_password_length 固定密码的总长度;
2)、validate_password_dictionary_file 指定密码验证的文件路径;
3)、validate_password_mixed_case_count 整个密码中至少要包含大/小写字母的总个数;
4)、validate_password_number_count 整个密码中至少要包含阿拉伯数字的个数;
5)、validate_password_policy 指定密码的强度验证等级,默认为 MEDIUM; 关于
validate_password_policy 的取值: 0/LOW:只验证长度; 1/MEDIUM:验证长度、数字、大小写、特殊字符;
2/STRONG:验证长度、数字、大小写、特殊字符、字典文件;
6)、validate_password_special_char_count 整个密码中至少要包含特殊字符的个数;重置密码(centos7)
忘记密码
1.设置MySQL为免密码登录
vi /etc/my.cnf
(部分Linux安装了vim,其命令则改为vim /etc/my.cnf
)按【i】键进入编辑模式,在[mysqld]下面加上“skip-grant-tables”,按【Esc】键,然后输入“:wq”保存并退出vi。重新启动MySQL服务(使配置生效,此步骤不能省略)
service mysqld restart
清空旧密码
mysql -u root –p (无需输入密码,直接按回车键进入)use mysqlupdate user set authentication_string = '' where user = 'root';quit
删除免密码登录代码“skip-grant-tables”
vi /etc/my.cnf,按【i】键进入编辑模式,删除[mysqld]下面的代码“skip-grant-tables”,按【Esc】键,然后输入“:wq”保存并退出vi。
重设密码
service mysqld restartmysql -u root –p (无需输入密码,直接按回车键进入)use mysqlALTER USER 'root'@'%' IDENTIFIED BY 'snaiL_12'; //密码要用引号括起来
报错
问题1
ALTER USER 'root'@'localhost' IDENTIFIED BY 'snaiL_123';
报错:ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
解决:
SHOW VARIABLES LIKE 'validate_password%';
1.2调整MySQL密码验证规则,修改 policy 和 length 的值。
set global validate_password——policy=0;(“0”等价于“LOW”,含义是只验证密码长度)set global validate_password.length=8;(因为我之前动过密码长度,这里我将密码长度设为8)
问题2
ALTER USER 'root'@'localhost' IDENTIFIED BY 'snaiL_123';
报错:
ERROR 1396 (HY000): Operation ALTER USER failed for 'root'@'localhost'
查看root账户的host
select user,host from user;
2.2注意看,我的host是“%”,你输入的命令可能是:
ALTERUSER 'root'@'localhost' IDENTIFIED BY 'Snail@10';
将命令改成:
ALTER USER 'root'@'%' IDENTIFIED BY 'Snail@10';
Navicat连接mysql出现1130 - Host XXX is not allowed to connect to this MySQL server
接上一条,安装完成后,navicat无法正常连接,这是由于Mysql配置了不支持远程连接引起的。
1.在安装Mysql数据库的主机上登录root用户
mysql -u root -p
2.依次执行如下命令:
use mysql; select host from user where user='root';
可以看到当前主机配置信息为localhost.
3.将Host设置为通配符%Host列指定了允许用户登录所使用的IP,比如user=root Host=192.168.1.1。这里的意思就是说root用户只能通过192.168.1.1的客户端去访问。 user=root Host=localhost,表示只能通过本机客户端去访问。而%是个通配符,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。如果Host=%,表示所有IP都有连接权限。
注意:在生产环境下不能为了省事将host设置为%,这样做会存在安全问题,具体的设置可以根据生产环境的IP进行设置;
update user set host = '%' where user ='root';
Host设置了“%”后便可以允许远程访问。
4..Host修改完成后记得执行flush privileges使配置立即生效
flush privileges;
批量插入数据很慢
批量提交事务
## 3、批量提交事务drop procedure if exists insertIntoUser;delimiter $$ create procedure insertIntoUser(in num int, in batchNum int) begin declare i int default 0; while i < num do set i = i + 1; set @username = concat('beigua', LPAD(i, 9, 0)); set @nickname = concat('北瓜', LPAD(i, 9, 0)); set @password = replace(uuid(), "-", ""); set @password_salt = replace(uuid(), "-", ""); set @user_no = i; set autocommit = 0; INSERT INTO user(username, password, password_salt, nickname, user_no, ip, mobile, mail, gender, type, status, is_deleted, created_time, updated_time) VALUES (@username, @password, @password_salt, @nickname, @user_no, '192.168.1.1', '18888888888', '18888888888@163.com', '0', '0', '0', '0', now(), now()); if i mod batchNum = 0 then commit; end if; end while; end $$
一次性提交所有事务
## 4、一次性提交事务drop procedure if exists insertIntoUser;delimiter $$ create procedure insertIntoUser(in num int) begin declare i int default 0; set autocommit = 0; while i < num do set i = i + 1; set @username = concat('beigua', LPAD(i, 9, 0)); set @nickname = concat('北瓜', LPAD(i, 9, 0)); set @password = replace(uuid(), "-", ""); set @password_salt = replace(uuid(), "-", ""); set @user_no = i; INSERT INTO user(username, password, password_salt, nickname, user_no, ip, mobile, mail, gender, type, status, is_deleted, created_time, updated_time) VALUES (@username, @password, @password_salt, @nickname, @user_no, '192.168.1.1', '18888888888', '18888888888@163.com', '0', '0', '0', '0', now(), now()); end while; commit; end $$
数据插入前加索引与数据插入后加索引对比
在插入数据的时候不要加过多索引,插完再加
修改参数
set global innodb_flush_log_at_trx_commit = 0;
如何设计数据库
游标、存储过程、函数
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
使用存储过程需要注意一些问题
- 注意分号的使用,语句后面没加分号会报错
- 存储过程既可以放查询里面,也可以放存储里面(工具栏-》函数-》存储)
- mysql存储过程每一句后面必须用;结尾,使用的临时字段需要在定义游标之前进行声明。
上面第三点的解释如下:
DECLARE s int DEFAULT 0;declare p_t_id bigint(20);declare varmodule int DEFAULT 0;declare varparam int DEFAULT 0; declare pid cursor for select product_id from products;DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1;
所有的declare必须放在游标声明前面(declare pid cursor这句)]]>- 运维 +数据库 - Docker +数据库 + +Linux 运维 +MySQL +- Mysql - -/posts/62688/ +Docker + +/posts/40991/ -Mysql变量定义与赋值 +局部变量
变量声明
declare a int default value 0;
变量赋值
//法一set a=10;//法二select user_name into uname from t_user where id = 2;
用户变量
使用set或select直接赋值,变量名以 @ 开头.
变量赋值
SET @a=1,@b=2;//法二select @变量名:=变量值select @变量名:=字段名 from table where ... limit 1;
系统变量
MySQL理论知识
事务并发异常
SQL 标准共定义了 3 种并发异常,这三种异常分别是脏读(Dirty Read)、不可重复读(Nnrepeatable Read)和幻读(Phantom Read)。
- 脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
脏读最大的问题就是可能会读到不存在的数据。
- 不可重复读
不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况。
事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读
幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。
举例:
假设有张用户表,这张表的 id 是主键。表中一开始有4条数据。我们再来看下出现 幻读 的场景
这里是在RR级别下研究(可重复读),因为 RU / RC 下还会存在脏读、不可重复读,故我们就以 RR 级别来研究 幻读,排除其他干扰。
1、事务A,查询是否存在 id=5 的记录,没有则插入,这是我们期望的正常业务逻辑。
2、这个时候 事务B 新增的一条 id=5 的记录,并提交事务。
3、事务A,再去查询 id=5 的时候,发现还是没有记录(因为这里是在RR级别下研究(可重复读),所以读到依然没有数据)
4、事务A,插入一条 id=5 的数据。
最终 事务A 提交事务,发现报错了。这就很奇怪,查的时候明明没有这条记录,但插入的时候 却告诉我 主键冲突,这就好像幻觉一样。这才是所有的幻读。
事务隔离级别
上面的隔离级别由上往下,级别依次会提高,但消耗的性能也会依次提高。我们总结一下四种隔离级别:
- 读未提交:允许读未提交数据,可能会发生脏读、不可重复读和幻读异常;
- 读已提交:只能读已经提交的数据,避免了脏读,但可能会出现不可重复读和幻读;
- 可重复读:即能保证在一个事务中多次读取,数据一致,但可能会出现幻读;
- 可串行化:最高的隔离级别,串行的执行事务,可以避免 3 种异常,但性能耗损最高。
数据类型
MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。
数值类型
日期和时间
字符串类型
Mysql语句
查看表结构
desc tabl_name;
创建数据表
CREATE TABLE table_name (column_name column_type);
实例1:
CREATE TABLE IF NOT EXISTS `runoob_tbl`( `runoob_id` INT UNSIGNED AUTO_INCREMENT, `runoob_title` VARCHAR(100) NOT NULL, `runoob_author` VARCHAR(40) NOT NULL, `submission_date` DATE, PRIMARY KEY ( `runoob_id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;
如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
ENGINE 设置存储引擎,CHARSET 设置编码。实例2:
int(10)的意思是假设有一个变量名为id,它的能显示的宽度能显示10位。在使用id时,假如我给id输入10,那么mysql会默认给你存储0000000010。当你输入的数据不足10位时,会自动帮你补全位数。假如我设计的id字段是int(20),那么我在给id输入10时,mysql会自动补全18个0,补到20位为止。int(M)的作用于int的范围明显是无关的,int(M)只是用来显示数据的宽度,我们能看到的宽度。当字段被设计为int类型,那么它的范围就已经被写死了(看上面的1.1节的内容),与M无关。
以上面这个为例,有几个注意事项:
1.表名和字段名外面的符号 ` 不是单引号,而是英文输入法状态下的反单引号,也就是键盘左上角 esc 按键下面的那一个 ~ 按键。
2.comment 后面的字段用单引号括起来注意:MySQL命令终止符为分号 ; 。
查看数据表
法一:
DESCRIBE 表名;
或简写成:
DESC 表名;
法二:
SHOW CREATE TABLE 表名;
在 SHOW CREATE TABLE 语句的结尾处(分号前面)添加\g或者\G参数可以改变展示形式。
插入数据
1.插入一行
INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );
注意:这个是添加一行数据,不是添加一列。添加一列要增加新的字段。
INSERT INTO teacher (name,age,id_number) VALUES ('秦小贤',18,'42011720200604088X');
注意:表名后面的字段没有引号,插入的数据,如果是字符串,要加引号
2.插入多行
INSERT INTO teacher(name,age,id_number)VALUES('王小花',19,'42011720200604077X'),('张晓丽',18,'42011720200604099X'),('刘美丽',20,'42011720200604020X'),('吴帅',21,'42011720200604022X'),('张平',22,'42011720200604033X')
3.插入一列
前提是这一列已经建好(通过ALTER),如果不加where,那是这个字段(一列)全部更新。
UPDATE table_name SET field1=new-value1, field2=new-value2[WHERE Clause]
修改表名和字段(增、删、改)
删除、添加、修改字段
//删除字段i//如果数据表中只剩余一个字段则无法使用DROP来删除字段。mysql> ALTER TABLE testalter_tbl DROP i;
添加字段:
mysql> ALTER TABLE testalter_tbl ADD i INT;
如果你需要指定新增字段的位置,可以使用MySQL提供的关键字 FIRST (设定位第一列), AFTER 字段名(设定位于某个字段之后)。
尝试以下 ALTER TABLE 语句, 在执行成功后,使用 SHOW COLUMNS 查看表结构的变化:
ALTER TABLE testalter_tbl DROP i;ALTER TABLE testalter_tbl ADD i INT FIRST;ALTER TABLE testalter_tbl DROP i;ALTER TABLE testalter_tbl ADD i INT AFTER c;
指定位置插入字段
//添加到第一个alter table 表名 add column 字段名 varchar(255) FIRST;//添加到指定字段后,记得加上符号,那个符号是esc按键下面的alter table person_param add column `module_name` VARCHAR(20) after `product_id`
修改字段类型及名称
删除的几种情况
- drop table table_name
删除表全部数据和表结构,立刻释放磁盘空间,不管是 Innodb 和 MyISAM;- truncate table table_name
删除表全部数据,保留表结构,立刻释放磁盘空间 ,不管是 Innodb 和 MyISAM;- delete from table_name
删除表全部数据,表结构不变,对于 MyISAM 会立刻释放磁盘空间,InnoDB 不会释放磁盘空间;- delete from table_name where xxx
带条件的删除,表结构不变,不管是 innodb 还是 MyISAM 都不会释放磁盘空间;- delete 操作以后,使用 optimize table table_name,会立刻释放磁盘空间,不管是 innodb 还是 myisam
delete from student where T_name = "张三";optimize table student;
- delete from 表以后虽然未释放磁盘空间,但是下次插入数据的时候,仍然可以使用这部分空间。
创建用户
默认用户为root,但是在Linux和mysql中,可以认为root用户就是各自系统的皇帝,对其它用户的数据有生杀大权,所以最好创建其它的用户来执行。
1.先登录mysql -uroot -p
2.创建一个只能在mysql服务器所在主机使用的用户,此处为localuser
create user '用户名'@'localhost' identified by '用户的密码';
localuser可以在mysql服务器所在主机正常使用
3.在另外一台主机登陆时,会报错
本机登陆:mysql -ulcocaluser -p远程登陆:mysql -h mysql服务器ip -ulocaluser -p
4、创建一个只能由特定远程主机使用的帐户,此处为limituser。
limituser只能在指定的主机使用。
create user 'limituser'@'远程主机ip' identified by '123';
本机登陆:mysql -ulcocaluser -p
远程登陆:mysql -h mysql服务器ip -ulocaluser -p
5、创建一个可以在本地和远程都可以登陆的用户,此处为unlimituser。
对,就是在创建用户时,host使用通配符%
create user 'unlimituser'@'%' identified by '123';
unlimituser用户服务器主机和远程主机登陆
6.删除用户
drop user 'mysqluser'@'host'
创建外键
创建表时增加外键
首先创建第一张被关联表Vendors商品供应商表。
-- 供应商列表CREATE TABLE Vendors (-- 供应商ID:主键列,自增长vend_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '供应商ID',-- 供应商名:可变字符,非空vend_name VARCHAR (30) NOT NULL COMMENT '供应商名',-- 供应商地址vend_address VARCHAR (100) NOT NULL COMMENT '地址',-- 供应商城市vend_city VARCHAR (20) NOT NULL COMMENT '城市',-- 供应商州vend_state VARCHAR (20) NOT NULL COMMENT '州',-- 供应商邮编vend_zip VARCHAR (20) NOT NULL COMMENT '邮编',-- 供应商国家vend_country VARCHAR (20) NOT NULL COMMENT '国家');
然后创建第二张关联表Products产品表。
-- 产品目录表CREATE TABLE Products (-- 产品ID:主键列,自增长prod_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,-- 供应商ID:外键vend_id INT NOT NULL COMMENT '供应商ID',-- 产品名prod_name VARCHAR (30) NOT NULL COMMENT '产品名',-- 产品价格prod_price DOUBLE NOT NULL COMMENT '产品价格',-- 产品描述prod_desc VARCHAR (100) COMMENT '产品描述',FOREIGN KEY (vend_id) REFERENCES Vendors (vend_id));
已存在表增加外键
首先删除刚才两张表所创建的外键。
然后通过下面指令对已经存在的表增加外键。语法如下:-- 已存在表增加外键//(主键字段)和 (外键字段)没有加括号会报错ALTER TABLE ZDZ ADD FOREIGN KEY (sd) REFERENCES Ws_para (snum);
连表查询
SELECT * from `products` a RIGHT JOIN `person_param` b ON a.product_id=b.product_id WHERE a.product_id=338;
连表查的第一步就是两个表要关连起来,在上面的代码就是 ON 后面的a.product_id=b.product_id
复制表数据,修改后再插入
INSERT INTO basic_param (origin_name,present_name,version,project_name) SELECT origin_name,present_name,(28),'xlh' FROM basic_param
mysql在linux上快速导入sql文件
mysql导入sql文件,如果文件稍大一些,速度会非常慢,以下脚本可以借鉴:
#!/bin/bashmysql -u root -pSBcaiyong@PASSword123 <<EOFset global innodb_flush_log_at_trx_commit=0;set global sync_binlog=0;use phenglei;source /home/yskj/lgf/db/xlh_dynpara/lgf.sql;set global innodb_flush_log_at_trx_commit=1;set global sync_binlog=1;EOFecho"数据库改变完成"
Mysql常见问题
mysql官网下载老版本
进入后依次选择:DOWNLOADS(下载)——>Community(社区)——MySQL Community Downloads
进入后往下拉,如下图选择Looking for previous GA versions(寻找以前的GA版本)centos安装mysql
本地下载mysql,但是xftp上传太慢,暂未找到解决的办法,所以尝试下面这个方法。
1 下载并安装MySQL官方的 Yum Repository
wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
使用上面的命令就直接下载了安装用的Yum Repository,大概25KB的样子,然后就可以直接yum安装了。
yum -y install mysql57-community-release-el7-10.noarch.rpm
之后就开始安装MySQL服务器。
yum -y install mysql-community-server
这步可能会花些时间,安装完成后就会覆盖掉之前的mariadb。
至此MySQL就安装完成了,然后是对MySQL的一些设置。2 MySQL数据库设置
systemctl start mysqld.service // 首先启动MySQLsystemctl status mysqld.service //查看MySQL运行状态
此时MySQL已经开始正常运行,不过要想进入MySQL还得先找出此时root用户的密码,通过如下命令可以在日志文件中找出密码:grep "password" /var/log/mysqld.log
如下命令进入数据库:
mysql -u root -p
输入初始密码,此时不能做任何事情,因为MySQL默认必须修改密码之后才能操作数据库:
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';//密码需要设置复杂点,不然会报错。原因是因为MySQL有密码设置的规范,具体是与validate_password_policy的值有关:
3.密码修改
上面那一步修改是必须的,否则没法二次修改密码先进入mysql
查看 mysql 初始的密码策略
SHOW VARIABLES LIKE 'validate_password%';
首先需要设置密码的验证强度等级,设置 validate_password_policy 的全局参数为 LOW 即可set global validate_password_policy=LOW;
当前密码长度为 8 ,如果不介意的话就不用修改了,按照通用的来讲,设置为 6 位的密码,设置 validate_password_length 的全局参数为 6 即可
set global validate_password_length=6;
现在可以为 mysql 设置简单密码了,只要满足六位的长度即可
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
注:在默认密码的长度最小值为 4 ,由 大/小写字母各一个 + 阿拉伯数字一个 + 特殊字符一个, 只要设置密码的长度小于 3
,都将自动设值为 4 ,如下图:
关于 mysql 密码策略相关参数;
1)、validate_password_length 固定密码的总长度;
2)、validate_password_dictionary_file 指定密码验证的文件路径;
3)、validate_password_mixed_case_count 整个密码中至少要包含大/小写字母的总个数;
4)、validate_password_number_count 整个密码中至少要包含阿拉伯数字的个数;
5)、validate_password_policy 指定密码的强度验证等级,默认为 MEDIUM; 关于
validate_password_policy 的取值: 0/LOW:只验证长度; 1/MEDIUM:验证长度、数字、大小写、特殊字符;
2/STRONG:验证长度、数字、大小写、特殊字符、字典文件;
6)、validate_password_special_char_count 整个密码中至少要包含特殊字符的个数;重置密码(centos7)
忘记密码
1.设置MySQL为免密码登录
vi /etc/my.cnf
(部分Linux安装了vim,其命令则改为vim /etc/my.cnf
)按【i】键进入编辑模式,在[mysqld]下面加上“skip-grant-tables”,按【Esc】键,然后输入“:wq”保存并退出vi。重新启动MySQL服务(使配置生效,此步骤不能省略)
service mysqld restart
清空旧密码
mysql -u root –p (无需输入密码,直接按回车键进入)use mysqlupdate user set authentication_string = '' where user = 'root';quit
删除免密码登录代码“skip-grant-tables”
vi /etc/my.cnf,按【i】键进入编辑模式,删除[mysqld]下面的代码“skip-grant-tables”,按【Esc】键,然后输入“:wq”保存并退出vi。
重设密码
service mysqld restartmysql -u root –p (无需输入密码,直接按回车键进入)use mysqlALTER USER 'root'@'%' IDENTIFIED BY 'snaiL_12'; //密码要用引号括起来
报错
问题1
ALTER USER 'root'@'localhost' IDENTIFIED BY 'snaiL_123';
报错:ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
解决:
SHOW VARIABLES LIKE 'validate_password%';
1.2调整MySQL密码验证规则,修改 policy 和 length 的值。
set global validate_password——policy=0;(“0”等价于“LOW”,含义是只验证密码长度)set global validate_password.length=8;(因为我之前动过密码长度,这里我将密码长度设为8)
问题2
ALTER USER 'root'@'localhost' IDENTIFIED BY 'snaiL_123';
报错:
ERROR 1396 (HY000): Operation ALTER USER failed for 'root'@'localhost'
查看root账户的host
select user,host from user;
2.2注意看,我的host是“%”,你输入的命令可能是:
ALTERUSER 'root'@'localhost' IDENTIFIED BY 'Snail@10';
将命令改成:
ALTER USER 'root'@'%' IDENTIFIED BY 'Snail@10';
Navicat连接mysql出现1130 - Host XXX is not allowed to connect to this MySQL server
接上一条,安装完成后,navicat无法正常连接,这是由于Mysql配置了不支持远程连接引起的。
1.在安装Mysql数据库的主机上登录root用户
mysql -u root -p
2.依次执行如下命令:
use mysql; select host from user where user='root';
可以看到当前主机配置信息为localhost.
3.将Host设置为通配符%Host列指定了允许用户登录所使用的IP,比如user=root Host=192.168.1.1。这里的意思就是说root用户只能通过192.168.1.1的客户端去访问。 user=root Host=localhost,表示只能通过本机客户端去访问。而%是个通配符,如果Host=192.168.1.%,那么就表示只要是IP地址前缀为“192.168.1.”的客户端都可以连接。如果Host=%,表示所有IP都有连接权限。
注意:在生产环境下不能为了省事将host设置为%,这样做会存在安全问题,具体的设置可以根据生产环境的IP进行设置;
update user set host = '%' where user ='root';
Host设置了“%”后便可以允许远程访问。
4..Host修改完成后记得执行flush privileges使配置立即生效
flush privileges;
批量插入数据很慢
批量提交事务
## 3、批量提交事务drop procedure if exists insertIntoUser;delimiter $$ create procedure insertIntoUser(in num int, in batchNum int) begin declare i int default 0; while i < num do set i = i + 1; set @username = concat('beigua', LPAD(i, 9, 0)); set @nickname = concat('北瓜', LPAD(i, 9, 0)); set @password = replace(uuid(), "-", ""); set @password_salt = replace(uuid(), "-", ""); set @user_no = i; set autocommit = 0; INSERT INTO user(username, password, password_salt, nickname, user_no, ip, mobile, mail, gender, type, status, is_deleted, created_time, updated_time) VALUES (@username, @password, @password_salt, @nickname, @user_no, '192.168.1.1', '18888888888', '18888888888@163.com', '0', '0', '0', '0', now(), now()); if i mod batchNum = 0 then commit; end if; end while; end $$
一次性提交所有事务
## 4、一次性提交事务drop procedure if exists insertIntoUser;delimiter $$ create procedure insertIntoUser(in num int) begin declare i int default 0; set autocommit = 0; while i < num do set i = i + 1; set @username = concat('beigua', LPAD(i, 9, 0)); set @nickname = concat('北瓜', LPAD(i, 9, 0)); set @password = replace(uuid(), "-", ""); set @password_salt = replace(uuid(), "-", ""); set @user_no = i; INSERT INTO user(username, password, password_salt, nickname, user_no, ip, mobile, mail, gender, type, status, is_deleted, created_time, updated_time) VALUES (@username, @password, @password_salt, @nickname, @user_no, '192.168.1.1', '18888888888', '18888888888@163.com', '0', '0', '0', '0', now(), now()); end while; commit; end $$
数据插入前加索引与数据插入后加索引对比
在插入数据的时候不要加过多索引,插完再加
修改参数
set global innodb_flush_log_at_trx_commit = 0;
如何设计数据库
游标、存储过程、函数
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
使用存储过程需要注意一些问题
- 注意分号的使用,语句后面没加分号会报错
- 存储过程既可以放查询里面,也可以放存储里面(工具栏-》函数-》存储)
- mysql存储过程每一句后面必须用;结尾,使用的临时字段需要在定义游标之前进行声明。
上面第三点的解释如下:
DECLARE s int DEFAULT 0;declare p_t_id bigint(20);declare varmodule int DEFAULT 0;declare varparam int DEFAULT 0; declare pid cursor for select product_id from products;DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1;
所有的declare必须放在游标声明前面(declare pid cursor这句)]]>docker下载 Docker安装
1)卸载旧版本
yum list installed | grep docker 列出当前所有docker的包
yum -y remove docker的包名称 卸载docker包
rm -rf /var/lib/docker 删除docker的所有镜像和容器
2)安装必要的软件包
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
3)设置下载的镜像仓库
sudo yum-config-manager \ —add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
如果下载失败,采用阿里源和清华源。(详见菜鸟教程)
4)列出需要安装的版本列表
yum list docker-ce —showduplicates | sort -r
5)安装指定版本(这里使用18.0.1版本)
sudo yum install docker-ce-18.06.1.ce
6)查看版本
docker -v
7)启动Docker
sudo systemctl start docker 启动
sudo systemctl enable docker 设置开机启动yum list docker-ce --showduplicates | sort -r
列出版本列表,看看是否下载成功添加镜像地址:
vi /etc/docker/daemon.json //没有这个文件也无妨,直接创建就好
这个地址的来源:
访问阿里云这个网址,要先登录:镜像加速页面docker启动失败
错误1:
如图所示:
原因:当时在 /etc/docker/daemon.json 添加了一行,但是忘了在第一行后面添加 “逗号”,加上就好。
错误2:
docker启动失败,有一个可能就是包(jar,war)有问题。可以先单独检测下包是否可以运行。如果包不能运行,就是代码有问题。
如果代码逻辑没有问题,甚至没有改动。可能要注意格式问题,比如少敲或多敲空格这种,这种错误往往看不出来,或者编译器没有明显的错误提示(idea对于这个问题就是标黄,但有时候又不影响,很容易不注意)镜像、仓库的关系
docker可以把服务和需要的库一起打包
- 拉取镜像
- Docker build
- 镜像->容器 docker run
- 容器->镜像 docker commit。
上面这图说明了三个得到镜像的途径
1. 从docker官方仓库拉取。
2. 使用dockerfile构建。
3. 从已有的镜像构建容器,进入容器里面(docker -it 容器名称 /bin/bash),做修改,再提交(docker commit)。//这里面 /bin/bash 是进入容器内部,交互运行,和前面的-it也有关系。可以做什么?
比如我拉取一个centos7镜像,进入里面把私有云那一堆东西全部装好。下次我就可以直接拿来用,把服务扔上去就好。
这个过程或者我可以用dockerfile来实现。docker基本操作
换源
- 编辑/etc/docker/daemon.json文件(没有该文件就创建),中加下面参数(注意json串的格式):
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com", "https://registry.docker-cn.com"]}
- 重启docker服务
systemctl restart docker
如果使用的是docker desktop,那就鼠标右键点击图案,来重启
镜像命令
docker search 镜像名称docker pull 镜像名 docker pull 镜像名:tagdocker images //查看本地所有镜像//提交镜像docker commit 容器id 镜像:版本号//删除镜像docker rmi -f 镜像名称 ##删除多个 其镜像ID或镜像用用空格隔开即可 docker rmi -f 镜像名/镜像ID 镜像名/镜像ID 镜像名/镜像ID##删除全部镜像 -a 意思为显示全部, -q 意思为只显示IDdocker rmi -f $(docker images -aq)//强制删除镜像docker image rm 镜像名称/镜像ID//保存镜像//将我们的镜像 保存为tar 压缩文件 这样方便镜像转移和保存 ,然后 可以在任何一台安装了docker的服务器上 加载这//个镜像docker save 镜像名/镜像ID -o 镜像保存在哪个位置与名字
容器命令
容器是由镜像创建而来。容器是Docker运行应用的载体,每个应用都分别运行在Docker的每个 容器中
docker run -i 镜像名称:标签 //运行容器(默认是前台运行) docker ps //查看运行的容器 docker ps -a //查询所有容器
实际命令分析:
docker run -d -it --hostname phdev -p 90:80 -p 8899:8899 -v /sys/fs/cgroup:/sys/fs/cgroup --privileged “镜像名字” /usr/sbin/init//-d 容器以后台方式运行,不会直接进入到容器里面//hostname 指定主机名称;//-p 是端口映射。90:80 访问服务器90端口就是访问容器80端口//-v 是挂载到指定目录// privileged /usr/sbin/init 是给容器赋予更高的权限
//以shell方式进入到一个已经运行的容器当中,和上一条命令搭配起来使用docker exec -it “容器id” /bin/bash
传输文件命令
- 传输文件命令——本地传到docker
docker cp “文件” 容器id:“路径”
- 从宿主机拷贝文件到容器中:
docker cp <path-on-host> <container-id-or-name>:<path-in-container>
docker cp sample.txt abcd1234:/tmp/
登录镜像仓库
docker login registry.cn-beijing.aliyuncs.com
然后依次输入用户名和密码
推送本地镜像到仓库
在登录后,先要把本地镜像给打上标签,和仓库对应起来。
sudo docker tag pytorch:v3 registry.cn-beijing.aliyuncs.com/tianchi_cfd1_rematch/pytorch:v3
然后再推送
sudo docker push registry.cn-beijing.aliyuncs.com/tianchi_cfd1_rematch/pytorch:v3
导出/导入镜像
方法1:save
//导出docker save imagesID > /存放位置/打包文件名.tar//导入docker load < 打包文件名.tar
方法2:export
//导出docker export 容器名 > /位置/打包名.tar//导入docker import < 打包名.tar
区别:
(1).export导出的镜像文件大小 小于 save保存的镜像(2).export 导出(import导入)是根据容器拿到的镜像,再导入时会丢失镜像所有的历史,所以无法进行回滚操作(docker tag );而save保存(load加载)的镜像,没有丢失镜像的历史,可以回滚到之前的层(layer)。(查看方式:docker images —tree)
删除镜像或容器
原文链接
方法一:删除所有未运行的容器(已经运行的删除不了,未运行的就一起被删除了)docker rm $(docker ps -a -q)
方法二:根据容器的状态,删除Exited状态的容器
docker rm $(docker ps -qf status=exited)
方法三:docker 1.13版本以后,可以使用 docker system 或 docker container命令清理容器。
//docker container prune 删除已停用容器docker container prune//删除关闭的容器、无用的数据卷和网络,以及dangling镜像docker system prune //命令清理得更加彻底,可以将没有容器使用Docker的镜像都删掉docker system prune -a
查看docker信息
docker info
可以看到docker的根路径是 /var/lib/docker
查看docker根路径的磁盘占用率df -Th /var/lib/docker
WSL2 安装docker
方法1:安装Docker Desktop
直接搜索,然后安装。
一下两张图片标出的地方需要勾选如果要重启的话,需要在界面图标处右键,选择重启
方法2:使用便捷腳本安裝 Docker
$ curl -fsSL https://get.docker.com -o get-docker.sh$ sudo sh get-docker.sh$ sudo service docker start
測試 docker run 可以順利進行耶!
]]>- @@ -518,11 +522,7 @@数据库 +运维 运维 -Linux - -数据库 - -MySQL +Docker @@ -547,10 +547,10 @@- @@ -560,23 +560,29 @@运维 -Linux +运维 +sed - Golang - -/posts/56435/ +Linux常用命令 + +/posts/48230/ -查看环境变量 +go version //查看版本go env //查看环境变量
安装golang、gopath、goroot
安装
windows安装一路点击ok就好。
Linux安装:
- wget https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz
- tar -zxvf go1.14.1.linux-amd64.tar.gz -C /usr/local # 解压
配置环境变量: Linux下有两个文件可以配置环境变量,其中/etc/profile是对所有用户生效的;$HOME/.profile是对当前用户生效的,根据自己的情况自行选择一个文件打开,添加如下两行代码,保存退出。
export GOROOT=/usr/local/goexport PATH=$PATH:$GOROOT/bin
修改/etc/profile后要重启生效,修改$HOME/.profile后使用source命令加载$HOME/.profile文件即可生效。 检查:
go version
gopath、goroot
goroot:
其实就是golang 的安装路径
当你安装好golang之后其实这个就已经有了gopath:
作用:存放sdk以外的第三方类库
自己收藏的可复用的代码
目录结构:$GOPATH目录约定有三个子目录
src存放源代码(比如:.go .c .h .s等) 按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。pkg编译时生成的中间文件(比如:.a) golang编译包时
bin编译后生成的可执行文件(为了方便,可以把此目录加入到 P A T H 变 量 中 , 如 果 有 多 个 g o p a t h , 那 么 使 用 PATH 变量中,如果有多个gopath,那么使用PATH变量中,如果有多个gopath,那么使用{GOPATH/bin:}/bin添加所有的bin目录)
package
包的定义
package 包名
注意事项:
- 一个文件夹下直接包含的文件只能归属于一个package,同样一个package的文件不能在多个文件夹下。
- 包名可以不和文件夹的名字一样。
- 包名为
main
的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不含main包的源代码则不会得到可执行文件包的使用
- 有时候我们单独写一个go文件,测试或验证某个功能,包名都写main就好
- 如果要引入自定义的包。如果import失败,看一下保存的信息。一般会提示GOROOT GOPATH找不到这个包,这个时候把这个文件夹放到上面的目录里面就可以导入了。
举例:
我目前的GOROOT是在这个目录下面,把文件夹放进去,里面是go文件,就可以引入了。go mod配置
go mod 主要用于管理第三方包,可以自动进行下载。要使用go mod,需要一些配置。
需要配置GO111MODULE 、GOPROXYgo mod使用
首先:
go mod init "modname"//modname一般是文件所处文件夹的名字//go mod init dynamic
如果要某文件要引入一些包,在import处写入,然后执行下面命令:
go mod tidy//或者go get -u
导入第三方包
背景:和公司合作开发,但是没有他们内部的开发环境,只能让他们把包单独抽出来
核心就是这里的replace,不用修改goproxy或者go111module等环境变量
语法
Context
Context主要做两件事情:
- 通知子协程提前退出
- 携带环境变量
channel
Goroutine(协程) 使用 Channel 传递数据
定义
// 只读 channelvar readOnlyChan <-chan int // channel 的类型为 int// 只写 channelvar writeOnlyChan chan<- int// 可读可写var ch chan int// 或者使用 make 直接初始化readOnlyChan1 := make(<-chan int, 2) // 只读且带缓存区的 channelreadOnlyChan2 := make(<-chan int) // 只读且不带缓存区 channelwriteOnlyChan3 := make(chan<- int, 4) // 只写且带缓存区 channelwriteOnlyChan4 := make(chan<- int) // 只写且不带缓存区 channelch := make(chan int, 10) // 可读可写且带缓存区ch <- 20 // 写数据i := <-ch // 读数据i, ok := <-ch // 还可以判断读取的数据
操作channel
操作 channel 一般有如下三种方式:
读 <-ch
写 ch<-
关闭 close(ch)
go func(){}()
go func() {.....}()
以并发的方式调用匿名函数func
详细解释:
func(name string) { fmt.Println("Your name is", name) } (str) //这里的(str)是?
其实,这就是在调用这个函数,这种写法等同于:
f := func(name string) { fmt.Println("Your name is", name) } f(str) //看吧,就是把函数复制给变量,变量(函数)传参
以下两段代码执行结果等同:
代码一:package mainimport ( "fmt")func main() { str := "xulinlin" func(name string) { fmt.Println("Your name is", name) }(str)}代码二:package mainimport ( "fmt")func main() { f := func(name string) { fmt.Println("Your name is", name) } f(str)}输出都是:Your name is xulinlin
defer
defer后面的函数在defer语句所在的函数执行结束的时候会被调用,用来做一些收尾工作
原文链接)
Println 与 Printf 的区别
- Println :可以打印出字符串,和变量
- Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量,不可以输出整形变量和整形
也就是说,当需要格式化输出信息时一般选择 Printf,其他时候用 Println 就可以了,比如:
a := 10fmt.Println(a) //rightfmt.Println("abc") //rightfmt.Printf("%d",a) //rightfmt.Printf(a) //error
printf格式化输出
通用输出
%v #仅输出该变量的值%+v #输出 该变量的值,如果是数组k/v 则将k/v都输出%#v #先输出结构体名字值,再输出结构体(字段名字+字段的值)%T #输出结构体名称%% #百分号
package main import ("fmt") type student struct {id intname string}func main() { ss := student{id: 1,name: "test"}fmt.Printf("%v \n",ss) //%v 当碰到数组时,仅输出value,不输出keyfmt.Printf("%+v \n",ss) //%+v 当碰到数组时,将key-value 都输出fmt.Printf("%#v \n",ss) //%#v 输出时,会将方法名 +k/v都输出fmt.Printf("%T \n",ss) //%T 输出结构体名称()fmt.Printf("%% \n") //%% 没有意义,只是输出一个%}
整数类型
%b 二进制表示 %c 相应Unicode码点所表示的字符 %d 十进制表示 %o 八进制表示 %q 单引号围绕的字符字面值,由Go语法安全地转义 %x 十六进制表示,字母形式为小写 a-f %X 十六进制表示,字母形式为大写 A-F %U Unicode格式:123,等同于 "U+007B"
package main import ( "fmt") func main() { ss := 68 fmt.Printf("%b \n",ss) //二进制表示 fmt.Printf("%c \n",ss) //Unicode码表示的字符 fmt.Printf("%d \n",ss) //十进制表示 fmt.Printf("%o \n",ss) //八进制 fmt.Printf("%q \n",ss) //输出字符,单引号包裹 fmt.Printf("%x \n",ss) //十六进制输出 小写 fmt.Printf("%X \n",ss) //十六进制输出 大写 fmt.Printf("%U \n",ss) //Unicode格式}
- 浮点数
%b无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat%e科学计数法,如-1234.456e+78%E科学计数法,如-1234.456E+78%f有小数部分但无指数部分,如123.456%F等价于%f%g根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)%G根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
package main import ( "fmt") func main() { fmt.Printf("%b \n",68.10) //二进制输出 fmt.Printf("%e \n",68.10) //科学计数法 e fmt.Printf("%E \n",68.10) //科学计数法 E fmt.Printf("%f \n",68.10) fmt.Printf("%g \n",68.10) fmt.Printf("%G \n",68.10) }
//结果
4792111478498918p-46
6.810000e+01
6.810000E+01
68.100000
68.1
68.1
- 布尔
%t true 或 false
- 字符串
%s 字符串或切片的无解译字节 %q 双引号围绕的字符串,由Go语法安全地转义 %x 十六进制,小写字母,每字节两个字符 %X 十六进制,大写字母,每字节两个字符
package main import ("fmt") func main() {fmt.Printf("%s","I'm a girl")fmt.Println()fmt.Printf("%q","I'm a girl")fmt.Println()fmt.Printf("%x","I'm a girl")fmt.Println()fmt.Printf("%X","I'm a girl")fmt.Println()}
//结果I'm a girl"I'm a girl"49276d2061206769726c49276D2061206769726C
占位符
原文链接
输出为:{Aric 21 3-1}{Name:Aric Age:21 Class:3-1}main.Student{Name:"Aric", Age:21, Class:"3-1"}main.Student676f6c616e67676F6C616E670xc000062150
下划线 “_”
“_”是特殊标识符,用来忽略结果。
1.下划线在import中
在Golang里,import的作用是导入其他package。
import 下划线(如:import hello/imp)的作用:当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import 引用该包。即使用【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数。
示例:src |+--- main.go |+--- hello | +--- hello.go
main.go
package mainimport _ "./hello"func main() { // hello.Print() //编译报错:./main.go:6:5: undefined: hello}
hello.go
package helloimport "fmt"func init() { fmt.Println("imp-init() come here.")}func Print() { fmt.Println("Hello!")}
输出结果:
imp-init() come here.
2.下划线在代码中
package mainimport ( "os")func main() { buf := make([]byte, 1024) f, _ := os.Open("/Users/***/Desktop/text.txt") defer f.Close() for { n, _ := f.Read(buf) if n == 0 { break } os.Stdout.Write(buf[:n]) }}
解释1:
下划线意思是忽略这个变量.比如os.Open,返回值为*os.File,error普通写法是f,err := os.Open("xxxxxxx")如果此时不需要知道返回的错误值就可以用f, _ := os.Open("xxxxxx")如此则忽略了error变量
解释2:
占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值。所以就把该值赋给下划线,意思是丢掉不要。这样编译器可以更好的优化,任何类型的单个值都可以丢给下划线。这种情况是占位用的,方法返回两个结果,而你只想要一个结果。那另一个就用 "_" 占位,而如果用变量的话,不使用,编译器是会报错的。
补充:
import "database/sql"import _ "github.com/go-sql-driver/mysql"
第二个import就是不直接使用mysql包,只是执行一下这个包的init函数,把mysql的驱动注册到sql包里,然后程序里就可以使用sql包来访问mysql数据库了。
= 和 :=的区别
// = 使用必须使用先var声明例如:var aa=100//或var b = 100//或var c int = 100 // := 是声明并赋值,并且系统自动推断类型,不需要var关键字d := 100
String()方法
对于定于了String()方法的类型,默认输出的时候会调用该方法,实现字符串的打印。例如下面代码:
package main import "fmt" type Man struct { name string} func (m Man) String() string { return "My name is :" + m.name} func main() { var m Man m.name = "SNS" fmt.Println(m)} 输出:My name is :SNS
使用指针
然而,如果使用func (m *Man) String() string方式定义函数,那么就不会自动调用该函数输出(go version go1.12.1 windows/amd64)。package main import "fmt" type Man struct { name string} func (m *Man) String() string { return "My name is :" + m.name} func main() { var m Man m.name = "SNS" fmt.Println(m)} 输出:{SNS}
String方法是接口方法,存在于fmt包里print.go文件下的Stringer接口。go使用fmt包的输出方法会自动调用String()方法。当重写的String是指针方法时,只有指针类型调用的时候才会正常调用,值类型调用的时候实际上没有执行重写的String方法;当重写的String方法是值方法时,无论指针类型和值类型均可调用重写的String方法。其实跟接口的实现有关,当值类型实现接口的时候,相当于值类型和该值的指针类型均实现了该接口;相反,当指针类型实现了该接口的时候,只有指针类型实现了接口,值类型是没有实现的。
最后一句改成
fmt.Println(&m)
因为你只为*Man
这个Man的指针类型重新定义了String()方法,所以只有在输出*Man
类型的数据时才会调用自定义的String()方法。
而你定义的m是Man类型的,所以才不会调用你定义的String方法。
所要么向楼上那位一样定义Man类型。要么就是在输出时,向Print函数传递Man类型的数据(改成fmt.Println(&m)
)方法接受者
原文链接
在go语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法就是定义了接受者的函数。接受者定义在func关键字和函数名之间:type Person struct { name string age int}func (p Person) say() { fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}
有了对方法及接受者的简单认识之后,接下来主要谈一下接受者的类型问题。
接受者类型可以是struct,也可以是指向struc的指针。
情况一:接受者是structpackage mainimport "fmt"type Person struct {name stringage int}func (p Person) say() {fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}func (p Person) older(){ p.age = p.age +1}func main() { var p1 Person = Person{"zhansan",16} p1.older() p1.say() //output: I'm zhangsan,16 years old var p2 *Person = &Person{"lisi",17} p2.older() p2.say() //output: I'm lisi,17 years old}
对于p1的调用,读者应该不会有什么疑问。
对于p2的调用可能存在这样的疑问,p2明明是个指针,为什么再调用了older方法之后,打印结果还是17 years old?
方法的接受者是Person而调用者是Person ,其实在p2调用时存在一个转换p2.older() -> p2.older(); p2.say() -> p2.say()p2是什么想必读者也是明白的(就一个p2指向Person实例)。那么疑问也就自然的解开了,方法执行时的接受者实际上还是一个值而非引用。情况二:接受者是指针
package mainimport "fmt"type Person struct {name stringage int}func (p *Person) say() {fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}func (p *Person) older(){ p.age = p.age +1}func main() { var p1 Person = Person{"zhansan",16} p1.older() p1.say() //output: I'm zhangsan,17 years old var p2 *Person = &Person{"lisi",17} p2.older() p2.say() //output: I'm lisi,18 years old}
p1的调用中也存在一个转换,
p1.older -> p1.older
p1.say() -> p1.say()用例:
package mainimport ( "fmt")//面向对象//go仅支持封装,不支持继承和多态//go语言中没有class,只要struct//不论地址还是结构本身,一律使用.来访问成员//要改变内容必须使用指针接收者//结构过大考虑指针接收者//值接收者是go语言特有//封装//名字一般使用CamelCase//首字母大写: public//首字母小写:private//包//每个目录一个包,包名可以与目录不一样//main包包含可执行入口,只有一个main包//为结构定义的方法必须放在同一个包内,但是可以是不同文件type treeNode struct { value int left, right *treeNode}func (node treeNode) print() { //显示定义和命名方法接收者(括号里) fmt.Print(node.value) //只有使用指针才可以改变结构内容 fmt.Println()}func (node *treeNode) setValue ( value int) { //使用指针作为方法接收者 if node == nil { fmt.Println("setting value to nil node") //nil指针也可以调用方法 return } node.value = value}func (node *treeNode ) traverse(){ if node == nil{ return } node.left.traverse() node.print() node.right.traverse()}func main() { var root treeNode fmt.Println(root) //{0 <nil> <nil>} root = treeNode{value:3} root.left = &treeNode{} root.right = &treeNode{5,nil,nil} root.right.left = new(treeNode) nodes := []treeNode { {value: 3}, {}, {6,nil,&root}, } fmt.Println(nodes) //[{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc04205a3e0}] root.print() // 3 fmt.Println() root.right.left.setValue(100) root.right.left.print() //100 fmt.Println() var pRoot *treeNode pRoot.setValue(200) //setting value to nil node pRoot = &root pRoot.setValue(300) pRoot.print() //300 root.traverse() //300 0 300 100 5}
结构体定义中的 json 单引号(``)
package mainimport ( "encoding/json" "fmt")//在处理json格式字符串的时候,经常会看到声明struct结构的时候,属性的右侧还有小米点括起来的内容。`TAB键左上角的按键,~线同一个键盘`type Student struct { StudentId string `json:"sid"` StudentName string `json:"sname"` StudentClass string `json:"class"` StudentTeacher string `json:"teacher"`}type StudentNoJson struct { StudentId string StudentName string StudentClass string StudentTeacher string}//可以选择的控制字段有三种:// -:不要解析这个字段// omitempty:当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,slice,string// FieldName:当解析 json 的时候,使用这个名字type StudentWithOption struct { StudentId string //默认使用原定义中的值 StudentName string `json:"sname"` // 解析(encode/decode) 的时候,使用 `sname`,而不是 `Field` StudentClass string `json:"class,omitempty"` // 解析的时候使用 `class`,如果struct 中这个值为空,就忽略它 StudentTeacher string `json:"-"` // 解析的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的}func main() { //NO.1 with json struct tag s := &Student{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} jsonString, _ := json.Marshal(s) fmt.Println(string(jsonString)) //{"sid":"1","sname":"fengxm","class":"0903","teacher":"feng"} newStudent := new(Student) json.Unmarshal(jsonString, newStudent) fmt.Println(newStudent) //&{1 fengxm 0903 feng} //Unmarshal 是怎么找到结构体中对应的值呢?比如给定一个 JSON key Filed,它是这样查找的: // 首先查找 tag 名字(关于 JSON tag 的解释参看下一节)为 Field 的字段 // 然后查找名字为 Field 的字段 // 最后再找名字为 FiElD 等大小写不敏感的匹配字段。 // 如果都没有找到,就直接忽略这个 key,也不会报错。这对于要从众多数据中只选择部分来使用非常方便。 //NO.2 without json struct tag so := &StudentNoJson{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} jsonStringO, _ := json.Marshal(so) fmt.Println(string(jsonStringO)) //{"StudentId":"1","StudentName":"fengxm","StudentClass":"0903","StudentTeacher":"feng"} //NO.3 StudentWithOption studentWO := new(StudentWithOption) js, _ := json.Marshal(studentWO) fmt.Println(string(js)) //{"StudentId":"","sname":""} studentWO2 := &StudentWithOption{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} js2, _ := json.Marshal(studentWO2) fmt.Println(string(js2)) //{"StudentId":"1","sname":"fengxm","class":"0903"}}
基本数据类型操作
数组
map相关操作
创建map
//初始化空mapprevNums := map[int]int{}//初始化countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}//value接收任意数据类型,用interfaceresMap := make(map[string]interface{})
查看元素在集合中是否存在
capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if (ok) { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") }
判断key是否存在
package main import "fmt" func main() { demo := map[string]bool{ "a": false, } //错误,a存在,但是返回false fmt.Println(demo["a"]) //正确判断方法 _, ok := demo["a"] fmt.Println(ok)}
if _, ok := map[key]; ok { // 存在}if _, ok := map[key]; !ok { // 不存在}
快速删除所有元素
直接重新生成map,名字相同
Map 实现去重与 set 的功能
package main var set = map[string]bool { } func main() { ... url := xxx if set[url] { // 表示集合中已经存在 return } set[url] = true // 否则如果不存在,设置为true} // 完成后,set的所有的key值为不重复的值
map转json
// map to jsonpackage mainimport ( "encoding/json" "fmt")func main() { s := []map[string]interface{}{} m1 := map[string]interface{}{"name": "John", "age": 10} m2 := map[string]interface{}{"name": "Alex", "age": 12} s = append(s, m1, m2) s = append(s, m2) b, err := json.Marshal(s) if err != nil { fmt.Println("json.Marshal failed:", err) return } fmt.Println("b:", string(b))}
切片
切片是动态数组,可以搭配结构体或map形成多重嵌套
var projects = make([]models.Project, 0)
上面的models.Project
是一个结构体,前面加一个[]就变成了接片,用make生成,指定初始长度为0(必须要指定一个值,反正自动增加)。
这样这个projects变量是切片,里面的数据类型是models.Project结构体切片去重
/* 在slice中去除重复的元素,其中a必须是已经排序的序列。 * params: * a: slice对象,如[]string, []int, []float64, ... * return: * []interface{}: 已经去除重复元素的新的slice对象 */func SliceRemoveDuplicate(a interface{}) (ret []interface{}) {if reflect.TypeOf(a).Kind() != reflect.Slice {fmt.Printf("<SliceRemoveDuplicate> <a> is not slice but %T\n", a)return ret} va := reflect.ValueOf(a)for i := 0; i < va.Len(); i++ {if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {continue}ret = append(ret, va.Index(i).Interface())} return ret}
测试代码:
func test_SliceRemoveDuplicate() {slice_string := []string{"a", "b", "c", "d", "a", "b", "c", "d"}slice_int := []int{1, 2, 3, 4, 5, 1, 2, 3, 4, 5}slice_float := []float64{1.11, 2.22, 3.33, 4.44, 1.11, 2.22, 3.33, 4.44} sort.Strings(slice_string)sort.Ints(slice_int)sort.Float64s(slice_float) fmt.Printf("slice_string = %v, %p\n", slice_string, slice_string)fmt.Printf("slice_int = %v, %p\n", slice_int, slice_int)fmt.Printf("slice_float = %v, %p\n", slice_float, slice_float) ret_slice_string := SliceRemoveDuplicate(slice_string)ret_slice_int := SliceRemoveDuplicate(slice_int)ret_slice_float := SliceRemoveDuplicate(slice_float) fmt.Printf("ret_slice_string = %v, %p\n", ret_slice_string, ret_slice_string)fmt.Printf("ret_slice_int = %v, %p\n", ret_slice_int, ret_slice_int)fmt.Printf("ret_slice_float = %v, %p\n", ret_slice_float, ret_slice_float) fmt.Printf("<after> slice_string = %v, %p\n", slice_string, slice_string)fmt.Printf("<after> slice_int = %v, %p\n", slice_int, slice_int)fmt.Printf("<after> slice_float = %v, %p\n", slice_float, slice_float)}
结果:
slice_string = [a a b b c c d d], 0xc042088000slice_int = [1 1 2 2 3 3 4 4 5 5], 0xc04200e1e0slice_float = [1.11 1.11 2.22 2.22 3.33 3.33 4.44 4.44], 0xc042014200 ret_slice_string = [a b c d], 0xc042034100ret_slice_int = [1 2 3 4 5], 0xc042088080ret_slice_float = [1.11 2.22 3.33 4.44], 0xc042034180 <after> slice_string = [a a b b c c d d], 0xc042088000<after> slice_int = [1 1 2 2 3 3 4 4 5 5], 0xc04200e1e0<after> slice_float = [1.11 1.11 2.22 2.22 3.33 3.33 4.44 4.44], 0xc042014200
插入
/* * 在Slice指定位置插入元素。 * params: * s: slice对象,类型为[]interface{} * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * 已经插入元素的slice,类型为[]interface{} */func SliceInsert(s []interface{}, index int, value interface{}) []interface{} {rear := append([]interface{}{}, s[index:]...)return append(append(s[:index], value), rear...)} /* * 在Slice指定位置插入元素。 * params: * s: slice对象指针,类型为*[]interface{} * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * 无 */func SliceInsert2(s *[]interface{}, index int, value interface{}) {rear := append([]interface{}{}, (*s)[index:]...)*s = append(append((*s)[:index], value), rear...)} /* * 在Slice指定位置插入元素。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * true: 插入成功 * false: 插入失败(不支持的数据类型) */func SliceInsert3(s interface{}, index int, value interface{}) bool {if ps, ok := s.(*[]string); ok {if val, ok := value.(string); ok {rear := append([]string{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)return true}} else if ps, ok := s.(*[]int); ok {if val, ok := value.(int); ok {rear := append([]int{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)}} else if ps, ok := s.(*[]float64); ok {if val, ok := value.(float64); ok {rear := append([]float64{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)}} else {fmt.Printf("<SliceInsert3> Unsupported type: %T\n", s)} return false}
说明:
- SliceInsert()方法是传入一个[]interface{}类型的slice对象,返回的也是一个[]interface{}类型的slice对象。
- SliceInsert2()方法是传入一个[]interface{}类型的slice对象指针,直接修改这个slice对象。
- SliceInsert3()方法是传入一个具体类型的slice对象指针(如[]string, []int等),方法中直接修改这个slice对象,返回操作是否成功的状态(bool)。
删除
/* * 删除Slice中的元素。 * params: * s: slice对象,类型为[]interface{} * index: 要删除元素的索引 * return: * 已经删除指定元素的slice,类型为[]interface{} * 说明:返回的序列与传入的序列地址不发生变化(但是传入的序列内容已经被修改,不能再使用) */func SliceRemove(s []interface{}, index int) []interface{} {return append(s[:index], s[index+1:]...)} /* * 删除Slice中的元素。 * params: * s: slice对象指针,类型为*[]interface{} * index: 要删除元素的索引 * return: * 无 * 说明:直接操作传入的Slice对象,传入的序列地址不变,但内容已经被修改 */func SliceRemove2(s *[]interface{}, index int) {*s = append((*s)[:index], (*s)[index+1:]...)} /* * 删除Slice中的元素。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * index: 要删除元素的索引 * return: * true: 删除成功 * false: 删除失败(不支持的数据类型) * 说明:直接操作传入的Slice对象,不需要转换为[]interface{}类型。 */func SliceRemove3(s interface{}, index int) bool {if ps, ok := s.(*[]string); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else if ps, ok := s.(*[]int); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else if ps, ok := s.(*[]float64); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else {fmt.Printf("<SliceRemove3> Unsupported type: %T\n", s)return false} return true}
清空
/* * 清空Slice,传入的slice对象地址发生变化。 * params: * s: slice对象指针,类型为*[]interface{} * return: * 无 */func SliceClear(s *[]interface{}) {*s = append([]interface{}{})} /* * 清空Slice,传入的slice对象地址不变。 * params: * s: slice对象指针,类型为*[]interface{} * return: * 无 */func SliceClear2(s *[]interface{}) {*s = (*s)[0:0]} /* * 清空Slice,传入的slice对象地址不变。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * return: * true: 清空成功 * false: 清空失败(不支持的数据类型) */func SliceClear3(s interface{}) bool {if ps, ok := s.(*[]string); ok {*ps = (*ps)[0:0]//*ps = append([]string{})} else if ps, ok := s.(*[]int); ok {*ps = (*ps)[0:0]//*ps = append([]int{})} else if ps, ok := s.(*[]float64); ok {*ps = (*ps)[0:0]//*ps = append([]float64{})} else {fmt.Printf("<SliceClear3> Unsupported type: %T\n", s)return false} return true}
常用操作
查看程序运行时间
import "time" // 引用time库函数start := time.Now() // 获取当前时间// 被测代码cost := time.Since(start) // 计算此时与start的时间差// time.Now().Sub(start)也可
查看当前go版本号
go version
如果要升级版本,直接去官网下载对应的版本就好。但是会直接卸载之前的版本。环境变量一般会自动处理,关闭窗口(vscode,git bash之类的),再打开就能看到已经好了。
单引号、双引号、反引号
Golang限定字符或者字符串一共三种引号,单引号(’’),双引号(“”) 以及反引号(``)。反引号就是标准键盘“Esc”按钮下面的那个键。
单引号,表示byte类型或rune类型,对应 uint8和int32类型,默认是 rune 类型。byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。
双引号,才是字符串,实际上是字符数组。可以用索引号访问某字节,也可以用len()函数来获取字符串所占的字节长度。
反引号,表示字符串字面量,但不支持任何转义序列。字面量 raw literal string 的意思是,你定义时写的啥样,它就啥样,你有换行,它就换行。你写转义字符,它也就展示转义字符。
反引号有时候能起到很好的作用,比如一个字符串里面有双引号,分号这种,并且分布的还不规律,用反引号括起来就好
字符串中包含双引号
反引号
str2 := `"www.hewe.vip"`fmt.Println(len(str2))fmt.Println(str2)//输出结果14"www.hewe.vip"
转义
str3 := "\"www.hewe.vip\""fmt.Println(len(str3))fmt.Println(str3)//输出结果14"www.hewe.vip"
使用strconv包
str := strconv.Quote("www.hewe.vip")fmt.Println(len(str))fmt.Println(str1)//输出结果14"www.hewe.vip"
变量类型转换
string转成int:int, err := strconv.Atoi(string)string转成int64:int64, err := strconv.ParseInt(string, 10, 64)int转成string:string := strconv.Itoa(int)int64转成string:string := strconv.FormatInt(int64,10)
具体例子:
package mainimport ("fmt""strconv")func main() {cfdversion :="100"newcfd,_ :=strconv.Atoi(cfdversion)fmt.Println(newcfd)}
注意事项:
1.要导入包
2.转换变量类型后要重新用一个名字,不能用之前的变量名
3.下划线那个地方是err,被省略了判断变量类型
方法一:
package mainimport ( "fmt")func main() { v1 := "123456" v2 := 12 fmt.Printf("v1 type:%T\n", v1) fmt.Printf("v2 type:%T\n", v2)}
方法二:
package mainimport ( "fmt" "reflect")func main() { v1 := "123456" v2 := 12 // reflect fmt.Println("v1 type:", reflect.TypeOf(v1)) fmt.Println("v2 type:", reflect.TypeOf(v2))}
判断类型是否为map
if reflect.ValueOf(map1).Kind() == reflect.Map { } else { }
json相关操作
发送json格式的http请求
发送json为参数的post请求,以结构体为载体
type RequestBody struct { Id int `json:"id"` Name string `json:"name"` } func testPost(id int, name string) { request := RequestBody{ Id: id, Name: name, } requestBody := new(bytes.Buffer) json.NewEncoder(requestBody).Encode(request) url := "https://test.com" req, err := http.NewRequest("POST", url, requestBody) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } defer resp.Body.Close() fmt.Println("response Status:", resp.Status) fmt.Println("response Headers:", resp.Header) body, _ := ioutil.ReadAll(resp.Body) fmt.Println("response Body:", string(body)) }
解析json文件
package mainimport (// "bytes""encoding/json""fmt""io/ioutil"// "reflect")func con_var_name (key string) string {var res stringbytes,_:=ioutil.ReadFile("C:/Users/76585/Desktop/para_compare_ver2.json")m :=make(map[string]interface{})err := json.Unmarshal([]byte(bytes),&m)if err != nil {fmt.Println("err=",err)return ""}for _, value :=range m{// fmt.Println(reflect.TypeOf(value.([]interface{})[1].(map[string]interface{})["name"]))if key==value.([]interface{})[0].(map[string]interface{})["name"]{res=keyreturn res}if key==value.([]interface{})[1].(map[string]interface{})["name"]{tmp :=value.([]interface{})[0].(map[string]interface{})["name"]res=tmp.(string)return res}}res=keyreturn res}
解析json文件,interface转int
因为json解析得到的数据是map[string]interface,里面的字段可能是数字,有时候需要取出来比较,
就需要将interface转为int。
需要先转为string,再用strconv.Atoi
,将string转为int。结构体解析为json
先上代码
最为关键的是结构体里面的成员变量名,首字母必须是大写,否则无法解析,解析出来的是空。
package mainimport ("encoding/json""fmt")type Product struct {Name stringProductId int64Number intPrice float64IsOnSale bool}func main() {var p Product// p := Product{}p.Name="apple"p.ProductId=1p.Number=100p.Price=3.45p.IsOnSale=falsedata, _ := json.Marshal(&p)fmt.Println(string(data))}
将json字符串解码到相应的数据结构
type StuRead struct { Name interface{} `json:"name"` Age interface{} HIgh interface{} sex interface{} Class interface{} `json:"class"` Test interface{}}type Class struct { Name string Grade int}func main() { //json字符中的"引号,需用\进行转义,否则编译出错 //json字符串沿用上面的结果,但对key进行了大小的修改,并添加了sex数据 data:="{\"name\":\"张三\",\"Age\":18,\"high\":true,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}" str:=[]byte(data) //1.Unmarshal的第一个参数是json字符串,第二个参数是接受json解析的数据结构。 //第二个参数必须是指针,否则无法接收解析的数据,如stu仍为空对象StuRead{} //2.可以直接stu:=new(StuRead),此时的stu自身就是指针 stu:=StuRead{} err:=json.Unmarshal(str,&stu) //解析失败会报错,如json字符串格式不对,缺"号,缺}等。 if err!=nil{ fmt.Println(err) } fmt.Println(stu)}
map转json
例子在前面 基本数据类型操作-》map相关操作
字符串操作
package mainimport ( "fmt" "strings")func main() { s := "smallming" //第一次出现的索引 fmt.Println(strings.Index(s, "l")) //最后一次出现的索引 fmt.Println(strings.LastIndex(s, "l")) //是否以指定内容开头 fmt.Println(strings.HasPrefix(s, "small")) //是否以指定内容结尾 fmt.Println(strings.HasSuffix(s, "ming")) //是否包含指定字符串 fmt.Println(strings.Contains(s, "mi")) //全变小写 fmt.Println(strings.ToLower(s)) //全大写 fmt.Println(strings.ToUpper(s)) //把字符串中前n个old子字符串替换成new字符串,如果n小于0表示全部替换. //如果n大于old个数也表示全部替换 fmt.Println(strings.Replace(s, "m", "k", -1)) //把字符串重复count遍 fmt.Println(strings.Repeat(s, 2)) //去掉字符串前后指定字符 fmt.Println(strings.Trim(s, " ")) //去空格可以使用strings.TrimSpace(s) //根据指定字符把字符串拆分成切片 fmt.Println(strings.Split(s, "m")) //使用指定分隔符把切片内容合并成字符串 arr := []string{"small", "ming"} fmt.Println(strings.Join(arr, ""))}
字符串替换
str = strings.Replace(str, " ", "", -1)
func Replace(s, old, new string, n int) string
返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串
字符串处理函数
Golang中的strings包:
Count(s string, str string) int:计算字符串str在s中的非重叠个数。如果str为空串则返回s中的字符(非字节)个数+1。Index(s string, str string) int :返回子串str在字符串s中第一次出现的位置。如果找不到则返回-1;如果str为空,则返回0。LastIndex(s string, str string) int: 返回子串str在字符串s中最后一次出现的位置。如果找不到则返回-1;如果str为空则返回字符串s的长度。IndexRune(s string, r rune) int :返回字符r在字符串s中第一次出现的位置。如果找不到则返回-1。IndexAny(s string, str string) int :返回字符串str中的任何一个字符在字符串s中第一次出现的位置。如果找不到或str为空则返回-1。LastIndexAny(s string, str string) int: 返回字符串str中的任何一个字符在字符串s中最后一次出现的位置。如果找不到或str为空则返回-1。Contains(s string, str string) bool:判断字符串s中是否包含个子串str。包含或者str为空则返回true。ContainsAny(s string, str string) bool:判断字符串s中是否包含个子串str中的任何一个字符。包含则返回true,如果str为空则返回false。ContainsRune(s string, r rune) bool:判断字符串s中是否包含字符r。SplitN(s, str string, n int) []string:以str为分隔符,将s切分成多个子串,结果中**不包含**str本身。如果str为空则将s切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。参数n表示最多切分出几个子串,超出的部分将不再切分,最后一个n包含了所有剩下的不切分。如果n为0,则返回nil;如果n小于0,则不限制切分个数,全部切分。SplitAfterN(s, str string, n int) []string:以str为分隔符,将s切分成多个子串,结果中**包含**str本身。如果str为空,则将s切分成Unicode字符列表。如果s 中没有str子串,则将整个s作为 []string 的第一个元素返回。参数n表示最多切分出几个子串,超出的部分将不再切分。如果n为0,则返回 nil;如果 n 小于 0,则不限制切分个数,全部切分。Split(s, str string) []string:以str为分隔符,将s切分成多个子切片,结果中**不包含**str本身。如果str为空,则将s切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。SplitAfter(s, str string) []string:以str为分隔符,将s切分成多个子切片,结果中**包含**str本身。如果 str 为空,则将 s 切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。Fields(s string) []string:以连续的空白字符为分隔符,将s切分成多个子串,结果中不包含空白字符本身。空白字符有:\t, \n, \v, \f, \r, ’ ‘, U+0085 (NEL), U+00A0 (NBSP) 。如果 s 中只包含空白字符,则返回一个空列表。FieldsFunc(s string, f func(rune) bool) []string:以一个或多个满足f(rune)的字符为分隔符,将s切分成多个子串,结果中不包含分隔符本身。如果s中没有满足f(rune)的字符,则返回一个空列表。Join(s []string, str string) string:将s中的子串连接成一个单独的字符串,子串之间用str分隔。HasPrefix(s string, prefix string) bool:判断字符串s是否以prefix开头。HasSuffix(s, suffix string) bool :判断字符串s是否以prefix结尾。Map(f func(rune) rune, s string) string:将s中满足f(rune)的字符替换为f(rune)的返回值。如果f(rune)返回负数,则相应的字符将被删除。Repeat(s string, n int) string:将n个字符串s连接成一个新的字符串。ToUpper(s string) string:将s中的所有字符修改为其大写格式。对于非ASCII字符,它的大写格式需要查表转换。ToLower(s string) string:将s中的所有字符修改为其小写格式。对于非ASCII字符,它的小写格式需要查表转换。ToTitle(s string) string:将s中的所有字符修改为其Title格式,大部分字符的Title格式就是Upper格式,只有少数字符的Title格式是特殊字符。这里的ToTitle主要给Title函数调用。TrimLeftFunc(s string, f func(rune) bool) string:删除s头部连续的满足f(rune)的字符。TrimRightFunc(s string, f func(rune) bool) string:删除s尾部连续的满足f(rune)的字符。TrimFunc(s string, f func(rune) bool) string:删除s首尾连续的满足f(rune)的字符。IndexFunc(s string, f func(rune) bool) int:返回s中第一个满足f(rune) 的字符的字节位置。如果没有满足 f(rune) 的字符,则返回 -1。LastIndexFunc(s string, f func(rune) bool) int:返回s中最后一个满足f(rune)的字符的字节位置。如果没有满足 f(rune) 的字符,则返回 -1。Trim(s string, str string) string:删除s首尾连续的包含在str中的字符。TrimLeft(s string, str string) string:删除s头部连续的包含在str中的字符串。TrimRight(s string, str string) string:删除s尾部连续的包含在str中的字符串。TrimSpace(s string) string:删除s首尾连续的的空白字符。TrimPrefix(s, prefix string) string:删除s头部的prefix字符串。如果s不是以prefix开头,则返回原始s。TrimSuffix(s, suffix string) string:删除s尾部的suffix字符串。如果s不是以suffix结尾,则返回原始s。(只去掉一次,注意和TrimRight区别)Replace(s, old, new string, n int) string:返回s的副本,并将副本中的old字符串替换为new字符串,替换次数为n次,如果n为-1,则全部替换;如果 old 为空,则在副本的每个字符之间都插入一个new。EqualFold(s1, s2 string) bool:比较UTF-8编码在小写的条件下是否相等,不区分大小写,同时它还会对特殊字符进行转换。比如将“ϕ”转换为“Φ”、将“DŽ”转换为“Dž”等,然后再进行比较。“==”比较字符串是否相等,区分大小写,返回bool。Compare(s1 string, s2 string) int1:比较字符串,区分大小写,比”==”速度快。相等为0,不相等为-1。
正则表达式
常用的元字符:
. 匹配除换行符以外的任意字符\w 匹配字母或数字或下划线或汉字\s 匹配任意的空白符\d 匹配数字\b 匹配单词的开始或结束^ 匹配字符串的开始$ 匹配字符串的结束
字符转义:
如果你想查找元字符本身的话,比如你查找.,或者,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用.和\。当然,要查找\本身,你也得用\。
重复
如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。
分枝条件:
正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用 “|” 把不同的规则分隔开。
分组:
重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作。
反义字符
例子:\S+匹配不包含空白符的字符串。常用的正则表达式函数:
reg = regexp.MustCompile(`匹配模式`)reg.FindAllString( )reg.ReplaceAllString()
例子
func main() {text := `Hello 世界!123 Go.` // 查找连续的小写字母reg := regexp.MustCompile(`[a-z]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["ello" "o"] // 查找连续的非小写字母reg = regexp.MustCompile(`[^a-z]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["H" " 世界!123 G" "."] // 查找连续的单词字母reg = regexp.MustCompile(`[\w]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "123" "Go"] // 查找连续的非单词字母、非空白字符reg = regexp.MustCompile(`[^\w\s]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界!" "."] // 查找连续的大写字母reg = regexp.MustCompile(`[[:upper:]]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["H" "G"] // 查找连续的非 ASCII 字符reg = regexp.MustCompile(`[[:^ascii:]]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界!"] // 查找连续的标点符号reg = regexp.MustCompile(`[\pP]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["!" "."] // 查找连续的非标点符号字符reg = regexp.MustCompile(`[\PP]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界" "123 Go"] // 查找连续的汉字reg = regexp.MustCompile(`[\p{Han}]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界"] // 查找连续的非汉字字符reg = regexp.MustCompile(`[\P{Han}]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello " "!123 Go."] // 查找 Hello 或 Goreg = regexp.MustCompile(`Hello|Go`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找行首以 H 开头,以空格结尾的字符串reg = regexp.MustCompile(`^H.*\s`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界!123 "] // 查找行首以 H 开头,以空白结尾的字符串(非贪婪模式)reg = regexp.MustCompile(`(?U)^H.*\s`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello "] // 查找以 hello 开头(忽略大小写),以 Go 结尾的字符串reg = regexp.MustCompile(`(?i:^hello).*Go`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界!123 Go"] // 查找 Go.reg = regexp.MustCompile(`\QGo.\E`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Go."] // 查找从行首开始,以空格结尾的字符串(非贪婪模式)reg = regexp.MustCompile(`(?U)^.* `)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello "] // 查找以空格开头,到行尾结束,中间不包含空格字符串reg = regexp.MustCompile(` [^ ]*$`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// [" Go."] // 查找“单词边界”之间的字符串reg = regexp.MustCompile(`(?U)\b.+\b`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" " 世界!" "123" " " "Go"] // 查找连续 1 次到 4 次的非空格字符,并以 o 结尾的字符串reg = regexp.MustCompile(`[^ ]{1,4}o`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找 Hello 或 Goreg = regexp.MustCompile(`(?:Hell|G)o`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找 Hello 或 Go,替换为 Hellooo、Goooreg = regexp.MustCompile(`(?PHell|G)o`)fmt.Printf("%q\n", reg.ReplaceAllString(text, "${n}ooo"))// "Hellooo 世界!123 Gooo." // 交换 Hello 和 Goreg = regexp.MustCompile(`(Hello)(.*)(Go)`)fmt.Printf("%q\n", reg.ReplaceAllString(text, "$3$2$1"))// "Go 世界!123 Hello." // 特殊字符的查找reg = regexp.MustCompile(`[\f\t\n\r\v\123\x7F\x{10FFFF}\\\^\$\.\*\+\?\{\}\(\)\[\]\|]`)fmt.Printf("%q\n", reg.ReplaceAllString("\f\t\n\r\v\123\x7F\U0010FFFF\\^$.*+?{}()[]|", "-"))// "----------------------"}
按行读文件
注意下这里的第二个方法,读到最后字符串为空,有时候可能会报错(被坑过),加一个判断条件,判断长度是否为0。(代码里面自己已经加了)
func Readlines(filename string) {// go 按行读取文件的方式有两种,// 第一 将读取到的整个文件内容按照 \n 分割// 使用bufio// 第一种lines, err := ioutil.ReadFile(filename)if err != nil {fmt.Println(err)} else {contents := string(lines)lines := strings.Split(contents, "\n")for _, line := range lines {fmt.Println(line)}}// 第二种fd, err := os.Open(filename)defer fd.Close()if err != nil {fmt.Println("read error:", err)}buff := bufio.NewReader(fd)for {data, _, eof := buff.ReadLine()if eof == io.EOF {break}if(len(data)==0){ break }fmt.Println(string(data))}}
字符串与切片的转换
package mainimport ("fmt""strings")func main() {s := []string{"1", "2", "3"}ss := fmt.Sprintf(strings.Join(s, ","))fmt.Println(ss)slice := strings.Split(ss, ",")fmt.Println(slice)//r := gin.Default()//r.GET("/", func(context *gin.Context) {//context.JSON(http.StatusOK,gin.H{//"message":"demo",//})//})//r.Run()}//D:\GoProject\gin-demo>go run main.go//1,2,3//[1 2 3]
读写文件
打开关闭文件
import ( "fmt" "os")func main() { // 打开文件 file, err := os.Open("e:/a.txt") if err != nil { fmt.Printf("打开文件出错:%v\n", err) } fmt.Println(file) // &{0xc00006a780} // 关闭文件 err = file.Close() if err != nil { fmt.Printf("关闭文件出错:%v\n", err) }}
读文件
法一:带缓冲
import ( "bufio" "fmt" "io" "os")func main() { // 打开文件 file, err := os.Open("e:/a.txt") if err != nil { fmt.Printf("打开文件出错:%v\n", err) } // 及时关闭文件句柄 defer file.Close() // bufio.NewReader(rd io.Reader) *Reader reader := bufio.NewReader(file) // 循环读取文件的内容 for { line, err := reader.ReadString('\n') // 读到一个换行符就结束 if err == io.EOF { // io.EOF表示文件的末尾 break } // 输出内容 fmt.Print(line) }}
法二:一次性读入,不适用于大文件
import ( "fmt" "io/ioutil")func main() { // 使用 io/ioutil.ReadFile 方法一次性将文件读取到内存中 filePath := "e:/.txt" content, err := ioutil.ReadFile(filePath) if err != nil { // log.Fatal(err) fmt.Printf("读取文件出错:%v", err) } fmt.Printf("%v\n", content) fmt.Printf("%v\n", string(content))}
写文件
示例1:
创建一个新文件,写入3行:”Hello World”
打开一个存在的文件,将原来的内容覆盖成新的内容,3行:”你好,世界”
打开一个存在的文件,在原来的内容基础上,追加3行:”你好,Golang”
打开一个存在的文件,将原来的内容读出显示在终端,并且追加3行:”你好,World”import ( "bufio" "fmt" "os")func main() { filePath := "e:/a.txt" // 此文件事先不存在 file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666) // O_CREATE 能创建文件 if err != nil { fmt.Printf("打开文件出错:%v", err) return } // 及时关闭文件句柄 defer file.Close() // 准备写入的内容 str := "Hello World\r\n" // 写入时,使用带缓冲方式的 bufio.NewWriter(w io.Writer) *Writer writer := bufio.NewWriter(file) // 使用for循环写入内容 for i := 0; i < 3; i++ { _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error) if err != nil { fmt.Printf("文件写入出错:%s", err) } } // 因为 writer 是带缓存的,所以需要 Flush 方法将缓冲中的数据真正写入到文件中 _ = writer.Flush()}
将 O_CREATE 修改为 O_TRUNC 模式即可,表示:打开文件并清空内容
将 O_TRUNC 修改为 O_APPEND 模式即可,表示:打开文件并在最后追加内容import ( "bufio" "fmt" "io" "os")func main() { filePath := "e:/a.txt" file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666) if err != nil { fmt.Printf("打开文件出错:%v", err) return } defer file.Close() // 先读取原来文件的内容,并显示在终端 reader := bufio.NewReader(file) for { str, err := reader.ReadString('\n') // 读到一个换行符就结束 if err == io.EOF { // io.EOF表示文件的末尾 break } // 输出内容 fmt.Print(str) } // 准备写入的内容 str := "你好,World\r\n" // 写入时,使用带缓冲方式的 bufio.NewWriter(w io.Writer) *Writer writer := bufio.NewWriter(file) // 使用for循环写入内容 for i := 0; i < 3; i++ { _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error) if err != nil { fmt.Printf("文件写入出错:%s", err) } } // 因为 writer 是带缓存的,所以需要 Flush 方法将缓冲中的数据真正写入到文件中 _ = writer.Flush()}4:读写模式(O_RDWR)
示例二:将一个文件写道另一个文件(两个文件均存在)
import ( "fmt" "io/ioutil")func main() { filePath1 := "e:/a.txt" filePath2 := "e:/b.txt" content, err := ioutil.ReadFile(filePath1) if err != nil { fmt.Printf("读取文件出错:%v", err) return } err = ioutil.WriteFile(filePath2, content, 0666) if err != nil { fmt.Printf("写入文件出错:%v", err) return }}
替换文件某一行内容
package mainimport ( "io/ioutil" "log" "strings")func main() { input, err := ioutil.ReadFile("C:/Users/76585/Desktop/key.hypara") if err != nil { log.Fatalln(err) } lines := strings.Split(string(input), "\n") for i, line := range lines { if strings.Contains(line, "nsimutask") { lines[i] = "int nsimutask = 0;" }if strings.Contains(line, "string parafilename") {lines[i] = "string parafilename = \"../bin/grid_para.hypara\";"break} } output := strings.Join(lines, "\n") err = ioutil.WriteFile("C:/Users/76585/Desktop/key.hypara", []byte(output), 0644) if err != nil { log.Fatalln(err) }}
判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用 os.Stat() 函数返回的错误值进行判断:
如果返回的错误为 nil,说明文件或文件夹存在;
如果返回的错误类型使用 os.IsNotExist() 判断为 true,说明文件或文件夹不存在;
如果返回的错误为其他类型,则不确定是否存在。package mainimport ( "fmt" "os")// 判断文件或文件夹是否存在func PathExist(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err}func main() { filePath := "e:/a.txt" flag, err := PathExist(filePath) if err != nil { fmt.Println(err) } fmt.Println(flag) // true}
拷贝文件
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
package mainimport ( "bufio" "fmt" "io" "os")// 将 srcFilePath 拷贝到 dstFilePathfunc CopyFile(dstFilePath string, srcFilePath string) (written int64, err error) { // 打开srcFilePath srcFile, err := os.Open(srcFilePath) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer srcFile.Close() // 通过 bufio/NewReader,传入 srcFile,获取到 reader reader := bufio.NewReader(srcFile) // 打开dstFilePath dstFile, err := os.OpenFile(dstFilePath, os.O_WRONLY | os.O_CREATE, 0666) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer dstFile.Close() // 通过 bufio/NewWriter,传入 dstFile,获取到 writer writer := bufio.NewWriter(dstFile) return io.Copy(writer, reader)}func main() { srcFilePath := "e:/a.mp4" dstFilePath := "f:/b.mp4" _, err := CopyFile(dstFilePath, srcFilePath) if err != nil { fmt.Printf("拷贝文件出错:%s", err) } fmt.Println("拷贝文件完成")}自己写一个函数完成拷贝文件
读取文件最后一行
func ReadFile(file_name string) (info string) { file, err := os.Open(file_name) if err != nil { log.Fatal(err) } defer file.Close() var lineText string scanner := bufio.NewScanner(file) for scanner.Scan() { lineText = scanner.Text() //fmt.Print(lineText) } return string(lineText)}
遍历目录
package mainimport ( "fmt" "os")func main() { fmt.Println("请输入一个目录的路径:") var path string _, _ = fmt.Scan(&path) // 打开目录 f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir) if err != nil { fmt.Printf("Open file failed:%s.\n", err) return } defer f.Close() // 读取目录 info, err := f.Readdir(-1) // -1 表示读取目录中所有目录项 // 遍历返回的切片 for _, fileInfo := range info { if fileInfo.IsDir() { fmt.Printf("%s是一个目录\n", fileInfo.Name()) } else { fmt.Printf("%s是一个文件\n", fileInfo.Name()) } }}
其它
统计一个文件中含有的英文、数字、空格以及其他字符数量。
package mainimport ( "bufio" "fmt" "io" "os")// 定义一个结构体,用于保存统计结果type CharCount struct { AlphaCount int // 记录英文个数 NumCount int // 记录数字的个数 SpaceCount int // 记录空格的个数 OtherCount int // 记录其它字符的个数}func main() { // 思路: 打开一个文件, 创一个 reader // 每读取一行,就去统计该行有多少个 英文、数字、空格和其他字符 // 然后将结果保存到一个结构体 filePath := "e:/a.txt" file, err := os.Open(filePath) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer file.Close() // 定义一个 CharCount 实例 var count CharCount //创建一个Reader reader := bufio.NewReader(file) // 开始循环的读取文件的内容 for { line, err := reader.ReadString('\n') if err == io.EOF { // 读到文件末尾就退出 break } // 遍历每一行(line),进行统计 for _, v := range line { switch { case v >= 'a' && v <= 'z': fallthrough // 穿透 case v >= 'A' && v <= 'Z': count.AlphaCount++ case v >= '0' && v <= '9': count.NumCount++ case v == ' ' || v == '\t': count.SpaceCount++ default : count.OtherCount++ } } } // 输出统计的结果看看是否正确 fmt.Printf("字符的个数为:%v\n数字的个数为:%v\n空格的个数为:%v\n其它字符个数:%v\n", count.AlphaCount, count.NumCount, count.SpaceCount, count.OtherCount)}
问题或其他知识
使用grpcurl
如果准备使用docker来运行,那么pull完成后,使用下面的命令:
docker run fullstorydev/grpcurl ip:"端口" list
“ip”和“端口” 记得替换map类型interface{}转换
有时候我们在map里面嵌套map
想取内存map的值就会出现以下问题cannot range over v (type interface {})
interface{} 与其他数据类型不能直接赋值
解决方法
]]>
转为map
v.(map[string] interface {})
转为int
value.(int)
在目标变量后面用. 括号int- 转为map
修改权限 修改文件用户组
chgrp: change group的简写,修改文件所属的用户组。
chgrp users test.log
如果要修改该目录下所有文件和目录,使用-R参数。
chgrp -R users test
修改文件所有者
chown :change owner的简写, 修改文件的所有者。
chown [-R] 账号名称 文件或目录
将所有者和组名称都修改为root。
chown root:root test.log
修改文件权限
运行sh文件命令
第一种(这种办法需要用chmod使得文件具备执行条件(x): chmod u+x datelog.sh):
/xx/xxx/xxx.sh //任意路径 ./XXX.sh //当前路径
第二种(这种办法不需要文件具备可执行的权限也可运行):
sh xxx.sh
解压缩命令
ZIP
zip [选项] 压缩包名 源文件或源目录-r:压缩目录示例:zip ana.zip anaconda-ks.cfg压缩多个文件:zip test.zip abc abcd
zip对应的解压缩命令为unzip:命令所在目录为/usr/bin/unzip,所有用户可执行unzip [选项] 压缩包名-d:指定解压缩位置-o:不必先询问用户,unzip执行后覆盖原有文件。//其它参数可自行查看unzip -d /tmp/ test.zipunzip -d /tmp -o test.zip //这条命令和上一条相比,可以不用询问直接覆盖
gz
注意:使用gzip压缩文件后会将原文件删除,如果想保留原文件则可以使用-c选项将压缩过程产生的标准输出写入一个新的文件中,示例如下:>的作用是覆盖内容,>>的作用是追加内容
压缩目录下的每个文件:下述命令会将123这个目录下的每个文件分别进行压缩,而不是将整个123目录进行压缩,也就是说gzip命令不会打包压缩解压缩也可以使用gunzip:
bz2
.bz2格式是Linux中的另一种常用压缩格式,该格式的压缩算法更先进,压缩比更高,但是压缩的时间要比.gz长,.bz2格式的压缩命令是bzip2,注意bzip2不能压缩目录,会报错
解压时如果原文件已存在则会报错,因此最好先将原文件删除tar
只是打包并不会压缩文件,.gz,.xz。这些才是压缩
.tar格式的打包和解打包都是使用tar命令,区别只是选项不同
打包示例:
打包多个文件:
解打包:.tar.gz和.tar.bz2
tar 压缩、解压缩都可以使用多线程
.tar.gz格式和.tar.bz2格式:使用tar命令后跟选项的方式实现tar命令和gzip或者bzip2命令的组合,实现同时进行打包和压缩,这也是最经常使用的压缩和解压缩方式
.tar.xz
默认压缩后的文件后缀为 xz,速度慢一些,但是压缩的会更小。
//常用参数-z强制执行压缩, 默认不保留源文件。压缩后的文件名为源文件.xz-d强制执行解压缩-l列出压缩文件的信息-k保留源文件不要删除-f强制覆盖输出文件和压缩链接-c写入到标准输出,输入文件不要删除-0..-9压缩比例,默认为6-e 使用更多的 CPU time 来进行压缩,提高压缩率。不会影响解压时所需要的内存。-T 指定线程数,默认是 1 ,当设置为 0 时使用和机器核心一样多的线程。--format= 指定压缩输出格式,可以是 raw、xz、lzma-v显示更详细的信息
注意点: 压缩后的文件时在和源文件同一个目录。当我们压缩的文件为 /home/nginx/logs/error.log-20191126 ,当我们在任意目录执行完 xz /home/nginx/logs/error.log-20191126 后,压缩后的文件路径是 /home/nginx/logs/error.log-20191126.xz.
//不保留源文件xz /home/nginx/logs/error.log-20191126//保留源文件xz -k /home/nginx/logs/error.log-20191126//解压缩文件xz -d /home/nginx/logs/error.log-20191126.xz//指定多线程数来进行压缩xz -T 4 /home/nginx/logs/error.log-20191126
查询或查看命令汇总
查看某个软件对应的软连接
ls -l "xxxx"
使用
ls -al
也可也,那就是查看整个目录下面的。which 与 whereis
which和whereis命令都是Linux操作系统下查找可执行文件路径的命令
which
这条命令主要是用来查找系统PATH目录下的可执行文件。说白了就是查找那些我们已经安装好的可以直接执行的命令,比如
swq123459@swq123459PC:~$ which ls/bin/ls
注意上述斜体字, which 查找的可执行文件,必须是要在 PATH 下的可执行文件,而不能是没有加入 PATH 的可执行文件,即使他就是可执行文件,但是没有加入到系统搜索路径,他仍然无法被 which 发现(好吧,有点啰嗦了)。whereis
这个命令可以用来查找二进制(命令)、源文件、man文件。与which不同的是这条命令可以是通过文件索引数据库而非PATH来查找的,所以查找的面比which要广。例如:
swq123459@swq123459PC:~$ whereis lsls: /bin/ls /usr/share/man/man1/ls.1.gz
可以看到,whereis不仅找到了 ls 可执行文件的位置,还找到了其 man 帮助文件,可见其搜索范围比较广,不局限于PATH。查看某个进程或者服务是否存在
ps -aux | grep xxx
查找文件及文件夹
find的主要用来查找文件,查找文件的用法我们比较熟悉,也可用它来查找文件夹,用法跟查找文件类似,只要在最后面指明查找的文件类型 -type d,如果不指定type类型,会将包含查找内容的文件和文件夹一起输出。find基本语法如下:find [PATH] [Option] [action]-newer file:file为一个存在的文件,列出比file还要新的文件名find / -mtime 0———0代表当前的时间,即从现在开始到24小时前,有改动过内容的文件都会被列出来find /etc -newer /etc/passwd———寻找/etc下面的文件,如果文件日期比/etc/passwd新就列出find / -name file——/代表全文搜索find /home -user Anmy——查找/home下属于Anmy的文件find / -nouser—— 查找系统中不属于任何人的文件,可以轻易找出那些不太正常的文件find / -name passed—— 查找文件名为passed的文件
若不指定查找类型,使用命令:find / -name AnmyTest 则会将目录和文件一同输出
若指定查找类型,使用命令:find / -name AnmyTest -type d 则只会将目录输出查看文件最后几行
//显示filename最后20行。tail -n 20 filename
查看端口
查看某个服务的状态
service ‘servicename’ status//centos7以上用 systemctl
例子
service sshd status //查看sshd服务的状态,可以看到它的进程号,如果不需要可以kill 杀死
lsof -i:端口号
可以看到 8000 端口已经被轻 nodejs 服务占用。netstat -tunlp | grep 端口号
用于显示 tcp,udp 的端口和进程等相关情况
-t (tcp) 仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化为数字
-l 仅列出在Listen(监听)的服务状态
-p 显示建立相关链接的程序名kill(杀死进程)
telnet(检测端口是否可用)
有时我们想知道端口是否开启。
tenlet ip 端口
上图表示:80端口开放,8899端口未开放。查看系统情况
cpu
lscpu //cpu架构、每个核的线程数都能看到
磁盘
lsblk //命令用来查看接入到系统中的块设备,默认输出分区、大小、挂载点等信息df -h //查看硬盘的使用情况
内存
free -h
系统版本
centos:
cat /etc/redhat-release
删除除了某个文件外的其他文件
shopt -s extglob (打开extglob模式)rm -fr !(file1)# 如果是多个要排除的,可以这样:rm -rf !(file1|file2)
复制/移动文件、文件改名
Linux 将一个文件夹的所有内容拷贝到另外一个文件夹
cp 命令使用 -r 参数可以将 packageA 下的所有文件拷贝到 packageB 中:
cp -r /home/packageA/* /home/cp/packageB/
将一个文件夹复制到另一个文件夹下,以下实例 packageA 文件会拷贝到 packageB 中:
cp -r /home/packageA /home/packageB
运行命令之后 packageB 文件夹下就有 packageA 文件夹了。
cp /xx/xx(a) /xx/xx(a)
:复制 ~~~~ //将a复制到bmv /xx /xx /xx/xx
:剪切mv 旧文件夹名 新文件夹名
//更改名字批量操作
批量删除文件夹
某个文件夹下,有很多文件名形如:checkpoint{epoch}{loss}.pth;其中epoch和loss是变量;我现在希望把epoch小于400的删除
ls checkpoint_*.pth | awk -F'_' '{if ($2 < 400) print $0}' | xargs rm
文件操作
touch命令(创建文件)
touch
命令用于修改文件或者目录的时间属性,包括存取时间和更改时间,若文件不存在,系统会建立一个新的文件。创建一个空白文件,如果文件已经存在,它将更改文件的访问时间。
touch /tmp/file.txt
创建多个文件。
touch /tmp/file1.txt /tmp/file2.txt /tmp/file3.txt
修改文件的修改时间并查看文件属性。
touch -m /tmp/file.txt && stat /tmp/file.txt
同时修改访问时间和修改时间并设置一个特定的访问与修改时间。
touch -am -t 202007010000.00 /tmp/file.txt && stat /tmp/file.txt
cat命令(显示文件内容)
cat
命令属于文件管理,用于连接文件并打印到标准输出设备上,cat
经常用来显示文件的内容,注意,当文件较大时,文本在屏幕上迅速闪过,会出现滚屏现象,此时往往看不清所显示的内容,为了控制滚屏,可以按Ctrl+S
键停止滚屏,按Ctrl+Q
键可以恢复滚屏,此外可以用more
等命令进行读文件并分页显示。使用
cat
命令创建一个文件,输入文件信息后按Ctrl+D
输出EOF
标识后结束输入。cat > file.txt
输出
file.txt
文件中的内容。cat file.txt
同时输出
file.txt
与file2.txt
文件中的内容。cat file.txt file2.txt
把
file.txt
文件的内容加上行号后追加到file2.txt
文件中。cat -n file.txt >> file2.txt
清空
file2.txt
文件,/dev/null
称为空设备,是一个特殊的设备文件,其会丢弃一切写入其中的数据,但报告写入操作成功,读取它则会立即得到一个EOF
。cat /dev/null > file2.txt
将
file.txt
与file2.txt
文件内容合并输出到file3.txt
。cat file.txt file2.txt > file3.txt
文件内容覆盖/追加内容(cat命令)
cat textfile1 > textfile2 //使用“>” 重定向后 文件 中原本的内容会被覆盖cat textfile1 >> textfile2 //">>" 代表 将输出的内容已追加的方式重定向到文件
cat 原单词concatenate(用途是连接文件或标准输入并打印。)
cat 命令用于将所有文件内容打印到屏幕上。
语法:cat 文件
Centos7特性
服务相关命令使用systemctl,之前的版本是service
systemctl (stop/restart/start) (服务)
systemctl restart nginx防火墙
开启端口(以80为例)
firewall-cmd —zone=public —add-port=80/tcp —permanent
//zone add permanent前面是两个横杠重启防火墙
systemctl restart firewalld.service
关闭防火墙
systemctl stop firewalld.service
开机禁用防火墙
systemctl disable firewalld.service
不同的系统命令可能不同
修改主机名
debian/ubuntu系列
第一步:vi /etc/hostname写入HOSTNAME=yourhostname保存后执行以下:hostname yourhostname 查看设置后的hostnamehostname
第二步:vi /etc/hosts修改成新的主机名
redhat/centos系列
vi /etc/sysconfig/network输入以下:HOSTNAME=yourhostname保存后执行以下:hostname yourhostname 查看设置后的hostnamehostname
vim相关操作
复制
1)单行复制
在命令模式下,将光标移动到将要复制的行处,按“yy”进行复制;
2)多行复制 在命令模式下,将光标移动到将要复制的首行处,按“nyy”复制n行;其中n为1、2、3……
【yy】 复制光标所在的那一行
【nyy】 复制光标所在的向下n行粘贴
在命令模式下,将光标移动到将要粘贴的行处,按“p”进行粘贴
【p,P】 p为将已经复制的数据在光标下一行粘贴;P为将已经复制的数据在光标上一行粘贴
删除
删除一行:dd
删除一个单词/光标之后的单词剩余部分:dw
删除当前字符:x
光标之后的该行部分:d$
文本删除
dd 删除一行
d$ 删除以当前字符开始的一行字符
ndd 删除以当前行开始的n行
dw 删除以当前字符开始的一个字
ndw 删除以当前字符开始的n个字
查找
【/word】 在文件中查找内容为word的字符串(向下查找)
【?word】 在文件中查找内容为word的字符串(向上查找)
【[n]】 表示重复查找动作,即查找下一个
【[N]】 反向查找下一个取消高亮
搜索后,我们打开别的文件,发现也被高亮了,怎么关闭高亮?
命令模式下,输入:nohlsearch 也可以:set nohlsearch; 当然,可以简写,noh或者set noh。
PS:nohlsearch是(no highlight search缩写)设置行号
如果编辑后,又想显示行号,同样操作按一下esc键,并输入:(冒号),输入set number ,并按回车键,完成后即显示行号
跳到指定行
在知道所查找的内容在文件中的具体位置时可以使用以下命令直接定位:
跳到文件指定行:比如跳到66行66+G(也就是66+shift+g)
当然你可以选择另一种跳转方式:命令行输入“ : n ” 然后回车
跳到文件第一行:gg (两个小写的G)跳到文件最后一行:shift+g (也就是G)
文件上下翻转
页翻转可以直接使用PgUp和PgDn
向前滚动一屏:Ctrl+F
向后滚动一屏:Ctrl+B
向前滚动半屏:Ctrl+D(向下)
向后滚动半屏:Ctrl+U(向上)
向下滚动一行,保持当前光标不动:Ctrl+E
向上滚动一行,保持当前光标不动:Ctrl+Y
当前行滚动:
当前行移动到屏幕顶部并滚动:Z+Enter
滚动指定行到屏幕顶部: 10Z+Enter(指定第十行)
当前行移动到屏幕中央并滚动:Z + .
当前行移动到屏幕底部并滚动:Z + -
当前屏幕操作:
H:大写h,移动到当前屏幕首行;nH移动到首行下的第n行
M:大写m,移动到当前屏幕中间行
L:大写l,移动到当前屏幕末行;nL移动到末行上面的第n行撤销上一步操作
【u】 撤消上一个操作
【[Ctrl] + r】 多次撤消
【.】 这是小数点键,重复上一个操作缩进:
插入模式下,ctrl+shift+d 减少缩进,ctrl+shift+t 增加缩进
vim编辑
1、进入插入模式(6个命令)
【i】 从目前光标所在处插入
【I】 从目前光标
【a】 从当前光标所在的下一个字符处开始插入
【A】 从光标所在行的最后一个字符处开始插入
【o】 英文小写字母o,在目前光标所在行的下一行处插入新的一行并开始插入
【O】 英文大写字母O,在目前光标所在行的上一行处插入新的一行并开始插入2、进入替换模式(2个命令)
【r】 只会替换光标所在的那一个字符一次
【R】 会一直替换光标所在字符,直到按下[ESC]键为止
【[ESC]】 退出编辑模式回到一般模式3、一般模式切换到命令行模式
【:w】 保存文件
【:w!】 若文件为只读,强制保存文件
【:q】 离开vi
【:q!】 不保存强制离开vi
【:wq】 保存后离开
【:wq!】 强制保存后离开
【:! command】 暂时离开vi到命令行下执行一个命令后的显示结果
【:set nu】 显示行号
【:set nonu】 取消显示行号
【:w newfile】 另存为
【:set fileencoding】 查看当前文件编码格式
【:set fileencoding=utf-8】 设置当前文件编码格式为utf-8,也可以设置成其他编码格式
【:set fileformat】 查看当前文件的断行格式(dos\windows,unix或macintosh)
【:set fileformat=unix】 将当前文件的断行格式设置为unix格式多窗口功能
【:sp [filename]】 打开一个新窗口,显示新文件,若只输入:sp,则两窗口显示同一个文件
【[Ctrl] + w + j】 光标移动到下方窗口
【[Ctrl] + w + k】 光标移动到上方窗口
【[Ctrl] + w + q】 离开当前窗口缩进
批量缩进
在程序代码界面,按esc,退出编辑模式,到命令模式,并在英语输入法下输入“:”
将所要批量缩进的行号写上,按照格式:“行号1,行号2>”输入命令,如要将2至9行批量缩进一个tab值,则命令为“2,9>”
输入完毕后,按回车可以执行,就可以看到2至9行全部缩进了一个tab值了,同样的,如果要缩回来一个tab值,则用命令“行号1,行号2<”即可
可视模式缩进
方法二是在可视模式下选择要移动的列,操作为,esc从编辑模式退到命令模式,将光标移到需要缩进的行的行首,然后按shift+v,可以看到该行已被选中,且左下角提示为“可视”
此时,按键盘上的上下左右方向键,如这里按向下的箭头,选中所有需要批量缩进的行
选择好了之后,按shift+>,是向前缩进一个tab值,按shift+<,则是缩回一个tab值,
vscode Remote SSH
ssh登录命令:
ssh username@ip -p port //密码登录 ,-p port可以不用输入,不输默认是22,因为linux默认也确实是22ssh username@ip -p port –i id_rsa //密钥登录//密钥生成的时候建议命名,不要使用默认的,否则多了容易分不清。
使用Remote SSH 插件访问linux,用密钥登录。需要把公钥放在服务器,私钥放在
.ssh
目录下
再配置VScode
点击Remote SSH的图标后再点击箭头所指的齿轮
会弹出菜单让你选择需要编辑的配置文件,一般选第一个
参数的含义分别为:Host 连接的主机的名称,可自定
Hostname 远程主机的IP地址
User 用于登录远程主机的用户名
Port 用于登录远程主机的端口
IdentityFile 本地的id_rsa的路径
右键点击Connect!
设置 SSH 通过密钥登录
注意事项
- 密钥的制作和用户有关。比如我在服务器上使用xxx制作的密钥,那么登录的话,是让ssh xxx@ip 可以免密登录,其它用户不行的。
- 如果我想免密登录服务器,那么是在服务器上面把密钥制作出来,将私钥传给别人,别人用私钥来访问。(感觉这样做不是很安全,看到的介绍都是把公钥给别人。但如果是公钥给别人,那就只有在本地制作密钥,再把公钥给服务器,但是用户就没法对上,后面有时间再想想吧)
制作密钥对
我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux
服务器。但是,一般的密码方式登录,容易有密码被暴力破解的问题。所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者禁用
root 账户登录。其实,有一个更好的办法来保证安全,而且让你可以放心地用 root 账户从远程登录——那就是通过密钥方式登录。密钥形式登录的原理是:利用密钥生成器制作一对密钥——一只公钥和一只私钥。将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录。这样一来,没有私钥,任何人都无法通过
SSH 暴力破解你的密码来远程登录到系统。此外,如果将公钥复制到其他账户甚至主机,利用私钥也可以登录。下面来讲解如何在 Linux 服务器上制作密钥对,将公钥添加给账户,设置 SSH,最后通过客户端登录。
首先在服务器上制作密钥对。首先用密码登录到你打算使用密钥登录的账户,然后执行以下命令:[root@host ~]$ ssh-keygen <== 建立密钥对Generating public/private rsa key pair.Enter file in which to save the key (/root/.ssh/id_rsa): <== 按 Enter。如果输入其它字符,比如test,那么生产的私钥是test,公钥是test.pub。说白了就是让你输入密钥文件名,不输入就采用默认的。Created directory '/root/.ssh'.Enter passphrase (empty for no passphrase): <== 输入密钥锁码,后续使用私钥登录的时候会要求输密码,建议输入;或直接按 Enter 留空Enter same passphrase again: <== 再输入一遍密钥锁码Your identification has been saved in /root/.ssh/id_rsa. <== 私钥Your public key has been saved in /root/.ssh/id_rsa.pub. <== 公钥The key fingerprint is:0f:d3:e7:1a:1c:bd:5c:03:f1:19:f1:22:df:9b:cc:08 root@host密钥锁码在使用私钥时必须输入,这样就可以保护私钥不被盗用。当然,也可以留空,实现无密码登录。现在,在 root 用户的家目录中生成了一个 .ssh 的隐藏目录,内含两个密钥文件。id_rsa 为私钥,id_rsa.pub 为公钥。
在服务器上安装公钥
键入以下命令,在服务器上安装公钥:[root@host ~]$ cd .ssh[root@host .ssh]$ cat id_rsa.pub >> authorized_keys如此便完成了公钥的安装。为了确保连接成功,请保证以下文件权限正确:[root@host .ssh]$ chmod 600 authorized_keys[root@host .ssh]$ chmod 700 ~/.ssh
设置 SSH,打开密钥登录功能
编辑 /etc/ssh/sshd_config 文件,进行如下设置:RSAAuthentication yesPubkeyAuthentication yes另外,请留意 root 用户能否通过 SSH 登录:PermitRootLogin yes当你完成全部设置,并以密钥方式登录成功后,再禁用密码登录:PasswordAuthentication no最后,重启 SSH 服务:[root@host .ssh]$ service sshd restart
普通用户制作密钥
如果是用普通用户来制作密钥,需要在普通用户的状态下执行上述命令,生成的密钥会在/home/yskj/.ssh目录下面(这里以yskj用户为例)
如果遇到无法登录,出现
Server refused our key
这样的错误,从两个方面出发。一个是authorized_keys与.ssh的权限,一定要按照前面提到的,一个是600,一个是700。
如果这样不行,那就看看/home目录下面或yskj目录下面的权限,有没有拥有者或组是root的,然后改正过来。将私钥下载到客户端,然后转换为 PuTTY 能使用的格式
使用 WinSCP、SFTP 等工具将私钥文件 id_rsa 下载到客户端机器上。然后打开 PuTTYGen,单击 Actions 中的 Load 按钮,载入你刚才下载到的私钥文件。如果你刚才设置了密钥锁码,这时则需要输入。
载入成功后,PuTTYGen 会显示密钥相关的信息。在 Key comment 中键入对密钥的说明信息,然后单击 Save private key 按钮即可将私钥文件存放为 PuTTY 能使用的格式。
今后,当你使用 PuTTY 登录时,可以在左侧的 Connection -> SSH -> Auth 中的 Private key file for authentication: 处选择你的私钥文件,然后即可登录了,过程中只需输入密钥锁码即可。
ssh客户端—xshell登录linux服务器
将服务器上生成的私钥,id_rsa下载到本地。
ssh-keygen命令详解
这条命令目的是为了本地机器ssh登录远程服务器无需输入密码
1.ssh-keygen
SSH 为 Secure Shell 的缩写,SSH 为建立在应用层基础上的安全协议。SSH
是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。
从客户端来看,SSH提供两种级别的安全验证:
第一种级别(基于口令的安全验证):只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人”这种方式的攻击。
第二种级别(基于密匙的安全验证)ssh-keygen:需要依靠密匙,你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在该服务器上你的主目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不需要在网络上传送口令。第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。但是整个登录的过程可能需要10秒。
ssh-keygen有很多的参数,比如这里的-t -b -C都是他的一些参数2.-t rsa
-t即指定密钥的类型,密钥的类型有两种,一种是RSA,一种是DSA:
RSA:RSA加密算法是一种非对称加密算法,是由三个麻省理工的牛人弄出来的,RSA是他们三个人姓的开头首字母组合。
DSA:Digital Signature Algorithm (DSA)是Schnorr和ElGamal签名算法的变种。
为了让两个linux机器之间使用ssh不需要用户名和密码。所以采用了数字签名RSA或者DSA来完成这个操作。ssh-keygen默认使用rsa密钥,所以不加-t rsa也行,如果你想生成dsa密钥,就需要加参数-t dsa。3.-b 4096
-b 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。命令中的4096指的是RSA密钥长度为4096位。
DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。这里额外补充一个知识
命令后面还可以增加-C “注释内容”
-C表示要提供一个新注释,用于识别这个密钥,可以是任何内容,一个用来识别的key小结:当你创建ssh的时候:-t 表示密钥的类型 ,-b表示密钥的长度,-C 用于识别这个密钥的注释 ,这个注释你可以输入任何内容
Centos 启动/停止/重启/开机自启动服务
systemctl start sshd //启动ssh服务systemctl stop sshd //停止ssh服务 systemctl restart sshd //重启ssh服务systemctl enable sshd //开机自启动ssh服务docker 和其他服务也适用
linux中的&& 和 &,| 和 ||
在linux中,&和&&,|和||介绍如下:
& 表示任务在后台执行,如要在后台运行redis-server,则有 redis-server &
&& 表示前一条命令执行成功时,才执行后一条命令 ,如 echo ‘1‘ && echo ‘2’
| 表示管道,上一条命令的输出,作为下一条命令参数,如 echo ‘yes’ | wc -l
|| 表示上一条命令执行失败后,才执行下一条命令,如 cat nofile || echo “fail”具体案例:
1.rpm -qa | grep mysqlrpm -qa会输出符合筛选条件的软件套件,然后使用grep 筛选与mysql相关的软件套件
rpm命令
Linux rpm 命令用于管理套件。
rpm(英文全拼:redhat package manager) 原本是 Red Hat Linux 发行版专门用来管理 Linux
各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux
易于安装,升级,间接提升了 Linux 的适用度。
因为是redhat的,所以这个命令对ubuntu不适用,一般就是centos用,看看是否安装了或有某个软件的套件实例:
1.安装软件
# rpm -hvi dejagnu-1.4.2-10.noarch.rpm 警告:dejagnu-1.4.2-10.noarch.rpm: V3 DSA 签名:NOKEY, key ID db42a60e准备... ################################################################ [100%]
2.显示软件安装信息
# rpm -qi dejagnu-1.4.2-10.noarch.rpm【第1次更新 教程、类似命令关联】
3.检查是否已经安装过mysql
rpm -qa | grep mysql
4.删除mysql
rpm -e --nodeps mysql-libs-5.1.73-5.el6_6.x86_64 //-e<套件档>或--erase<套件档> 删除指定的套件。//--nodeps 不验证套件档的相互关联性。
更换yum源
- 建议先备份 /etc/yum.repos.d/ 内的文件(CentOS 7 及之前为 CentOS-Base.repo,CentOS 8 为CentOS-Linux-*.repo)
- 然后编辑 /etc/yum.repos.d/ 中的相应文件,在 mirrorlist= 开头行前面加 # 注释掉;并将 baseurl= 开头行取消注释(如果被注释的话),把该行内的域名(例如mirror.centos.org)替换为 mirrors.tuna.tsinghua.edu.cn。
- 以上步骤可以被下方的命令一步完成
sudo sed -e 's|^mirrorlist=|#mirrorlist=|g' \ -e 's|^#baseurl=http://mirror.centos.org|baseurl=https://mirrors.tuna.tsinghua.edu.cn|g' \ -i.bak \ /etc/yum.repos.d/CentOS-*.repo
注意其中的*通配符,如果只需要替换一些文件中的源,请自行增删。
注意,如果需要启用其中一些 repo,需要将其中的 enabled=0 改为 enabled=1。4.最后,更新软件包缓存
]]>sudo yum makecache
- 编程语言 +Linux - @@ -585,29 +591,23 @@Golang +Linux + +运维 + +ssh + +Vim - Linux常用命令 - -/posts/48230/ +Golang + +/posts/56435/ -修改权限 +修改文件用户组
chgrp: change group的简写,修改文件所属的用户组。
chgrp users test.log
如果要修改该目录下所有文件和目录,使用-R参数。
chgrp -R users test
修改文件所有者
chown :change owner的简写, 修改文件的所有者。
chown [-R] 账号名称 文件或目录
将所有者和组名称都修改为root。
chown root:root test.log
修改文件权限
运行sh文件命令
第一种(这种办法需要用chmod使得文件具备执行条件(x): chmod u+x datelog.sh):
/xx/xxx/xxx.sh //任意路径 ./XXX.sh //当前路径
第二种(这种办法不需要文件具备可执行的权限也可运行):
sh xxx.sh
解压缩命令
ZIP
zip [选项] 压缩包名 源文件或源目录-r:压缩目录示例:zip ana.zip anaconda-ks.cfg压缩多个文件:zip test.zip abc abcd
zip对应的解压缩命令为unzip:命令所在目录为/usr/bin/unzip,所有用户可执行unzip [选项] 压缩包名-d:指定解压缩位置-o:不必先询问用户,unzip执行后覆盖原有文件。//其它参数可自行查看unzip -d /tmp/ test.zipunzip -d /tmp -o test.zip //这条命令和上一条相比,可以不用询问直接覆盖
gz
注意:使用gzip压缩文件后会将原文件删除,如果想保留原文件则可以使用-c选项将压缩过程产生的标准输出写入一个新的文件中,示例如下:>的作用是覆盖内容,>>的作用是追加内容
压缩目录下的每个文件:下述命令会将123这个目录下的每个文件分别进行压缩,而不是将整个123目录进行压缩,也就是说gzip命令不会打包压缩解压缩也可以使用gunzip:
bz2
.bz2格式是Linux中的另一种常用压缩格式,该格式的压缩算法更先进,压缩比更高,但是压缩的时间要比.gz长,.bz2格式的压缩命令是bzip2,注意bzip2不能压缩目录,会报错
解压时如果原文件已存在则会报错,因此最好先将原文件删除tar
只是打包并不会压缩文件,.gz,.xz。这些才是压缩
.tar格式的打包和解打包都是使用tar命令,区别只是选项不同
打包示例:
打包多个文件:
解打包:.tar.gz和.tar.bz2
tar 压缩、解压缩都可以使用多线程
.tar.gz格式和.tar.bz2格式:使用tar命令后跟选项的方式实现tar命令和gzip或者bzip2命令的组合,实现同时进行打包和压缩,这也是最经常使用的压缩和解压缩方式
.tar.xz
默认压缩后的文件后缀为 xz,速度慢一些,但是压缩的会更小。
//常用参数-z强制执行压缩, 默认不保留源文件。压缩后的文件名为源文件.xz-d强制执行解压缩-l列出压缩文件的信息-k保留源文件不要删除-f强制覆盖输出文件和压缩链接-c写入到标准输出,输入文件不要删除-0..-9压缩比例,默认为6-e 使用更多的 CPU time 来进行压缩,提高压缩率。不会影响解压时所需要的内存。-T 指定线程数,默认是 1 ,当设置为 0 时使用和机器核心一样多的线程。--format= 指定压缩输出格式,可以是 raw、xz、lzma-v显示更详细的信息
注意点: 压缩后的文件时在和源文件同一个目录。当我们压缩的文件为 /home/nginx/logs/error.log-20191126 ,当我们在任意目录执行完 xz /home/nginx/logs/error.log-20191126 后,压缩后的文件路径是 /home/nginx/logs/error.log-20191126.xz.
//不保留源文件xz /home/nginx/logs/error.log-20191126//保留源文件xz -k /home/nginx/logs/error.log-20191126//解压缩文件xz -d /home/nginx/logs/error.log-20191126.xz//指定多线程数来进行压缩xz -T 4 /home/nginx/logs/error.log-20191126
查询或查看命令汇总
查看某个软件对应的软连接
ls -l "xxxx"
使用
ls -al
也可也,那就是查看整个目录下面的。which 与 whereis
which和whereis命令都是Linux操作系统下查找可执行文件路径的命令
which
这条命令主要是用来查找系统PATH目录下的可执行文件。说白了就是查找那些我们已经安装好的可以直接执行的命令,比如
swq123459@swq123459PC:~$ which ls/bin/ls
注意上述斜体字, which 查找的可执行文件,必须是要在 PATH 下的可执行文件,而不能是没有加入 PATH 的可执行文件,即使他就是可执行文件,但是没有加入到系统搜索路径,他仍然无法被 which 发现(好吧,有点啰嗦了)。whereis
这个命令可以用来查找二进制(命令)、源文件、man文件。与which不同的是这条命令可以是通过文件索引数据库而非PATH来查找的,所以查找的面比which要广。例如:
swq123459@swq123459PC:~$ whereis lsls: /bin/ls /usr/share/man/man1/ls.1.gz
可以看到,whereis不仅找到了 ls 可执行文件的位置,还找到了其 man 帮助文件,可见其搜索范围比较广,不局限于PATH。查看某个进程或者服务是否存在
ps -aux | grep xxx
查找文件及文件夹
find的主要用来查找文件,查找文件的用法我们比较熟悉,也可用它来查找文件夹,用法跟查找文件类似,只要在最后面指明查找的文件类型 -type d,如果不指定type类型,会将包含查找内容的文件和文件夹一起输出。find基本语法如下:find [PATH] [Option] [action]-newer file:file为一个存在的文件,列出比file还要新的文件名find / -mtime 0———0代表当前的时间,即从现在开始到24小时前,有改动过内容的文件都会被列出来find /etc -newer /etc/passwd———寻找/etc下面的文件,如果文件日期比/etc/passwd新就列出find / -name file——/代表全文搜索find /home -user Anmy——查找/home下属于Anmy的文件find / -nouser—— 查找系统中不属于任何人的文件,可以轻易找出那些不太正常的文件find / -name passed—— 查找文件名为passed的文件
若不指定查找类型,使用命令:find / -name AnmyTest 则会将目录和文件一同输出
若指定查找类型,使用命令:find / -name AnmyTest -type d 则只会将目录输出查看文件最后几行
//显示filename最后20行。tail -n 20 filename
查看端口
查看某个服务的状态
service ‘servicename’ status//centos7以上用 systemctl
例子
service sshd status //查看sshd服务的状态,可以看到它的进程号,如果不需要可以kill 杀死
lsof -i:端口号
可以看到 8000 端口已经被轻 nodejs 服务占用。netstat -tunlp | grep 端口号
用于显示 tcp,udp 的端口和进程等相关情况
-t (tcp) 仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化为数字
-l 仅列出在Listen(监听)的服务状态
-p 显示建立相关链接的程序名kill(杀死进程)
telnet(检测端口是否可用)
有时我们想知道端口是否开启。
tenlet ip 端口
上图表示:80端口开放,8899端口未开放。查看系统情况
cpu
lscpu //cpu架构、每个核的线程数都能看到
磁盘
lsblk //命令用来查看接入到系统中的块设备,默认输出分区、大小、挂载点等信息df -h //查看硬盘的使用情况
内存
free -h
系统版本
centos:
cat /etc/redhat-release
删除除了某个文件外的其他文件
shopt -s extglob (打开extglob模式)rm -fr !(file1)# 如果是多个要排除的,可以这样:rm -rf !(file1|file2)
复制/移动文件、文件改名
Linux 将一个文件夹的所有内容拷贝到另外一个文件夹
cp 命令使用 -r 参数可以将 packageA 下的所有文件拷贝到 packageB 中:
cp -r /home/packageA/* /home/cp/packageB/
将一个文件夹复制到另一个文件夹下,以下实例 packageA 文件会拷贝到 packageB 中:
cp -r /home/packageA /home/packageB
运行命令之后 packageB 文件夹下就有 packageA 文件夹了。
cp /xx/xx(a) /xx/xx(a)
:复制 ~~~~ //将a复制到bmv /xx /xx /xx/xx
:剪切mv 旧文件夹名 新文件夹名
//更改名字touch命令(创建文件)
touch
命令用于修改文件或者目录的时间属性,包括存取时间和更改时间,若文件不存在,系统会建立一个新的文件。创建一个空白文件,如果文件已经存在,它将更改文件的访问时间。
touch /tmp/file.txt
创建多个文件。
touch /tmp/file1.txt /tmp/file2.txt /tmp/file3.txt
修改文件的修改时间并查看文件属性。
touch -m /tmp/file.txt && stat /tmp/file.txt
同时修改访问时间和修改时间并设置一个特定的访问与修改时间。
touch -am -t 202007010000.00 /tmp/file.txt && stat /tmp/file.txt
cat命令(显示文件内容)
cat
命令属于文件管理,用于连接文件并打印到标准输出设备上,cat
经常用来显示文件的内容,注意,当文件较大时,文本在屏幕上迅速闪过,会出现滚屏现象,此时往往看不清所显示的内容,为了控制滚屏,可以按Ctrl+S
键停止滚屏,按Ctrl+Q
键可以恢复滚屏,此外可以用more
等命令进行读文件并分页显示。使用
cat
命令创建一个文件,输入文件信息后按Ctrl+D
输出EOF
标识后结束输入。cat > file.txt
输出
file.txt
文件中的内容。cat file.txt
同时输出
file.txt
与file2.txt
文件中的内容。cat file.txt file2.txt
把
file.txt
文件的内容加上行号后追加到file2.txt
文件中。cat -n file.txt >> file2.txt
清空
file2.txt
文件,/dev/null
称为空设备,是一个特殊的设备文件,其会丢弃一切写入其中的数据,但报告写入操作成功,读取它则会立即得到一个EOF
。cat /dev/null > file2.txt
将
file.txt
与file2.txt
文件内容合并输出到file3.txt
。cat file.txt file2.txt > file3.txt
Centos7特性
服务相关命令使用systemctl,之前的版本是service
systemctl (stop/restart/start) (服务)
systemctl restart nginx防火墙
开启端口(以80为例)
firewall-cmd —zone=public —add-port=80/tcp —permanent
//zone add permanent前面是两个横杠重启防火墙
systemctl restart firewalld.service
关闭防火墙
systemctl stop firewalld.service
开机禁用防火墙
systemctl disable firewalld.service
不同的系统命令可能不同
修改主机名
debian/ubuntu系列
第一步:vi /etc/hostname写入HOSTNAME=yourhostname保存后执行以下:hostname yourhostname 查看设置后的hostnamehostname
第二步:vi /etc/hosts修改成新的主机名
redhat/centos系列
vi /etc/sysconfig/network输入以下:HOSTNAME=yourhostname保存后执行以下:hostname yourhostname 查看设置后的hostnamehostname
vim相关操作
复制
1)单行复制
在命令模式下,将光标移动到将要复制的行处,按“yy”进行复制;
2)多行复制 在命令模式下,将光标移动到将要复制的首行处,按“nyy”复制n行;其中n为1、2、3……
【yy】 复制光标所在的那一行
【nyy】 复制光标所在的向下n行粘贴
在命令模式下,将光标移动到将要粘贴的行处,按“p”进行粘贴
【p,P】 p为将已经复制的数据在光标下一行粘贴;P为将已经复制的数据在光标上一行粘贴
删除
删除一行:dd
删除一个单词/光标之后的单词剩余部分:dw
删除当前字符:x
光标之后的该行部分:d$
文本删除
dd 删除一行
d$ 删除以当前字符开始的一行字符
ndd 删除以当前行开始的n行
dw 删除以当前字符开始的一个字
ndw 删除以当前字符开始的n个字
查找
【/word】 在文件中查找内容为word的字符串(向下查找)
【?word】 在文件中查找内容为word的字符串(向上查找)
【[n]】 表示重复查找动作,即查找下一个
【[N]】 反向查找下一个取消高亮
搜索后,我们打开别的文件,发现也被高亮了,怎么关闭高亮?
命令模式下,输入:nohlsearch 也可以:set nohlsearch; 当然,可以简写,noh或者set noh。
PS:nohlsearch是(no highlight search缩写)设置行号
如果编辑后,又想显示行号,同样操作按一下esc键,并输入:(冒号),输入set number ,并按回车键,完成后即显示行号
跳到指定行
在知道所查找的内容在文件中的具体位置时可以使用以下命令直接定位:
跳到文件指定行:比如跳到66行66+G(也就是66+shift+g)
当然你可以选择另一种跳转方式:命令行输入“ : n ” 然后回车
跳到文件第一行:gg (两个小写的G)跳到文件最后一行:shift+g (也就是G)
文件上下翻转
页翻转可以直接使用PgUp和PgDn
向前滚动一屏:Ctrl+F
向后滚动一屏:Ctrl+B
向前滚动半屏:Ctrl+D(向下)
向后滚动半屏:Ctrl+U(向上)
向下滚动一行,保持当前光标不动:Ctrl+E
向上滚动一行,保持当前光标不动:Ctrl+Y
当前行滚动:
当前行移动到屏幕顶部并滚动:Z+Enter
滚动指定行到屏幕顶部: 10Z+Enter(指定第十行)
当前行移动到屏幕中央并滚动:Z + .
当前行移动到屏幕底部并滚动:Z + -
当前屏幕操作:
H:大写h,移动到当前屏幕首行;nH移动到首行下的第n行
M:大写m,移动到当前屏幕中间行
L:大写l,移动到当前屏幕末行;nL移动到末行上面的第n行撤销上一步操作
【u】 撤消上一个操作
【[Ctrl] + r】 多次撤消
【.】 这是小数点键,重复上一个操作缩进:
插入模式下,ctrl+shift+d 减少缩进,ctrl+shift+t 增加缩进
vim编辑
1、进入插入模式(6个命令)
【i】 从目前光标所在处插入
【I】 从目前光标
【a】 从当前光标所在的下一个字符处开始插入
【A】 从光标所在行的最后一个字符处开始插入
【o】 英文小写字母o,在目前光标所在行的下一行处插入新的一行并开始插入
【O】 英文大写字母O,在目前光标所在行的上一行处插入新的一行并开始插入2、进入替换模式(2个命令)
【r】 只会替换光标所在的那一个字符一次
【R】 会一直替换光标所在字符,直到按下[ESC]键为止
【[ESC]】 退出编辑模式回到一般模式3、一般模式切换到命令行模式
【:w】 保存文件
【:w!】 若文件为只读,强制保存文件
【:q】 离开vi
【:q!】 不保存强制离开vi
【:wq】 保存后离开
【:wq!】 强制保存后离开
【:! command】 暂时离开vi到命令行下执行一个命令后的显示结果
【:set nu】 显示行号
【:set nonu】 取消显示行号
【:w newfile】 另存为
【:set fileencoding】 查看当前文件编码格式
【:set fileencoding=utf-8】 设置当前文件编码格式为utf-8,也可以设置成其他编码格式
【:set fileformat】 查看当前文件的断行格式(dos\windows,unix或macintosh)
【:set fileformat=unix】 将当前文件的断行格式设置为unix格式多窗口功能
【:sp [filename]】 打开一个新窗口,显示新文件,若只输入:sp,则两窗口显示同一个文件
【[Ctrl] + w + j】 光标移动到下方窗口
【[Ctrl] + w + k】 光标移动到上方窗口
【[Ctrl] + w + q】 离开当前窗口缩进
批量缩进
在程序代码界面,按esc,退出编辑模式,到命令模式,并在英语输入法下输入“:”
将所要批量缩进的行号写上,按照格式:“行号1,行号2>”输入命令,如要将2至9行批量缩进一个tab值,则命令为“2,9>”
输入完毕后,按回车可以执行,就可以看到2至9行全部缩进了一个tab值了,同样的,如果要缩回来一个tab值,则用命令“行号1,行号2<”即可
可视模式缩进
方法二是在可视模式下选择要移动的列,操作为,esc从编辑模式退到命令模式,将光标移到需要缩进的行的行首,然后按shift+v,可以看到该行已被选中,且左下角提示为“可视”
此时,按键盘上的上下左右方向键,如这里按向下的箭头,选中所有需要批量缩进的行
选择好了之后,按shift+>,是向前缩进一个tab值,按shift+<,则是缩回一个tab值,
vscode Remote SSH
ssh登录命令:
ssh username@ip -p port //密码登录 ,-p port可以不用输入,不输默认是22,因为linux默认也确实是22ssh username@ip -p port –i id_rsa //密钥登录//密钥生成的时候建议命名,不要使用默认的,否则多了容易分不清。
使用Remote SSH 插件访问linux,用密钥登录。需要把公钥放在服务器,私钥放在
.ssh
目录下
再配置VScode
点击Remote SSH的图标后再点击箭头所指的齿轮
会弹出菜单让你选择需要编辑的配置文件,一般选第一个
参数的含义分别为:Host 连接的主机的名称,可自定
Hostname 远程主机的IP地址
User 用于登录远程主机的用户名
Port 用于登录远程主机的端口
IdentityFile 本地的id_rsa的路径
右键点击Connect!
设置 SSH 通过密钥登录
注意事项
- 密钥的制作和用户有关。比如我在服务器上使用xxx制作的密钥,那么登录的话,是让ssh xxx@ip 可以免密登录,其它用户不行的。
- 如果我想免密登录服务器,那么是在服务器上面把密钥制作出来,将私钥传给别人,别人用私钥来访问。(感觉这样做不是很安全,看到的介绍都是把公钥给别人。但如果是公钥给别人,那就只有在本地制作密钥,再把公钥给服务器,但是用户就没法对上,后面有时间再想想吧)
制作密钥对
我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux
服务器。但是,一般的密码方式登录,容易有密码被暴力破解的问题。所以,一般我们会将 SSH 的端口设置为默认的 22 以外的端口,或者禁用
root 账户登录。其实,有一个更好的办法来保证安全,而且让你可以放心地用 root 账户从远程登录——那就是通过密钥方式登录。密钥形式登录的原理是:利用密钥生成器制作一对密钥——一只公钥和一只私钥。将公钥添加到服务器的某个账户上,然后在客户端利用私钥即可完成认证并登录。这样一来,没有私钥,任何人都无法通过
SSH 暴力破解你的密码来远程登录到系统。此外,如果将公钥复制到其他账户甚至主机,利用私钥也可以登录。下面来讲解如何在 Linux 服务器上制作密钥对,将公钥添加给账户,设置 SSH,最后通过客户端登录。
首先在服务器上制作密钥对。首先用密码登录到你打算使用密钥登录的账户,然后执行以下命令:[root@host ~]$ ssh-keygen <== 建立密钥对Generating public/private rsa key pair.Enter file in which to save the key (/root/.ssh/id_rsa): <== 按 Enter。如果输入其它字符,比如test,那么生产的私钥是test,公钥是test.pub。说白了就是让你输入密钥文件名,不输入就采用默认的。Created directory '/root/.ssh'.Enter passphrase (empty for no passphrase): <== 输入密钥锁码,后续使用私钥登录的时候会要求输密码,建议输入;或直接按 Enter 留空Enter same passphrase again: <== 再输入一遍密钥锁码Your identification has been saved in /root/.ssh/id_rsa. <== 私钥Your public key has been saved in /root/.ssh/id_rsa.pub. <== 公钥The key fingerprint is:0f:d3:e7:1a:1c:bd:5c:03:f1:19:f1:22:df:9b:cc:08 root@host密钥锁码在使用私钥时必须输入,这样就可以保护私钥不被盗用。当然,也可以留空,实现无密码登录。现在,在 root 用户的家目录中生成了一个 .ssh 的隐藏目录,内含两个密钥文件。id_rsa 为私钥,id_rsa.pub 为公钥。
在服务器上安装公钥
键入以下命令,在服务器上安装公钥:[root@host ~]$ cd .ssh[root@host .ssh]$ cat id_rsa.pub >> authorized_keys如此便完成了公钥的安装。为了确保连接成功,请保证以下文件权限正确:[root@host .ssh]$ chmod 600 authorized_keys[root@host .ssh]$ chmod 700 ~/.ssh
设置 SSH,打开密钥登录功能
编辑 /etc/ssh/sshd_config 文件,进行如下设置:RSAAuthentication yesPubkeyAuthentication yes另外,请留意 root 用户能否通过 SSH 登录:PermitRootLogin yes当你完成全部设置,并以密钥方式登录成功后,再禁用密码登录:PasswordAuthentication no最后,重启 SSH 服务:[root@host .ssh]$ service sshd restart
普通用户制作密钥
如果是用普通用户来制作密钥,需要在普通用户的状态下执行上述命令,生成的密钥会在/home/yskj/.ssh目录下面(这里以yskj用户为例)
如果遇到无法登录,出现
Server refused our key
这样的错误,从两个方面出发。一个是authorized_keys与.ssh的权限,一定要按照前面提到的,一个是600,一个是700。
如果这样不行,那就看看/home目录下面或yskj目录下面的权限,有没有拥有者或组是root的,然后改正过来。将私钥下载到客户端,然后转换为 PuTTY 能使用的格式
使用 WinSCP、SFTP 等工具将私钥文件 id_rsa 下载到客户端机器上。然后打开 PuTTYGen,单击 Actions 中的 Load 按钮,载入你刚才下载到的私钥文件。如果你刚才设置了密钥锁码,这时则需要输入。
载入成功后,PuTTYGen 会显示密钥相关的信息。在 Key comment 中键入对密钥的说明信息,然后单击 Save private key 按钮即可将私钥文件存放为 PuTTY 能使用的格式。
今后,当你使用 PuTTY 登录时,可以在左侧的 Connection -> SSH -> Auth 中的 Private key file for authentication: 处选择你的私钥文件,然后即可登录了,过程中只需输入密钥锁码即可。
ssh客户端—xshell登录linux服务器
将服务器上生成的私钥,id_rsa下载到本地。
ssh-keygen命令详解
这条命令目的是为了本地机器ssh登录远程服务器无需输入密码
1.ssh-keygen
SSH 为 Secure Shell 的缩写,SSH 为建立在应用层基础上的安全协议。SSH
是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。
从客户端来看,SSH提供两种级别的安全验证:
第一种级别(基于口令的安全验证):只要你知道自己帐号和口令,就可以登录到远程主机。所有传输的数据都会被加密,但是不能保证你正在连接的服务器就是你想连接的服务器。可能会有别的服务器在冒充真正的服务器,也就是受到“中间人”这种方式的攻击。
第二种级别(基于密匙的安全验证)ssh-keygen:需要依靠密匙,你必须为自己创建一对密匙,并把公用密匙放在需要访问的服务器上。如果你要连接到SSH服务器上,客户端软件就会向服务器发出请求,请求用你的密匙进行安全验证。服务器收到请求之后,先在该服务器上你的主目录下寻找你的公用密匙,然后把它和你发送过来的公用密匙进行比较。如果两个密匙一致,服务器就用公用密匙加密“质询”(challenge)并把它发送给客户端软件。客户端软件收到“质询”之后就可以用你的私人密匙解密再把它发送给服务器。用这种方式,你必须知道自己密匙的口令。但是,与第一种级别相比,第二种级别不需要在网络上传送口令。第二种级别不仅加密所有传送的数据,而且“中间人”这种攻击方式也是不可能的(因为他没有你的私人密匙)。但是整个登录的过程可能需要10秒。
ssh-keygen有很多的参数,比如这里的-t -b -C都是他的一些参数2.-t rsa
-t即指定密钥的类型,密钥的类型有两种,一种是RSA,一种是DSA:
RSA:RSA加密算法是一种非对称加密算法,是由三个麻省理工的牛人弄出来的,RSA是他们三个人姓的开头首字母组合。
DSA:Digital Signature Algorithm (DSA)是Schnorr和ElGamal签名算法的变种。
为了让两个linux机器之间使用ssh不需要用户名和密码。所以采用了数字签名RSA或者DSA来完成这个操作。ssh-keygen默认使用rsa密钥,所以不加-t rsa也行,如果你想生成dsa密钥,就需要加参数-t dsa。3.-b 4096
-b 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。命令中的4096指的是RSA密钥长度为4096位。
DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。这里额外补充一个知识
命令后面还可以增加-C “注释内容”
-C表示要提供一个新注释,用于识别这个密钥,可以是任何内容,一个用来识别的key小结:当你创建ssh的时候:-t 表示密钥的类型 ,-b表示密钥的长度,-C 用于识别这个密钥的注释 ,这个注释你可以输入任何内容
Centos 启动/停止/重启/开机自启动服务
systemctl start sshd //启动ssh服务systemctl stop sshd //停止ssh服务 systemctl restart sshd //重启ssh服务systemctl enable sshd //开机自启动ssh服务docker 和其他服务也适用
文件内容覆盖/追加内容(cat命令)
cat textfile1 > textfile2 //使用“>” 重定向后 文件 中原本的内容会被覆盖cat textfile1 >> textfile2 //">>" 代表 将输出的内容已追加的方式重定向到文件
cat 原单词concatenate(用途是连接文件或标准输入并打印。)
cat 命令用于将所有文件内容打印到屏幕上。
语法:cat 文件
linux中的&& 和 &,| 和 ||
在linux中,&和&&,|和||介绍如下:
& 表示任务在后台执行,如要在后台运行redis-server,则有 redis-server &
&& 表示前一条命令执行成功时,才执行后一条命令 ,如 echo ‘1‘ && echo ‘2’
| 表示管道,上一条命令的输出,作为下一条命令参数,如 echo ‘yes’ | wc -l
|| 表示上一条命令执行失败后,才执行下一条命令,如 cat nofile || echo “fail”具体案例:
1.rpm -qa | grep mysqlrpm -qa会输出符合筛选条件的软件套件,然后使用grep 筛选与mysql相关的软件套件
rpm命令
Linux rpm 命令用于管理套件。
rpm(英文全拼:redhat package manager) 原本是 Red Hat Linux 发行版专门用来管理 Linux
各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux
易于安装,升级,间接提升了 Linux 的适用度。
因为是redhat的,所以这个命令对ubuntu不适用,一般就是centos用,看看是否安装了或有某个软件的套件实例:
1.安装软件
# rpm -hvi dejagnu-1.4.2-10.noarch.rpm 警告:dejagnu-1.4.2-10.noarch.rpm: V3 DSA 签名:NOKEY, key ID db42a60e准备... ################################################################ [100%]
2.显示软件安装信息
# rpm -qi dejagnu-1.4.2-10.noarch.rpm【第1次更新 教程、类似命令关联】
3.检查是否已经安装过mysql
rpm -qa | grep mysql
4.删除mysql
rpm -e --nodeps mysql-libs-5.1.73-5.el6_6.x86_64 //-e<套件档>或--erase<套件档> 删除指定的套件。//--nodeps 不验证套件档的相互关联性。
更换yum源
- 建议先备份 /etc/yum.repos.d/ 内的文件(CentOS 7 及之前为 CentOS-Base.repo,CentOS 8 为CentOS-Linux-*.repo)
- 然后编辑 /etc/yum.repos.d/ 中的相应文件,在 mirrorlist= 开头行前面加 # 注释掉;并将 baseurl= 开头行取消注释(如果被注释的话),把该行内的域名(例如mirror.centos.org)替换为 mirrors.tuna.tsinghua.edu.cn。
- 以上步骤可以被下方的命令一步完成
sudo sed -e 's|^mirrorlist=|#mirrorlist=|g' \ -e 's|^#baseurl=http://mirror.centos.org|baseurl=https://mirrors.tuna.tsinghua.edu.cn|g' \ -i.bak \ /etc/yum.repos.d/CentOS-*.repo
注意其中的*通配符,如果只需要替换一些文件中的源,请自行增删。
注意,如果需要启用其中一些 repo,需要将其中的 enabled=0 改为 enabled=1。4.最后,更新软件包缓存
]]>sudo yum makecache
查看环境变量 go version //查看版本go env //查看环境变量
安装golang、gopath、goroot
安装
windows安装一路点击ok就好。
Linux安装:
- wget https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz
- tar -zxvf go1.14.1.linux-amd64.tar.gz -C /usr/local # 解压
配置环境变量: Linux下有两个文件可以配置环境变量,其中/etc/profile是对所有用户生效的;$HOME/.profile是对当前用户生效的,根据自己的情况自行选择一个文件打开,添加如下两行代码,保存退出。
export GOROOT=/usr/local/goexport PATH=$PATH:$GOROOT/bin
修改/etc/profile后要重启生效,修改$HOME/.profile后使用source命令加载$HOME/.profile文件即可生效。 检查:
go version
gopath、goroot
goroot:
其实就是golang 的安装路径
当你安装好golang之后其实这个就已经有了gopath:
作用:存放sdk以外的第三方类库
自己收藏的可复用的代码
目录结构:$GOPATH目录约定有三个子目录
src存放源代码(比如:.go .c .h .s等) 按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。pkg编译时生成的中间文件(比如:.a) golang编译包时
bin编译后生成的可执行文件(为了方便,可以把此目录加入到 P A T H 变 量 中 , 如 果 有 多 个 g o p a t h , 那 么 使 用 PATH 变量中,如果有多个gopath,那么使用PATH变量中,如果有多个gopath,那么使用{GOPATH/bin:}/bin添加所有的bin目录)
package
包的定义
package 包名
注意事项:
- 一个文件夹下直接包含的文件只能归属于一个package,同样一个package的文件不能在多个文件夹下。
- 包名可以不和文件夹的名字一样。
- 包名为
main
的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不含main包的源代码则不会得到可执行文件包的使用
- 有时候我们单独写一个go文件,测试或验证某个功能,包名都写main就好
- 如果要引入自定义的包。如果import失败,看一下保存的信息。一般会提示GOROOT GOPATH找不到这个包,这个时候把这个文件夹放到上面的目录里面就可以导入了。
举例:
我目前的GOROOT是在这个目录下面,把文件夹放进去,里面是go文件,就可以引入了。go mod配置
go mod 主要用于管理第三方包,可以自动进行下载。要使用go mod,需要一些配置。
需要配置GO111MODULE 、GOPROXYgo mod使用
首先:
go mod init "modname"//modname一般是文件所处文件夹的名字//go mod init dynamic
如果要某文件要引入一些包,在import处写入,然后执行下面命令:
go mod tidy//或者go get -u
导入第三方包
背景:和公司合作开发,但是没有他们内部的开发环境,只能让他们把包单独抽出来
核心就是这里的replace,不用修改goproxy或者go111module等环境变量
语法
Context
Context主要做两件事情:
- 通知子协程提前退出
- 携带环境变量
channel
Goroutine(协程) 使用 Channel 传递数据
定义
// 只读 channelvar readOnlyChan <-chan int // channel 的类型为 int// 只写 channelvar writeOnlyChan chan<- int// 可读可写var ch chan int// 或者使用 make 直接初始化readOnlyChan1 := make(<-chan int, 2) // 只读且带缓存区的 channelreadOnlyChan2 := make(<-chan int) // 只读且不带缓存区 channelwriteOnlyChan3 := make(chan<- int, 4) // 只写且带缓存区 channelwriteOnlyChan4 := make(chan<- int) // 只写且不带缓存区 channelch := make(chan int, 10) // 可读可写且带缓存区ch <- 20 // 写数据i := <-ch // 读数据i, ok := <-ch // 还可以判断读取的数据
操作channel
操作 channel 一般有如下三种方式:
读 <-ch
写 ch<-
关闭 close(ch)
go func(){}()
go func() {.....}()
以并发的方式调用匿名函数func
详细解释:
func(name string) { fmt.Println("Your name is", name) } (str) //这里的(str)是?
其实,这就是在调用这个函数,这种写法等同于:
f := func(name string) { fmt.Println("Your name is", name) } f(str) //看吧,就是把函数复制给变量,变量(函数)传参
以下两段代码执行结果等同:
代码一:package mainimport ( "fmt")func main() { str := "xulinlin" func(name string) { fmt.Println("Your name is", name) }(str)}代码二:package mainimport ( "fmt")func main() { f := func(name string) { fmt.Println("Your name is", name) } f(str)}输出都是:Your name is xulinlin
defer
defer后面的函数在defer语句所在的函数执行结束的时候会被调用,用来做一些收尾工作
原文链接)
Println 与 Printf 的区别
- Println :可以打印出字符串,和变量
- Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量,不可以输出整形变量和整形
也就是说,当需要格式化输出信息时一般选择 Printf,其他时候用 Println 就可以了,比如:
a := 10fmt.Println(a) //rightfmt.Println("abc") //rightfmt.Printf("%d",a) //rightfmt.Printf(a) //error
printf格式化输出
通用输出
%v #仅输出该变量的值%+v #输出 该变量的值,如果是数组k/v 则将k/v都输出%#v #先输出结构体名字值,再输出结构体(字段名字+字段的值)%T #输出结构体名称%% #百分号
package main import ("fmt") type student struct {id intname string}func main() { ss := student{id: 1,name: "test"}fmt.Printf("%v \n",ss) //%v 当碰到数组时,仅输出value,不输出keyfmt.Printf("%+v \n",ss) //%+v 当碰到数组时,将key-value 都输出fmt.Printf("%#v \n",ss) //%#v 输出时,会将方法名 +k/v都输出fmt.Printf("%T \n",ss) //%T 输出结构体名称()fmt.Printf("%% \n") //%% 没有意义,只是输出一个%}
整数类型
%b 二进制表示 %c 相应Unicode码点所表示的字符 %d 十进制表示 %o 八进制表示 %q 单引号围绕的字符字面值,由Go语法安全地转义 %x 十六进制表示,字母形式为小写 a-f %X 十六进制表示,字母形式为大写 A-F %U Unicode格式:123,等同于 "U+007B"
package main import ( "fmt") func main() { ss := 68 fmt.Printf("%b \n",ss) //二进制表示 fmt.Printf("%c \n",ss) //Unicode码表示的字符 fmt.Printf("%d \n",ss) //十进制表示 fmt.Printf("%o \n",ss) //八进制 fmt.Printf("%q \n",ss) //输出字符,单引号包裹 fmt.Printf("%x \n",ss) //十六进制输出 小写 fmt.Printf("%X \n",ss) //十六进制输出 大写 fmt.Printf("%U \n",ss) //Unicode格式}
- 浮点数
%b无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat%e科学计数法,如-1234.456e+78%E科学计数法,如-1234.456E+78%f有小数部分但无指数部分,如123.456%F等价于%f%g根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)%G根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
package main import ( "fmt") func main() { fmt.Printf("%b \n",68.10) //二进制输出 fmt.Printf("%e \n",68.10) //科学计数法 e fmt.Printf("%E \n",68.10) //科学计数法 E fmt.Printf("%f \n",68.10) fmt.Printf("%g \n",68.10) fmt.Printf("%G \n",68.10) }
//结果
4792111478498918p-46
6.810000e+01
6.810000E+01
68.100000
68.1
68.1
- 布尔
%t true 或 false
- 字符串
%s 字符串或切片的无解译字节 %q 双引号围绕的字符串,由Go语法安全地转义 %x 十六进制,小写字母,每字节两个字符 %X 十六进制,大写字母,每字节两个字符
package main import ("fmt") func main() {fmt.Printf("%s","I'm a girl")fmt.Println()fmt.Printf("%q","I'm a girl")fmt.Println()fmt.Printf("%x","I'm a girl")fmt.Println()fmt.Printf("%X","I'm a girl")fmt.Println()}
//结果I'm a girl"I'm a girl"49276d2061206769726c49276D2061206769726C
占位符
原文链接
输出为:{Aric 21 3-1}{Name:Aric Age:21 Class:3-1}main.Student{Name:"Aric", Age:21, Class:"3-1"}main.Student676f6c616e67676F6C616E670xc000062150
下划线 “_”
“_”是特殊标识符,用来忽略结果。
1.下划线在import中
在Golang里,import的作用是导入其他package。
import 下划线(如:import hello/imp)的作用:当导入一个包时,该包下的文件里所有init()函数都会被执行,然而,有些时候我们并不需要把整个包都导入进来,仅仅是是希望它执行init()函数而已。这个时候就可以使用 import 引用该包。即使用【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数。
示例:src |+--- main.go |+--- hello | +--- hello.go
main.go
package mainimport _ "./hello"func main() { // hello.Print() //编译报错:./main.go:6:5: undefined: hello}
hello.go
package helloimport "fmt"func init() { fmt.Println("imp-init() come here.")}func Print() { fmt.Println("Hello!")}
输出结果:
imp-init() come here.
2.下划线在代码中
package mainimport ( "os")func main() { buf := make([]byte, 1024) f, _ := os.Open("/Users/***/Desktop/text.txt") defer f.Close() for { n, _ := f.Read(buf) if n == 0 { break } os.Stdout.Write(buf[:n]) }}
解释1:
下划线意思是忽略这个变量.比如os.Open,返回值为*os.File,error普通写法是f,err := os.Open("xxxxxxx")如果此时不需要知道返回的错误值就可以用f, _ := os.Open("xxxxxx")如此则忽略了error变量
解释2:
占位符,意思是那个位置本应赋给某个值,但是咱们不需要这个值。所以就把该值赋给下划线,意思是丢掉不要。这样编译器可以更好的优化,任何类型的单个值都可以丢给下划线。这种情况是占位用的,方法返回两个结果,而你只想要一个结果。那另一个就用 "_" 占位,而如果用变量的话,不使用,编译器是会报错的。
补充:
import "database/sql"import _ "github.com/go-sql-driver/mysql"
第二个import就是不直接使用mysql包,只是执行一下这个包的init函数,把mysql的驱动注册到sql包里,然后程序里就可以使用sql包来访问mysql数据库了。
= 和 :=的区别
// = 使用必须使用先var声明例如:var aa=100//或var b = 100//或var c int = 100 // := 是声明并赋值,并且系统自动推断类型,不需要var关键字d := 100
String()方法
对于定于了String()方法的类型,默认输出的时候会调用该方法,实现字符串的打印。例如下面代码:
package main import "fmt" type Man struct { name string} func (m Man) String() string { return "My name is :" + m.name} func main() { var m Man m.name = "SNS" fmt.Println(m)} 输出:My name is :SNS
使用指针
然而,如果使用func (m *Man) String() string方式定义函数,那么就不会自动调用该函数输出(go version go1.12.1 windows/amd64)。package main import "fmt" type Man struct { name string} func (m *Man) String() string { return "My name is :" + m.name} func main() { var m Man m.name = "SNS" fmt.Println(m)} 输出:{SNS}
String方法是接口方法,存在于fmt包里print.go文件下的Stringer接口。go使用fmt包的输出方法会自动调用String()方法。当重写的String是指针方法时,只有指针类型调用的时候才会正常调用,值类型调用的时候实际上没有执行重写的String方法;当重写的String方法是值方法时,无论指针类型和值类型均可调用重写的String方法。其实跟接口的实现有关,当值类型实现接口的时候,相当于值类型和该值的指针类型均实现了该接口;相反,当指针类型实现了该接口的时候,只有指针类型实现了接口,值类型是没有实现的。
最后一句改成
fmt.Println(&m)
因为你只为*Man
这个Man的指针类型重新定义了String()方法,所以只有在输出*Man
类型的数据时才会调用自定义的String()方法。
而你定义的m是Man类型的,所以才不会调用你定义的String方法。
所要么向楼上那位一样定义Man类型。要么就是在输出时,向Print函数传递Man类型的数据(改成fmt.Println(&m)
)方法接受者
原文链接
在go语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法就是定义了接受者的函数。接受者定义在func关键字和函数名之间:type Person struct { name string age int}func (p Person) say() { fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}
有了对方法及接受者的简单认识之后,接下来主要谈一下接受者的类型问题。
接受者类型可以是struct,也可以是指向struc的指针。
情况一:接受者是structpackage mainimport "fmt"type Person struct {name stringage int}func (p Person) say() {fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}func (p Person) older(){ p.age = p.age +1}func main() { var p1 Person = Person{"zhansan",16} p1.older() p1.say() //output: I'm zhangsan,16 years old var p2 *Person = &Person{"lisi",17} p2.older() p2.say() //output: I'm lisi,17 years old}
对于p1的调用,读者应该不会有什么疑问。
对于p2的调用可能存在这样的疑问,p2明明是个指针,为什么再调用了older方法之后,打印结果还是17 years old?
方法的接受者是Person而调用者是Person ,其实在p2调用时存在一个转换p2.older() -> p2.older(); p2.say() -> p2.say()p2是什么想必读者也是明白的(就一个p2指向Person实例)。那么疑问也就自然的解开了,方法执行时的接受者实际上还是一个值而非引用。情况二:接受者是指针
package mainimport "fmt"type Person struct {name stringage int}func (p *Person) say() {fmt.Printf("I'm %s,%d years old\n",p.name,p.age)}func (p *Person) older(){ p.age = p.age +1}func main() { var p1 Person = Person{"zhansan",16} p1.older() p1.say() //output: I'm zhangsan,17 years old var p2 *Person = &Person{"lisi",17} p2.older() p2.say() //output: I'm lisi,18 years old}
p1的调用中也存在一个转换,
p1.older -> p1.older
p1.say() -> p1.say()用例:
package mainimport ( "fmt")//面向对象//go仅支持封装,不支持继承和多态//go语言中没有class,只要struct//不论地址还是结构本身,一律使用.来访问成员//要改变内容必须使用指针接收者//结构过大考虑指针接收者//值接收者是go语言特有//封装//名字一般使用CamelCase//首字母大写: public//首字母小写:private//包//每个目录一个包,包名可以与目录不一样//main包包含可执行入口,只有一个main包//为结构定义的方法必须放在同一个包内,但是可以是不同文件type treeNode struct { value int left, right *treeNode}func (node treeNode) print() { //显示定义和命名方法接收者(括号里) fmt.Print(node.value) //只有使用指针才可以改变结构内容 fmt.Println()}func (node *treeNode) setValue ( value int) { //使用指针作为方法接收者 if node == nil { fmt.Println("setting value to nil node") //nil指针也可以调用方法 return } node.value = value}func (node *treeNode ) traverse(){ if node == nil{ return } node.left.traverse() node.print() node.right.traverse()}func main() { var root treeNode fmt.Println(root) //{0 <nil> <nil>} root = treeNode{value:3} root.left = &treeNode{} root.right = &treeNode{5,nil,nil} root.right.left = new(treeNode) nodes := []treeNode { {value: 3}, {}, {6,nil,&root}, } fmt.Println(nodes) //[{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc04205a3e0}] root.print() // 3 fmt.Println() root.right.left.setValue(100) root.right.left.print() //100 fmt.Println() var pRoot *treeNode pRoot.setValue(200) //setting value to nil node pRoot = &root pRoot.setValue(300) pRoot.print() //300 root.traverse() //300 0 300 100 5}
结构体定义中的 json 单引号(``)
package mainimport ( "encoding/json" "fmt")//在处理json格式字符串的时候,经常会看到声明struct结构的时候,属性的右侧还有小米点括起来的内容。`TAB键左上角的按键,~线同一个键盘`type Student struct { StudentId string `json:"sid"` StudentName string `json:"sname"` StudentClass string `json:"class"` StudentTeacher string `json:"teacher"`}type StudentNoJson struct { StudentId string StudentName string StudentClass string StudentTeacher string}//可以选择的控制字段有三种:// -:不要解析这个字段// omitempty:当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,slice,string// FieldName:当解析 json 的时候,使用这个名字type StudentWithOption struct { StudentId string //默认使用原定义中的值 StudentName string `json:"sname"` // 解析(encode/decode) 的时候,使用 `sname`,而不是 `Field` StudentClass string `json:"class,omitempty"` // 解析的时候使用 `class`,如果struct 中这个值为空,就忽略它 StudentTeacher string `json:"-"` // 解析的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的}func main() { //NO.1 with json struct tag s := &Student{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} jsonString, _ := json.Marshal(s) fmt.Println(string(jsonString)) //{"sid":"1","sname":"fengxm","class":"0903","teacher":"feng"} newStudent := new(Student) json.Unmarshal(jsonString, newStudent) fmt.Println(newStudent) //&{1 fengxm 0903 feng} //Unmarshal 是怎么找到结构体中对应的值呢?比如给定一个 JSON key Filed,它是这样查找的: // 首先查找 tag 名字(关于 JSON tag 的解释参看下一节)为 Field 的字段 // 然后查找名字为 Field 的字段 // 最后再找名字为 FiElD 等大小写不敏感的匹配字段。 // 如果都没有找到,就直接忽略这个 key,也不会报错。这对于要从众多数据中只选择部分来使用非常方便。 //NO.2 without json struct tag so := &StudentNoJson{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} jsonStringO, _ := json.Marshal(so) fmt.Println(string(jsonStringO)) //{"StudentId":"1","StudentName":"fengxm","StudentClass":"0903","StudentTeacher":"feng"} //NO.3 StudentWithOption studentWO := new(StudentWithOption) js, _ := json.Marshal(studentWO) fmt.Println(string(js)) //{"StudentId":"","sname":""} studentWO2 := &StudentWithOption{StudentId: "1", StudentName: "fengxm", StudentClass: "0903", StudentTeacher: "feng"} js2, _ := json.Marshal(studentWO2) fmt.Println(string(js2)) //{"StudentId":"1","sname":"fengxm","class":"0903"}}
基本数据类型操作
数组
map相关操作
创建map
//初始化空mapprevNums := map[int]int{}//初始化countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}//value接收任意数据类型,用interfaceresMap := make(map[string]interface{})
查看元素在集合中是否存在
capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if (ok) { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") }
判断key是否存在
package main import "fmt" func main() { demo := map[string]bool{ "a": false, } //错误,a存在,但是返回false fmt.Println(demo["a"]) //正确判断方法 _, ok := demo["a"] fmt.Println(ok)}
if _, ok := map[key]; ok { // 存在}if _, ok := map[key]; !ok { // 不存在}
快速删除所有元素
直接重新生成map,名字相同
Map 实现去重与 set 的功能
package main var set = map[string]bool { } func main() { ... url := xxx if set[url] { // 表示集合中已经存在 return } set[url] = true // 否则如果不存在,设置为true} // 完成后,set的所有的key值为不重复的值
map转json
// map to jsonpackage mainimport ( "encoding/json" "fmt")func main() { s := []map[string]interface{}{} m1 := map[string]interface{}{"name": "John", "age": 10} m2 := map[string]interface{}{"name": "Alex", "age": 12} s = append(s, m1, m2) s = append(s, m2) b, err := json.Marshal(s) if err != nil { fmt.Println("json.Marshal failed:", err) return } fmt.Println("b:", string(b))}
切片
切片是动态数组,可以搭配结构体或map形成多重嵌套
var projects = make([]models.Project, 0)
上面的models.Project
是一个结构体,前面加一个[]就变成了接片,用make生成,指定初始长度为0(必须要指定一个值,反正自动增加)。
这样这个projects变量是切片,里面的数据类型是models.Project结构体切片去重
/* 在slice中去除重复的元素,其中a必须是已经排序的序列。 * params: * a: slice对象,如[]string, []int, []float64, ... * return: * []interface{}: 已经去除重复元素的新的slice对象 */func SliceRemoveDuplicate(a interface{}) (ret []interface{}) {if reflect.TypeOf(a).Kind() != reflect.Slice {fmt.Printf("<SliceRemoveDuplicate> <a> is not slice but %T\n", a)return ret} va := reflect.ValueOf(a)for i := 0; i < va.Len(); i++ {if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {continue}ret = append(ret, va.Index(i).Interface())} return ret}
测试代码:
func test_SliceRemoveDuplicate() {slice_string := []string{"a", "b", "c", "d", "a", "b", "c", "d"}slice_int := []int{1, 2, 3, 4, 5, 1, 2, 3, 4, 5}slice_float := []float64{1.11, 2.22, 3.33, 4.44, 1.11, 2.22, 3.33, 4.44} sort.Strings(slice_string)sort.Ints(slice_int)sort.Float64s(slice_float) fmt.Printf("slice_string = %v, %p\n", slice_string, slice_string)fmt.Printf("slice_int = %v, %p\n", slice_int, slice_int)fmt.Printf("slice_float = %v, %p\n", slice_float, slice_float) ret_slice_string := SliceRemoveDuplicate(slice_string)ret_slice_int := SliceRemoveDuplicate(slice_int)ret_slice_float := SliceRemoveDuplicate(slice_float) fmt.Printf("ret_slice_string = %v, %p\n", ret_slice_string, ret_slice_string)fmt.Printf("ret_slice_int = %v, %p\n", ret_slice_int, ret_slice_int)fmt.Printf("ret_slice_float = %v, %p\n", ret_slice_float, ret_slice_float) fmt.Printf("<after> slice_string = %v, %p\n", slice_string, slice_string)fmt.Printf("<after> slice_int = %v, %p\n", slice_int, slice_int)fmt.Printf("<after> slice_float = %v, %p\n", slice_float, slice_float)}
结果:
slice_string = [a a b b c c d d], 0xc042088000slice_int = [1 1 2 2 3 3 4 4 5 5], 0xc04200e1e0slice_float = [1.11 1.11 2.22 2.22 3.33 3.33 4.44 4.44], 0xc042014200 ret_slice_string = [a b c d], 0xc042034100ret_slice_int = [1 2 3 4 5], 0xc042088080ret_slice_float = [1.11 2.22 3.33 4.44], 0xc042034180 <after> slice_string = [a a b b c c d d], 0xc042088000<after> slice_int = [1 1 2 2 3 3 4 4 5 5], 0xc04200e1e0<after> slice_float = [1.11 1.11 2.22 2.22 3.33 3.33 4.44 4.44], 0xc042014200
插入
/* * 在Slice指定位置插入元素。 * params: * s: slice对象,类型为[]interface{} * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * 已经插入元素的slice,类型为[]interface{} */func SliceInsert(s []interface{}, index int, value interface{}) []interface{} {rear := append([]interface{}{}, s[index:]...)return append(append(s[:index], value), rear...)} /* * 在Slice指定位置插入元素。 * params: * s: slice对象指针,类型为*[]interface{} * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * 无 */func SliceInsert2(s *[]interface{}, index int, value interface{}) {rear := append([]interface{}{}, (*s)[index:]...)*s = append(append((*s)[:index], value), rear...)} /* * 在Slice指定位置插入元素。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * index: 要插入元素的位置索引 * value: 要插入的元素 * return: * true: 插入成功 * false: 插入失败(不支持的数据类型) */func SliceInsert3(s interface{}, index int, value interface{}) bool {if ps, ok := s.(*[]string); ok {if val, ok := value.(string); ok {rear := append([]string{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)return true}} else if ps, ok := s.(*[]int); ok {if val, ok := value.(int); ok {rear := append([]int{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)}} else if ps, ok := s.(*[]float64); ok {if val, ok := value.(float64); ok {rear := append([]float64{}, (*ps)[index:]...)*ps = append(append((*ps)[:index], val), rear...)}} else {fmt.Printf("<SliceInsert3> Unsupported type: %T\n", s)} return false}
说明:
- SliceInsert()方法是传入一个[]interface{}类型的slice对象,返回的也是一个[]interface{}类型的slice对象。
- SliceInsert2()方法是传入一个[]interface{}类型的slice对象指针,直接修改这个slice对象。
- SliceInsert3()方法是传入一个具体类型的slice对象指针(如[]string, []int等),方法中直接修改这个slice对象,返回操作是否成功的状态(bool)。
删除
/* * 删除Slice中的元素。 * params: * s: slice对象,类型为[]interface{} * index: 要删除元素的索引 * return: * 已经删除指定元素的slice,类型为[]interface{} * 说明:返回的序列与传入的序列地址不发生变化(但是传入的序列内容已经被修改,不能再使用) */func SliceRemove(s []interface{}, index int) []interface{} {return append(s[:index], s[index+1:]...)} /* * 删除Slice中的元素。 * params: * s: slice对象指针,类型为*[]interface{} * index: 要删除元素的索引 * return: * 无 * 说明:直接操作传入的Slice对象,传入的序列地址不变,但内容已经被修改 */func SliceRemove2(s *[]interface{}, index int) {*s = append((*s)[:index], (*s)[index+1:]...)} /* * 删除Slice中的元素。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * index: 要删除元素的索引 * return: * true: 删除成功 * false: 删除失败(不支持的数据类型) * 说明:直接操作传入的Slice对象,不需要转换为[]interface{}类型。 */func SliceRemove3(s interface{}, index int) bool {if ps, ok := s.(*[]string); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else if ps, ok := s.(*[]int); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else if ps, ok := s.(*[]float64); ok {*ps = append((*ps)[:index], (*ps)[index+1:]...)} else {fmt.Printf("<SliceRemove3> Unsupported type: %T\n", s)return false} return true}
清空
/* * 清空Slice,传入的slice对象地址发生变化。 * params: * s: slice对象指针,类型为*[]interface{} * return: * 无 */func SliceClear(s *[]interface{}) {*s = append([]interface{}{})} /* * 清空Slice,传入的slice对象地址不变。 * params: * s: slice对象指针,类型为*[]interface{} * return: * 无 */func SliceClear2(s *[]interface{}) {*s = (*s)[0:0]} /* * 清空Slice,传入的slice对象地址不变。 * params: * s: slice对象的指针,如*[]string, *[]int, ... * return: * true: 清空成功 * false: 清空失败(不支持的数据类型) */func SliceClear3(s interface{}) bool {if ps, ok := s.(*[]string); ok {*ps = (*ps)[0:0]//*ps = append([]string{})} else if ps, ok := s.(*[]int); ok {*ps = (*ps)[0:0]//*ps = append([]int{})} else if ps, ok := s.(*[]float64); ok {*ps = (*ps)[0:0]//*ps = append([]float64{})} else {fmt.Printf("<SliceClear3> Unsupported type: %T\n", s)return false} return true}
常用操作
查看程序运行时间
import "time" // 引用time库函数start := time.Now() // 获取当前时间// 被测代码cost := time.Since(start) // 计算此时与start的时间差// time.Now().Sub(start)也可
查看当前go版本号
go version
如果要升级版本,直接去官网下载对应的版本就好。但是会直接卸载之前的版本。环境变量一般会自动处理,关闭窗口(vscode,git bash之类的),再打开就能看到已经好了。
单引号、双引号、反引号
Golang限定字符或者字符串一共三种引号,单引号(’’),双引号(“”) 以及反引号(``)。反引号就是标准键盘“Esc”按钮下面的那个键。
单引号,表示byte类型或rune类型,对应 uint8和int32类型,默认是 rune 类型。byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。
双引号,才是字符串,实际上是字符数组。可以用索引号访问某字节,也可以用len()函数来获取字符串所占的字节长度。
反引号,表示字符串字面量,但不支持任何转义序列。字面量 raw literal string 的意思是,你定义时写的啥样,它就啥样,你有换行,它就换行。你写转义字符,它也就展示转义字符。
反引号有时候能起到很好的作用,比如一个字符串里面有双引号,分号这种,并且分布的还不规律,用反引号括起来就好
字符串中包含双引号
反引号
str2 := `"www.hewe.vip"`fmt.Println(len(str2))fmt.Println(str2)//输出结果14"www.hewe.vip"
转义
str3 := "\"www.hewe.vip\""fmt.Println(len(str3))fmt.Println(str3)//输出结果14"www.hewe.vip"
使用strconv包
str := strconv.Quote("www.hewe.vip")fmt.Println(len(str))fmt.Println(str1)//输出结果14"www.hewe.vip"
变量类型转换
string转成int:int, err := strconv.Atoi(string)string转成int64:int64, err := strconv.ParseInt(string, 10, 64)int转成string:string := strconv.Itoa(int)int64转成string:string := strconv.FormatInt(int64,10)
具体例子:
package mainimport ("fmt""strconv")func main() {cfdversion :="100"newcfd,_ :=strconv.Atoi(cfdversion)fmt.Println(newcfd)}
注意事项:
1.要导入包
2.转换变量类型后要重新用一个名字,不能用之前的变量名
3.下划线那个地方是err,被省略了判断变量类型
方法一:
package mainimport ( "fmt")func main() { v1 := "123456" v2 := 12 fmt.Printf("v1 type:%T\n", v1) fmt.Printf("v2 type:%T\n", v2)}
方法二:
package mainimport ( "fmt" "reflect")func main() { v1 := "123456" v2 := 12 // reflect fmt.Println("v1 type:", reflect.TypeOf(v1)) fmt.Println("v2 type:", reflect.TypeOf(v2))}
判断类型是否为map
if reflect.ValueOf(map1).Kind() == reflect.Map { } else { }
json相关操作
发送json格式的http请求
发送json为参数的post请求,以结构体为载体
type RequestBody struct { Id int `json:"id"` Name string `json:"name"` } func testPost(id int, name string) { request := RequestBody{ Id: id, Name: name, } requestBody := new(bytes.Buffer) json.NewEncoder(requestBody).Encode(request) url := "https://test.com" req, err := http.NewRequest("POST", url, requestBody) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } defer resp.Body.Close() fmt.Println("response Status:", resp.Status) fmt.Println("response Headers:", resp.Header) body, _ := ioutil.ReadAll(resp.Body) fmt.Println("response Body:", string(body)) }
解析json文件
package mainimport (// "bytes""encoding/json""fmt""io/ioutil"// "reflect")func con_var_name (key string) string {var res stringbytes,_:=ioutil.ReadFile("C:/Users/76585/Desktop/para_compare_ver2.json")m :=make(map[string]interface{})err := json.Unmarshal([]byte(bytes),&m)if err != nil {fmt.Println("err=",err)return ""}for _, value :=range m{// fmt.Println(reflect.TypeOf(value.([]interface{})[1].(map[string]interface{})["name"]))if key==value.([]interface{})[0].(map[string]interface{})["name"]{res=keyreturn res}if key==value.([]interface{})[1].(map[string]interface{})["name"]{tmp :=value.([]interface{})[0].(map[string]interface{})["name"]res=tmp.(string)return res}}res=keyreturn res}
解析json文件,interface转int
因为json解析得到的数据是map[string]interface,里面的字段可能是数字,有时候需要取出来比较,
就需要将interface转为int。
需要先转为string,再用strconv.Atoi
,将string转为int。结构体解析为json
先上代码
最为关键的是结构体里面的成员变量名,首字母必须是大写,否则无法解析,解析出来的是空。
package mainimport ("encoding/json""fmt")type Product struct {Name stringProductId int64Number intPrice float64IsOnSale bool}func main() {var p Product// p := Product{}p.Name="apple"p.ProductId=1p.Number=100p.Price=3.45p.IsOnSale=falsedata, _ := json.Marshal(&p)fmt.Println(string(data))}
将json字符串解码到相应的数据结构
type StuRead struct { Name interface{} `json:"name"` Age interface{} HIgh interface{} sex interface{} Class interface{} `json:"class"` Test interface{}}type Class struct { Name string Grade int}func main() { //json字符中的"引号,需用\进行转义,否则编译出错 //json字符串沿用上面的结果,但对key进行了大小的修改,并添加了sex数据 data:="{\"name\":\"张三\",\"Age\":18,\"high\":true,\"sex\":\"男\",\"CLASS\":{\"naME\":\"1班\",\"GradE\":3}}" str:=[]byte(data) //1.Unmarshal的第一个参数是json字符串,第二个参数是接受json解析的数据结构。 //第二个参数必须是指针,否则无法接收解析的数据,如stu仍为空对象StuRead{} //2.可以直接stu:=new(StuRead),此时的stu自身就是指针 stu:=StuRead{} err:=json.Unmarshal(str,&stu) //解析失败会报错,如json字符串格式不对,缺"号,缺}等。 if err!=nil{ fmt.Println(err) } fmt.Println(stu)}
map转json
例子在前面 基本数据类型操作-》map相关操作
字符串操作
package mainimport ( "fmt" "strings")func main() { s := "smallming" //第一次出现的索引 fmt.Println(strings.Index(s, "l")) //最后一次出现的索引 fmt.Println(strings.LastIndex(s, "l")) //是否以指定内容开头 fmt.Println(strings.HasPrefix(s, "small")) //是否以指定内容结尾 fmt.Println(strings.HasSuffix(s, "ming")) //是否包含指定字符串 fmt.Println(strings.Contains(s, "mi")) //全变小写 fmt.Println(strings.ToLower(s)) //全大写 fmt.Println(strings.ToUpper(s)) //把字符串中前n个old子字符串替换成new字符串,如果n小于0表示全部替换. //如果n大于old个数也表示全部替换 fmt.Println(strings.Replace(s, "m", "k", -1)) //把字符串重复count遍 fmt.Println(strings.Repeat(s, 2)) //去掉字符串前后指定字符 fmt.Println(strings.Trim(s, " ")) //去空格可以使用strings.TrimSpace(s) //根据指定字符把字符串拆分成切片 fmt.Println(strings.Split(s, "m")) //使用指定分隔符把切片内容合并成字符串 arr := []string{"small", "ming"} fmt.Println(strings.Join(arr, ""))}
字符串替换
str = strings.Replace(str, " ", "", -1)
func Replace(s, old, new string, n int) string
返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串
字符串处理函数
Golang中的strings包:
Count(s string, str string) int:计算字符串str在s中的非重叠个数。如果str为空串则返回s中的字符(非字节)个数+1。Index(s string, str string) int :返回子串str在字符串s中第一次出现的位置。如果找不到则返回-1;如果str为空,则返回0。LastIndex(s string, str string) int: 返回子串str在字符串s中最后一次出现的位置。如果找不到则返回-1;如果str为空则返回字符串s的长度。IndexRune(s string, r rune) int :返回字符r在字符串s中第一次出现的位置。如果找不到则返回-1。IndexAny(s string, str string) int :返回字符串str中的任何一个字符在字符串s中第一次出现的位置。如果找不到或str为空则返回-1。LastIndexAny(s string, str string) int: 返回字符串str中的任何一个字符在字符串s中最后一次出现的位置。如果找不到或str为空则返回-1。Contains(s string, str string) bool:判断字符串s中是否包含个子串str。包含或者str为空则返回true。ContainsAny(s string, str string) bool:判断字符串s中是否包含个子串str中的任何一个字符。包含则返回true,如果str为空则返回false。ContainsRune(s string, r rune) bool:判断字符串s中是否包含字符r。SplitN(s, str string, n int) []string:以str为分隔符,将s切分成多个子串,结果中**不包含**str本身。如果str为空则将s切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。参数n表示最多切分出几个子串,超出的部分将不再切分,最后一个n包含了所有剩下的不切分。如果n为0,则返回nil;如果n小于0,则不限制切分个数,全部切分。SplitAfterN(s, str string, n int) []string:以str为分隔符,将s切分成多个子串,结果中**包含**str本身。如果str为空,则将s切分成Unicode字符列表。如果s 中没有str子串,则将整个s作为 []string 的第一个元素返回。参数n表示最多切分出几个子串,超出的部分将不再切分。如果n为0,则返回 nil;如果 n 小于 0,则不限制切分个数,全部切分。Split(s, str string) []string:以str为分隔符,将s切分成多个子切片,结果中**不包含**str本身。如果str为空,则将s切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。SplitAfter(s, str string) []string:以str为分隔符,将s切分成多个子切片,结果中**包含**str本身。如果 str 为空,则将 s 切分成Unicode字符列表。如果s中没有str子串,则将整个s作为[]string的第一个元素返回。Fields(s string) []string:以连续的空白字符为分隔符,将s切分成多个子串,结果中不包含空白字符本身。空白字符有:\t, \n, \v, \f, \r, ’ ‘, U+0085 (NEL), U+00A0 (NBSP) 。如果 s 中只包含空白字符,则返回一个空列表。FieldsFunc(s string, f func(rune) bool) []string:以一个或多个满足f(rune)的字符为分隔符,将s切分成多个子串,结果中不包含分隔符本身。如果s中没有满足f(rune)的字符,则返回一个空列表。Join(s []string, str string) string:将s中的子串连接成一个单独的字符串,子串之间用str分隔。HasPrefix(s string, prefix string) bool:判断字符串s是否以prefix开头。HasSuffix(s, suffix string) bool :判断字符串s是否以prefix结尾。Map(f func(rune) rune, s string) string:将s中满足f(rune)的字符替换为f(rune)的返回值。如果f(rune)返回负数,则相应的字符将被删除。Repeat(s string, n int) string:将n个字符串s连接成一个新的字符串。ToUpper(s string) string:将s中的所有字符修改为其大写格式。对于非ASCII字符,它的大写格式需要查表转换。ToLower(s string) string:将s中的所有字符修改为其小写格式。对于非ASCII字符,它的小写格式需要查表转换。ToTitle(s string) string:将s中的所有字符修改为其Title格式,大部分字符的Title格式就是Upper格式,只有少数字符的Title格式是特殊字符。这里的ToTitle主要给Title函数调用。TrimLeftFunc(s string, f func(rune) bool) string:删除s头部连续的满足f(rune)的字符。TrimRightFunc(s string, f func(rune) bool) string:删除s尾部连续的满足f(rune)的字符。TrimFunc(s string, f func(rune) bool) string:删除s首尾连续的满足f(rune)的字符。IndexFunc(s string, f func(rune) bool) int:返回s中第一个满足f(rune) 的字符的字节位置。如果没有满足 f(rune) 的字符,则返回 -1。LastIndexFunc(s string, f func(rune) bool) int:返回s中最后一个满足f(rune)的字符的字节位置。如果没有满足 f(rune) 的字符,则返回 -1。Trim(s string, str string) string:删除s首尾连续的包含在str中的字符。TrimLeft(s string, str string) string:删除s头部连续的包含在str中的字符串。TrimRight(s string, str string) string:删除s尾部连续的包含在str中的字符串。TrimSpace(s string) string:删除s首尾连续的的空白字符。TrimPrefix(s, prefix string) string:删除s头部的prefix字符串。如果s不是以prefix开头,则返回原始s。TrimSuffix(s, suffix string) string:删除s尾部的suffix字符串。如果s不是以suffix结尾,则返回原始s。(只去掉一次,注意和TrimRight区别)Replace(s, old, new string, n int) string:返回s的副本,并将副本中的old字符串替换为new字符串,替换次数为n次,如果n为-1,则全部替换;如果 old 为空,则在副本的每个字符之间都插入一个new。EqualFold(s1, s2 string) bool:比较UTF-8编码在小写的条件下是否相等,不区分大小写,同时它还会对特殊字符进行转换。比如将“ϕ”转换为“Φ”、将“DŽ”转换为“Dž”等,然后再进行比较。“==”比较字符串是否相等,区分大小写,返回bool。Compare(s1 string, s2 string) int1:比较字符串,区分大小写,比”==”速度快。相等为0,不相等为-1。
正则表达式
常用的元字符:
. 匹配除换行符以外的任意字符\w 匹配字母或数字或下划线或汉字\s 匹配任意的空白符\d 匹配数字\b 匹配单词的开始或结束^ 匹配字符串的开始$ 匹配字符串的结束
字符转义:
如果你想查找元字符本身的话,比如你查找.,或者,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用.和\。当然,要查找\本身,你也得用\。
重复
如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。
分枝条件:
正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用 “|” 把不同的规则分隔开。
分组:
重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作。
反义字符
例子:\S+匹配不包含空白符的字符串。常用的正则表达式函数:
reg = regexp.MustCompile(`匹配模式`)reg.FindAllString( )reg.ReplaceAllString()
例子
func main() {text := `Hello 世界!123 Go.` // 查找连续的小写字母reg := regexp.MustCompile(`[a-z]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["ello" "o"] // 查找连续的非小写字母reg = regexp.MustCompile(`[^a-z]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["H" " 世界!123 G" "."] // 查找连续的单词字母reg = regexp.MustCompile(`[\w]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "123" "Go"] // 查找连续的非单词字母、非空白字符reg = regexp.MustCompile(`[^\w\s]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界!" "."] // 查找连续的大写字母reg = regexp.MustCompile(`[[:upper:]]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["H" "G"] // 查找连续的非 ASCII 字符reg = regexp.MustCompile(`[[:^ascii:]]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界!"] // 查找连续的标点符号reg = regexp.MustCompile(`[\pP]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["!" "."] // 查找连续的非标点符号字符reg = regexp.MustCompile(`[\PP]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界" "123 Go"] // 查找连续的汉字reg = regexp.MustCompile(`[\p{Han}]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["世界"] // 查找连续的非汉字字符reg = regexp.MustCompile(`[\P{Han}]+`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello " "!123 Go."] // 查找 Hello 或 Goreg = regexp.MustCompile(`Hello|Go`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找行首以 H 开头,以空格结尾的字符串reg = regexp.MustCompile(`^H.*\s`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界!123 "] // 查找行首以 H 开头,以空白结尾的字符串(非贪婪模式)reg = regexp.MustCompile(`(?U)^H.*\s`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello "] // 查找以 hello 开头(忽略大小写),以 Go 结尾的字符串reg = regexp.MustCompile(`(?i:^hello).*Go`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello 世界!123 Go"] // 查找 Go.reg = regexp.MustCompile(`\QGo.\E`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Go."] // 查找从行首开始,以空格结尾的字符串(非贪婪模式)reg = regexp.MustCompile(`(?U)^.* `)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello "] // 查找以空格开头,到行尾结束,中间不包含空格字符串reg = regexp.MustCompile(` [^ ]*$`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// [" Go."] // 查找“单词边界”之间的字符串reg = regexp.MustCompile(`(?U)\b.+\b`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" " 世界!" "123" " " "Go"] // 查找连续 1 次到 4 次的非空格字符,并以 o 结尾的字符串reg = regexp.MustCompile(`[^ ]{1,4}o`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找 Hello 或 Goreg = regexp.MustCompile(`(?:Hell|G)o`)fmt.Printf("%q\n", reg.FindAllString(text, -1))// ["Hello" "Go"] // 查找 Hello 或 Go,替换为 Hellooo、Goooreg = regexp.MustCompile(`(?PHell|G)o`)fmt.Printf("%q\n", reg.ReplaceAllString(text, "${n}ooo"))// "Hellooo 世界!123 Gooo." // 交换 Hello 和 Goreg = regexp.MustCompile(`(Hello)(.*)(Go)`)fmt.Printf("%q\n", reg.ReplaceAllString(text, "$3$2$1"))// "Go 世界!123 Hello." // 特殊字符的查找reg = regexp.MustCompile(`[\f\t\n\r\v\123\x7F\x{10FFFF}\\\^\$\.\*\+\?\{\}\(\)\[\]\|]`)fmt.Printf("%q\n", reg.ReplaceAllString("\f\t\n\r\v\123\x7F\U0010FFFF\\^$.*+?{}()[]|", "-"))// "----------------------"}
按行读文件
注意下这里的第二个方法,读到最后字符串为空,有时候可能会报错(被坑过),加一个判断条件,判断长度是否为0。(代码里面自己已经加了)
func Readlines(filename string) {// go 按行读取文件的方式有两种,// 第一 将读取到的整个文件内容按照 \n 分割// 使用bufio// 第一种lines, err := ioutil.ReadFile(filename)if err != nil {fmt.Println(err)} else {contents := string(lines)lines := strings.Split(contents, "\n")for _, line := range lines {fmt.Println(line)}}// 第二种fd, err := os.Open(filename)defer fd.Close()if err != nil {fmt.Println("read error:", err)}buff := bufio.NewReader(fd)for {data, _, eof := buff.ReadLine()if eof == io.EOF {break}if(len(data)==0){ break }fmt.Println(string(data))}}
字符串与切片的转换
package mainimport ("fmt""strings")func main() {s := []string{"1", "2", "3"}ss := fmt.Sprintf(strings.Join(s, ","))fmt.Println(ss)slice := strings.Split(ss, ",")fmt.Println(slice)//r := gin.Default()//r.GET("/", func(context *gin.Context) {//context.JSON(http.StatusOK,gin.H{//"message":"demo",//})//})//r.Run()}//D:\GoProject\gin-demo>go run main.go//1,2,3//[1 2 3]
读写文件
打开关闭文件
import ( "fmt" "os")func main() { // 打开文件 file, err := os.Open("e:/a.txt") if err != nil { fmt.Printf("打开文件出错:%v\n", err) } fmt.Println(file) // &{0xc00006a780} // 关闭文件 err = file.Close() if err != nil { fmt.Printf("关闭文件出错:%v\n", err) }}
读文件
法一:带缓冲
import ( "bufio" "fmt" "io" "os")func main() { // 打开文件 file, err := os.Open("e:/a.txt") if err != nil { fmt.Printf("打开文件出错:%v\n", err) } // 及时关闭文件句柄 defer file.Close() // bufio.NewReader(rd io.Reader) *Reader reader := bufio.NewReader(file) // 循环读取文件的内容 for { line, err := reader.ReadString('\n') // 读到一个换行符就结束 if err == io.EOF { // io.EOF表示文件的末尾 break } // 输出内容 fmt.Print(line) }}
法二:一次性读入,不适用于大文件
import ( "fmt" "io/ioutil")func main() { // 使用 io/ioutil.ReadFile 方法一次性将文件读取到内存中 filePath := "e:/.txt" content, err := ioutil.ReadFile(filePath) if err != nil { // log.Fatal(err) fmt.Printf("读取文件出错:%v", err) } fmt.Printf("%v\n", content) fmt.Printf("%v\n", string(content))}
写文件
示例1:
创建一个新文件,写入3行:”Hello World”
打开一个存在的文件,将原来的内容覆盖成新的内容,3行:”你好,世界”
打开一个存在的文件,在原来的内容基础上,追加3行:”你好,Golang”
打开一个存在的文件,将原来的内容读出显示在终端,并且追加3行:”你好,World”import ( "bufio" "fmt" "os")func main() { filePath := "e:/a.txt" // 此文件事先不存在 file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666) // O_CREATE 能创建文件 if err != nil { fmt.Printf("打开文件出错:%v", err) return } // 及时关闭文件句柄 defer file.Close() // 准备写入的内容 str := "Hello World\r\n" // 写入时,使用带缓冲方式的 bufio.NewWriter(w io.Writer) *Writer writer := bufio.NewWriter(file) // 使用for循环写入内容 for i := 0; i < 3; i++ { _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error) if err != nil { fmt.Printf("文件写入出错:%s", err) } } // 因为 writer 是带缓存的,所以需要 Flush 方法将缓冲中的数据真正写入到文件中 _ = writer.Flush()}
将 O_CREATE 修改为 O_TRUNC 模式即可,表示:打开文件并清空内容
将 O_TRUNC 修改为 O_APPEND 模式即可,表示:打开文件并在最后追加内容import ( "bufio" "fmt" "io" "os")func main() { filePath := "e:/a.txt" file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666) if err != nil { fmt.Printf("打开文件出错:%v", err) return } defer file.Close() // 先读取原来文件的内容,并显示在终端 reader := bufio.NewReader(file) for { str, err := reader.ReadString('\n') // 读到一个换行符就结束 if err == io.EOF { // io.EOF表示文件的末尾 break } // 输出内容 fmt.Print(str) } // 准备写入的内容 str := "你好,World\r\n" // 写入时,使用带缓冲方式的 bufio.NewWriter(w io.Writer) *Writer writer := bufio.NewWriter(file) // 使用for循环写入内容 for i := 0; i < 3; i++ { _, err := writer.WriteString(str) // func (b *Writer) WriteString(s string) (int, error) if err != nil { fmt.Printf("文件写入出错:%s", err) } } // 因为 writer 是带缓存的,所以需要 Flush 方法将缓冲中的数据真正写入到文件中 _ = writer.Flush()}4:读写模式(O_RDWR)
示例二:将一个文件写道另一个文件(两个文件均存在)
import ( "fmt" "io/ioutil")func main() { filePath1 := "e:/a.txt" filePath2 := "e:/b.txt" content, err := ioutil.ReadFile(filePath1) if err != nil { fmt.Printf("读取文件出错:%v", err) return } err = ioutil.WriteFile(filePath2, content, 0666) if err != nil { fmt.Printf("写入文件出错:%v", err) return }}
替换文件某一行内容
package mainimport ( "io/ioutil" "log" "strings")func main() { input, err := ioutil.ReadFile("C:/Users/76585/Desktop/key.hypara") if err != nil { log.Fatalln(err) } lines := strings.Split(string(input), "\n") for i, line := range lines { if strings.Contains(line, "nsimutask") { lines[i] = "int nsimutask = 0;" }if strings.Contains(line, "string parafilename") {lines[i] = "string parafilename = \"../bin/grid_para.hypara\";"break} } output := strings.Join(lines, "\n") err = ioutil.WriteFile("C:/Users/76585/Desktop/key.hypara", []byte(output), 0644) if err != nil { log.Fatalln(err) }}
判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用 os.Stat() 函数返回的错误值进行判断:
如果返回的错误为 nil,说明文件或文件夹存在;
如果返回的错误类型使用 os.IsNotExist() 判断为 true,说明文件或文件夹不存在;
如果返回的错误为其他类型,则不确定是否存在。package mainimport ( "fmt" "os")// 判断文件或文件夹是否存在func PathExist(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err}func main() { filePath := "e:/a.txt" flag, err := PathExist(filePath) if err != nil { fmt.Println(err) } fmt.Println(flag) // true}
拷贝文件
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
package mainimport ( "bufio" "fmt" "io" "os")// 将 srcFilePath 拷贝到 dstFilePathfunc CopyFile(dstFilePath string, srcFilePath string) (written int64, err error) { // 打开srcFilePath srcFile, err := os.Open(srcFilePath) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer srcFile.Close() // 通过 bufio/NewReader,传入 srcFile,获取到 reader reader := bufio.NewReader(srcFile) // 打开dstFilePath dstFile, err := os.OpenFile(dstFilePath, os.O_WRONLY | os.O_CREATE, 0666) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer dstFile.Close() // 通过 bufio/NewWriter,传入 dstFile,获取到 writer writer := bufio.NewWriter(dstFile) return io.Copy(writer, reader)}func main() { srcFilePath := "e:/a.mp4" dstFilePath := "f:/b.mp4" _, err := CopyFile(dstFilePath, srcFilePath) if err != nil { fmt.Printf("拷贝文件出错:%s", err) } fmt.Println("拷贝文件完成")}自己写一个函数完成拷贝文件
读取文件最后一行
func ReadFile(file_name string) (info string) { file, err := os.Open(file_name) if err != nil { log.Fatal(err) } defer file.Close() var lineText string scanner := bufio.NewScanner(file) for scanner.Scan() { lineText = scanner.Text() //fmt.Print(lineText) } return string(lineText)}
遍历目录
package mainimport ( "fmt" "os")func main() { fmt.Println("请输入一个目录的路径:") var path string _, _ = fmt.Scan(&path) // 打开目录 f, err := os.OpenFile(path, os.O_RDONLY, os.ModeDir) if err != nil { fmt.Printf("Open file failed:%s.\n", err) return } defer f.Close() // 读取目录 info, err := f.Readdir(-1) // -1 表示读取目录中所有目录项 // 遍历返回的切片 for _, fileInfo := range info { if fileInfo.IsDir() { fmt.Printf("%s是一个目录\n", fileInfo.Name()) } else { fmt.Printf("%s是一个文件\n", fileInfo.Name()) } }}
其它
统计一个文件中含有的英文、数字、空格以及其他字符数量。
package mainimport ( "bufio" "fmt" "io" "os")// 定义一个结构体,用于保存统计结果type CharCount struct { AlphaCount int // 记录英文个数 NumCount int // 记录数字的个数 SpaceCount int // 记录空格的个数 OtherCount int // 记录其它字符的个数}func main() { // 思路: 打开一个文件, 创一个 reader // 每读取一行,就去统计该行有多少个 英文、数字、空格和其他字符 // 然后将结果保存到一个结构体 filePath := "e:/a.txt" file, err := os.Open(filePath) if err != nil { fmt.Printf("打开文件出错:%s\n", err) return } defer file.Close() // 定义一个 CharCount 实例 var count CharCount //创建一个Reader reader := bufio.NewReader(file) // 开始循环的读取文件的内容 for { line, err := reader.ReadString('\n') if err == io.EOF { // 读到文件末尾就退出 break } // 遍历每一行(line),进行统计 for _, v := range line { switch { case v >= 'a' && v <= 'z': fallthrough // 穿透 case v >= 'A' && v <= 'Z': count.AlphaCount++ case v >= '0' && v <= '9': count.NumCount++ case v == ' ' || v == '\t': count.SpaceCount++ default : count.OtherCount++ } } } // 输出统计的结果看看是否正确 fmt.Printf("字符的个数为:%v\n数字的个数为:%v\n空格的个数为:%v\n其它字符个数:%v\n", count.AlphaCount, count.NumCount, count.SpaceCount, count.OtherCount)}
问题或其他知识
使用grpcurl
如果准备使用docker来运行,那么pull完成后,使用下面的命令:
docker run fullstorydev/grpcurl ip:"端口" list
“ip”和“端口” 记得替换map类型interface{}转换
有时候我们在map里面嵌套map
想取内存map的值就会出现以下问题cannot range over v (type interface {})
interface{} 与其他数据类型不能直接赋值
解决方法
]]>
转为map
v.(map[string] interface {})
转为int
value.(int)
在目标变量后面用. 括号int- 转为map
- Linux +编程语言 - @@ -616,33 +616,35 @@运维 - -Linux - -ssh - -Vim +Golang - Linux知识点+问题 - -/posts/54220/ +工具使用技巧集合 + +/posts/300/ -问题汇总 +error while loading shared libraries错误解决办法
背景:求解器执行的适合报找不到libmpiexec.so.12这个东西,但是在/opt/mpich/lib下面有这个东西。路径什么的也都加入到了 环境变量里面 (~/.bash_profile或~/.bashrc)。还是无法解决问题
原文链接
出现这类错误表示,系统不知道xxx.so放在哪个目录下,这时候就要在/etc/ld.so.conf中加入xxx.so所在的目录。运行命令
sudo gedit /etc/ld.so.conf
在第一行后面空一格 添加/usr/local/lib 保存。运行sudo /sbin/ldconfig
更新新建用户之后不显示用户名和路径问题解决
先说一下如何新建用户并指定目录为根目录:
- 新建用户,当前用户必须为root用户
useradd -d /home/cron/log -m bbee
-d指定目录文件夹
-m新账号名
-c comment 指定一段注释性描述。
-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
-g 用户组 指定用户所属的用户组。
-G 用户组,用户组 指定用户所属的附加组。
-s Shell文件 指定用户的登录Shell。
-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。
- 设置密码
passwd bbee
接下来会提示你输入两次密码使用新用户登录的时候,出现了问题
在Linux下新增的用户登录后只有一个$,没有显示用户名和主机名,如下:$ cd ~ $ ls$ ls -a
原因是新建的用户未指定shell。我们只需将其指定为一个shell即可。下面提供两种办法:
方法一(成功)
查看下当前用户使用的是什么shellecho $SHELL
查看系统支持shell类型cat /etc/shells
使用usermod命令修改shell类型
root@iZ2zeijeb6un95h:~# usermod -s /bin/bash bbee
方法二(为尝试)
在新建用户的时候指定shell
useradd -d /home/cron/log -s /bin/bash -m bbee
make命令
make 和 cmake
什么是make
make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接。
什么是Makefile
简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接。makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。makefile在一些简单的工程完全可以用人工手写,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。这时候就出现了Cmake工具。
什么是Cmake
cmake可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你就不用再自己去修改了。cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。
当然如果你用IDE,类似VS这些一般它都能帮你弄好了,你只需要按一下那个三角形。
简单总结cmake用来转译CMakeLists.txt,在linux下它会生成Makefile,来给make执行。Makefile+make可理解为类unix环境下的项目管理工具, 而cmake是抽象层次更高的项目管理工具。
下面给出其关系图:
./configure && make && make install
./configure
源码的安装一般由3个步骤组成:配置(configure)、编译(make)、安装(make install)。
configure文件是一个可执行的脚本文件,是用来检测你的安装平台的目标特征的。比如它会检测你是不是有CC或GCC,并不是需要CC或GCC.
它有很多选项,在待安装的源码目录下使用命令./configure –help
可以输出详细的选项列表。其中—prefix选项是配置安装目录,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在/usr/local/lib,配置文件默认放在/usr/local/etc,其它的资源文件放在/usr /local/share,比较凌乱。
如果配置了—prefix,如:
$ ./configure —prefix=/usr/local/test
安装后的所有资源文件都会被放在/usr/local/test目录中,不会分散到其他目录。
使用—prefix选项的另一个好处是方便卸载软件或移植软件;当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;而移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统下)。
当然要卸载程序,也可以在原来的make目录下用一次make uninstall,但前提是Makefile文件有uninstall命令(nodejs的源码包里有uninstall命令,测试版本v0.10.35)。
关于卸载:
如果没有配置—prefix选项,源码包也没有提供make uninstall,则可以通过以下方式可以完整卸载:找一个临时目录重新安装一遍,如:
$ ./configure —prefix=/tmp/to_remove && make install然后遍历/tmp/to_remove的文件,删除对应安装位置的文件即可(因为/tmp/to_remove里的目录结构就是没有配置—prefix选项时的目录结构)。
make
make 是用来编译的,它从Makefile中读取指令,然后编译。可以使用多核来make。
make -j2
如果是8核,那就用make -j8。
这一步就是编译,大多数的源代码包都经过这一步进行编译(当然有些perl或python编写的软件需要调用perl或python来进行编译)。如果 在 make 过程中出现 error ,你就要记下错误代码(注意不仅仅是最后一行),然后你可以向开发者提交 bugreport(一般在 INSTALL 里有提交地址),或者你的系统少了一些依赖库等,这些需要自己仔细研究错误代码。可能遇到的错误:make * 没有指明目标并且找不到 makefile。 停止。问题很明了,没有Makefile,怎么办,原来是要先./configure 一下,再make。
make install
可以使用多核安装 make -j2 install
这条命令来进行安装(当然有些软件需要先运行 make check 或 make test 来进行一些测试),这一步一般需要你有 root 权限(因为要向系统写入文件)。
这个命令用与安装,可以携带一个参数。
PREFIX=/home/lgf
表示安装路径,在安装mpi的时候出现过这个参数。Linux 权限
目录、文件夹的权限如何分配
linux系统上面权限是一个很重要的问题,每一个目录(/opt,/home,/usr)权限到底该如何设置,什么时候使用sudo。
以单个用户(以yskj为例)为例,每一个用户自能在指定的目录拥有读、写、执行权限,不能访问别人的目录。一般来说是在/home/yskj/
/opt等目录拥有者和组几乎全是root。比如mpi在/opt,普通用户登录可以使用吗?答案是可以的。
上图看出两个事情。
- 拥有者和组都是root
- 其他用户(yskj)是拥有读和执行权限
使用者、目录、文件
首先,我们需要知道Linux中的权限是十分重要的,而且权限分为两类:一类是使用者的权限,一类是文件以及目录的是否可读、写、执行的权限。
拥有者–所属组–other
首先很多人不明白这三个使用者的权限是什么意思。一般情况下,拥有者是这个文件的创建者,即哪个用户创建的这个文件。并且在创建新用户的时候会创建出一个同名的组,这个拥有者默认包含在这个所属组中。我们先来理一理这三者的联系去区别,对于初学者来说,我们可以把这三者想象成数学中的集合,拥有者是元素,整个Linux大环境是全集,而所属组是一个一个的小集合,看张图吧。
拥有者就是一个一个的小红点,每个都在自己的所属组里,而且一个拥有者可以在多个所属组里。例如:1可以在所属组1,也可以在所属组2,也可以在所属组3…可以自定义设置。other就是对于所属组1来说,除所属组1中的所有拥有者外,其他的拥有者、所属组都是other。
值得注意的是,在Linux下,有一个超级用户–root,有全部的权限,凌驾一切之上。
下面是刚才所讲操作的具体命令:## user1是生成的用户名创建一个用户:useradd user给他加上密码:password user1删除一个用户:userdel user1写作 userdel -r user1 时会删除user1上的文件创建新增组:groupadd group1删除组:groupdel group1让user1用户归为usergroup1组:useradd -g usergroup1 user1让user1用户也归为usergroup2组:useradd -G usergroup2 user1切换用户:su user1
有时,可能我们会需要更改一个用户,或者更改一个所属组,以使其他拥有者或者其他所属组没有权限打开读写执行这个文件或目录。这时我们需要拥戴的命令是:
修改拥有者:chmod 用户名 文件名/目录名 但注意:普通用户的操作命令要加上sudo chmod 用户名 文件名sudo的作用是仅当前操作暂时为超级权限。当然,回车过后要输入当前拥有者的密码修改所属组:普通用户--sudo chgrp 所属组名 文件名/目录名
Linux中,输入”ll”(小写L)或者”ls -l”可以显示文件的详细信息。
若sudo失败,则进入超级用户权限,再执行chown root test.c
sudo失败的原因,需要将用户添加进sudoers文件:
报错信息为:dlm is not in the sudoers file. This incident will be reported.
解决方法可以看我的另一篇博客:
http://blog.csdn.net/sinat_36118270/article/details/628990932.读、写、执行
Linux中,一个文件或目录的权限有四种
分别是无、读、写、执行权限
分别用“-”“r”“w”“x”表示
在文件列表中,使用”ll”或者”ls -l”命令查看文件详细信息,如图:一个文件或者目录前面共有10位前置字符,第一位表示文件类型,说到这,插一句,在Linux中可以认为“一切皆文件”,且Linux下文件不以文件后缀名区分,而是以第一个字符区分。在细分一下,文件分为:
普通文件:第一个字符为“ - ”;目录:第一个字符为“d”;---directory链接文件:第一个字符为“l”,常见的有两类:软链接(相当于windows中的快捷方式)、硬链接; ---link设备和设备文件 块设备(硬盘):第一个字符为“b”,最小单位为块(字节),支持随机访问。 字符设备(键盘,显示器):第一个字符为“c”,最小单位为字节,只允许按顺序读取。---char套接字:第一个字符为“s”;---sockets管道:第一个字符为“p”;---pine
图中所示,一个文件前有10个前缀字符,除第一个为文件类型外,剩下的9个都是文件的权限
三三一组,分别对应拥有者、所属组、other
即拥有者拥有三个权限,读写执行,所属组和other也相同
排列顺序为:读写执行
每个权限有则用对应的字母表示,无则使用“-”
例如:读写权限:“rw-”;写执行权限:“-wx”
且权限也可使用数字表示:每个位就相当于一个2进制数字,有此权限则为“1”,无则为“0”
例如:读写权限:“rw-”= 6;写执行权限:“-wx”= 3;
在命令输入时,更改拥有者的权限为“u (+/-) (r/w/x)”,括号是为了区分,“+”为增加权限,“-”为去除权限,还可对一个文件或者目录的不同权限修改“u+r-w+x”即为增加读、执行权限,去除写权限。
相关命令为:首先,给定一个文件,默认权限为"rw- rw- r--",即为"664"增加拥有者的执行权限:chmod u+x file(file为文件名) chmod 764 file增加other的写权限:chmod o+w file chmod 666 file去除所属组的读权限:chmod o-r file chmod 624 file增加拥有者的读权限,去除写。执行权限:chmod u+r-w-x file chmod 464 file
那么对于目录呢?对于一个目录来说,照上图来看,也有拥有者、 所素组、other,而每一个也有自己的读、写、执行权限,有什么用呢?
目录的读权限决定进入这个目录后,使用“ls”、“ll”以及这个家族的命令是否可以显示该目录的内容;
目录的写权限决定进入这个目录后,是否可以使用“mkdir”创建目录,是否可以使用“touch”创建文件…;
目录的执行权限决定是否可以进入这个目录。
那么,剩下的对于一个目录权限的多种操作就不用多少了吧。
umask
最后,还有个umask很重要,需要我们去理解记忆
umask是我们linux系统里面的默认权限的补集,一般在我们的系统中,umask=002。表示我们创建的文件的默认权限是664。
注意,我们创建的所有文件的默认权限为664,即“rw-rw-r–”。
不包含拥有者和所属组的执行权限以及other的写和执行权限。
所以我们要在更改umask后,计算文件权限时,基础上也不能加上拥有者和所属组的执行权限以及other的写和执行权限,除非更改的权限值给他们中的一个或多个赋上了相应的权限。
umask可以自己更改,直接敲出来umask “0xxx”就ok。此后,我们的权限就为“664-xxx”
初始值为:“rw-rw-r–”即为“664”
我们设置的umask=032,即为“— -wx -w- ”,
因为二者是互补的关系,所以umask中出现的权限不能出现在新创建的文件中,又因为默认情况下新创建的文件没有拥有者和所属组的执行权限以及other的写和执行权限。
所以file_1的文件权限为:“rw- r– r–”。文件权限符含义
文件权限符以 d 开头的代表是文件夹
drwxrwxrwx文件权限符以 - 开头的代表是文件(包括硬链接文件,硬链接文件相当于原文件的备份,可以与原文件做到同步更新)也有可能是可执行程序
-rwxrwxrwx文件权限符以 l 开头的代表是软链接文件,软链接文件相当于原文件的快捷方式
- 文件权限符以 c 开头的代表是字符设备文件,例:鼠标、键盘;
- 文件权限符以 b 开头的代表是块设备文件,例:硬盘;
gcc升级到最新版本
Centos7
Centos 7默认gcc版本为4.8,有时需要更高版本的,这里以升级至8.3.1版本为例,分别执行下面三条命令即可,无需手动下载源码编译
1、安装centos-release-scl
sudo yum install centos-release-scl
2、安装devtoolset,注意,如果想安装7.版本的,就改成devtoolset-7-gcc,以此类推
sudo yum install devtoolset-8-gcc*
3、激活对应的devtoolset,所以你可以一次安装多个版本的devtoolset,需要的时候用下面这条命令切换到对应的版本
scl enable devtoolset-8 bash
大功告成,查看一下gcc版本
gcc -v
显示为 gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
补充:这条激活命令只对本次会话有效,重启会话后还是会变回原来的4.8.5版本,要想随意切换可按如下操作。
首先,安装的devtoolset是在 /opt/rh 目录下的,如图
每个版本的目录下面都有个 enable 文件,如果需要启用某个版本,只需要执行source ./enable
所以要想切换到某个版本,只需要执行
source /opt/rh/devtoolset-8/enable
可以将对应版本的切换命令写个shell文件放在配了环境变量的目录下,需要时随时切换,或者开机自启
4、直接替换旧的gcc
旧的gcc是运行的 /usr/bin/gcc,所以将该目录下的gcc/g++替换为刚安装的新版本gcc软连接,免得每次enablemv /usr/bin/gcc /usr/bin/gcc-4.8.5ln -s /opt/rh/devtoolset-8/root/bin/gcc /usr/bin/gccmv /usr/bin/g++ /usr/bin/g++-4.8.5ln -s /opt/rh/devtoolset-8/root/bin/g++ /usr/bin/g++gcc --versiong++ --version
分区+挂载
分区
详细信息:来源该博客
在linux下,一个硬盘要先分区,然后才能挂载到目录上。和windows相同。
问题:如何确定文件或目录在那个磁盘分区?
df -h /homedf -h /home/test.txt
通过上面的命令就可以看出文件或目录是在那个磁盘分区里面
挂载
fdisk /dev/sdb
这一步是对sdb这个磁盘分区。
接下来是格式化:mkfs -t ext4 /dev/sdb1
挂载:将分区和目录联系起来
mount 设备名 目录名
mount /dev/sdb1 /home/newdisk
这个方法重启会失效,设置永久挂载。
vim /etc/fstab
解除挂载
umount 设备名
远程调用shell脚本找不到库
背景:本地把服务编译完成,通过git bash传到服务器,然后ssh调用服务器上写好的脚本,显示找不到某个库(以安装)。以mobaxterm方式登录服务器,执行脚本没有问题。
原因:配置文件没有被加载
解决方法:
- 在Remote机上的shell脚本的开头重新配置“需要用到”的环境变量(本文所遇到的是mpi的一个库)
- 在Remote shell的开头设置,用source使.basn_profile文件生效
shell脚本中export无效的原分析和解决方法
问题场景:shell脚本中的export怎么都无法起作用
测试需要导入大量临时的环境变量,单个export又比较麻烦,因此创建shell脚本简化场景:
#!bin/bash export PATH=$PATH:/usr/lib/java/jre export PATH=$PATH:/usr/lib/java/bin
就这么个系统环境变量配置,通过./path.sh 或者 sh path.sh怎么执行都无法设置成功,为什么单独执行export一点问题都没有,但是写到shell脚本中执行,export却怎么都不起作用?
原因
- shell是一个进程,每个进程拥有独立的存储空间,进程间数据不可见
一个shell中的系统环境变量(用export定义的变量)只会对当前shell或者他的子shell有效,该shell结束之后,变量生命周期结束(并不能返回到父shell中)- export定义的变量,对当前shell及子shell有效;不用export定义的变量,仅对本shell有效
- 执行脚本时,是创建了一个新的子shell进程运行,脚本执行完成后,该子shell进程自动退出
因此,子shell中定义的系统环境变量是无法作用于父shell的。解决办法
上文可知,父shell可将自己的环境变量写入子shell,但子shell无法将自己空间中的数据写入父shell(至少export不行),如何达到我们的需求,那就不要创建子shell,仅导入shell文件内的内容
. path.th
或者
source path.th
“.”、“source”、“sh”、“./”、“export”的区别
]]>
- source 同“.”, 用于使shell读入指定的shell文件,并依次执行文件中的所有语句(当前shell)
- sh 创建一个子shell,继承父shell的环境变量,同时在子shell中执行脚本里面的语句
- ./ 当脚本文件具有可执行属性时,与sh无异,./filename是因为当前目录并未在PATH中
- export 设置或显示环境变量,临时作用于当前shell
科研工具使用 latex
latex安装
latex、mathtype公式结合
有的期刊的公式无法用word自带的编辑,只能用mathtype。
安装好mathtype后,就可以在word里面使用公式,一般来说有两种方式一种是mathtype里面的显示,直接写就好
第二种是在页面中,用latex形式。$a_i$,然后点击mathtype切换到latex就可转换为公式。数学公式图片转latex
zotero
安装插件
插件一般从github上面下载,直接搜就好。实在不知道就百度,下载的格式一般是xpi。
然后进入到zotero。工具-》附加组件-》“右上角齿轮”-》Install Add-on From File
注意:安装的版本不要pre-release,否则部分功能是没有的。要在latest或者之前的
zotero记笔记
需要提前安装好zotero better notes插件
安装好以后左侧会出现工作区
Better Notes引入工作区的概念,工作区是一个单独的Zotero标签页或单独窗口,分为三栏,从左到右依次为:大纲区,主笔记区,预览笔记区
- 大纲区相当于主笔记区的一个导航
- 主笔记可以创建多个,但是每一次只能有一个被使用,要想使用别的主笔记,需要将当前笔记设为主笔记
- 主笔记一般来说是针对某一类文章的笔记,而条目笔记是针对每一篇文章的笔记,每篇文章可以创建多个条目笔记,主笔记里面没有特别多的内容,但是可以将其与条目笔记关联起来,最终在预览笔记区就能看到。
- 一般来说主笔记是专门有一个分类,叫My Notes。(不清楚我的为什么没有)
添加条目笔记:
链接到指定主笔记位置,主笔记里面还可以设置二三级标题,将条目笔记连接到此
zotero抓取pdf元数据失败
背景:将下载好的pdf导入zotero中,知网抓取元数据失败
方法:
手动创建条目
把PDF拖到该条目下
zotero同步
背景
zotero同步分为两种:
数据同步和文件同步数据同步:数据同步内容可以包含文献库的条目、笔记、链接、标签等等。在Zotero中,可以使用Zotero提供的同步服务同步除了附件之外的所有内容,以便在不同的支持Zotero的设备上使用Zotero。英文版内容为:[library items, notes, links, tags, etc.—everything except attachment files]。此外,还可以登录 Zotero.org 在线查看文献库。数据同步是免费和无限的,无需文件同步即可使用。
文件同步:数据同步将同步库项目,但不同步附件(PDF,音频和视频文件,图像等)。官方提供的网盘只有300M,PDF多了后肯定不够用
解决思路
核心思路:把题录数据和附件分开存储
实际操作
在不同的电脑做好以下操作:
- 安装zotero软件、zotfile插件、茉莉花插件
zotero账号注册,同步设置。看下图,有一个框不要勾选
网盘同步(可以使用坚果云、百度网盘、onedrive等)。我是用的是onedrive,只需要在onedrive中新建一个文件夹来存放pdf文件
找到zotFile首选项,把上一步设置的文件夹位置填进去,下面的子文件夹命名方式表示作者的姓氏(详细的可以去查一下)
找到[首选项]->[高级]->[文件和文件夹],把根目录的路径设置为上面的路径,这一步是为了让你的zotero知道附件存储在哪
将文件移动到网盘,zotero里面只用剩一个链接。可以看到移动后,pdf文件图标左下角有一个链接的图案
- 其他设备使用,进入zotero后,点击右边的绿色同步按钮,等待即可
常用软件或IDE
MobaXterm
密钥登录保持长时间连接
Settings->Configuration
postman
模拟后端数据
选择 Mock Servers。 创建一个mock。记下这个url
在集合里面新建request,
- 再add example
- 将之前赋值的url,放上去,可以加一个/test 这个符号。下面可以放数据。浏览器访问这个网址就返回这个数据
jupyter
Cell中 Code 和 Markdown的切换
在一个cell中(在command模式下) 按下 y, 进入Code 按下m, 进入Markdown
VSCode
快速重启
打开命令面板:
Ctrl+shift+p
输入:Reload Window
查询
- 寻找文件夹中的文本内容
工具栏,编辑->查找
- 查找文件夹中的文件:
ctrl+p
列选择快捷键
保证打开新文件不覆盖旧文件
首先进行搜索,快捷键ctrl+shift+p,在搜索框里输入settings进行搜索,看到User Setting选项,点击打开
打开以后看到如下界面,输入enablePreview(选项在Workbench工作台中)搜索,然后将箭头指示的地方的对勾取消即可,再次打开文件就不会覆盖原窗口文件了
remote ssh无法连接
有时候使用插件remote ssh连接失败,会出现”lockfile” 这个词,解决办法就是在vscode的设置里面搜索”remote ssh:lockfile”,然后勾选。
IDEA
springboot中的pom.xml没有变蓝色
新建一个springboot项目,pom.xml没有变蓝色,前面没有蓝色的m。也不能run和debug。
解决办法:intellij idea折叠文件夹展开
intellij idea快速生成main方法、for循环、out输出
Ctrl+d
替换指定内容
idea替换快捷键有两种:1、“ctrl+r”快捷键,用于当前文件内容替换,指的是在当前打开的文件中替换匹配的字符,只操作一个文件;2、“ctrl+shift+r”快捷键,用于在路径中替换。
快速添加 getter、setter 方法
Alt+Insert
代码对比工具
sublime
SSH客户端
MobaXterm
选择它的原因是因为可以直接将windonws下的文件拖拽过来,不用通过专门的传输软件(xftp),且不用命令可以看到目录结构
复制
不用设置,MobaXTerm 里面选取内容就已经复制了,如图,白色的内容就已经成功复制了哈哈哈哈,真方便。
如果不行,看看是否是这里没有勾上(在 setting 里的 Configuration里面):粘贴
MobaXterm默认的复制键不是 ctrl+v,当初复制服务器密码的时候老出错,一度怀疑密码错了。
这个快捷键可以设置好了,现在 复制-粘贴 就是:选中,Ctrl + V
下载文件到本地
选中目标文件,右击 ,Download。
修改字体
修改完成后要重新新建会话才会生效。
Everything
很多文件搜索不到
工具->选项->左侧找”索引”->点击里面的”强制重建”
可解决这个问题,以避免卸载重装
Microsoft
visio
快捷键集合
箭头反转
点箭头,然后Ctrl+h即可
Word
项目封面问题
封面经常会对不齐,可以新建一个3列表格。在表格里面调整对齐。显示word文档所有格式
在模板处直接改动,会出现格式突然不一样,即使只粘贴文字也不行;如果自己制作格式,有可能不知不觉会影响其它部分的格式。
公式添加编号并右对齐
在公式内部的末尾添加:#(1)
还是按照模板的要求制作格式,最后两个相互对比
先看下效果图
光标指到那一部分就显示那一部分的格式。
如何制作:
- 打开word软件,菜单栏中选择“文件”,在弹出的界面中,选中“选项”。
- 在弹出的word选项属性中,点击选择“自定义功能区”。
- 在选择命令区中,下拉列表框,选择“不在功能区命令”,按字母排序的顺序,在列中快速找到“显示格式”,
- 在右侧的中,选择主选项卡,展开视图——显示,并选中。
- 再点击新建组,输入名字“显示格式”,然后点击添加,将显示格式添加进去,然后确定。
- 接着我们就可以在视图中找“显示格式”,点击起用显示格,在右边就可以看到显示格式的属性。
Word中插入公式后行距变大的解决办法
撰写论文时,在word中插入公式后,行间距变大,与纯文字的行间距不一致,解决办法如下:
光标定位到出现异常的段落,右键选择“段落”→“缩进和间距”,找到间距下面的“如果定义了文档网格,则对齐到网格”,取消勾选即可。
PPT
Excel
visio
添加连接点,并且与另一个连接点对齐
- 点击工具上的这个 X 形工具。可以看到,最左边的矩形只有一个连接点,为了画出需要的框图,需要手动添加四个连接点。
- 选中不需要添加连接点的那个图形(这里以中间部分最上方第一个矩形为例,选中它)。然后点击工具里的连接线
- 从选中的那个矩形最左边的连接点引出一条水平的连接线(把鼠标移动到那个连接点出,看到连接点变绿之后,按住鼠标向左拖动一段,然后松开鼠标),效果如下
- 把鼠标光标移动到绘图区域上方的刻度上,然后向下拉出一条参考线,使参考线和刚才引出的水平连接线重合。(刻度就是红箭头所指的那个)
- 先按住ctrl键,滑动鼠标滚轮,将图像放大,方便操作。然后继续按住ctrl键,移动鼠标到参考线和左边竖长的那个矩形的交点,看到它变白之后点击一下,对齐的连接点就添加成功了。
这一步很关键,如果连接点添加到了参考线,那么是无法连接的,要确保连接点添加在形状上,可以通过放大,找打参考线和形状的交接点,然后多点几次鼠标找一找
输出矢量图
首先“ctrl+A”,然后选择“另存为”,保存类型选择“Tag图像文件格式”,接着在输出里面设置,压缩格式选为“LZW”,接着是“256色”,然后选择“打印机”,下面是“源”,然后点击确定就可以了。这样绝对是满足投稿要求的,分辨率为300dpi。
插入公式
- 使用wps自带的公式编辑器
首先打开Visio,任意新建一个Visio绘图。点击插入——对象——WPS公式3.0,最后点击确定,会弹出WPS的公式编辑器。
在公式编辑器中,输入自己想要的公式后,点击关闭窗口,即可成功插入公式。
- 使用mathtype(推荐)
打开Visio,任意新建一个Visio绘图。点击插入——对象——MathType 6.0 Equation,点击确定,会弹出MathType公式编辑器。
不小心关闭了某个页面
恢复:
ctrl+shift+t
快速开启powershell
在需要启动powershell的文件里面
]]>shift+右键
- Linux +工具 - @@ -682,43 +684,33 @@运维 +office -Linux +IDE -持续集成 +编辑器 -gcc +快捷键 -Linux权限用法 +vscode -make&cmake +visio + +postman - 计算机基础知识 - -/posts/7909/ +Linux知识点+问题 + +/posts/54220/ -数据结构 +哈希表
环境变量
环境变量是系统变量当中的一种,就是PATH。Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。
说白了,把可执行程序的路径放到环境变量里面,那么以后在任意的路径下就可以直接使用这个可执行程序,而不用输入绝对路径,方便。
1.在Windows中,是由可视化的窗口模式展现出来的
2.linux中,在/etc/profile
文件中设置
可以直接用vim进入文件进行设置,也可以用下面的语句echo "export PATH=${PATH}:/usr/local/go/bon" >> /etc/profile
最好还是用vim进行修改,用echo输入到为你文件中,会出现冗余,直接添加比较好。用冒号分隔。
修改好以后,需要更新环境变量
source /etc/profile
计组+操作系统
cpu工作流程
CPU的根本任务就是执行指令,对计算机来说最终都是一串由“0”和“1”组成的序列。CPU从逻辑上可以划分成3个模块,分别是控制单元、运算单元和存储单元,这三部分由CPU内部总线连接起来。如下所示:
控制单元:控制单元是整个CPU的指挥控制中心,由程序计数器PC(Program Counter), 指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和操作控制器OC(Operation Controller)等,对协调整个电脑有序工作极为重要。它根据用户预先编好的程序,依次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(分析)确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号。操作控制器OC中主要包括节拍脉冲发生器、控制矩阵、时钟脉冲发生器、复位电路和启停电路等控制逻辑。
运算单元:是运算器的核心。可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)。相对控制单元而言,运算器接受控制单元的命令而进行动作,即运算单元所进行的全部操作都是由控制单元发出的控制信号来指挥的,所以它是执行部件。
存储单元:包括CPU片内缓存和寄存器组,是CPU中暂时存放数据的地方,里面保存着那些等待处理的数据,或已经处理过的数据,CPU访问寄存器所用的时间要比访问内存的时间短。采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积和集成度所限,寄存器组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。这个是我们以后要介绍这个重点,这里先提一下。
CPU的运行原理(简洁版):
取指令:CPU的控制器从内存读取一条指令并放入指令寄存器。指令的格式一般是这个样子滴:
指令译码:指令寄存器中的指令经过译码,决定该指令应进行何种操作(就是指令里的操作码)、操作数在哪里(操作数的地址)。
执行指令,分两个阶段“取操作数”和“进行运算”。
修改指令计数器,决定下一条指令的地址。
微机设计
其中:
- 时钟 (clock) 对 CPU 内部操作与系统其他组件进行同步。
- 控制单元 (control unit, CU) 协调参与机器指令执行的步骤序列。
- 算术逻辑单元 (arithmetic logic unit, ALU) 执行算术运算,如加法和减法,以及逻辑运算,如 AND(与)、OR(或)和 NOT(非)。
CPU 通过主板上 CPU 插座的引脚与计算机其他部分相连。大部分引脚连接的是数据总线、控制总线和地址总线。
内存存储单元 (memory storage unit) 用于在程序运行时保存指令与数据。它接受来自 CPU 的数据请求,将数据从随机存储器 (RAM) 传输到 CPU,并从 CPU 传输到内存。
由于所有的数据处理都在 CPU 内进行,因此保存在内存中的程序在执行前需要被复制到 CPU 中。程序指令在复制到 CPU 时,可以一次复制一条,也可以一次复制多条。
总线 (bus) 是一组并行线,用于将数据从计算机一个部分传送到另一个部分。一个计算机系统通常包含四类总线:数据类、I/O 类、控制类和地址类。
数据总线 (data bus) 在 CPU 和内存之间传输指令和数据。I/O 总线在 CPU 和系统输入 / 输出设备之间传输数据。控制总线 (control bus) 用二进制信号对所有连接在系统总线上设备的行为进行同步。当前执行指令在 CPU 和内存之间传输数据时,地址总线 (address bus) 用于保持指令和数据的地址。
时钟与 CPU 和系统总线相关的每一个操作都是由一个恒定速率的内部时钟脉冲来进行同步。机器指令的基本时间单位是机器周期 (machine cycle) 或时钟周期 (clock cycle)。
时钟周期持续时间用时钟速度的倒数来计算,而时钟速度则用每秒振荡数来衡量。例如,一个每秒振荡 10 亿次 (1GHz) 的时钟,其时钟周期为 10 亿分之 1 秒 (1 纳秒 )。(CPU主频)
执行一条机器指令最少需要 1 个时钟周期,有几个需要的时钟则超过了 50 个(比如 8088 处理器中的乘法指令)。由于在 CPU、系统总线和内存电路之间存在速度差异,因此,需要访问内存的指令常常需要空时钟周期,也被称为等待状态 (wait states)。
指令执行周期
原文链接
一条机器指令不会神奇地一下就执行完成。CPU 在执行一条机器指令时,需要经过一系列预先定义好的步骤,这些步骤被称为指令执行周期 (instruction execution cycle)。假设现在指令指针寄存器中已经有了想要执行指令的地址,下面就是执行步骤:
1) CPU 从被称为指令队列 (instruction queue) 的内存区域取得指令,之后立即增加指令指针的值。
2) CPU 对指令的二进制位模式进行译码。这种位模式可能会表示该指令有操作数(输入值)。
3) 如果有操作数,CPU 就从寄存器和内存中取得操作数。有时,这步还包括了地址计算。
4) 使用步骤 3 得到的操作数,CPU 执行该指令。同时更新部分状态标志位,如零标志 (Zero)、进位标志 (Carry) 和溢出标志 (Overflow)。
5) 如果输出操作数也是该指令的一部分,则 CPU 还需要存放其执行结果。
通常将上述听起来很复杂的过程简化为三个步骤:取指 (Fetch)、译码 (Decode) 和执行 (Execute)。操作数 (operand) 是指操作过程中输入或输出的值。例如,表达式 Z=X+Y 有两个输入操作数 (X 和 Y),—个输岀操作数 (Z)。
下图是一个典型 CPU 中的数据流框图。该图表现了在指令执行周期中相互交互部件之间的关系。在从内存读取程序指令之前,将其地址放到地址总线上。然后,内存控制器将所需代码送到数据总线上,存入代码高速缓存 (code cache)。指令指针的值决定下一条将要执行的指令。指令由指令译码器分析,并产生相应的数值信号送往控制单元,其协调 ALU 和浮点单元。虽然图中没有画出控制总线,但是其上传输的信号用系统时钟协调不同 CPU 部件之间的数据传输。
缓存
原文链接
作为一个常见现象,计算机从内存读取数据比从内部寄存器读取速度要慢很多。这是因为从内存读取一个值,需要经过下述步骤:将想要读取的值的地址放到地址总线上。
设置处理器 RD(读取)引脚(改变 RD 的值)。
等待一个时钟周期给存储器芯片进行响应。
将数据从数据总线复制到目标操作数。
上述每一步常常只需要一个时钟周期,时钟周期是基于处理器内固定速率时钟节拍的一种时间测量方法。计算机的 CPU 通常是用其时钟速率来描述。例如,速率为 1.2GHz 意味着时钟节拍或振荡为每秒 12 亿次。因此,考虑到每个时钟周期仅为 1/1 200 000 000 秒,4 个时钟周期也是非常快的。但是,与 CPU 寄存器相比,这个速度还是慢了,因为访问寄存器一般只需要 1 个时钟周期。
CPU和主存之间直接数据传输的方式转变成CPU和cache之间直接数据传输。cache负责和主存之间数据传输。
加载并执行程序
在程序执行之前,需要用一种工具程序将其加载到内存,这种工具程序称为程序加载器 (program loader)。加载后,操作系统必须将 CPU 指向程序的入口,即程序开始执行的地址。以下步骤是对这一过程的详细分解。
1) 操作系统(OS)在当前磁盘目录下搜索程序的文件名。如果找不到,则在预定目录列表(称为路径(path))下搜索文件名。当 OS 无法检索到文件名时,它会发出一个出错信息。
2) 如果程序文件被找到,OS 就访问磁盘目录中的程序文件基本信息,包括文件大小,及其在磁盘驱动器上的物理位置。
3) OS 确定内存中下一个可使用的位置,将程序文件加载到内存。为该程序分配内存块,并将程序大小和位置信息加入表中(有时称为描述符表(descriptor table))。另外,OS 可能调整程序内指针的值,使得它们包括程序数据地址。
4) OS 开始执行程序的第一条机器指令(程序入口)。当程序开始执行后,就成为一个进程(process)。OS 为这个进程分配一个标识号(进程 ID),用于在执行期间对其进行追踪。
5) 进程自动运行。OS 的工作是追踪进程的执行,并响应系统资源的请求。这些资源包括内存、磁盘文件和输入输出设备等。
6) 进程结束后,就会从内存中移除。
不论使用哪个版本的 Microsoft Windows,按下 Ctrl-Alt-Delete 组合键,可以选择任务管理器(task manager)选项。在任务管理器窗口可以查看应用程序和进程列表。
应用程序列表中列出了当前正在运行的完整程序名称,比如,Windows 浏览器,或者 Microsoft Visual C++。如果选择进程列表,则会看见一长串进程名。其中的每个进程都是一个独立于其他进程的,并处于运行中的小程序。
可以连续追踪每个进程使用的 CPU 时间和内存容量。在某些情况下,选定一个进程名称后,按下 Delete 键就可以关闭该进程。
指令
- 一条指令就是机器语言的一个语句,它是一组有意义的二进制代码。
- 一台计算机的所有指令的集合构成该机的指令系统,也称为指令集。
指令的格式
从最基本的结构上来说:一条指令通常要包括操作码字段和地址码字段两部分:
操作码字段告诉用户做什么操作?
地址码告诉用户对谁操作?寻址方式
指令寻址的方式包括两部分:
一种是指令的寻址
(是不是很晕?指令寻址怎么又包括指令寻址。hh因为这里的指令寻址指的是具体的操作码上发出的指令。是狭义上的指令寻址。)
另一种是数据的寻址(可以理解为地址码上操作数的地址寻址)指令寻址方式
程序执行跳转指令,将程序计数器中的数据改为7。
- 顺序寻址
从0开始执行,我们就需要在pc中写入地址0。执行完零号指令后,由于这是普通的取数指令,因此程序计数器自动+1,于是cpu开始执行指令1。
- 跳转寻址
知道碰到跳转指令,也就是指令3,程序执行跳转指令,将程序计数器中的数据改为7。数据寻址方式
计算机网络
cookie和session
cookie和seesion主要区别
星巴克开始优惠活动,每消费10杯咖啡,会免费赠送1杯。考虑到一个人一次性消费10杯咖啡几乎不可能,所以需要采取某种方式来记录顾客的消费数量。
1)分给顾客一张卡片,每消费一次记录一次;
2)发给顾客一张卡片,上面有卡号,顾客每消费一次,由店员在操作机上记录一次。
而方案一和二正是对应的客户端记录和服务端记录。与之相对应的正是cookie和session。 好了,我们进入正题。
什么是cookie
HTTP是一种无状态协议,服务器没有办法单单从网络连接上面知道访问者的身份,为了解决这个问题,就诞生了Cookie
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie
客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie。
cookie的弊端
cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,
session
Session 的出现正是为了解决这个问题。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的, 而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为JSESIONID 的一个 Cookie。
Session机制是一种服务端的机制,服务器使用一种类似散列表的结构来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端里的请求里是否已包含了一个session标识—sessionID,
如果已经包含一个sessionID,则说明以前已经为此客户端创建过session,服务器就按照sessionID把这个session检索出来使用
如果客户端请求不包含sessionID,则为此客户端创建一个session并且声称一个与此session相关联的sessionID,
sessionID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个sessionID将被在本次响应中返回给客户端保存。
centOS7 桥接模式设置静态Ip的方法步骤
前言
如果你虚拟机IP是自动获取的,难免会碰到IP经常变动,给xshell相关连接工具使用造成不便,那么怎么固定IP且正常访问外网地址呢?由于主机网络采用的是自动获取IP方式,所以每次重启机器可能导致IP地址的变更,不利于项目的部署和运行
静态ip就是虚拟机的ip 外界想要连你的虚拟机的ip
虚拟机选择桥接模式
注意虚拟机网卡要选择主机上网的网卡
虚拟机设置静态ip(关键步骤)
虚拟机的网卡名称是ens33
然后用vim编辑以下文件
/etc/sysconfig/network-scripts/ifcfg-xxx
上面的xxx就是虚拟机的网卡名称,不同的linux发行版名称可能不同。原因见原链接
需要修改的属性:
ONBOOT=YES #no换成yesBOOTPROTO=static #dhcp换成static添加:IPADDR=192.168.230.129 #静态ip地址 与主机中IP的前三位一致,最后一位需要修改,不要跟主机ip一样导致发生冲突GATEWAY=192.168.230.1 #默认网关 和物理主机一样就可以了NETMASK=255.255.255.0 #子网掩码 和物理主机一样就可以了DNS1=8.8.8.8 #DNS,写谷歌的地址就可以了。
具体解释:
TYPE=Ethernet # 网卡类型:为以太网PROXY_METHOD=none # 代理方式:关闭状态BROWSER_ONLY=no # 只是浏览器:否BOOTPROTO=dhcp # 网卡的引导协议:DHCP[中文名称: 动态主机配置协议]DEFROUTE=yes # 默认路由:是, 不明白的可以百度关键词 `默认路由`IPV4_FAILURE_FATAL=no # 是不开启IPV4致命错误检测:否IPV6INIT=yes # IPV6是否自动初始化: 是[不会有任何影响, 现在还没用到IPV6]IPV6_AUTOCONF=yes # IPV6是否自动配置:是[不会有任何影响, 现在还没用到IPV6]IPV6_DEFROUTE=yes # IPV6是否可以为默认路由:是[不会有任何影响, 现在还没用到IPV6]IPV6_FAILURE_FATAL=no # 是不开启IPV6致命错误检测:否IPV6_ADDR_GEN_MODE=stable-privacy # IPV6地址生成模型:stable-privacy [这只一种生成IPV6的策略]NAME=ens33 # 网卡物理设备名称UUID=f47bde51-fa78-4f79-b68f-d5dd90cfc698 # 通用唯一识别码, 每一个网卡都会有, 不能重复, 否两台linux只有一台网卡可用DEVICE=ens33 # 网卡设备名称, 必须和 `NAME` 值一样ONBOOT=no # 是否开机启动, 要想网卡开机就启动或通过 `systemctl restart network`控制网卡,必须设置为 `yes`
重启网络服务
#centos7systemctl restart network
测试
输入命令ping www.baidu.com(测试外网)
输入命令ping 192.168.2.153(宿主机ip)
注意:
如果无法ping通宿主机,很有可能是因为防火墙。
宿主机无法ping通虚拟机,也有可能是因为防火墙。VMware三种网卡解析
虚拟网卡名 网络属性 定义 VMnet0 物理网卡 Bridge桥接 Vmnet1 虚拟网卡 host-only仅主机 VMnet8 虚拟网卡 NAT 但是我们查看主机的网络链接,有时却看不见VMnet0
通过网络配置这里,能够看见使用那张网卡(这里是virtualbox,VMware也是差不多)
所以根据上面两张图的对比,本地网络连接中的以太网3就是桥接模式使用的网卡。VM虚拟机-三种网络连接方式(桥接、NAT、仅主机模式)
桥接
桥接,即架设了一条桥,让虚拟机成为一台真正的计算机,直接连入到实际网络中了。
因此,它使得虚拟机能被分配到一个网络中独立的IP,所有网络功能完全和在网络中的真实机器一样,它和主机连接在同一个交换机上(此交换机通过vmnet0模拟),处于同一个 LAN,它可以访问网内任何一台机器。
此模式下虚拟机:
可以与主机相互访问
可以与网络中其他主机相互访问
可以与其他虚拟机相互访问
所以,桥接模式下的虚拟机,你把它直接认为是真实计算机就行了。
默认情况下DHCP会自动为虚拟机配置网络,但如果你需要在桥接模式下,手动为虚拟系统配置IP地址时,配置的虚拟机的ip不能是已经被占用的地址,还要和宿主机器处于同一网段,不然会造成地址冲突,只有这样虚拟系统才能和宿主机器以及外网进行通信。
复制物理网络连接状态
一般在虚拟机设置为桥接时就能看见这个选项(无特殊要求默认不用勾选)
这个选项是只在移动设备上有用,比如在笔记本上使用VMware软件,最开始主机用有线连接的局域网,开启虚拟机(使用桥接),虚拟机系统获取的局域网地址为192.168.1.4。然后你把主机的有线拔掉,连接上同一局域网的wifi时,如果你选择了复制物理网络连接状态这个选项,那你的虚拟机系统的IP不会变化(还是192.168.1.4),如果你没有选择复制物理网络连接状态这个选项,那你的虚拟机系统的IP可能就会发生变化,比如变为192.168.1.5。
NAT
NAT,虚拟机访问网络的所有数据都是由主机提供的,虚拟机并不真实存在于网络中,主机与网络中的任何机器都不能查看和访问到虚拟机的存在。
虚拟机和主机之间通过VMnet8这个网卡来模拟路由器的作用,进行nat地址转换功能,负责将虚拟机发到 VMnet8 的包进行地址转换之后发到实际的网络上,再将实际网络上返回的包进行地址转换后通过 VMnet8 发送给虚拟机。
此模式下虚拟机:
可以单向访问主机 可以单向访问其他网络中主机 不可以访问其他虚拟机
虚拟机可以访问主机能访问到的所有网络,但是对于主机以及主机网络上的其他机器,虚拟机又是不可见的,甚至主机也访问不到虚拟机。包括所有nat模式下的虚拟机之间相互都不能访问,虚拟机与虚拟机各自完全独立,相互间无法通过网络访问彼此。
仅主机
仅主机,虚拟机的与主机通过VMnet1连接,VMnet1不提供提供任何路由服务,因此虚拟机只能和宿主机进行通信,而不能连接到实际网络上。
其实就是NAT模式去除了nat地址转换功能
此模式下虚拟机:
可以与主机互相访问 不可以访问其他网络中主机 不可以访问其他虚拟机
仅主机模式看着很简单,但是其实这是一种比较复杂的模式,其他模式可以实现的功能,在仅主机模式下,通过虚拟机及网卡的设置都可以被实现。所以该模式需要有比较扎实的网络基础知识
这种模式下,我们可以理解为虚拟机在主机中模拟出一张专供虚拟机使用的网卡VMnet1,所有虚拟机都是连接到该网卡上的,我们可以通过设置这张网卡来实现上网及其他很多功能,比如(网卡共享、网卡桥接等)。
正向代理和反向代理区别
一 什么是代理
代理其实就是一个中介,A和B本来可以直连,中间插入一个C,C就是中介。
刚开始的时候,代理多数是帮助内网client访问外网server用的
后来出现了反向代理,”反向”这个词在这儿的意思其实是指方向相反,即代理将来自外网客户端的请求转发到内网服务器,从外到内二 正向代理
正向代理类似一个跳板机,代理访问外部资源
比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了
正向代理的用途:
(1)访问原来无法访问的资源,如google
(2) 可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
三 反向代理
反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器反向代理的作用:
(1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网(2)负载均衡,通过反向代理服务器来优化网站的负载
四 总结
正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端看图理解一:
看图理解二:
正向代理中,proxy和client同属一个LAN,对server透明;
反向代理中,proxy和server同属一个LAN,对client透明。
实际上proxy在两种代理中做的事都是代为收发请求和响应,不过从结构上来看正好左右互换了下,所以把后出现的那种代理方式叫成了反向代理总结:
正向代理: 买票的黄牛反向代理: 租房的代理
Nginx
原文链接
“Nginx 是一款轻量级的 HTTP 服务器,采用事件驱动的异步非阻塞处理方式框架,这让其具有极好的 IO 性能,时常用于服务端的反向代理和负载均衡。”
Nginx 是一款 http 服务器 (或叫web服务器)。它是由俄罗斯人 伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发的,并于2004年首次公开发布的。web服务器:负责处理和响应用户请求,一般也称为http服务器,如 Apache、IIS、Nginx应用服务器:存放和运行系统程序的服务器,负责处理程序中的业务逻辑,如 Tomcat、Weblogic、Jboss(现在大多数应用服务器也包含了web服务器的功能)
Nginx 是什么,总结一下就是这些:
- 一种轻量级的web服务器
- 设计思想是事件驱动的异步非阻塞处理(类node.js)
- 占用内存少、启动速度快、并发能力强
- 使用C语言开发
- 扩展性好,第三方插件非常多
- 在互联网项目中广泛应用
Nginx配置
安装/卸载
安装、卸载、启动自查找资料
修改配置
经常要用到的几个文件路径:/usr/local/etc/nginx/nginx.conf (nginx配置文件路径)/usr/local/var/www (nginx服务器默认的根目录)/usr/local/Cellar/nginx/1.17.9 (nginx的安装路径)/usr/local/var/log/nginx/error.log (nginx默认的日志路径)
nginx 默认配置文件简介:
## 首尾配置暂时忽略server { # 当nginx接到请求后,会匹配其配置中的service模块 # 匹配方法就是将请求携带的host和port去跟配置中的server_name和listen相匹配 listen 8080; server_name localhost; # 定义当前虚拟主机(站点)匹配请求的主机名 location / { root html; # Nginx默认值 # 设定Nginx服务器返回的文档名 index index.html index.htm; # 先找根目录下的index.html,如果没有再找index.htm }}## 首尾配置暂时忽略
server{ } 其实是包含在 http{ } 内部的。每一个 server{ } 是一个虚拟主机(站点)。
上面代码块的意思是:当一个请求叫做localhost:8080请求nginx服务器时,该请求就会被匹配进该代码块的 server{ } 中执行。
当然 nginx 的配置非常多,用的时候可以根据文档进行配置。英文文档:nginx.org/en/docs/
中文文档:www.nginx.cn/doc/Nginx有哪些应用?
主要有4大应用(动静分离、正向代理、反向代理、负载均衡)动静分离
如上图所示,动静分离其实就是 Nginx 服务器将接收到的请求分为动态请求和静态请求。
静态请求直接从 nginx 服务器所设定的根目录路径去取对应的资源,动态请求转发给真实的后台(前面所说的应用服务器,如图中的Tomcat)去处理。
这样做不仅能给应用服务器减轻压力,将后台api接口服务化,还能将前后端代码分开并行开发和部署。(传送门:nginx动静分离的好处)server { listen 8080; server_name localhost; location / { root html; # Nginx默认值 index index.html index.htm; } # 静态化配置,所有静态请求都转发给 nginx 处理,存放目录为 my-project location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|js|css)$ { root /usr/local/var/www/my-project; # 静态请求所代理到的根目录 } # 动态请求匹配到path为'node'的就转发到8002端口处理 location /node/ { proxy_pass http://localhost:8002; # 充当服务代理 }}
访问静态资源 nginx 服务器会返回 my-project 里面的文件,如获取 index.html:
访问动态请求 nginx 服务器会将它从8002端口请求到的内容,原封不动的返回回去:
负载均衡
负载均衡是什么?
随着业务的不断增长和用户的不断增多,一台服务已经满足不了系统要求了。这个时候就出现了服务器 集群。
在服务器集群中,Nginx 可以将接收到的客户端请求“均匀地”(严格讲并不一定均匀,可以通过设置权重)分配到这个集群中所有的服务器上。这个就叫做负载均衡。
负载均衡的示意图如下:
负载均衡的作用分摊服务器集群压力
保证客户端访问的稳定性前面也提到了,负载均衡可以解决分摊服务器集群压力的问题。除此之外,Nginx还带有健康检查(服务器心跳检查)功能,会定期轮询向集群里的所有服务器发送健康检查请求,来检查集群中是否有服务器处于异常状态。
一旦发现某台服务器异常,那么在这以后代理进来的客户端请求都不会被发送到该服务器上(直健康检查发现该服务器已恢复正常),从而保证客户端访问的稳定性。配置负载均衡
配置一个简单的负载均衡并不复杂,代码如下:
## 负载均衡:设置domainupstream domain { server localhost:8000; server localhost:8001;}server { listen 8080; server_name localhost; location / { # root html; # Nginx默认值 # index index.html index.htm; proxy_pass http://domain; # 负载均衡配置,请求会被平均分配到8000和8001端口 proxy_set_header Host $host:$server_port; }}
8000和8001是我本地用 Node.js 起的两个服务,负载均衡成功后可以看到访问 localhost:8080 有时会访问到8000端口的页面,有时会访问到8001端口的页面。
能看到这个效果,就说明你配置的负载均衡策略生效了。
实际项目中的负载均衡远比这个案例要更加复杂,但是万变不离其宗,都是根据这个理想模型衍生出来的。
受集群单台服务器内存等资源的限制,负载均衡集群的服务器也不能无限增多。但因其良好的容错机制,负载均衡成为了实现高可用架构中必不可少的一环。ping和ssh
ping 和 ssh 的连通性是相互独立的。如果你不能 ping 通一个 IP,不意味着你不能 ssh 到它;同样,如果你可以 ping 通一个 IP,也不意味着你可以 ssh 到它。
ping 和 ssh 使用的是不同的网络协议和端口。ping 使用的是 ICMP (Internet Control Message Protocol),而 ssh 使用的是 TCP 协议,默认端口为22。这意味着以下几种情况可能发生:
ICMP 被阻止:在许多网络环境中,出于安全原因,ICMP 可能被防火墙或路由器屏蔽。这意味着你可能无法 ping 通一个 IP,但是还是可以通过 ssh 连接它。
SSH 服务未运行:即使你可以 ping 通一个 IP,如果那台计算机上没有运行 SSH 服务,或者 SSH 服务配置不当,你仍然无法通过 ssh 连接它。
SSH 端口被阻止:某些网络可能允许 ICMP,但阻止了 SSH 的默认端口(22)。这可能是因为网络管理员想要防止未经授权的远程访问。
使用非默认端口的 SSH:有时,为了安全或其他原因,SSH 可能配置为在非默认端口上运行。在这种情况下,即使默认的 SSH 端口被阻止,你仍然可能能够连接,只要你知道正确的端口号。
代理软件全局模型ip是否被修改
使用全局代理模式时,你的公共 IP 地址(从外部服务器或网站看到的 IP 地址)会被修改为代理服务器的 IP 地址。这是因为所有的网络请求都通过代理服务器进行,在到达最终的服务器或网站之前,首先到达代理服务器,然后由代理服务器转发。因此,对于外部服务器或网站,它们看到的请求似乎是来自代理服务器,而不是你的原始设备。
不过本地cmd查看ip是没有变化的
编译
不同的语言,不同的开发环境,编译出的东西不一定一样。
比如C/C++,windows下编出的是后缀为exe的可执行程序,双击就能直接运行。但如果在linux下编出的后缀是没有exe的,是一个可运行的二进制文件。原因是因为编译器不同,linux环境的编译器一般是gcc,windows下一般是MinGW等(用VSCODE跑C一般就是这个编译器)
不过java比较特殊,因为编出来的class文件是运行在JVM上,在os上一层,与操作系统没有直接联系。所以windows编出来的class,或者打包的tar/war可以直接扔到服务器(linux)上使用(B站黑马程序员的jenkins教程—(SpringCloud微服务部署)就是这样的)
Web
Session 与 Cookie
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
Cookie机制
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。
而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
什么是Cookie
Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
Session机制
Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
什么是Session
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
cookie和session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上.
简单的说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,
客户端每次请求服务器的时候会发送 当前会话的session_id,服务器根据当前session_id判断相应的用户数据标志,以确定用户是否登录,或具有某种权限。
由于数据是存储在服务器 上面,所以你不能伪造,但是如果你能够获取某个登录用户的session_id,用特殊的浏览器伪造该用户的请求也是能够成功的。
session_id是服务器和客户端链接时候随机分配的,一般来说是不会有重复,但如果有大量的并发请求,也不是没有重复的可能性。
Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session也会失效。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
4、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
密码学
MD5加密
为什么要对密码MD5
密码明文传递或者直接写到数据库中,都有被偷看的风险为什么要对密码做两次MD5
现在存在的一些反查md5的软件,做两次为了更好的保密整体流程如何实现
1.整体加密流程
MD5(MD5(pass明文+固定salt)+随机salt)第一次固定salt写死在前端第二次加密采用随机的salt 并将每次生成的salt保存在数据库中
2.登录流程:
前端对用户输入的密码进行md5加密(固定的salt)将加密后的密码传递到后端后端使用用户id取出用户信息后端对加密后的密码在进行md5加密(取出盐),然后与数据库中存储的密码进行对比,ok登录成功,否则登录失败
3.注册流程
前端对用户输入的密码进行md5加密(固定的salt)将加密后的密码传递到后端后端随机生成一个salt,使用生成salt对前端传过来的密码进行加密,然后将加密后密码和salt一起保存到db中
硬件知识
cpu核心数与线程数
物理 cpu 数(physical cpu)
指主板上实际插入的 cpu 硬件个数(socket)。(但是这一概念经常被泛泛的说成是 cpu 数,这很容易导致与 core 数,processor 数等概念混淆,所以此处强调是物理 cpu 数)。
由于在主板上引入多个 cpu 插槽需要更复杂的硬件支持(连接不同插槽的 cpu 到内存和其他资源),通常只会在服务器上才这样做。在家用电脑中,一般主板上只会有一个 cpu 插槽。
核心(core)
一开始,每个物理 cpu 上只有一个核心(a single core),对操作系统而言,也就是同一时刻只能运行一个进程/线程。 为了提高性能,cpu 厂商开始在单个物理 cpu 上增加核心(实实在在的硬件存在),也就出现了双核心 cpu(dual-core cpu)以及多核心 cpu(multiple cores),这样一个双核心 cpu 就是同一时刻能够运行两个进程/线程的。同时多线程技术(simultaneous multithreading)和 超线程技术(hyper–threading/HT)
本质一样,是为了提高单个 core 同一时刻能够执行的多线程数的技术(充分利用单个 core 的计算能力,尽量让其“一刻也不得闲”)。
simultaneous multithreading 缩写是 SMT,AMD 和其他 cpu 厂商的称呼。 hyper–threading 是 Intel 的称呼,可以认为 hyper–threading 是 SMT 的一种具体技术实现。
在类似技术下,产生了如下等价术语:
- 虚拟 core: virtual core
- 逻辑 processer: logical processor
- 线程:thread
所以可以这样说:某款采用 SMT 技术的 4 核心 AMD cpu 提供了 8 线程同时执行的能力;某款采用 HT 技术的 2 核心 Intel cpu 提供了 4 线程同时执行的能力。
查看 cpu 信息
1.linux系统://法一lscpu
CPU(s): 24On-line CPU(s) list: 0-23Thread(s) per core: 2Core(s) per socket: 6Socket(s): 2
//法二cat /proc/cpuinfo
processor : 0vendor_id : GenuineIntelcpu family : 6model : 60model name : Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHzstepping : 3microcode : 0x22cpu MHz : 2393.631cache size : 6144 KBphysical id : 0siblings : 8core id : 0cpu cores : 4...
其中有几个physical id,机器上就安装了几个物理CPU
cpu core记录了每个物理CPU,内部有几个物理核
siblings 代表每个物理CPU有多少个逻辑核2.windows
多线程程序线程数
为了让我们的多线程程序更好的利用 cpu 资源,我们通常会先了解机器拥有的 processor 数,有若干手段可以获取这一信息:
- cpuinfo 中查看:比如上文中的 cat /proc/cpuinfo | grep “processor” | wc -l
- top 命令查看:cpu0,cpu1,…
- 编程:比如在 Java 中用 Runtime.getRuntime().availableProcessors()
具体在多线程程序中设置线程数多大,对计算密集型的程序有的建议是 processor count + 1,有的建议是 processor count 的 1.5 倍,都是经验值,实测为准。
小结
- 一台完整的计算机可能包含一到多个物理 cpu
- 从单个物理 cpu (physical cpu)的角度看,其可能是单核心、双核心甚至多核心的
- 从单个核心(core)的角度看,还有 SMT / HT 等技术让每个 core 对计算机操作系统而言用起来像多个物理 core 差不多
总的逻辑 cpu 数 = 物理 cpu 数 每颗物理 cpu 的核心数 每个核心的超线程数
cpu 线程与进程关系
进程与线程
两种常见解释
1.进程和线程都是一个时间段的描述,是CPU工作时间段的描述。
2.进程是资源分配的最小单位,线程是CPU调度的最小单位解释:
- CPU太快了,只有缓存存储器SRAM才能勉强追上它的速度,因此,一台机器上同时开30个程序,CPU可以把这30个程序变成顺序执行,每个只执行一小段,立马切换到下一个程序,再执行一小段,再切回来,人是无感知的。
- 一个程序准备开始执行的时候,相关资源必须要准备好,比如RAM地址,显卡,磁盘资源,这些准备好的东西打包一起就叫做上下文环境,然后CPU开始执行程序A,当然只执行了一小段时间,CPU就要切换到别的程序执行B,以保证几个程序的并发,切换之前要把A的上下问状态保存起来,下次切回来的时候接着用。
- 因此,进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文
- 进程的颗粒度太大,每次都要有上下的调入,保存,调出。线程就是进程的小分支,比如进程A有a,b,c三个线程,那么线程a,b,c就共享了进程A的上下文环境,成为了更细小的执行时间。
程序中的线程与CPU线程
看到这里会懵逼,假设一台8CPU32核的服务器,是不是跑的程序最多只能开32个线程呢?
答案当然是否定的,我们常说的进程中的线程,与CPU的线程,虽然都叫线程,但完全不是一回事。
程序的线程是软件概念,一个程序可以有多个线程,可以在一个CPU核上轮流并发执行。
CPU的线程是硬件的概念,就是核。八线程就是能让八个线程并行执行。linux中的线程
暂时来不及总结,原文链接有。
概念
脚手架、框架、架构
]]>
- 脚手架是指一个项目模板,通过这个模板可以生成固定模板的项目。
- 框架一般是说应用框架,就是别人已经搭建好的成熟组件,我们只需要填代码就行,比如Spring
Boot就是一个框架,我们要开发spring应用,就可以在这个框架里面按照它的规范去写代码。- 架构是指解决特定业务场景的技术解决方案。
问题汇总 error while loading shared libraries错误解决办法
背景:求解器执行的适合报找不到libmpiexec.so.12这个东西,但是在/opt/mpich/lib下面有这个东西。路径什么的也都加入到了 环境变量里面 (~/.bash_profile或~/.bashrc)。还是无法解决问题
原文链接
出现这类错误表示,系统不知道xxx.so放在哪个目录下,这时候就要在/etc/ld.so.conf中加入xxx.so所在的目录。运行命令
sudo gedit /etc/ld.so.conf
在第一行后面空一格 添加/usr/local/lib 保存。运行sudo /sbin/ldconfig
更新新建用户之后不显示用户名和路径问题解决
先说一下如何新建用户并指定目录为根目录:
- 新建用户,当前用户必须为root用户
useradd -d /home/cron/log -m bbee
-d指定目录文件夹
-m新账号名
-c comment 指定一段注释性描述。
-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
-g 用户组 指定用户所属的用户组。
-G 用户组,用户组 指定用户所属的附加组。
-s Shell文件 指定用户的登录Shell。
-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。
- 设置密码
passwd bbee
接下来会提示你输入两次密码使用新用户登录的时候,出现了问题
在Linux下新增的用户登录后只有一个$,没有显示用户名和主机名,如下:$ cd ~ $ ls$ ls -a
原因是新建的用户未指定shell。我们只需将其指定为一个shell即可。下面提供两种办法:
方法一(成功)
查看下当前用户使用的是什么shellecho $SHELL
查看系统支持shell类型cat /etc/shells
使用usermod命令修改shell类型
root@iZ2zeijeb6un95h:~# usermod -s /bin/bash bbee
方法二(为尝试)
在新建用户的时候指定shell
useradd -d /home/cron/log -s /bin/bash -m bbee
make命令
make 和 cmake
什么是make
make工具可以看成是一个智能的批处理工具,它本身并没有编译和链接的功能,而是用类似于批处理的方式—通过调用makefile文件中用户指定的命令来进行编译和链接。
什么是Makefile
简单的说就像一首歌的乐谱,make工具就像指挥家,指挥家根据乐谱指挥整个乐团怎么样演奏,make工具就根据makefile中的命令进行编译和链接。makefile命令中就包含了调用gcc(也可以是别的编译器)去编译某个源文件的命令。makefile在一些简单的工程完全可以用人工手写,但是当工程非常大的时候,手写makefile也是非常麻烦的,如果换了个平台makefile又要重新修改。这时候就出现了Cmake工具。
什么是Cmake
cmake可以更加简单的生成makefile文件给上面那个make用。当然cmake还有其他功能,就是可以跨平台生成对应平台能用的makefile,你就不用再自己去修改了。cmake根据什么生成makefile呢?它又要根据一个叫CMakeLists.txt文件(学名:组态档)去生成makefile。到最后CMakeLists.txt文件谁写啊?亲,是你自己手写的。
当然如果你用IDE,类似VS这些一般它都能帮你弄好了,你只需要按一下那个三角形。
简单总结cmake用来转译CMakeLists.txt,在linux下它会生成Makefile,来给make执行。Makefile+make可理解为类unix环境下的项目管理工具, 而cmake是抽象层次更高的项目管理工具。
下面给出其关系图:
./configure && make && make install
./configure
源码的安装一般由3个步骤组成:配置(configure)、编译(make)、安装(make install)。
configure文件是一个可执行的脚本文件,是用来检测你的安装平台的目标特征的。比如它会检测你是不是有CC或GCC,并不是需要CC或GCC.
它有很多选项,在待安装的源码目录下使用命令./configure –help
可以输出详细的选项列表。其中—prefix选项是配置安装目录,如果不配置该选项,安装后可执行文件默认放在/usr /local/bin,库文件默认放在/usr/local/lib,配置文件默认放在/usr/local/etc,其它的资源文件放在/usr /local/share,比较凌乱。
如果配置了—prefix,如:
$ ./configure —prefix=/usr/local/test
安装后的所有资源文件都会被放在/usr/local/test目录中,不会分散到其他目录。
使用—prefix选项的另一个好处是方便卸载软件或移植软件;当某个安装的软件不再需要时,只须简单的删除该安装目录,就可以把软件卸载得干干净净;而移植软件只需拷贝整个目录到另外一个机器即可(相同的操作系统下)。
当然要卸载程序,也可以在原来的make目录下用一次make uninstall,但前提是Makefile文件有uninstall命令(nodejs的源码包里有uninstall命令,测试版本v0.10.35)。
关于卸载:
如果没有配置—prefix选项,源码包也没有提供make uninstall,则可以通过以下方式可以完整卸载:找一个临时目录重新安装一遍,如:
$ ./configure —prefix=/tmp/to_remove && make install然后遍历/tmp/to_remove的文件,删除对应安装位置的文件即可(因为/tmp/to_remove里的目录结构就是没有配置—prefix选项时的目录结构)。
make
make 是用来编译的,它从Makefile中读取指令,然后编译。可以使用多核来make。
make -j2
如果是8核,那就用make -j8。
这一步就是编译,大多数的源代码包都经过这一步进行编译(当然有些perl或python编写的软件需要调用perl或python来进行编译)。如果 在 make 过程中出现 error ,你就要记下错误代码(注意不仅仅是最后一行),然后你可以向开发者提交 bugreport(一般在 INSTALL 里有提交地址),或者你的系统少了一些依赖库等,这些需要自己仔细研究错误代码。可能遇到的错误:make * 没有指明目标并且找不到 makefile。 停止。问题很明了,没有Makefile,怎么办,原来是要先./configure 一下,再make。
make install
可以使用多核安装 make -j2 install
这条命令来进行安装(当然有些软件需要先运行 make check 或 make test 来进行一些测试),这一步一般需要你有 root 权限(因为要向系统写入文件)。
这个命令用与安装,可以携带一个参数。
PREFIX=/home/lgf
表示安装路径,在安装mpi的时候出现过这个参数。Linux 权限
目录、文件夹的权限如何分配
linux系统上面权限是一个很重要的问题,每一个目录(/opt,/home,/usr)权限到底该如何设置,什么时候使用sudo。
以单个用户(以yskj为例)为例,每一个用户自能在指定的目录拥有读、写、执行权限,不能访问别人的目录。一般来说是在/home/yskj/
/opt等目录拥有者和组几乎全是root。比如mpi在/opt,普通用户登录可以使用吗?答案是可以的。
上图看出两个事情。
- 拥有者和组都是root
- 其他用户(yskj)是拥有读和执行权限
使用者、目录、文件
首先,我们需要知道Linux中的权限是十分重要的,而且权限分为两类:一类是使用者的权限,一类是文件以及目录的是否可读、写、执行的权限。
拥有者–所属组–other
首先很多人不明白这三个使用者的权限是什么意思。一般情况下,拥有者是这个文件的创建者,即哪个用户创建的这个文件。并且在创建新用户的时候会创建出一个同名的组,这个拥有者默认包含在这个所属组中。我们先来理一理这三者的联系去区别,对于初学者来说,我们可以把这三者想象成数学中的集合,拥有者是元素,整个Linux大环境是全集,而所属组是一个一个的小集合,看张图吧。
拥有者就是一个一个的小红点,每个都在自己的所属组里,而且一个拥有者可以在多个所属组里。例如:1可以在所属组1,也可以在所属组2,也可以在所属组3…可以自定义设置。other就是对于所属组1来说,除所属组1中的所有拥有者外,其他的拥有者、所属组都是other。
值得注意的是,在Linux下,有一个超级用户–root,有全部的权限,凌驾一切之上。
下面是刚才所讲操作的具体命令:## user1是生成的用户名创建一个用户:useradd user给他加上密码:password user1删除一个用户:userdel user1写作 userdel -r user1 时会删除user1上的文件创建新增组:groupadd group1删除组:groupdel group1让user1用户归为usergroup1组:useradd -g usergroup1 user1让user1用户也归为usergroup2组:useradd -G usergroup2 user1切换用户:su user1
有时,可能我们会需要更改一个用户,或者更改一个所属组,以使其他拥有者或者其他所属组没有权限打开读写执行这个文件或目录。这时我们需要拥戴的命令是:
修改拥有者:chmod 用户名 文件名/目录名 但注意:普通用户的操作命令要加上sudo chmod 用户名 文件名sudo的作用是仅当前操作暂时为超级权限。当然,回车过后要输入当前拥有者的密码修改所属组:普通用户--sudo chgrp 所属组名 文件名/目录名
Linux中,输入”ll”(小写L)或者”ls -l”可以显示文件的详细信息。
若sudo失败,则进入超级用户权限,再执行chown root test.c
sudo失败的原因,需要将用户添加进sudoers文件:
报错信息为:dlm is not in the sudoers file. This incident will be reported.
解决方法可以看我的另一篇博客:
http://blog.csdn.net/sinat_36118270/article/details/628990932.读、写、执行
Linux中,一个文件或目录的权限有四种
分别是无、读、写、执行权限
分别用“-”“r”“w”“x”表示
在文件列表中,使用”ll”或者”ls -l”命令查看文件详细信息,如图:一个文件或者目录前面共有10位前置字符,第一位表示文件类型,说到这,插一句,在Linux中可以认为“一切皆文件”,且Linux下文件不以文件后缀名区分,而是以第一个字符区分。在细分一下,文件分为:
普通文件:第一个字符为“ - ”;目录:第一个字符为“d”;---directory链接文件:第一个字符为“l”,常见的有两类:软链接(相当于windows中的快捷方式)、硬链接; ---link设备和设备文件 块设备(硬盘):第一个字符为“b”,最小单位为块(字节),支持随机访问。 字符设备(键盘,显示器):第一个字符为“c”,最小单位为字节,只允许按顺序读取。---char套接字:第一个字符为“s”;---sockets管道:第一个字符为“p”;---pine
图中所示,一个文件前有10个前缀字符,除第一个为文件类型外,剩下的9个都是文件的权限
三三一组,分别对应拥有者、所属组、other
即拥有者拥有三个权限,读写执行,所属组和other也相同
排列顺序为:读写执行
每个权限有则用对应的字母表示,无则使用“-”
例如:读写权限:“rw-”;写执行权限:“-wx”
且权限也可使用数字表示:每个位就相当于一个2进制数字,有此权限则为“1”,无则为“0”
例如:读写权限:“rw-”= 6;写执行权限:“-wx”= 3;
在命令输入时,更改拥有者的权限为“u (+/-) (r/w/x)”,括号是为了区分,“+”为增加权限,“-”为去除权限,还可对一个文件或者目录的不同权限修改“u+r-w+x”即为增加读、执行权限,去除写权限。
相关命令为:首先,给定一个文件,默认权限为"rw- rw- r--",即为"664"增加拥有者的执行权限:chmod u+x file(file为文件名) chmod 764 file增加other的写权限:chmod o+w file chmod 666 file去除所属组的读权限:chmod o-r file chmod 624 file增加拥有者的读权限,去除写。执行权限:chmod u+r-w-x file chmod 464 file
那么对于目录呢?对于一个目录来说,照上图来看,也有拥有者、 所素组、other,而每一个也有自己的读、写、执行权限,有什么用呢?
目录的读权限决定进入这个目录后,使用“ls”、“ll”以及这个家族的命令是否可以显示该目录的内容;
目录的写权限决定进入这个目录后,是否可以使用“mkdir”创建目录,是否可以使用“touch”创建文件…;
目录的执行权限决定是否可以进入这个目录。
那么,剩下的对于一个目录权限的多种操作就不用多少了吧。
umask
最后,还有个umask很重要,需要我们去理解记忆
umask是我们linux系统里面的默认权限的补集,一般在我们的系统中,umask=002。表示我们创建的文件的默认权限是664。
注意,我们创建的所有文件的默认权限为664,即“rw-rw-r–”。
不包含拥有者和所属组的执行权限以及other的写和执行权限。
所以我们要在更改umask后,计算文件权限时,基础上也不能加上拥有者和所属组的执行权限以及other的写和执行权限,除非更改的权限值给他们中的一个或多个赋上了相应的权限。
umask可以自己更改,直接敲出来umask “0xxx”就ok。此后,我们的权限就为“664-xxx”
初始值为:“rw-rw-r–”即为“664”
我们设置的umask=032,即为“— -wx -w- ”,
因为二者是互补的关系,所以umask中出现的权限不能出现在新创建的文件中,又因为默认情况下新创建的文件没有拥有者和所属组的执行权限以及other的写和执行权限。
所以file_1的文件权限为:“rw- r– r–”。文件权限符含义
文件权限符以 d 开头的代表是文件夹
drwxrwxrwx文件权限符以 - 开头的代表是文件(包括硬链接文件,硬链接文件相当于原文件的备份,可以与原文件做到同步更新)也有可能是可执行程序
-rwxrwxrwx文件权限符以 l 开头的代表是软链接文件,软链接文件相当于原文件的快捷方式
- 文件权限符以 c 开头的代表是字符设备文件,例:鼠标、键盘;
- 文件权限符以 b 开头的代表是块设备文件,例:硬盘;
gcc升级到最新版本
Centos7
Centos 7默认gcc版本为4.8,有时需要更高版本的,这里以升级至8.3.1版本为例,分别执行下面三条命令即可,无需手动下载源码编译
1、安装centos-release-scl
sudo yum install centos-release-scl
2、安装devtoolset,注意,如果想安装7.版本的,就改成devtoolset-7-gcc,以此类推
sudo yum install devtoolset-8-gcc*
3、激活对应的devtoolset,所以你可以一次安装多个版本的devtoolset,需要的时候用下面这条命令切换到对应的版本
scl enable devtoolset-8 bash
大功告成,查看一下gcc版本
gcc -v
显示为 gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
补充:这条激活命令只对本次会话有效,重启会话后还是会变回原来的4.8.5版本,要想随意切换可按如下操作。
首先,安装的devtoolset是在 /opt/rh 目录下的,如图
每个版本的目录下面都有个 enable 文件,如果需要启用某个版本,只需要执行source ./enable
所以要想切换到某个版本,只需要执行
source /opt/rh/devtoolset-8/enable
可以将对应版本的切换命令写个shell文件放在配了环境变量的目录下,需要时随时切换,或者开机自启
4、直接替换旧的gcc
旧的gcc是运行的 /usr/bin/gcc,所以将该目录下的gcc/g++替换为刚安装的新版本gcc软连接,免得每次enablemv /usr/bin/gcc /usr/bin/gcc-4.8.5ln -s /opt/rh/devtoolset-8/root/bin/gcc /usr/bin/gccmv /usr/bin/g++ /usr/bin/g++-4.8.5ln -s /opt/rh/devtoolset-8/root/bin/g++ /usr/bin/g++gcc --versiong++ --version
分区+挂载
分区
详细信息:来源该博客
在linux下,一个硬盘要先分区,然后才能挂载到目录上。和windows相同。
问题:如何确定文件或目录在那个磁盘分区?
df -h /homedf -h /home/test.txt
通过上面的命令就可以看出文件或目录是在那个磁盘分区里面
挂载
fdisk /dev/sdb
这一步是对sdb这个磁盘分区。
接下来是格式化:mkfs -t ext4 /dev/sdb1
挂载:将分区和目录联系起来
mount 设备名 目录名
mount /dev/sdb1 /home/newdisk
这个方法重启会失效,设置永久挂载。
vim /etc/fstab
解除挂载
umount 设备名
远程调用shell脚本找不到库
背景:本地把服务编译完成,通过git bash传到服务器,然后ssh调用服务器上写好的脚本,显示找不到某个库(以安装)。以mobaxterm方式登录服务器,执行脚本没有问题。
原因:配置文件没有被加载
解决方法:
- 在Remote机上的shell脚本的开头重新配置“需要用到”的环境变量(本文所遇到的是mpi的一个库)
- 在Remote shell的开头设置,用source使.basn_profile文件生效
shell脚本中export无效的原分析和解决方法
问题场景:shell脚本中的export怎么都无法起作用
测试需要导入大量临时的环境变量,单个export又比较麻烦,因此创建shell脚本简化场景:
#!bin/bash export PATH=$PATH:/usr/lib/java/jre export PATH=$PATH:/usr/lib/java/bin
就这么个系统环境变量配置,通过./path.sh 或者 sh path.sh怎么执行都无法设置成功,为什么单独执行export一点问题都没有,但是写到shell脚本中执行,export却怎么都不起作用?
原因
- shell是一个进程,每个进程拥有独立的存储空间,进程间数据不可见
一个shell中的系统环境变量(用export定义的变量)只会对当前shell或者他的子shell有效,该shell结束之后,变量生命周期结束(并不能返回到父shell中)- export定义的变量,对当前shell及子shell有效;不用export定义的变量,仅对本shell有效
- 执行脚本时,是创建了一个新的子shell进程运行,脚本执行完成后,该子shell进程自动退出
因此,子shell中定义的系统环境变量是无法作用于父shell的。解决办法
上文可知,父shell可将自己的环境变量写入子shell,但子shell无法将自己空间中的数据写入父shell(至少export不行),如何达到我们的需求,那就不要创建子shell,仅导入shell文件内的内容
. path.th
或者
source path.th
“.”、“source”、“sh”、“./”、“export”的区别
]]>
- source 同“.”, 用于使shell读入指定的shell文件,并依次执行文件中的所有语句(当前shell)
- sh 创建一个子shell,继承父shell的环境变量,同时在子shell中执行脚本里面的语句
- ./ 当脚本文件具有可执行属性时,与sh无异,./filename是因为当前目录并未在PATH中
- export 设置或显示环境变量,临时作用于当前shell
- 计算机基础知识 - -web - -操作系统 +Linux - @@ -727,35 +719,43 @@VM - -nginx - -编译 - -环境变量 +Linux -Cookie +运维 -Session +持续集成 -密码学 +gcc -硬件知识 +Linux权限用法 -线程、进程 +make&cmake - 工具使用技巧集合 - -/posts/300/ +计算机基础知识 + +/posts/7909/ -科研工具使用 +latex
latex安装
latex、mathtype公式结合
有的期刊的公式无法用word自带的编辑,只能用mathtype。
安装好mathtype后,就可以在word里面使用公式,一般来说有两种方式一种是mathtype里面的显示,直接写就好
第二种是在页面中,用latex形式。$a_i$,然后点击mathtype切换到latex就可转换为公式。数学公式图片转latex
zotero
安装插件
插件一般从github上面下载,直接搜就好。实在不知道就百度,下载的格式一般是xpi。
然后进入到zotero。工具-》附加组件-》“右上角齿轮”-》Install Add-on From File
注意:安装的版本不要pre-release,否则部分功能是没有的。要在latest或者之前的
zotero记笔记
需要提前安装好zotero better notes插件
安装好以后左侧会出现工作区
Better Notes引入工作区的概念,工作区是一个单独的Zotero标签页或单独窗口,分为三栏,从左到右依次为:大纲区,主笔记区,预览笔记区
- 大纲区相当于主笔记区的一个导航
- 主笔记可以创建多个,但是每一次只能有一个被使用,要想使用别的主笔记,需要将当前笔记设为主笔记
- 主笔记一般来说是针对某一类文章的笔记,而条目笔记是针对每一篇文章的笔记,每篇文章可以创建多个条目笔记,主笔记里面没有特别多的内容,但是可以将其与条目笔记关联起来,最终在预览笔记区就能看到。
- 一般来说主笔记是专门有一个分类,叫My Notes。(不清楚我的为什么没有)
添加条目笔记:
链接到指定主笔记位置,主笔记里面还可以设置二三级标题,将条目笔记连接到此
zotero抓取pdf元数据失败
背景:将下载好的pdf导入zotero中,知网抓取元数据失败
方法:
手动创建条目
把PDF拖到该条目下
zotero同步
背景
zotero同步分为两种:
数据同步和文件同步数据同步:数据同步内容可以包含文献库的条目、笔记、链接、标签等等。在Zotero中,可以使用Zotero提供的同步服务同步除了附件之外的所有内容,以便在不同的支持Zotero的设备上使用Zotero。英文版内容为:[library items, notes, links, tags, etc.—everything except attachment files]。此外,还可以登录 Zotero.org 在线查看文献库。数据同步是免费和无限的,无需文件同步即可使用。
文件同步:数据同步将同步库项目,但不同步附件(PDF,音频和视频文件,图像等)。官方提供的网盘只有300M,PDF多了后肯定不够用
解决思路
核心思路:把题录数据和附件分开存储
实际操作
在不同的电脑做好以下操作:
- 安装zotero软件、zotfile插件、茉莉花插件
zotero账号注册,同步设置。看下图,有一个框不要勾选
网盘同步(可以使用坚果云、百度网盘、onedrive等)。我是用的是onedrive,只需要在onedrive中新建一个文件夹来存放pdf文件
找到zotFile首选项,把上一步设置的文件夹位置填进去,下面的子文件夹命名方式表示作者的姓氏(详细的可以去查一下)
找到[首选项]->[高级]->[文件和文件夹],把根目录的路径设置为上面的路径,这一步是为了让你的zotero知道附件存储在哪
将文件移动到网盘,zotero里面只用剩一个链接。可以看到移动后,pdf文件图标左下角有一个链接的图案
- 其他设备使用,进入zotero后,点击右边的绿色同步按钮,等待即可
常用软件或IDE
MobaXterm
密钥登录保持长时间连接
Settings->Configuration
postman
模拟后端数据
选择 Mock Servers。 创建一个mock。记下这个url
在集合里面新建request,
- 再add example
- 将之前赋值的url,放上去,可以加一个/test 这个符号。下面可以放数据。浏览器访问这个网址就返回这个数据
jupyter
Cell中 Code 和 Markdown的切换
在一个cell中(在command模式下) 按下 y, 进入Code 按下m, 进入Markdown
VSCode
快速重启
打开命令面板:
Ctrl+shift+p
输入:Reload Window
查询
- 寻找文件夹中的文本内容
工具栏,编辑->查找
- 查找文件夹中的文件:
ctrl+p
列选择快捷键
保证打开新文件不覆盖旧文件
首先进行搜索,快捷键ctrl+shift+p,在搜索框里输入settings进行搜索,看到User Setting选项,点击打开
打开以后看到如下界面,输入enablePreview(选项在Workbench工作台中)搜索,然后将箭头指示的地方的对勾取消即可,再次打开文件就不会覆盖原窗口文件了
remote ssh无法连接
有时候使用插件remote ssh连接失败,会出现”lockfile” 这个词,解决办法就是在vscode的设置里面搜索”remote ssh:lockfile”,然后勾选。
IDEA
springboot中的pom.xml没有变蓝色
新建一个springboot项目,pom.xml没有变蓝色,前面没有蓝色的m。也不能run和debug。
解决办法:intellij idea折叠文件夹展开
intellij idea快速生成main方法、for循环、out输出
Ctrl+d
替换指定内容
idea替换快捷键有两种:1、“ctrl+r”快捷键,用于当前文件内容替换,指的是在当前打开的文件中替换匹配的字符,只操作一个文件;2、“ctrl+shift+r”快捷键,用于在路径中替换。
快速添加 getter、setter 方法
Alt+Insert
代码对比工具
sublime
SSH客户端
MobaXterm
选择它的原因是因为可以直接将windonws下的文件拖拽过来,不用通过专门的传输软件(xftp),且不用命令可以看到目录结构
复制
不用设置,MobaXTerm 里面选取内容就已经复制了,如图,白色的内容就已经成功复制了哈哈哈哈,真方便。
如果不行,看看是否是这里没有勾上(在 setting 里的 Configuration里面):粘贴
MobaXterm默认的复制键不是 ctrl+v,当初复制服务器密码的时候老出错,一度怀疑密码错了。
这个快捷键可以设置好了,现在 复制-粘贴 就是:选中,Ctrl + V
下载文件到本地
选中目标文件,右击 ,Download。
修改字体
修改完成后要重新新建会话才会生效。
Everything
很多文件搜索不到
工具->选项->左侧找”索引”->点击里面的”强制重建”
可解决这个问题,以避免卸载重装
Microsoft
visio
快捷键集合
箭头反转
点箭头,然后Ctrl+h即可
Word
项目封面问题
封面经常会对不齐,可以新建一个3列表格。在表格里面调整对齐。显示word文档所有格式
在模板处直接改动,会出现格式突然不一样,即使只粘贴文字也不行;如果自己制作格式,有可能不知不觉会影响其它部分的格式。
公式添加编号并右对齐
在公式内部的末尾添加:#(1)
还是按照模板的要求制作格式,最后两个相互对比
先看下效果图
光标指到那一部分就显示那一部分的格式。
如何制作:
- 打开word软件,菜单栏中选择“文件”,在弹出的界面中,选中“选项”。
- 在弹出的word选项属性中,点击选择“自定义功能区”。
- 在选择命令区中,下拉列表框,选择“不在功能区命令”,按字母排序的顺序,在列中快速找到“显示格式”,
- 在右侧的中,选择主选项卡,展开视图——显示,并选中。
- 再点击新建组,输入名字“显示格式”,然后点击添加,将显示格式添加进去,然后确定。
- 接着我们就可以在视图中找“显示格式”,点击起用显示格,在右边就可以看到显示格式的属性。
Word中插入公式后行距变大的解决办法
撰写论文时,在word中插入公式后,行间距变大,与纯文字的行间距不一致,解决办法如下:
光标定位到出现异常的段落,右键选择“段落”→“缩进和间距”,找到间距下面的“如果定义了文档网格,则对齐到网格”,取消勾选即可。
PPT
Excel
visio
添加连接点,并且与另一个连接点对齐
- 点击工具上的这个 X 形工具。可以看到,最左边的矩形只有一个连接点,为了画出需要的框图,需要手动添加四个连接点。
- 选中不需要添加连接点的那个图形(这里以中间部分最上方第一个矩形为例,选中它)。然后点击工具里的连接线
- 从选中的那个矩形最左边的连接点引出一条水平的连接线(把鼠标移动到那个连接点出,看到连接点变绿之后,按住鼠标向左拖动一段,然后松开鼠标),效果如下
- 把鼠标光标移动到绘图区域上方的刻度上,然后向下拉出一条参考线,使参考线和刚才引出的水平连接线重合。(刻度就是红箭头所指的那个)
- 先按住ctrl键,滑动鼠标滚轮,将图像放大,方便操作。然后继续按住ctrl键,移动鼠标到参考线和左边竖长的那个矩形的交点,看到它变白之后点击一下,对齐的连接点就添加成功了。
这一步很关键,如果连接点添加到了参考线,那么是无法连接的,要确保连接点添加在形状上,可以通过放大,找打参考线和形状的交接点,然后多点几次鼠标找一找
输出矢量图
首先“ctrl+A”,然后选择“另存为”,保存类型选择“Tag图像文件格式”,接着在输出里面设置,压缩格式选为“LZW”,接着是“256色”,然后选择“打印机”,下面是“源”,然后点击确定就可以了。这样绝对是满足投稿要求的,分辨率为300dpi。
插入公式
- 使用wps自带的公式编辑器
首先打开Visio,任意新建一个Visio绘图。点击插入——对象——WPS公式3.0,最后点击确定,会弹出WPS的公式编辑器。
在公式编辑器中,输入自己想要的公式后,点击关闭窗口,即可成功插入公式。
- 使用mathtype(推荐)
打开Visio,任意新建一个Visio绘图。点击插入——对象——MathType 6.0 Equation,点击确定,会弹出MathType公式编辑器。
不小心关闭了某个页面
恢复:
ctrl+shift+t
快速开启powershell
在需要启动powershell的文件里面
]]>shift+右键
数据结构 哈希表
环境变量
环境变量是系统变量当中的一种,就是PATH。Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。
说白了,把可执行程序的路径放到环境变量里面,那么以后在任意的路径下就可以直接使用这个可执行程序,而不用输入绝对路径,方便。
1.在Windows中,是由可视化的窗口模式展现出来的
2.linux中,在/etc/profile
文件中设置
可以直接用vim进入文件进行设置,也可以用下面的语句echo "export PATH=${PATH}:/usr/local/go/bon" >> /etc/profile
最好还是用vim进行修改,用echo输入到为你文件中,会出现冗余,直接添加比较好。用冒号分隔。
修改好以后,需要更新环境变量
source /etc/profile
计组+操作系统
cpu工作流程
CPU的根本任务就是执行指令,对计算机来说最终都是一串由“0”和“1”组成的序列。CPU从逻辑上可以划分成3个模块,分别是控制单元、运算单元和存储单元,这三部分由CPU内部总线连接起来。如下所示:
控制单元:控制单元是整个CPU的指挥控制中心,由程序计数器PC(Program Counter), 指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和操作控制器OC(Operation Controller)等,对协调整个电脑有序工作极为重要。它根据用户预先编好的程序,依次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(分析)确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号。操作控制器OC中主要包括节拍脉冲发生器、控制矩阵、时钟脉冲发生器、复位电路和启停电路等控制逻辑。
运算单元:是运算器的核心。可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)。相对控制单元而言,运算器接受控制单元的命令而进行动作,即运算单元所进行的全部操作都是由控制单元发出的控制信号来指挥的,所以它是执行部件。
存储单元:包括CPU片内缓存和寄存器组,是CPU中暂时存放数据的地方,里面保存着那些等待处理的数据,或已经处理过的数据,CPU访问寄存器所用的时间要比访问内存的时间短。采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积和集成度所限,寄存器组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。这个是我们以后要介绍这个重点,这里先提一下。
CPU的运行原理(简洁版):
取指令:CPU的控制器从内存读取一条指令并放入指令寄存器。指令的格式一般是这个样子滴:
指令译码:指令寄存器中的指令经过译码,决定该指令应进行何种操作(就是指令里的操作码)、操作数在哪里(操作数的地址)。
执行指令,分两个阶段“取操作数”和“进行运算”。
修改指令计数器,决定下一条指令的地址。
微机设计
其中:
- 时钟 (clock) 对 CPU 内部操作与系统其他组件进行同步。
- 控制单元 (control unit, CU) 协调参与机器指令执行的步骤序列。
- 算术逻辑单元 (arithmetic logic unit, ALU) 执行算术运算,如加法和减法,以及逻辑运算,如 AND(与)、OR(或)和 NOT(非)。
CPU 通过主板上 CPU 插座的引脚与计算机其他部分相连。大部分引脚连接的是数据总线、控制总线和地址总线。
内存存储单元 (memory storage unit) 用于在程序运行时保存指令与数据。它接受来自 CPU 的数据请求,将数据从随机存储器 (RAM) 传输到 CPU,并从 CPU 传输到内存。
由于所有的数据处理都在 CPU 内进行,因此保存在内存中的程序在执行前需要被复制到 CPU 中。程序指令在复制到 CPU 时,可以一次复制一条,也可以一次复制多条。
总线 (bus) 是一组并行线,用于将数据从计算机一个部分传送到另一个部分。一个计算机系统通常包含四类总线:数据类、I/O 类、控制类和地址类。
数据总线 (data bus) 在 CPU 和内存之间传输指令和数据。I/O 总线在 CPU 和系统输入 / 输出设备之间传输数据。控制总线 (control bus) 用二进制信号对所有连接在系统总线上设备的行为进行同步。当前执行指令在 CPU 和内存之间传输数据时,地址总线 (address bus) 用于保持指令和数据的地址。
时钟与 CPU 和系统总线相关的每一个操作都是由一个恒定速率的内部时钟脉冲来进行同步。机器指令的基本时间单位是机器周期 (machine cycle) 或时钟周期 (clock cycle)。
时钟周期持续时间用时钟速度的倒数来计算,而时钟速度则用每秒振荡数来衡量。例如,一个每秒振荡 10 亿次 (1GHz) 的时钟,其时钟周期为 10 亿分之 1 秒 (1 纳秒 )。(CPU主频)
执行一条机器指令最少需要 1 个时钟周期,有几个需要的时钟则超过了 50 个(比如 8088 处理器中的乘法指令)。由于在 CPU、系统总线和内存电路之间存在速度差异,因此,需要访问内存的指令常常需要空时钟周期,也被称为等待状态 (wait states)。
指令执行周期
原文链接
一条机器指令不会神奇地一下就执行完成。CPU 在执行一条机器指令时,需要经过一系列预先定义好的步骤,这些步骤被称为指令执行周期 (instruction execution cycle)。假设现在指令指针寄存器中已经有了想要执行指令的地址,下面就是执行步骤:
1) CPU 从被称为指令队列 (instruction queue) 的内存区域取得指令,之后立即增加指令指针的值。
2) CPU 对指令的二进制位模式进行译码。这种位模式可能会表示该指令有操作数(输入值)。
3) 如果有操作数,CPU 就从寄存器和内存中取得操作数。有时,这步还包括了地址计算。
4) 使用步骤 3 得到的操作数,CPU 执行该指令。同时更新部分状态标志位,如零标志 (Zero)、进位标志 (Carry) 和溢出标志 (Overflow)。
5) 如果输出操作数也是该指令的一部分,则 CPU 还需要存放其执行结果。
通常将上述听起来很复杂的过程简化为三个步骤:取指 (Fetch)、译码 (Decode) 和执行 (Execute)。操作数 (operand) 是指操作过程中输入或输出的值。例如,表达式 Z=X+Y 有两个输入操作数 (X 和 Y),—个输岀操作数 (Z)。
下图是一个典型 CPU 中的数据流框图。该图表现了在指令执行周期中相互交互部件之间的关系。在从内存读取程序指令之前,将其地址放到地址总线上。然后,内存控制器将所需代码送到数据总线上,存入代码高速缓存 (code cache)。指令指针的值决定下一条将要执行的指令。指令由指令译码器分析,并产生相应的数值信号送往控制单元,其协调 ALU 和浮点单元。虽然图中没有画出控制总线,但是其上传输的信号用系统时钟协调不同 CPU 部件之间的数据传输。
缓存
原文链接
作为一个常见现象,计算机从内存读取数据比从内部寄存器读取速度要慢很多。这是因为从内存读取一个值,需要经过下述步骤:将想要读取的值的地址放到地址总线上。
设置处理器 RD(读取)引脚(改变 RD 的值)。
等待一个时钟周期给存储器芯片进行响应。
将数据从数据总线复制到目标操作数。
上述每一步常常只需要一个时钟周期,时钟周期是基于处理器内固定速率时钟节拍的一种时间测量方法。计算机的 CPU 通常是用其时钟速率来描述。例如,速率为 1.2GHz 意味着时钟节拍或振荡为每秒 12 亿次。因此,考虑到每个时钟周期仅为 1/1 200 000 000 秒,4 个时钟周期也是非常快的。但是,与 CPU 寄存器相比,这个速度还是慢了,因为访问寄存器一般只需要 1 个时钟周期。
CPU和主存之间直接数据传输的方式转变成CPU和cache之间直接数据传输。cache负责和主存之间数据传输。
加载并执行程序
在程序执行之前,需要用一种工具程序将其加载到内存,这种工具程序称为程序加载器 (program loader)。加载后,操作系统必须将 CPU 指向程序的入口,即程序开始执行的地址。以下步骤是对这一过程的详细分解。
1) 操作系统(OS)在当前磁盘目录下搜索程序的文件名。如果找不到,则在预定目录列表(称为路径(path))下搜索文件名。当 OS 无法检索到文件名时,它会发出一个出错信息。
2) 如果程序文件被找到,OS 就访问磁盘目录中的程序文件基本信息,包括文件大小,及其在磁盘驱动器上的物理位置。
3) OS 确定内存中下一个可使用的位置,将程序文件加载到内存。为该程序分配内存块,并将程序大小和位置信息加入表中(有时称为描述符表(descriptor table))。另外,OS 可能调整程序内指针的值,使得它们包括程序数据地址。
4) OS 开始执行程序的第一条机器指令(程序入口)。当程序开始执行后,就成为一个进程(process)。OS 为这个进程分配一个标识号(进程 ID),用于在执行期间对其进行追踪。
5) 进程自动运行。OS 的工作是追踪进程的执行,并响应系统资源的请求。这些资源包括内存、磁盘文件和输入输出设备等。
6) 进程结束后,就会从内存中移除。
不论使用哪个版本的 Microsoft Windows,按下 Ctrl-Alt-Delete 组合键,可以选择任务管理器(task manager)选项。在任务管理器窗口可以查看应用程序和进程列表。
应用程序列表中列出了当前正在运行的完整程序名称,比如,Windows 浏览器,或者 Microsoft Visual C++。如果选择进程列表,则会看见一长串进程名。其中的每个进程都是一个独立于其他进程的,并处于运行中的小程序。
可以连续追踪每个进程使用的 CPU 时间和内存容量。在某些情况下,选定一个进程名称后,按下 Delete 键就可以关闭该进程。
指令
- 一条指令就是机器语言的一个语句,它是一组有意义的二进制代码。
- 一台计算机的所有指令的集合构成该机的指令系统,也称为指令集。
指令的格式
从最基本的结构上来说:一条指令通常要包括操作码字段和地址码字段两部分:
操作码字段告诉用户做什么操作?
地址码告诉用户对谁操作?寻址方式
指令寻址的方式包括两部分:
一种是指令的寻址
(是不是很晕?指令寻址怎么又包括指令寻址。hh因为这里的指令寻址指的是具体的操作码上发出的指令。是狭义上的指令寻址。)
另一种是数据的寻址(可以理解为地址码上操作数的地址寻址)指令寻址方式
程序执行跳转指令,将程序计数器中的数据改为7。
- 顺序寻址
从0开始执行,我们就需要在pc中写入地址0。执行完零号指令后,由于这是普通的取数指令,因此程序计数器自动+1,于是cpu开始执行指令1。
- 跳转寻址
知道碰到跳转指令,也就是指令3,程序执行跳转指令,将程序计数器中的数据改为7。数据寻址方式
计算机网络
cookie和session
cookie和seesion主要区别
星巴克开始优惠活动,每消费10杯咖啡,会免费赠送1杯。考虑到一个人一次性消费10杯咖啡几乎不可能,所以需要采取某种方式来记录顾客的消费数量。
1)分给顾客一张卡片,每消费一次记录一次;
2)发给顾客一张卡片,上面有卡号,顾客每消费一次,由店员在操作机上记录一次。
而方案一和二正是对应的客户端记录和服务端记录。与之相对应的正是cookie和session。 好了,我们进入正题。
什么是cookie
HTTP是一种无状态协议,服务器没有办法单单从网络连接上面知道访问者的身份,为了解决这个问题,就诞生了Cookie
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie
客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie。
cookie的弊端
cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,
session
Session 的出现正是为了解决这个问题。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的, 而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为JSESIONID 的一个 Cookie。
Session机制是一种服务端的机制,服务器使用一种类似散列表的结构来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端里的请求里是否已包含了一个session标识—sessionID,
如果已经包含一个sessionID,则说明以前已经为此客户端创建过session,服务器就按照sessionID把这个session检索出来使用
如果客户端请求不包含sessionID,则为此客户端创建一个session并且声称一个与此session相关联的sessionID,
sessionID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个sessionID将被在本次响应中返回给客户端保存。
centOS7 桥接模式设置静态Ip的方法步骤
前言
如果你虚拟机IP是自动获取的,难免会碰到IP经常变动,给xshell相关连接工具使用造成不便,那么怎么固定IP且正常访问外网地址呢?由于主机网络采用的是自动获取IP方式,所以每次重启机器可能导致IP地址的变更,不利于项目的部署和运行
静态ip就是虚拟机的ip 外界想要连你的虚拟机的ip
虚拟机选择桥接模式
注意虚拟机网卡要选择主机上网的网卡
虚拟机设置静态ip(关键步骤)
虚拟机的网卡名称是ens33
然后用vim编辑以下文件
/etc/sysconfig/network-scripts/ifcfg-xxx
上面的xxx就是虚拟机的网卡名称,不同的linux发行版名称可能不同。原因见原链接
需要修改的属性:
ONBOOT=YES #no换成yesBOOTPROTO=static #dhcp换成static添加:IPADDR=192.168.230.129 #静态ip地址 与主机中IP的前三位一致,最后一位需要修改,不要跟主机ip一样导致发生冲突GATEWAY=192.168.230.1 #默认网关 和物理主机一样就可以了NETMASK=255.255.255.0 #子网掩码 和物理主机一样就可以了DNS1=8.8.8.8 #DNS,写谷歌的地址就可以了。
具体解释:
TYPE=Ethernet # 网卡类型:为以太网PROXY_METHOD=none # 代理方式:关闭状态BROWSER_ONLY=no # 只是浏览器:否BOOTPROTO=dhcp # 网卡的引导协议:DHCP[中文名称: 动态主机配置协议]DEFROUTE=yes # 默认路由:是, 不明白的可以百度关键词 `默认路由`IPV4_FAILURE_FATAL=no # 是不开启IPV4致命错误检测:否IPV6INIT=yes # IPV6是否自动初始化: 是[不会有任何影响, 现在还没用到IPV6]IPV6_AUTOCONF=yes # IPV6是否自动配置:是[不会有任何影响, 现在还没用到IPV6]IPV6_DEFROUTE=yes # IPV6是否可以为默认路由:是[不会有任何影响, 现在还没用到IPV6]IPV6_FAILURE_FATAL=no # 是不开启IPV6致命错误检测:否IPV6_ADDR_GEN_MODE=stable-privacy # IPV6地址生成模型:stable-privacy [这只一种生成IPV6的策略]NAME=ens33 # 网卡物理设备名称UUID=f47bde51-fa78-4f79-b68f-d5dd90cfc698 # 通用唯一识别码, 每一个网卡都会有, 不能重复, 否两台linux只有一台网卡可用DEVICE=ens33 # 网卡设备名称, 必须和 `NAME` 值一样ONBOOT=no # 是否开机启动, 要想网卡开机就启动或通过 `systemctl restart network`控制网卡,必须设置为 `yes`
重启网络服务
#centos7systemctl restart network
测试
输入命令ping www.baidu.com(测试外网)
输入命令ping 192.168.2.153(宿主机ip)
注意:
如果无法ping通宿主机,很有可能是因为防火墙。
宿主机无法ping通虚拟机,也有可能是因为防火墙。VMware三种网卡解析
虚拟网卡名 网络属性 定义 VMnet0 物理网卡 Bridge桥接 Vmnet1 虚拟网卡 host-only仅主机 VMnet8 虚拟网卡 NAT 但是我们查看主机的网络链接,有时却看不见VMnet0
通过网络配置这里,能够看见使用那张网卡(这里是virtualbox,VMware也是差不多)
所以根据上面两张图的对比,本地网络连接中的以太网3就是桥接模式使用的网卡。VM虚拟机-三种网络连接方式(桥接、NAT、仅主机模式)
桥接
桥接,即架设了一条桥,让虚拟机成为一台真正的计算机,直接连入到实际网络中了。
因此,它使得虚拟机能被分配到一个网络中独立的IP,所有网络功能完全和在网络中的真实机器一样,它和主机连接在同一个交换机上(此交换机通过vmnet0模拟),处于同一个 LAN,它可以访问网内任何一台机器。
此模式下虚拟机:
可以与主机相互访问
可以与网络中其他主机相互访问
可以与其他虚拟机相互访问
所以,桥接模式下的虚拟机,你把它直接认为是真实计算机就行了。
默认情况下DHCP会自动为虚拟机配置网络,但如果你需要在桥接模式下,手动为虚拟系统配置IP地址时,配置的虚拟机的ip不能是已经被占用的地址,还要和宿主机器处于同一网段,不然会造成地址冲突,只有这样虚拟系统才能和宿主机器以及外网进行通信。
复制物理网络连接状态
一般在虚拟机设置为桥接时就能看见这个选项(无特殊要求默认不用勾选)
这个选项是只在移动设备上有用,比如在笔记本上使用VMware软件,最开始主机用有线连接的局域网,开启虚拟机(使用桥接),虚拟机系统获取的局域网地址为192.168.1.4。然后你把主机的有线拔掉,连接上同一局域网的wifi时,如果你选择了复制物理网络连接状态这个选项,那你的虚拟机系统的IP不会变化(还是192.168.1.4),如果你没有选择复制物理网络连接状态这个选项,那你的虚拟机系统的IP可能就会发生变化,比如变为192.168.1.5。
NAT
NAT,虚拟机访问网络的所有数据都是由主机提供的,虚拟机并不真实存在于网络中,主机与网络中的任何机器都不能查看和访问到虚拟机的存在。
虚拟机和主机之间通过VMnet8这个网卡来模拟路由器的作用,进行nat地址转换功能,负责将虚拟机发到 VMnet8 的包进行地址转换之后发到实际的网络上,再将实际网络上返回的包进行地址转换后通过 VMnet8 发送给虚拟机。
此模式下虚拟机:
可以单向访问主机 可以单向访问其他网络中主机 不可以访问其他虚拟机
虚拟机可以访问主机能访问到的所有网络,但是对于主机以及主机网络上的其他机器,虚拟机又是不可见的,甚至主机也访问不到虚拟机。包括所有nat模式下的虚拟机之间相互都不能访问,虚拟机与虚拟机各自完全独立,相互间无法通过网络访问彼此。
仅主机
仅主机,虚拟机的与主机通过VMnet1连接,VMnet1不提供提供任何路由服务,因此虚拟机只能和宿主机进行通信,而不能连接到实际网络上。
其实就是NAT模式去除了nat地址转换功能
此模式下虚拟机:
可以与主机互相访问 不可以访问其他网络中主机 不可以访问其他虚拟机
仅主机模式看着很简单,但是其实这是一种比较复杂的模式,其他模式可以实现的功能,在仅主机模式下,通过虚拟机及网卡的设置都可以被实现。所以该模式需要有比较扎实的网络基础知识
这种模式下,我们可以理解为虚拟机在主机中模拟出一张专供虚拟机使用的网卡VMnet1,所有虚拟机都是连接到该网卡上的,我们可以通过设置这张网卡来实现上网及其他很多功能,比如(网卡共享、网卡桥接等)。
正向代理和反向代理区别
一 什么是代理
代理其实就是一个中介,A和B本来可以直连,中间插入一个C,C就是中介。
刚开始的时候,代理多数是帮助内网client访问外网server用的
后来出现了反向代理,”反向”这个词在这儿的意思其实是指方向相反,即代理将来自外网客户端的请求转发到内网服务器,从外到内二 正向代理
正向代理类似一个跳板机,代理访问外部资源
比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了
正向代理的用途:
(1)访问原来无法访问的资源,如google
(2) 可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
三 反向代理
反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器反向代理的作用:
(1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网(2)负载均衡,通过反向代理服务器来优化网站的负载
四 总结
正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端看图理解一:
看图理解二:
正向代理中,proxy和client同属一个LAN,对server透明;
反向代理中,proxy和server同属一个LAN,对client透明。
实际上proxy在两种代理中做的事都是代为收发请求和响应,不过从结构上来看正好左右互换了下,所以把后出现的那种代理方式叫成了反向代理总结:
正向代理: 买票的黄牛反向代理: 租房的代理
Nginx
原文链接
“Nginx 是一款轻量级的 HTTP 服务器,采用事件驱动的异步非阻塞处理方式框架,这让其具有极好的 IO 性能,时常用于服务端的反向代理和负载均衡。”
Nginx 是一款 http 服务器 (或叫web服务器)。它是由俄罗斯人 伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发的,并于2004年首次公开发布的。web服务器:负责处理和响应用户请求,一般也称为http服务器,如 Apache、IIS、Nginx应用服务器:存放和运行系统程序的服务器,负责处理程序中的业务逻辑,如 Tomcat、Weblogic、Jboss(现在大多数应用服务器也包含了web服务器的功能)
Nginx 是什么,总结一下就是这些:
- 一种轻量级的web服务器
- 设计思想是事件驱动的异步非阻塞处理(类node.js)
- 占用内存少、启动速度快、并发能力强
- 使用C语言开发
- 扩展性好,第三方插件非常多
- 在互联网项目中广泛应用
Nginx配置
安装/卸载
安装、卸载、启动自查找资料
修改配置
经常要用到的几个文件路径:/usr/local/etc/nginx/nginx.conf (nginx配置文件路径)/usr/local/var/www (nginx服务器默认的根目录)/usr/local/Cellar/nginx/1.17.9 (nginx的安装路径)/usr/local/var/log/nginx/error.log (nginx默认的日志路径)
nginx 默认配置文件简介:
## 首尾配置暂时忽略server { # 当nginx接到请求后,会匹配其配置中的service模块 # 匹配方法就是将请求携带的host和port去跟配置中的server_name和listen相匹配 listen 8080; server_name localhost; # 定义当前虚拟主机(站点)匹配请求的主机名 location / { root html; # Nginx默认值 # 设定Nginx服务器返回的文档名 index index.html index.htm; # 先找根目录下的index.html,如果没有再找index.htm }}## 首尾配置暂时忽略
server{ } 其实是包含在 http{ } 内部的。每一个 server{ } 是一个虚拟主机(站点)。
上面代码块的意思是:当一个请求叫做localhost:8080请求nginx服务器时,该请求就会被匹配进该代码块的 server{ } 中执行。
当然 nginx 的配置非常多,用的时候可以根据文档进行配置。英文文档:nginx.org/en/docs/
中文文档:www.nginx.cn/doc/Nginx有哪些应用?
主要有4大应用(动静分离、正向代理、反向代理、负载均衡)动静分离
如上图所示,动静分离其实就是 Nginx 服务器将接收到的请求分为动态请求和静态请求。
静态请求直接从 nginx 服务器所设定的根目录路径去取对应的资源,动态请求转发给真实的后台(前面所说的应用服务器,如图中的Tomcat)去处理。
这样做不仅能给应用服务器减轻压力,将后台api接口服务化,还能将前后端代码分开并行开发和部署。(传送门:nginx动静分离的好处)server { listen 8080; server_name localhost; location / { root html; # Nginx默认值 index index.html index.htm; } # 静态化配置,所有静态请求都转发给 nginx 处理,存放目录为 my-project location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|js|css)$ { root /usr/local/var/www/my-project; # 静态请求所代理到的根目录 } # 动态请求匹配到path为'node'的就转发到8002端口处理 location /node/ { proxy_pass http://localhost:8002; # 充当服务代理 }}
访问静态资源 nginx 服务器会返回 my-project 里面的文件,如获取 index.html:
访问动态请求 nginx 服务器会将它从8002端口请求到的内容,原封不动的返回回去:
负载均衡
负载均衡是什么?
随着业务的不断增长和用户的不断增多,一台服务已经满足不了系统要求了。这个时候就出现了服务器 集群。
在服务器集群中,Nginx 可以将接收到的客户端请求“均匀地”(严格讲并不一定均匀,可以通过设置权重)分配到这个集群中所有的服务器上。这个就叫做负载均衡。
负载均衡的示意图如下:
负载均衡的作用分摊服务器集群压力
保证客户端访问的稳定性前面也提到了,负载均衡可以解决分摊服务器集群压力的问题。除此之外,Nginx还带有健康检查(服务器心跳检查)功能,会定期轮询向集群里的所有服务器发送健康检查请求,来检查集群中是否有服务器处于异常状态。
一旦发现某台服务器异常,那么在这以后代理进来的客户端请求都不会被发送到该服务器上(直健康检查发现该服务器已恢复正常),从而保证客户端访问的稳定性。配置负载均衡
配置一个简单的负载均衡并不复杂,代码如下:
## 负载均衡:设置domainupstream domain { server localhost:8000; server localhost:8001;}server { listen 8080; server_name localhost; location / { # root html; # Nginx默认值 # index index.html index.htm; proxy_pass http://domain; # 负载均衡配置,请求会被平均分配到8000和8001端口 proxy_set_header Host $host:$server_port; }}
8000和8001是我本地用 Node.js 起的两个服务,负载均衡成功后可以看到访问 localhost:8080 有时会访问到8000端口的页面,有时会访问到8001端口的页面。
能看到这个效果,就说明你配置的负载均衡策略生效了。
实际项目中的负载均衡远比这个案例要更加复杂,但是万变不离其宗,都是根据这个理想模型衍生出来的。
受集群单台服务器内存等资源的限制,负载均衡集群的服务器也不能无限增多。但因其良好的容错机制,负载均衡成为了实现高可用架构中必不可少的一环。ping和ssh
ping 和 ssh 的连通性是相互独立的。如果你不能 ping 通一个 IP,不意味着你不能 ssh 到它;同样,如果你可以 ping 通一个 IP,也不意味着你可以 ssh 到它。
ping 和 ssh 使用的是不同的网络协议和端口。ping 使用的是 ICMP (Internet Control Message Protocol),而 ssh 使用的是 TCP 协议,默认端口为22。这意味着以下几种情况可能发生:
ICMP 被阻止:在许多网络环境中,出于安全原因,ICMP 可能被防火墙或路由器屏蔽。这意味着你可能无法 ping 通一个 IP,但是还是可以通过 ssh 连接它。
SSH 服务未运行:即使你可以 ping 通一个 IP,如果那台计算机上没有运行 SSH 服务,或者 SSH 服务配置不当,你仍然无法通过 ssh 连接它。
SSH 端口被阻止:某些网络可能允许 ICMP,但阻止了 SSH 的默认端口(22)。这可能是因为网络管理员想要防止未经授权的远程访问。
使用非默认端口的 SSH:有时,为了安全或其他原因,SSH 可能配置为在非默认端口上运行。在这种情况下,即使默认的 SSH 端口被阻止,你仍然可能能够连接,只要你知道正确的端口号。
代理软件全局模型ip是否被修改
使用全局代理模式时,你的公共 IP 地址(从外部服务器或网站看到的 IP 地址)会被修改为代理服务器的 IP 地址。这是因为所有的网络请求都通过代理服务器进行,在到达最终的服务器或网站之前,首先到达代理服务器,然后由代理服务器转发。因此,对于外部服务器或网站,它们看到的请求似乎是来自代理服务器,而不是你的原始设备。
不过本地cmd查看ip是没有变化的
编译
不同的语言,不同的开发环境,编译出的东西不一定一样。
比如C/C++,windows下编出的是后缀为exe的可执行程序,双击就能直接运行。但如果在linux下编出的后缀是没有exe的,是一个可运行的二进制文件。原因是因为编译器不同,linux环境的编译器一般是gcc,windows下一般是MinGW等(用VSCODE跑C一般就是这个编译器)
不过java比较特殊,因为编出来的class文件是运行在JVM上,在os上一层,与操作系统没有直接联系。所以windows编出来的class,或者打包的tar/war可以直接扔到服务器(linux)上使用(B站黑马程序员的jenkins教程—(SpringCloud微服务部署)就是这样的)
Web
Session 与 Cookie
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
Cookie机制
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。
而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
什么是Cookie
Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
Session机制
Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
什么是Session
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
cookie和session的区别
1、cookie数据存放在客户的浏览器上,session数据放在服务器上.
简单的说,当你登录一个网站的时候,如果web服务器端使用的是session,那么所有的数据都保存在服务器上面,
客户端每次请求服务器的时候会发送 当前会话的session_id,服务器根据当前session_id判断相应的用户数据标志,以确定用户是否登录,或具有某种权限。
由于数据是存储在服务器 上面,所以你不能伪造,但是如果你能够获取某个登录用户的session_id,用特殊的浏览器伪造该用户的请求也是能够成功的。
session_id是服务器和客户端链接时候随机分配的,一般来说是不会有重复,但如果有大量的并发请求,也不是没有重复的可能性。
Session是由应用服务器维持的一个服务器端的存储空间,用户在连接服务器时,会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端,用Cookie保存的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不用开发人员干预的。所以一旦客户端禁用Cookie,那么Session也会失效。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。
4、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。
5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
密码学
MD5加密
为什么要对密码MD5
密码明文传递或者直接写到数据库中,都有被偷看的风险为什么要对密码做两次MD5
现在存在的一些反查md5的软件,做两次为了更好的保密整体流程如何实现
1.整体加密流程
MD5(MD5(pass明文+固定salt)+随机salt)第一次固定salt写死在前端第二次加密采用随机的salt 并将每次生成的salt保存在数据库中
2.登录流程:
前端对用户输入的密码进行md5加密(固定的salt)将加密后的密码传递到后端后端使用用户id取出用户信息后端对加密后的密码在进行md5加密(取出盐),然后与数据库中存储的密码进行对比,ok登录成功,否则登录失败
3.注册流程
前端对用户输入的密码进行md5加密(固定的salt)将加密后的密码传递到后端后端随机生成一个salt,使用生成salt对前端传过来的密码进行加密,然后将加密后密码和salt一起保存到db中
硬件知识
cpu核心数与线程数
物理 cpu 数(physical cpu)
指主板上实际插入的 cpu 硬件个数(socket)。(但是这一概念经常被泛泛的说成是 cpu 数,这很容易导致与 core 数,processor 数等概念混淆,所以此处强调是物理 cpu 数)。
由于在主板上引入多个 cpu 插槽需要更复杂的硬件支持(连接不同插槽的 cpu 到内存和其他资源),通常只会在服务器上才这样做。在家用电脑中,一般主板上只会有一个 cpu 插槽。
核心(core)
一开始,每个物理 cpu 上只有一个核心(a single core),对操作系统而言,也就是同一时刻只能运行一个进程/线程。 为了提高性能,cpu 厂商开始在单个物理 cpu 上增加核心(实实在在的硬件存在),也就出现了双核心 cpu(dual-core cpu)以及多核心 cpu(multiple cores),这样一个双核心 cpu 就是同一时刻能够运行两个进程/线程的。同时多线程技术(simultaneous multithreading)和 超线程技术(hyper–threading/HT)
本质一样,是为了提高单个 core 同一时刻能够执行的多线程数的技术(充分利用单个 core 的计算能力,尽量让其“一刻也不得闲”)。
simultaneous multithreading 缩写是 SMT,AMD 和其他 cpu 厂商的称呼。 hyper–threading 是 Intel 的称呼,可以认为 hyper–threading 是 SMT 的一种具体技术实现。
在类似技术下,产生了如下等价术语:
- 虚拟 core: virtual core
- 逻辑 processer: logical processor
- 线程:thread
所以可以这样说:某款采用 SMT 技术的 4 核心 AMD cpu 提供了 8 线程同时执行的能力;某款采用 HT 技术的 2 核心 Intel cpu 提供了 4 线程同时执行的能力。
查看 cpu 信息
1.linux系统://法一lscpu
CPU(s): 24On-line CPU(s) list: 0-23Thread(s) per core: 2Core(s) per socket: 6Socket(s): 2
//法二cat /proc/cpuinfo
processor : 0vendor_id : GenuineIntelcpu family : 6model : 60model name : Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHzstepping : 3microcode : 0x22cpu MHz : 2393.631cache size : 6144 KBphysical id : 0siblings : 8core id : 0cpu cores : 4...
其中有几个physical id,机器上就安装了几个物理CPU
cpu core记录了每个物理CPU,内部有几个物理核
siblings 代表每个物理CPU有多少个逻辑核2.windows
多线程程序线程数
为了让我们的多线程程序更好的利用 cpu 资源,我们通常会先了解机器拥有的 processor 数,有若干手段可以获取这一信息:
- cpuinfo 中查看:比如上文中的 cat /proc/cpuinfo | grep “processor” | wc -l
- top 命令查看:cpu0,cpu1,…
- 编程:比如在 Java 中用 Runtime.getRuntime().availableProcessors()
具体在多线程程序中设置线程数多大,对计算密集型的程序有的建议是 processor count + 1,有的建议是 processor count 的 1.5 倍,都是经验值,实测为准。
小结
- 一台完整的计算机可能包含一到多个物理 cpu
- 从单个物理 cpu (physical cpu)的角度看,其可能是单核心、双核心甚至多核心的
- 从单个核心(core)的角度看,还有 SMT / HT 等技术让每个 core 对计算机操作系统而言用起来像多个物理 core 差不多
总的逻辑 cpu 数 = 物理 cpu 数 每颗物理 cpu 的核心数 每个核心的超线程数
cpu 线程与进程关系
进程与线程
两种常见解释
1.进程和线程都是一个时间段的描述,是CPU工作时间段的描述。
2.进程是资源分配的最小单位,线程是CPU调度的最小单位解释:
- CPU太快了,只有缓存存储器SRAM才能勉强追上它的速度,因此,一台机器上同时开30个程序,CPU可以把这30个程序变成顺序执行,每个只执行一小段,立马切换到下一个程序,再执行一小段,再切回来,人是无感知的。
- 一个程序准备开始执行的时候,相关资源必须要准备好,比如RAM地址,显卡,磁盘资源,这些准备好的东西打包一起就叫做上下文环境,然后CPU开始执行程序A,当然只执行了一小段时间,CPU就要切换到别的程序执行B,以保证几个程序的并发,切换之前要把A的上下问状态保存起来,下次切回来的时候接着用。
- 因此,进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文
- 进程的颗粒度太大,每次都要有上下的调入,保存,调出。线程就是进程的小分支,比如进程A有a,b,c三个线程,那么线程a,b,c就共享了进程A的上下文环境,成为了更细小的执行时间。
程序中的线程与CPU线程
看到这里会懵逼,假设一台8CPU32核的服务器,是不是跑的程序最多只能开32个线程呢?
答案当然是否定的,我们常说的进程中的线程,与CPU的线程,虽然都叫线程,但完全不是一回事。
程序的线程是软件概念,一个程序可以有多个线程,可以在一个CPU核上轮流并发执行。
CPU的线程是硬件的概念,就是核。八线程就是能让八个线程并行执行。linux中的线程
暂时来不及总结,原文链接有。
概念
脚手架、框架、架构
]]>
- 脚手架是指一个项目模板,通过这个模板可以生成固定模板的项目。
- 框架一般是说应用框架,就是别人已经搭建好的成熟组件,我们只需要填代码就行,比如Spring
Boot就是一个框架,我们要开发spring应用,就可以在这个框架里面按照它的规范去写代码。- 架构是指解决特定业务场景的技术解决方案。
- 工具 +计算机基础知识 + +web + +操作系统 - @@ -842,10 +842,10 @@office +VM -IDE +nginx -编辑器 +编译 -快捷键 +环境变量 -vscode +Cookie -visio +Session -postman +密码学 + +硬件知识 + +线程、进程 - 运维 -Linux +运维 +git 代码管理 @@ -912,35 +912,6 @@ -- - - -Jenkins知识点 - -/posts/10218/ - -流水线语法找不到模板 - - -
有时候在“流水线语言”板块找不到模板,即使安装了相对于的插件。如上图,安装了publish over ssh插件就会出现这个选项,但是当时没有。解决办法:
重启jenkins。初始域名后面加/restartip:port/restart
Publish over ssh连接失败
解决办法:
这里要填密钥的密码无法执行远程脚本
背景:
创建一个jenkins作业,通过ssh在另一台服务器上运行脚本,实现从harbor仓库拉取docker镜像,并运行。
问题: 运行jenkins作业/流水线,在对应的服务器没有镜像和运行的容器,且构建过程没有错误输出。分析:
由于是照着黑马程序员的视频和资料来的,由于输出的信息不太一样,以为是哪里操作有问题,或者脚本不对,但是重复所有过程和按照网上教程修改脚本均没有成功。然后单独运行脚本,发现出错,类似于下图。反应过来是因为Docker没有把Harbor加入信任列表中,
加入就好
vim /etc/docker/daemon.json
再次构建出现了错误信息,搜索得知是因为这个命令默认有个时间限制,超过这个时间限制就会出错,断开,类似于联网超时。]]>把时间改为0就好
- - - - -运维 - -- - - -运维 - -持续集成 - -Jenkins - -+ + 零散知识 @@ -983,6 +954,35 @@+ + + diff --git a/tags/CFD/index.html b/tags/CFD/index.html index 6be4bcdc0..6c426d623 100644 --- a/tags/CFD/index.html +++ b/tags/CFD/index.html @@ -369,289 +369,289 @@Jenkins知识点 + +/posts/10218/ + +流水线语法找不到模板 + + +
有时候在“流水线语言”板块找不到模板,即使安装了相对于的插件。如上图,安装了publish over ssh插件就会出现这个选项,但是当时没有。解决办法:
重启jenkins。初始域名后面加/restartip:port/restart
Publish over ssh连接失败
解决办法:
这里要填密钥的密码无法执行远程脚本
背景:
创建一个jenkins作业,通过ssh在另一台服务器上运行脚本,实现从harbor仓库拉取docker镜像,并运行。
问题: 运行jenkins作业/流水线,在对应的服务器没有镜像和运行的容器,且构建过程没有错误输出。分析:
由于是照着黑马程序员的视频和资料来的,由于输出的信息不太一样,以为是哪里操作有问题,或者脚本不对,但是重复所有过程和按照网上教程修改脚本均没有成功。然后单独运行脚本,发现出错,类似于下图。反应过来是因为Docker没有把Harbor加入信任列表中,
加入就好
vim /etc/docker/daemon.json
再次构建出现了错误信息,搜索得知是因为这个命令默认有个时间限制,超过这个时间限制就会出错,断开,类似于联网超时。]]>把时间改为0就好
+ + + + +运维 + ++ + + +运维 + +持续集成 + +Jenkins + +- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + chip-active " + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + chip-default " + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/Cookie/index.html b/tags/Cookie/index.html index 3eea81658..5882f2cd9 100644 --- a/tags/Cookie/index.html +++ b/tags/Cookie/index.html @@ -369,289 +369,289 @@- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + chip-default " + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + chip-active " + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/DFS/index.html b/tags/DFS/index.html index 7b3176c9e..24c94bab3 100644 --- a/tags/DFS/index.html +++ b/tags/DFS/index.html @@ -369,289 +369,289 @@- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + chip-active " + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + chip-default " + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/Docker/index.html b/tags/Docker/index.html index 07aadb7b4..2b856e2c2 100644 --- a/tags/Docker/index.html +++ b/tags/Docker/index.html @@ -369,289 +369,289 @@- + Docker - 1 + chip-default " + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + chip-active " + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 @@ -1081,7 +1081,7 @@-+diff --git a/tags/Golang/index.html b/tags/Golang/index.html index 05e2ba3f5..2c1374ffe 100644 --- a/tags/Golang/index.html +++ b/tags/Golang/index.html @@ -369,289 +369,289 @@- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + chip-default " + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + chip-active " + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/IDE/index.html b/tags/IDE/index.html index 44f6ede40..713ffc20a 100644 --- a/tags/IDE/index.html +++ b/tags/IDE/index.html @@ -369,289 +369,289 @@- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + chip-active " + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + chip-default " + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/Java/index.html b/tags/Java/index.html index a92c3d99c..5947c54e8 100644 --- a/tags/Java/index.html +++ b/tags/Java/index.html @@ -369,289 +369,289 @@- + Docker - 1 + data-tagname="动态规划" style="background-color: #F9EBEA;">动态规划 + 2 - + 运维 - 9 + data-tagname="数据结构" style="background-color: #F5EEF8;">数据结构 + 2 - + Linux - 7 + data-tagname="递归" style="background-color: #D5F5E3;">递归 + 1 - + Slurm + data-tagname="DFS" style="background-color: #E8F8F5;">DFS 1 - + 深度学习代码问题 - 3 + data-tagname="数据库" style="background-color: #FEF9E7;">数据库 + 1 - + 深度学习理论知识 - 1 + data-tagname="Linux" style="background-color: #F8F9F9;">Linux + 7 - + 循环神经网络(RNN) - 1 + data-tagname="运维" style="background-color: #82E0AA;">运维 + 9 - + 卷积神经网络(CNN) + data-tagname="MySQL" style="background-color: #D7BDE2;">MySQL 1 - + 深度学习数据操作 + data-tagname="Docker" style="background-color: #A3E4D7;">Docker 1 - + pytorch与cuda安装 - 1 + data-tagname="git" style="background-color: #85C1E9;">git + 2 - + 数据库 + data-tagname="代码管理" style="background-color: #F8C471;">代码管理 1 - + MySQL + data-tagname="sed" style="background-color: #F9E79F;">sed 1 - + sed - 1 + data-tagname="深度学习代码问题" style="background-color: #82E0AA;">深度学习代码问题 + 3 - + Golang + data-tagname="深度学习理论知识" style="background-color: #F9E79F;">深度学习理论知识 1 - + git - 2 + data-tagname="循环神经网络(RNN)" style="background-color: #D5F5E3;">循环神经网络(RNN) + 1 - + 代码管理 + data-tagname="卷积神经网络(CNN)" style="background-color: #F8F9F9;">卷积神经网络(CNN) 1 - + 正则表达式 + data-tagname="深度学习数据操作" style="background-color: #D5F5E3;">深度学习数据操作 1 - + singularity + data-tagname="pytorch与cuda安装" style="background-color: #F5EEF8;">pytorch与cuda安装 1 - + ssh + data-tagname="Slurm" style="background-color: #F5EEF8;">Slurm 1 - + Vim - 1 + data-tagname="Vue" style="background-color: #D5F5E3;">Vue + 2 - + 持续集成 + data-tagname="React" style="background-color: #F9EBEA;">React 2 - + gcc - 1 + data-tagname="JavaScript" style="background-color: #85C1E9;">JavaScript + 3 - + Linux权限用法 - 1 + data-tagname="Json" style="background-color: #F9E79F;">Json + 2 - + make&cmake + data-tagname="ssh" style="background-color: #F8F9F9;">ssh 1 - + 牛客网 + data-tagname="Vim" style="background-color: #D5F5E3;">Vim 1 - + Vue - 2 + data-tagname="Golang" style="background-color: #F5EEF8;">Golang + 1 - + React - 2 + data-tagname="牛客网" style="background-color: #D7BDE2;">牛客网 + 1 - + JavaScript - 3 + data-tagname="singularity" style="background-color: #85C1E9;">singularity + 1 - + Json - 2 + data-tagname="并行计算" style="background-color: #F5EEF8;">并行计算 + 1 - + npm + data-tagname="CFD" style="background-color: #F9EBEA;">CFD 1 - + yarn + data-tagname="向量化" style="background-color: #E8F8F5;">向量化 1 - + 梯子 + data-tagname="容器" style="background-color: #85C1E9;">容器 1 @@ -666,406 +666,406 @@ - + 电源 + data-tagname="正则表达式" style="background-color: #FFF;">正则表达式 1 - + VM - 2 + data-tagname="office" style="background-color: #A3E4D7;">office + 1 - + 设计模式 - 1 + data-tagname="IDE" style="background-color: #F9E79F;">IDE + 2 - + nginx - 1 + data-tagname="编辑器" style="background-color: #85C1E9;">编辑器 + 2 - + 编译 + data-tagname="快捷键" style="background-color: #FEF9E7;">快捷键 1 - + 环境变量 - 1 + data-tagname="vscode" style="background-color: #F9E79F;">vscode + 2 - + Cookie + data-tagname="visio" style="background-color: #F8C471;">visio 1 - + Session + data-tagname="postman" style="background-color: #FFF;">postman 1 - + 密码学 + data-tagname="发烧" style="background-color: #F9EBEA;">发烧 1 - + 硬件知识 + data-tagname="做饭" style="background-color: #F9E79F;">做饭 1 - + 线程、进程 + data-tagname="金融" style="background-color: #A3E4D7;">金融 1 - + 动态规划 - 2 + data-tagname="思考" style="background-color: #FEF9E7;">思考 + 1 - + 数据结构 - 2 + data-tagname="求职" style="background-color: #85C1E9;">求职 + 1 - + 递归 + data-tagname="政治" style="background-color: #F9EBEA;">政治 1 - + DFS + data-tagname="照片模糊处理" style="background-color: #FFF;">照片模糊处理 1 - + Jenkins + data-tagname="win10" style="background-color: #F9E79F;">win10 1 - + Spring + data-tagname="专利" style="background-color: #E8F8F5;">专利 1 - + Java - 2 + chip-default " + data-tagname="bat" style="background-color: #85C1E9;">bat + 1 - + SpringBoot - 2 + data-tagname="博客" style="background-color: #F8F9F9;">博客 + 1 - + Typescript + data-tagname="带宽" style="background-color: #D7BDE2;">带宽 1 - + 并行计算 + data-tagname="数学" style="background-color: #D7BDE2;">数学 1 - + CFD + data-tagname="npm" style="background-color: #F9EBEA;">npm 1 - + 向量化 + data-tagname="yarn" style="background-color: #FEF9E7;">yarn 1 - + 容器 + data-tagname="梯子" style="background-color: #82E0AA;">梯子 1 - + 发烧 - 1 + data-tagname="持续集成" style="background-color: #FFF;">持续集成 + 2 - + 做饭 + data-tagname="gcc" style="background-color: #F8C471;">gcc 1 - + Yarn + data-tagname="Linux权限用法" style="background-color: #FEF9E7;">Linux权限用法 1 - + office + data-tagname="make&cmake" style="background-color: #E8F8F5;">make&cmake 1 - + IDE - 2 + data-tagname="电源" style="background-color: #A3E4D7;">电源 + 1 - + 编辑器 - 2 + data-tagname="设计模式" style="background-color: #85C1E9;">设计模式 + 1 - + 快捷键 - 1 + data-tagname="VM" style="background-color: #F9EBEA;">VM + 2 - + vscode - 2 + data-tagname="nginx" style="background-color: #FEF9E7;">nginx + 1 - + visio + data-tagname="编译" style="background-color: #F8F9F9;">编译 1 - + postman + data-tagname="环境变量" style="background-color: #FEF9E7;">环境变量 1 - + 照片模糊处理 + data-tagname="Cookie" style="background-color: #D7BDE2;">Cookie 1 - + win10 + data-tagname="Session" style="background-color: #FEF9E7;">Session 1 - + 专利 + data-tagname="密码学" style="background-color: #F8F9F9;">密码学 1 - + bat + data-tagname="硬件知识" style="background-color: #A3E4D7;">硬件知识 1 - + 博客 + data-tagname="线程、进程" style="background-color: #F8C471;">线程、进程 1 - + 带宽 + data-tagname="Jenkins" style="background-color: #D7BDE2;">Jenkins 1 - + 数学 + data-tagname="Typescript" style="background-color: #F9EBEA;">Typescript 1 - + 思考 + data-tagname="Spring" style="background-color: #F9E79F;">Spring 1 - + 求职 - 1 + chip-active " + data-tagname="Java" style="background-color: #FFF;">Java + 2 - + 政治 - 1 + data-tagname="SpringBoot" style="background-color: #F9E79F;">SpringBoot + 2 - + 金融 + data-tagname="Yarn" style="background-color: #A3E4D7;">Yarn 1 diff --git a/tags/JavaScript/index.html b/tags/JavaScript/index.html index 13da1a0a1..df5375ae6 100644 --- a/tags/JavaScript/index.html +++ b/tags/JavaScript/index.html @@ -369,289 +369,289 @@