[转帖]自动化运维:一键自动化脚本-shell
https://www.cnblogs.com/luoahong/articles/8456203.html
shell函数
1、分别在服务器和客户端上创建www用户
|
1
2
|
useradd wwwid wwww |
- 所有的web服务,都应该使用普通用户,所有的web服务都不应该监听80端口,除非负载均衡。8080
- 普通用户能启动80端口吗?通过和科技,比如给命令设置suid
- 生产指定uid
2、保证www用户登录其他的节点都不要输入密码
服务器端:
|
1
2
3
4
5
6
7
8
9
10
|
[root@node1 ~]# useradd www[root@node1 ~]# id www[root@node1 ~]# passwd www[root@node1 ~]# su www[root@node1 ~]# cd /home/www/[www@node1 ~]$ ssh-copy-id -i .ssh/id_rsa.pub www@172.16.14.116[www@node1 ~]$ ssh 172.16.14.116Last failed login: Sat Jan 13 09:56:41 CST 2018 from 172.16.14.115 on ssh:nottyThere were 3 failed login attempts since the last successful login.Last login: Sat Jan 13 09:23:02 2018 |
客户端:
|
1
2
3
4
5
6
7
8
9
10
|
[root@node2 ~]# useradd www[root@node2 ~]# id www[root@node2 ~]# passwd www[root@node2 ~]# su www[root@node2 ~]# cd /home/www/[www@node2 ~]$ ssh-copy-id -i .ssh/id_rsa.pub www@172.16.14.115[www@node2 ~]$ ssh 172.16.14.115Last failed login: Sat Jan 13 09:56:41 CST 2018 from 172.16.14.115 on ssh:nottyThere were 3 failed login attempts since the last successful login.Last login: Sat Jan 13 09:23:02 2018 |
3、写一个复杂的脚本
先把框架写出来,使用echo来测试框架的流程是否正确
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#!/bin/bash # Shell Env SHELL_NAME="deploy.sh" SHELL_DIR="/home/www" SHELL_LOG="{SHELL_DIR}/${SHELL_NAME}.log" # Code Env CODE_DIR="/deploy/code/deploy/" TMP_DIR="/deploy/config" TAR_DIR="/deploy/tar" LOCK_FILE="/tmp/deploy.lock" usage(){ echo $"Usage: $0[ deploy|rollback]" } shell_lock(){ touch ${LOCK_FILE} } shell_unlock(){ rm -f ${LOCK_FILE} } code_get(){ echo code_get; sleep 2; } code_build(){ echo code_build; sleep 2; } code_config(){ echo code_config; sleep 2; } code_tar(){ echo code_tar; sleep 2; } code_scp(){ echo code_scp; sleep 1; } cluster_node_remove(){ echo cluster_node_remove; sleep 1; } code_deploy(){ echo code_deploy; } config_diff(){ echo config__diff; } code_test(){ echo code_test; } cluster_node_in(){ echo cluster_node_in; } main(){ if [ -f $LOCK_FILE ];then echo "Deploy is running"&& exit; fi DEPLOY_METHOD=$1 case $DEPLOY_METHOD in deploy) shell_lock; code_get; code_build; code_config; code_tar; code_scp; cluster_node_remove; code_deploy; config_diff; code_test; cluster_node_in; shell_unlock; ;; rollback) shell_lock; rollback; shell_unlock; ;; *) usage; esac } |
4、什么也没提示?在末尾添加 man $1
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
main(){ DEPLOY_METHOD=$1 case $DEPLOY_METHOD in deploy) shell_lock; ;; rollback) shell_lock; ;; *) usage; esac}main $1 |
本节小结:
1、凡是不记录日志的脚本就是耍流氓

2、这个脚本能不能多个人同时执行?
1、最好不要,但是运维团队有很多人,我如何知道别人有没有执行?
答:我写一个锁文件

1、锁文件放那?
答:放在/tmp/deploy.lock" 因为放在下www没有权限
2、系统的锁文件放哪里?
|
1
|
/var/run/lock |
3、看不出来?
答:sleep60秒

4、不是脚本的$1匙函数的$1

5、在两台电脑上都要用到所以定义为变量

2、功能实现
1、日志函数
|
1
2
3
4
5
6
7
8
9
10
11
|
#Date/Time VeriablesLOG_DATE='date "+%Y-%m-%d"'LOG_TIME='date "+%H-%M-%S"'CDATE=$(date "+%Y-%m-%d")CTIME=$(date "+%H-%M-%S")....writelog(){ LOGINFO=$1 echo "${CDATE} ${CTIME}: ${SHELL_NAME}: ${LOGINFO}" >> ${SHELL_LOG}} |
1、希望在很多地方记录日志
echo一行,写到一个文件里,记日志还要记时间
方法1:写一个日志的函数,每次调用这个函数
方法2:在每一个函数里写一个echo,然后写在那个位置,还要记时间
2、日志函数的好处?
- 这个函数可以复制,以后写别的脚本直接改改就可以
- 每一个函数里都写一个函数
3、shell是如何解析的?
从上倒下逐行执行
4、遇到函数怎么办?
先加载不执行
5、日志的内容从哪来?
从参数来:$1
6、脚本名称:
当前日期+脚本名称+日志内容
难保你以后会写在一起所以要区分开
7、时间是不是不能变?
我就不需要它变:
1、打包的时候不能变,包里有有日期和时间包名不能变
2、打包如何命名?
是scp时间不对,包就找不着了
|
1
2
|
LOG_DATE='date "+%Y-%m-%d"'LOG_TIME='date "+%H-%M-%S"' |
- 一个是让执行 记日志用的
- 一个不让执行 做别的用处
- 已经执行了,在后面就不变了
2、get代码函数
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#Code EnvPRO_NAME="web-demo"CODE_DIR="/deploy/code/web-demo"CONFIG_DIR="/deploy/config/web-demo"TMP_DIR="/deploy/tmp"LOCK_FILE="/tmp/deploy.lock".......code_get(){ writelog "code_get"; cd $CODE_DIR && echo "git pull" cp -r ${CODE_DIR} ${TMP_DIR}/ API_VERL=$(git show |grep commit | cut -d ' ' -f2) API_VER=$(echo ${API_VERL:0:6}) |
1、代码应该放在那?
放在CODE_DIR="/deploy/code/web-demo"目录下
2、配置文件能直接放CODE_DIR这吗?
不能,专门用于git更新的目录
如果你把文件拷贝到这里,所有的包里面都有这个文件
一不小心多放了一个,那个可不会pull
3、怎样区分是仓库的还是我copy过来的?
也能区分 看git状态,本地状态 正常区分不了
更新完之后copy走,放着也行出故障了你就知道是什么意思了!
4、复制到哪?
TMP_DIR
5、为什么对web-demo要重命名?
- 复制过去要重命名
- 打包的时候还要重命名
- 每次都覆盖那就乱了
6、要怎样重命名?
时间+版本号?
1、svn怎样获取版本号?
2、git如何获取版本号?
API_VERL=$(git show |grep commit | cut -d ' ' -f2)
3、配置文件函数
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#Code EnvPRO_NAME="web-demo"CODE_DIR="/deploy/code/web-demo"CONFIG_DIR="/deploy/config/web-demo"TMP_DIR="/deploy/tmp"LOCK_FILE="/tmp/deploy.lock"......code_config(){ writelog "code_config" /bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}" PKG_NAME="${PKG_NAME}"_"$API_VER"_"${CDATE}-${CTIME}" cd ${TMP_DIR} && mv ${PKG_NAME} ${PKG_NAME}}} |
1、我是哪个项目的配置文件?
|
1
|
CONFIG_DIR="/deploy/config/web-demo" |
2、为什么加/bin/cp -r?
|
1
|
/bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}" |
1、有可能我文件下还有目录呢!
2、我拷贝的那个目录下有那个配置文件
有一测试有权限,犯二提交了一个相同的配置文件 你没有覆盖连到测试库了
大家打开的都是测试的库
开发可以错,测试可以错,运维不可以错 为什么你上线的时候也没发现?
每一个小细节都是有意义不是瞎写
3、复制和打包可以放在一个里面,为什么把包名做成一个变量?
- 很多地方都用到
- 包名很长
- 包名本身也包含变量
4、为什么要全写成变量
因为不只为这一个脚本,写别的脚本改改就可以啦!
5、为什么tmp需要定期进行清理?
部署几个月可以,时间久磁盘就满了
有时间了就好删除了,解决了各种方式
只有版本号你怎样删?把15年的全删了
4、打包函数
|
1
2
3
4
5
|
code_tar(){ writelog "code_tar" cd ${TMP_DIR} && tar czf ${PKG_NAME}.tar.gz ${PKG_NAME} writelog "${PKG_NAME}.tar.gz"} |
1、为什么要写&&?
函数和函数之间可不知道上一级目录是什么!!!
不单独搞一行,如果目录不存在进去了可能不是你想要的
5、scp到目标服务器
|
1
2
3
4
5
6
7
|
code_scp(){ writelog "code_scp" for node in $PRE_LIST;do for node in $GROUP1_LIST;do scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/ done} |
1、统一用一个包的好处?
完全可以写一个for循环
我要加机器 在列表里加一行
减机器 在列表里减去一行
标准化的好处
2、为什么不能直接写在/opt?
- 没有权限
- 在opt创建一个/opt/webroot/复制到这
6、部署函数
|
1
2
3
4
5
6
7
8
|
group1_deploy(){ writelog "remove from cluster" for node in $GROUP1_LIST;do ssh $node "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" done scp ${CONFIG_DIR}/other/192.168.56.12.crontab.xml 192.168.56.12:/webroot/web-demo/crontab.xml} |
1、项目之间应该保持独立才对
2、你的目录放在那?
所有生产的web服务器的家目录都写在/webroot的项目名称下
3、为什么先创建软链接然后在复制差异文件?
路径写的少,要不然你写到解压后的路径下
如果没有生成我就不复制
生产部署的时候,没部署成功结果scp复制过去了
4、第一次手动创建一个因为 没有会报错。
|
1
2
3
4
|
su -wwwcd /webroot/touch web-demo用salt就要先touch文件 |
5、&&不能去掉,因为以后部署时候我要先删除才能软链接
6、一个软连接连一毫秒都花不了
3、脚本扩展
1、每个节点上各装一个apache
|
1
|
yum install httpd -y |
2、修改配置文件以下两处
|
1
|
vim /etc/httpd/conf/httpd.conf |

1、测试函数
|
1
2
3
4
5
6
7
8
|
url_test(){ URL=$1 curl -s --head $URL |grep '200 ok' if [ $? -ne 0 ];then shell_unlock; echo "test error" && exit; fi} |
1、测试一能访问就加入集群不能访问就移除集群
2、部署一个测一个通了才能加到集群里
生产是一个组一个组测试
并行和串行相结合
每一个组一个预生产节点
直接部署第二个节点
2、主函数
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
main(){ if [ -f $LOCK_FILE ]; then echo "Deploy is running" && exit; fi DEPLOY_METHOD=$1 ROLLBACK_VER=$2 case $DEPLOY_METHOD in deploy) shell_lock; code_get; code_build; code_config; code_tar; code_scp; pre_deploy; pre_test; group1_deploy; group1_test; shell_unlock; ;; rollback) shell_lock; rollback $ROLLBACK_VER; shell_unlock; ;; *) usage; esac}main $1 $2 |
1、先判断是否有文件,存在说明有人在执行直接退出
2、你是要部署还是要回滚,要是是部署先锁住脚本
从git上获取文件
进行编译
复制配置文件进去
打包并重命名
scp到所有机器(不分组)
晚上要做一个不算停机维护,所有机器都需要同时重启
涉及到数据一致性
组一部署集群
测试组一集群
4、秒级回滚

在某个地方记住上一个版本是啥,部署把版本写在一个文件里(紧急回滚的一个函数),然后读这个文件
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
rollback(){if [ -z $1 ];then shell_unlock; echo "please input rollback version" && exit;fi case $1 in list) ls -l /opt/webroot/*.tar.gz ;; *) rollback_fun $1 esac} |
部署还是回滚$1,回滚到那个版本是$2
传list的我就列出来,不传我就回滚
我可以只部署预生产,我部署机肯定有我其他的节点没有
|
1
2
3
4
|
rollback_fun(){ for node in $ROLLBACK_LIST;do ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo" done |
1、如果list存在我就执行,不存在就结束for循环
2、远程ssh执行命令,引起来才能当成一个,因为中间还有空格

3、脚本的$2传到回滚函数里就是$1

5、gitlab部署和回滚
安装gitlab私有仓库,地址见运维社区gitlab
1、登陆修改root密码
2、备份:每天备份每小时也行
越频繁越好
分布式每个人的本地都有
6、完整脚本构造
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#!/bin/bash#Dir Listmkdir -p /deploy/code/web-demomkdir -p /deploy/config/web-demo/basemkdir -p /deploy/config/web-demo/othermkdir -p /deploy/tarmkdir -p /deploy/tmpmkdir -p /opt/webrootmkdir /webrootchown -R www.www /deploychown -R www.www /opt/webrootchown -R www.www /webroot#Node ListPRE_LIST="192.168.56.11"GROUP1_LIST="192.168.56.12"ROLLBACK_LIST="192.168.56.11 192.168.56.12"#Date/Time VeriablesLOG_DATE='date "+%Y-%m-%d"'LOG_TIME='date "+%H-%M-%S"'CDATE=$(date "+%Y-%m-%d")CTIME=$(date "+%H-%M-%S")#Shell EnvSHELL_NAME="deploy_all.sh"SHELL_DIR="/home/www/"SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.log"#Code EnvPRO_NAME="web-demo"CODE_DIR="/deploy/code/web-demo"CONFIG_DIR="/deploy/config/web-demo"TMP_DIR="/deploy/tmp"LOCK_FILE="/tmp/deploy.lock"usage(){ echo $"Usage: $0{deploy|rollback[ list|version ]}"}writelog(){ LOGINFO=$1 echo "${CDATE} ${CTIME}: ${SHELL_NAME}: ${LOGINFO}" >> ${SHELL_LOG}}shell_lock(){ touch ${LOCK_FILE}}url_test(){ URL=$1 curl -s --head $URL |grep '200 ok' if [ $? -ne 0 ];then shell_unlock; echo "test error" && exit; fi}shell_unlock(){ rm -f ${LOCK_FILE}}code_get(){ writelog "code_get"; cd $CODE_DIR && echo "git pull" cp -r ${CODE_DIR} ${TMP_DIR}/ API_VERL=$(git show |grep commit | cut -d ' ' -f2) API_VER=$(echo ${API_VERL:0:6})}code_build(){ echo code_Build}code_config(){ writelog "code_config" /bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}" PKG_NAME="${PKG_NAME}"_"$API_VER"_"${CDATE}-${CTIME}" cd ${TMP_DIR} && mv ${PKG_NAME} ${PKG_NAME}}}code_tar(){ writelog "code_tar" cd ${TMP_DIR} && tar czf ${PKG_NAME}.tar.gz ${PKG_NAME} writelog "${PKG_NAME}.tar.gz"}code_scp(){ writelog "code_scp" for node in $PRE_LIST;do for node in $GROUP1_LIST;do scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/ done}pre_deploy(){ writelog "remove from cluster" ssh $PRE_LIST "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" ssh $PRE_LIST "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo"}pre_test(){ url_test "http://${PRE_LIST}/index.html" echo "add to cluster"}group1_deploy(){ writelog "remove from cluster" for node in $GROUP1_LIST;do ssh $node "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" done scp ${CONFIG_DIR}/other/192.168.56.12.crontab.xml 192.168.56.12:/webroot/web-demo/crontab.xml}group1_test(){ url_test "http://192.168.56.12/index.html" echo "add to cluster"}rollback_fun(){ for node in $ROLLBACK_LIST;do ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo" done}rollback(){if [ -z $1 ];then shell_unlock; echo "please input rollback version" && exit;fi case $1 in list) ls -l /opt/webroot/*.tar.gz ;; *) rollback_fun $1 esac}main(){ if [ -f $LOCK_FILE ]; then echo "Deploy is running" && exit; fi DEPLOY_METHOD=$1 ROLLBACK_VER=$2 case $DEPLOY_METHOD in deploy) shell_lock; code_get; code_build; code_config; code_tar; code_scp; pre_deploy; pre_test; group1_deploy; group1_test; shell_unlock; ;; rollback) shell_lock; rollback $ROLLBACK_VER; shell_unlock; ;; *) usage; esac}main $1 $2 |
转载地址:https://github.com/unixhot/deploy-shell
[转帖]自动化运维:一键自动化脚本-shell的更多相关文章
- Linux centosVMware 自动化运维认识自动化运维、启动salt相关服务、saltstack配置认证、salt-key命令用法、saltstack远程执行命令、saltstack - grains、saltstack – pillar
一.认识自动化运维 传统运维效率低,大多工作人为完成 传统运维工作繁琐,容易出错 传统运维每日重复做相同的事情 传统运维没有标准化流程 传统运维的脚本繁多,不能方便管理 自动化运维就是要解决上面所有问 ...
- 自动化运维——一键安装MySQL
根据项目需要,前段时间在搞EMM系统各种安装包的自动化部署工作,主要包括一键安装和一键启动\停止功能.总结记录下来,以供后用. 本文主要是自动安装MySQL5.7.11版,Linux版脚本在CentO ...
- 自动化运维——MySQL备份脚本(二)
使用if语句编写MySQL备份脚本 代码: #!/bin/bash #auro backup mysql db #by steve yu #define backup path BAK_DIR=/da ...
- CheungSSH国产自动化运维工具开源Web界面
CheungSSH web2.0 发布文档 CheungSSH 简介 CheungSSH是一款国人自主研发的Linux运维自动化管理服务器软件,秉着为企业降低运营成本,解放管理员双手和自动化生产的理念 ...
- 使用Ansible实现数据中心自动化运维管理
长久以来,IT 运维在企业内部一直是个耗人耗力的事情.随着虚拟化的大量应用.私有云.容器的不断普及,数据中心内部的压力愈发增加.传统的自动化工具,往往是面向于数据中心特定的一类对象,例如操作系统.虚拟 ...
- 技术沙龙|京东云DevOps自动化运维技术实践
自动化测试体系不完善.缺少自助式的持续交付平台.系统间耦合度高服务拆分难度大.成熟的DevOps工程师稀缺,缺少敏捷文化--这些都是DevOps 在落地过程中,或多或少会碰到的问题,DevOps发展任 ...
- 自动化运维工具-Ansible之7-roles
自动化运维工具-Ansible之7-roles 目录 自动化运维工具-Ansible之7-roles Ansible Roles基本概述 Ansible Roles目录结构 Ansible Roles ...
- 自动化运维工具-Ansible之5-流程控制
自动化运维工具-Ansible之5-流程控制 目录 自动化运维工具-Ansible之5-流程控制 playbook条件语句 单条件 多条件 多条件运算 示例 playbook循环语句 with_ite ...
- 自动化运维工具-Ansible之2-ad-hoc
自动化运维工具-Ansible之2-ad-hoc 目录 自动化运维工具-Ansible之2-ad-hoc Ansible ad-hoc Ansible命令模块 Ansible软件管理模块 Ansibl ...
- 真正云原生的智能运维体系,阿里云发布ECS自动化运维套件
云计算的发展,推动了自动化运维.DevOps.AIOps 等趋势的兴起,在业务快速变化的今天,企业希望通过一套自动化运维的专家系统提高运维效率,为业务提供支撑. 传统的方式下,打造一套成熟的 DevO ...
随机推荐
- 理论+示例,详解GaussDB(DWS)资源管理
摘要:合理地管理和分配系统资源,是保证数据库系统稳定高效运行的关键. 本文分享自华为云社区<GaussDB(DWS)资源管理能力介绍与应用示例>,作者: 门前一棵葡萄树 . 一.资源管理能 ...
- 累加求和 1~ n求和
a=1 ~ n 的求和 \[\sum_{a=1}^n a \] 公式:(首项 + 末项) * 项数/2 如果 a=1. n = 10 => (1+10)10/2 = 55 Python 代码 a ...
- Sublime Text Python 代码提示插件 Anaconda
1.Ctrl+Shift+P -> install package 安装 Anaconda 查看Python 安装路径 { "python_interpreter":&quo ...
- 深入了解 ReadDirectoryChangesW 并应用其监控文件目录
简介 监视指定目录的更改,并将有关更改的信息打印到控制台,该功能的实现不仅可以在内核层,在应用层同样可以.程序中使用 ReadDirectoryChangesW 函数来监视目录中的更改,并使用 FIL ...
- VWAP 订单的最佳执行方法:随机控制法
更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 引言:相关研究 在当今的投资领域,算法交易正迅速成为客户获取和清算股票头寸的首选方法. 通常,被委托者会 ...
- 解读IDC《中国视频云市场跟踪》最新报告,视频云将趋向何方?
国际权威咨询公司IDC发布<中国视频云市场跟踪(2021 H2)>报告,阿里云连续四年稳居中国视频云整体市场份额第一,整体市场份额占比达26.9%. 时至2021,中国视频云的数字背后 近 ...
- async await 异步下载 异步代码加锁 lock 异步缓存
async await 异步下载 异步代码加锁 lock 异步缓存 FTP异步下载代码: /// <summary> /// 异步下载文件 /// </summary> /// ...
- Vue | vuex安装失败解决的方法实例
Vuex是一个专为Vue.js应用程序开发的状态管理模式 下面这篇文章主要给大家介绍了关于vuex安装失败解决的方法,文中通过图文介绍的非常详细,需要的朋友可以参考下 1.报错信息: npm ERR! ...
- 倾斜摄影技术构建图扑 WebGIS 智慧展馆
前言 智慧展馆通过"云大物移智链"等技术将"物"(展品.设备.环境等)进行互联,并感知"人"(工作人员.观众等)的行为.结合 GIS.BIM ...
- python使用sql批量插入数据+查看执行的语句+动态sql创建表+动态创建索引
class Test(): cursor = connection.cursor() data_to_insert = [] sql = "INSERT INTO test_t (id, n ...