MongoDb初识

MongoDb初识

https://www.cnblogs.com/my-blogs-for-everone/articles/9749842.html

MongoDb 命令查询所有数据库列表

1
show dbs

如果想查看当前连接在哪个数据库下面,可以直接输入db
db

切换到test数据库下面
use test

想查看test下有哪些表或者叫collection,可以输入
show collections

想知道mongodb支持哪些命令,可以直接输入help
help

想知道当前数据库支持哪些方法
db.help();

想知道当前数据库下的表或者表collection支持哪些方法,可以使用以下命令
db.user.help(); user为表名

根据条件查找数据
通过条件查询: db.foo.find( { x : 77 } , { name : 1 , x : 1 } )

  1. 超级用户相关:

      #增加或修改用户密码  
    
      db.addUser('admin','pwd')  
    
      #查看用户列表  
    
      db.system.users.find()  
    
      #用户认证  
    
      db.auth('admin','pwd')  
    
      #删除用户  
    
      db.removeUser('mongodb')  
    

    #查看所有用户

      show users  
    
      #查看所有数据库  
    
      show dbs  
    
      #查看所有的collection  
    
      show collections  
    
      #查看各collection的状态  
    
      db.printCollectionStats()  
    
      #查看主从复制状态  
    
      db.printReplicationInfo()  
    
      #修复数据库  
    
      db.repairDatabase()  
    
      #设置记录profiling,0=off 1=slow 2=all  
    
      db.setProfilingLevel(1)  
    
      #查看profiling  
     show profile  
    
      #拷贝数据库  
    
      db.copyDatabase('mail_addr','mail_addr_tmp')  
    
      #删除collection  
    
      db.mail_addr.drop()  
    
      #删除当前的数据库  
    
      db.dropDatabase()  
    
    1. 客户端连接

      /usr/local/mongodb/bin/mongo user_addr -u user -p 'pwd'  
      
    2. 增删改

       #存储嵌套的对象  
      
      db.foo.save({'name':'ysz','address':{'city':'beijing','post':100096},'phone':[138,139]})  
      
      #存储数组对象  
      
      db.user_addr.save({'Uid':'yushunzhi@sohu.com','Al':['test-1@sohu.com','test-2@sohu.com']})  
      
      #根据query条件修改,如果不存在则插入,允许修改多条记录  
      

      db.foo.update({‘yy’:5},{‘$set’:{‘xx’:2}},upsert=true,multi=true)

      #删除yy=5的记录  
      
      db.foo.remove({'yy':5})  
      
      #删除所有的记录  
      

      db.foo.remove()

    3. 索引

      增加索引:1(ascending),-1(descending)  
      
      db.things.ensureIndex({firstname: 1, lastname: 1}, {unique: true});  
      
      #索引子对象  
      
      db.user_addr.ensureIndex({'Al.Em': 1})  
      
      #查看索引信息  
      
      db.deliver_status.getIndexes()  
      
      db.deliver_status.getIndexKeys()  
      
      #根据索引名删除索引  
      

      db.user_addr.dropIndex(‘Al.Em_1’)

    4. 查询

      查找所有  
      
      db.foo.find()  
      
      #查找一条记录  
      
      db.foo.findOne()  
      
      #根据条件检索10条记录  
      
      db.foo.find({'msg':'Hello 1'}).limit(10)  
      
      #sort排序  
      
      db.deliver_status.find({'From':'yushunzhi@sohu.com'}).sort({'Dt',-1})  
      
      db.deliver_status.find().sort({'Ct':-1}).limit(1)  
      

      #count操作

      db.user_addr.count()

      #distinct操作

      db.foo.distinct(‘msg’)
      #>操作

      db.foo.find({“timestamp”: {“$gte” : 2}})

      #子对象的查找

      db.foo.find({‘address.city’:’beijing’})

    5. 管理

      查看collection数据的大小  
      
      db.deliver_status.dataSize()  
      
      #查看colleciont状态  
      
      db.deliver_status.stats()  
      
      #查询所有索引的大小  
      
      db.deliver_status.totalIndexSize()
      
Dockerfile笔记
yapi-docker部署

yapi-docker部署

部署步骤

使用DockerCompose构建部署Yapi.md

填坑

docker images生成部署时。发现docker-entrypoint.sh没有初始化数据库,只有运行代码,没有添加初始化命令:

npm run install-server //安装程序,初始化数据库索引和管理员账号,管理员账号名可在 config.json 配置`

补救措施:

1
2
3
4
5
6
[root@VM-4-16-centos /]# docker exec -it 2e43588c8b1f bash
root@2e43588c8b1f:/# node api/vendors/server/install.js
(node:65) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
log: mongodb load success...
初始化管理员账号成功,账号名:"xxxxx@gmail.com",密码:"ymfe.org"

config.json内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"port": "3000",
"adminAccount": "admin@admin.com",//yapi管理员账号
"timeout":120000,
"db": {
"servername": "192.168.0.1",
"DATABASE": "yapi",
"port": 27017,
"user": "yapi",
"pass": "123456",
"authSource": ""
}
}

参考

Docker部署YApi和使用问题
顶尖 API 文档管理工具 (YAPI)

docker初识

docker初识

Docker

容器与镜像的关系类似于面向对象编程中的对象与类。

Docker 面向对象
容器 对象
镜像

容器与镜像的关系

安装

参考:Ubuntu Docker 安装

docker命令

列出docker的命令帮助,在docker xx 后面 加上 --help 可以查看具体的命令用法和参数注解

cmd-help.sh
1
docker --help

创建镜像

列出所有镜像:

1
docker images -a

获取一个新的ubuntu 13.10版本镜像

1
docker pull ubuntu:13.10

查找centos镜像

1
docker search centos

删除ubuntu镜像

1
docker rmi ubuntu

创建镜像
1、从已经创建的容器中更新镜像,并且提交这个镜像
2、使用 Dockerfile 指令来创建一个新的镜像

创建容器

容器管理命令

code 操作 备注
docker ps 查看正在运行的容器
docker ps -s -a 查看当前所有容器
docker stop 容器名称 停止容器
docker restart 容器名称 重启容器
docker kill 容器名称 杀死容器
docker rm -f 镜像ID或者镜像名 删除已经停止的容器
docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 运行容器
docker run --name mynginx -d nginx:latest 使用docker镜像nginx:latest以后台模式启动一个容器,并将容器命名为mynginx。
  • OPTIONS说明:
    • -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
    • -d: 后台运行容器,并返回容器ID;
    • -i: 以交互模式运行容器,通常与 -t 同时使用;
    • -P: 随机端口映射,容器内部端口随机映射到主机的端口
    • -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
    • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
    • –name=”nginx-lb”: 为容器指定一个名称;
    • –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
    • –dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
    • -h “mars”: 指定容器的hostname;
    • -e username=”ritchie”: 设置环境变量;
    • –env-file=[]: 从指定文件读入环境变量;
    • –cpuset=”0-2” or –cpuset=”0,1,2”: 绑定容器到指定CPU运行;
    • -m :设置容器使用内存最大值;
    • –net=”bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
    • –link=[]: 添加链接到另一个容器;
    • –expose=[]: 开放一个端口或一组端口;
    • –volume , -v: 绑定一个卷

example

运行一个新容器,映射至5600端口,挂载目录

1
docker run -dit -v $PWD/ql/config:/ql/config -v $PWD/ql/log:/ql/log -v $PWD/ql/db:/ql/db -v $PWD/ql/repo:/ql/repo -v $PWD/ql/raw:/ql/raw -v $PWD/ql/scripts:/ql/scripts -v $PWD/ql/jbot:/ql/jbot -p 5600:5600 --name qinglong --hostname qinglong --restart always whyour/qinglong:latest

摘录

docker镜像管理命令
查看本地镜像

docker images #查看当前机器的所有镜像

docker images –q # 查看所用镜像的id

docker search 镜像名称 #搜索镜像,网络中查找需要的镜像

docker pull 镜像名称 #从Docker仓库拉取镜像,名称:版本号

docker push 镜像名称 #推送镜像

docker rmi 镜像名称/镜像id #删除本地机器的镜像

docker rmi docker images -q # 删除所有本地镜像

docker tag 镜像名称:tag 镜像名称:tag #为一个镜像打tag

docker save {image_name} > {new_image_name}.tar #镜像打包成一个tar包

docker load < {image_name}.tar #解压一个镜像tar包

docker查看日志信息
docker logs -f 容器名称 #查看容器日志

docker info #查看docker服务的信息

docker inspect 容器名称 # 获取镜像的元信息,详细信息

与容器交互的命令
进入正在运行的容器
docker exec -it 容器ID或者容器名 /bin/bash

exec的意思是在容器中运行⼀个命令。 /bin/bash是固有写法,作用是因为docker后台必须运行一个进程,否则容器就会退出,在这里表示启动容器后启动 bash。

退出容器
exit #退出也关闭容器;

Ctrl+P+Q #退出不关闭容器

创建启动容器时,使用–v参数设置数据卷
docker run … –v 宿主机目录/文件:容器内目录/文件

数据卷宿主机的一个目录或文件

目录必须是绝对路径

如果目录不存在,会自动创建

可以挂载多个数据卷

拷贝文件
docker cp 主机文件路径 容器ID或容器名:容器路径 #宿主机文件拷贝到容器中

docker cp 容器ID或容器名:容器路径 主机文件路径 #容器文件拷贝到宿主机中

kettle填坑

kettle填坑

写在前面

为了将MSSqlserver数据传输至MySql数据库,一开始的选择有很多,类似DataXES,但是碍于运行环境是windows server 2012,且服务器环境不方便更改,查阅一番后,决定使用kettle进行数据的同步。

  • 环境
    • 系统:windows server 2012R2 64位
    • 内存:8GB
    • JDK:1.8

kettle配置统一参数

一、kettle的常用配置文件说明:

  • ${user.home} 表示WINDOWS的当前用户的文档文件夹,在C:\Users文件夹下

  • ${user.home}/ .kettle /repositories.xml: 该文件保存了用户设置的所有资源库信息,包括资源库名称,资源库需要的数据库连接参数等。该文件中定义的资源库将显示在spoon启动后出现的选择资源库下拉列表中,注意该文件的编码是UTF-8,资源库的名称尽量使用英文。

  • ${user.home}/ kettle/kettle.properties: 该文件保存了转换或作业中需要的变量, spoon启动后会自动加载该文件里定义的变量。

  • ${user.home}/.kettle/shared.xml: 该文件里保存了共享对象,共享对象可以是Database connections, Steps,Slave servers ,Partition schemas ,Cluster schemas。对象共享实质上就是将对象序列化的过程, spoon启动时,会加载shared.xml文件中定义的所有对象。

  • lib\kettle-engine.jar\kettle-jobs.xml: 该文件中定义了Spoon启动时需要加载的作业项。

lib\kettle-engine.jarkettle-partition-plugins.xml: 该文件中定义了Spoon启动时需要加载的分区插件。

  • lib\kettle-engine.jar\kettle- plugins.xml: 该文件中定义了Spoon启动时步骤和作业项插件的加载路径。
  • lib\kettle-engine.jar\kettle-steps.xml: 该文件中定义了Spoon启动时需要加载的转换步骤。

二、修改repositories.xml的位置

找到kettle所在文件夹下的Spoon.bat 文件,在里面查找出Kettle home所在的代码段,在 cd %KETTLE_DIR%代码上一行加上代码:set KETTLE_HOME=D:/AAA/BBB/CCC

  • 如上,表示把文件重新等位到D:/AAA/BBB/CCC这个文件夹下。(注意:文件夹的斜杠和WINDOWS的目录斜杠是反的)
  • 复制 ${user.home} 文件夹下的 .kettle 文件夹到D:/AAA/BBB/CCC目录下。
  • 启动kettle程序。

参考:
Kettle日常使用汇总整理
Kettle使用统一的数据库配置
kettle学习笔记10—配置文件的使用
Kettle环境配置-kettle.properties
部份配置:
部份配置

kettle配置作业、转换

参考:Kettle—使用手册

在配置完成后,执行作业的方式有两种:

  • 使用spoon.bat打开UI程序进行执行
    • 优点:作业执行很稳定,不会异常退出。
    • 缺点:吃内存,配置不高的服务器可能压力大。懂不懂1GB起步。😓
  • 使用Kitchen.bat脚本执行作业
    • 优点:可以结合windows的任务执行计划程序进行定时启动作业,占用内存少,完成后自动退出。
    • 缺点:一些耗时长的任务会有闪退的情况,或者数据量太大,执行时只取了一部分,会造成数据丢失。

配置重抽作业的逻辑

初始化时在作业里设置开始时间、结束时间变量值,执行转换前判断变量值,未到时间点,执行转换。执行一次转换后,更新变量,循环判断,实现重抽的需求。
作业流程配置:

重抽作业流程
转换流程配置:

数据转换流程

更新时间变量JavaScript代码:

updatetime.js
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
//Script here

//var st =getVariable("StTime",'2021-01-01')

//var end =getVariable("End",'2021-7-27')

//Alert("st:"+st+"end:"+end)

var stDate =StTime;// str2date(st,'yyyy-MM-dd')
var endDate =End;// str2date(end,'yyyy-MM-dd')

if(stDate==null|| endDate==null) {
//Alert("error")
false;
}else{
var diff = dateDiff(stDate,endDate,'d')
var flag = false;
if(diff>=1){
var nextDate =dateAdd(stDate,'d',1);
//Alert("next:"+nextDate)
var strDate=date2str(stDate,'yyyy-MM-dd');
var strNextDate = date2str(nextDate,'yyyy-MM-dd');

//setVariable("StTime",strNextDate,‘p’)
//Alert("开始时间:"+strDate+"结束时间:"+strNextDate)

return true
}else {
return false
}
}

定时启动无界面kettle任务

使用CMD脚本+windows任务计划程序实现定时启动作业执行。耗时在2小时以内的作业可以使用脚本方式,执行时间长的作业最好使用spoon窗口程序执行,不会存在执行一半退出的情况。
bat文件保存要按ANSI编码进行保存

Task.bat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@REM @echo off

@REM if "%1" == "h" goto begin
@REM mshta vbscript:createobject("wscript.shell").run("%~nx0 h",0)(window.close)&&exit
@REM :begin

:: Kitchen.bat所在路径盘符?
D:
:: Kitchen.bat所在目录? ? ?
cd D:\PDI\data-integration\

:: 作业(job)文件路径和日志文件路径
:: 执行kitchen执行job,并写入日志
Kitchen.bat /file:F:\pdi-ce-9.1.0.0-324\WorkSpace\测试.kjb /level:Base >F:\pdi-ce-9.1.0.0-324\Log

Kitchen.bat参数详细说明:Use Command Line Tools to Run Transformations and Jobs

参考资料

Kettle系列教程-第一章
kettle-数据同步比较数据(删除更新)
Kettle之定时运行Job
kettle教程—kettle作业调度,根据更新时间增量更新
Kettle实现循环增量抽取数据
Learn About the PDI Client (Spoon)-官方文档
Use Command Line Tools to Run Transformations and Jobs

PDI【Kettle】(一)

PDI【Kettle】(一)

Kettle中JavaScript内置函数说明

转换组件-脚本-JavaScript【Spoon】

我们在使用JavaScript组件的时候,在左侧核心树对象栏中可以看到Kettle为我们提供了很多简洁强大的内置函数,帮助我们在写脚本的时候对数据、参数变量等能很轻松的做处理,体验编码的感觉.本篇将详细介绍JavaScript组件中的函数功能

脚本组件包含的函数主要包括六大类,分别是:

  • 字符串类型的函数(String Functions)
  • 浮点型的函数(Numeric Functions)
  • 日期类型函数(Date Functions)
  • 逻辑判断型函数(Logic Functions)
  • 特殊的函数(Special Functions)
  • 文件处理类函数(File Functions)

字符串类型函数(String Functions)

顾名思义,字符串类型的函数肯定是针对字符串类型的参数、变量进行处理操作的函数

日期转字符串(date2str)

日期转字符串函数date2str主要有4个方法,分别是:

  • date2str(date):传入日期实例,转换成字符串类型
  • date2str(date,format):传入日期和格式化参数,进行格式化转换
  • date2str(date,format,iso):传入日期和参数格式化及ISO代码进行转换,(DE = German, EN = English, FR = France, …)
  • date2str(date,format,iso,zone):传入时区进行格式化,例如北京时区(GMT+8)

日期格式化参数format参数类型供参考:

1
2
3
4
5
6
7
yy / yyyy - 06 / 2006
MM / MMM / MMMMM - 11 / Nov / November
d / dd - 1 / 01
E / EEEE - Tue / Tuesday
hh / HH - 11 / 23
m / mm - 5 / 05
s / ss - 8 / 08

代码示例:

1
2
3
4
5
6
7
var dValue = new Date();
writeToLog(date2str(dValue,"dd.MM.yyyy"));
writeToLog(date2str(dValue,"dd.MM.yyyy HH:mm:ss"));
writeToLog(date2str(dValue,"E.MMM.yyyy","DE"));
writeToLog(date2str(dValue,"dd.MM.yyyy HH:mm:ss","EN"));
writeToLog(date2str(dValue,"dd.MM.yyyy HH:mm:ss","ZH", "GMT+8"));
writeToLog(date2str(dValue,"yyyy-MM-dd HH:mm:ss","ZH", "GMT+8"));

以上代码在控制台将会输出如下:

1
2
3
4
5
6
2019/08/19 10:12:56 - JavaScript代码.0 - 19.08.2019
2019/08/19 10:12:56 - JavaScript代码.0 - 19.08.2019 10:12:56
2019/08/19 10:12:56 - JavaScript代码.0 - Mo.Aug.2019
2019/08/19 10:12:56 - JavaScript代码.0 - 19.08.2019 10:12:56
2019/08/19 10:12:56 - JavaScript代码.0 - 19.08.2019 10:12:56
2019/08/19 10:12:56 - JavaScript代码.0 - 2019-08-19 10:12:56

转义HTMLescapeHtml(html)

代码如下:

1
2
3
var html="<h1>我是H2标题</h2>";

writeToLog(escapeHtml(html))

最终输出

1
2019/08/19 10:16:13 - JavaScript代码.0 - &lt;h1&gt;&#25105;&#26159;H2&#26631;&#39064;&lt;/h2&gt;

转义SQL(escapeSQL(var))

1
2
var str1 = "SELECT * FROM CUSTOMER WHERE NAME='" + escapeSQL("McHale's Navy") + "'";  
writeToLog(str1)

该函数会把单引号转成双引号,输出结果如下:

1
2019/08/19 10:18:59 - JavaScript代码.0 - SELECT * FROM CUSTOMER WHERE NAME='McHale''s Navy'

构造定长字符串(fillString(char,length))

代码示例如下:

1
2
writeToLog(fillString("x",10));
writeToLog(fillString("A",3));

最终会输出10个X和3个A,输出结果如下:

1
2
2019/08/19 10:24:08 - JavaScript代码.0 - xxxxxxxxxx
2019/08/19 10:24:08 - JavaScript代码.0 - AAA

需要注意的是第一个是一个char类型的单字符,不能是字符串

统计字符串出现频次(getOcuranceString(str,searchStr))

第一个参数是要搜索的完整字符串,第二个参数是要搜索统计的字符串

代码示例:

1
2
3
4
var sef='2007-09-11';

writeToLog(getOcuranceString(sef,'0'))
writeToLog(getOcuranceString(sef,'00'))

我们分别统计字符串0和00最终出现的次数,此时,日志最终打印的次数是3和1:

1
2
2019/08/19 10:28:45 - JavaScript代码.0 - 3
2019/08/19 10:28:45 - JavaScript代码.0 - 1

获取字符串下标索引(indexOf)

获取下标索引主要有2个重构函数,分别是:

  • indexOf(string,subString):获取出现字符串的索引开始位置
  • indexOf(string,subString,fromIndex);指定开始位置,获取字符串索引开始位置

代码示例:

1
2
3
4
5
6
var str1= "Hello Pentaho!";
var str2= indexOf(str1, "Pentaho");
var str3= indexOf(str1, "o", 7);
writeToLog("Input : " + str1);
writeToLog("Index of 'Pentaho' : " + str2);
writeToLog("index of 'o', search from position 7 : " + str3);

最终控制台输出:

1
2
3
2019/08/19 10:34:16 - JavaScript代码.0 - Input : Hello Pentaho!
2019/08/19 10:34:16 - JavaScript代码.0 - Index of 'Pentaho' : 6
2019/08/19 10:34:16 - JavaScript代码.0 - index of 'o', search from position 7 : 12

首字母大写(initCap)

对指定字符串首字母大写处理,来看代码示例:

1
2
3
4
var str1 = "my home";      
writeToLog(initCap(str1));
writeToLog(initCap('test a aaa cw'));
writeToLog(initCap('myhome'));

此时,最终控制台输出如下:

1
2
3
2019/08/19 10:41:27 - JavaScript代码.0 - My Home
2019/08/19 10:41:27 - JavaScript代码.0 - Test A Aaa Cw
2019/08/19 10:41:27 - JavaScript代码.0 - Myhome

字符串转小写(lower)

将传入字符串全部转小写

代码如下:

1
2
3
4
5
var str1= "Hello World!";
var str2= lower(str1);
writeToLog("Input:" + str1);
writeToLog("Converted to LowerCase:" + str2);
writeToLog(lower('DDDHelloSWxss'))

响应内容

1
2
3
2019/08/19 10:43:09 - JavaScript代码.0 - Input:Hello World!
2019/08/19 10:43:09 - JavaScript代码.0 - Converted to LowerCase:hello world!
2019/08/19 10:43:09 - JavaScript代码.0 - dddhelloswxss

字符串填充左侧(lpad(string,char,length))

用指定长度的给定字符将字符串填充到左侧

参数定义:

  • 1:传入字符串
  • 2:填充单字符
  • 3:填充单字符长度

如果length长度超过给定字符串的长度,将对填充字符串做减法,例如:

1
2
var str1= "Hello World!"; 
writeToLog("Lpad:" + lpad(str1, "x",20));

此时,最终输出结果为:

1
2019/08/19 10:46:38 - JavaScript代码.0 - Lpad:xxxxxxxxHello World!

最终的完成长度是20个字符长度,因此填充的单字符x并没有填充20次

如果length长度小于给定字符串的长度,则默认返回原字符串,不做填充,代码示例:

1
2
var str1= "Hello World!"; 
writeToLog("Lpad:" + lpad(str1, "x",5));

此时最终的输出结果为:

1
2019/08/19 10:46:38 - JavaScript代码.0 - Lpad:Hello World!

去空字符(ltrim)

从左侧开始去除空字符串

数字转字符串(num2str)

给定数字,转换为字符串,主要有3个构造函数:

  • num2str(num):转换num数字为字符串
  • num2str(num,format):格式化数字为指定字符串
  • num2str(num,format,iso):按照本地ISO编码进行格式化

代码示例如下:

1
2
3
4
5
6
var d1 = 123.40;
var d2 = -123.40;
writeToLog(num2str(d1));
writeToLog(num2str(d1, "0.00"));
writeToLog(num2str(d1, "0.00", "EN"));
writeToLog(num2str(d2, "0.00;(0.00)", "EN"));

最终控制台输出:

1
2
3
4
2019/08/19 11:00:17 - JavaScript代码.0 - 123.4
2019/08/19 11:00:17 - JavaScript代码.0 - 123.40
2019/08/19 11:00:17 - JavaScript代码.0 - 123.40
2019/08/19 11:00:17 - JavaScript代码.0 - (123.40)

XML保护标签函数转换(protectXMLCDATA)

传入给定字符串,添加标准保护,代码示例

1
2
var str1 = "my home";      
writeToLog(protectXMLCDATA(str1));

此时,将会给变量str1加上保护标签

1
2019/08/19 11:02:09 - JavaScript代码.0 - <![CDATA[my home]]>

移除字符串中CRLF字符(removeCRLF(str))

给定字符串中删除CR END LF的字符串

替换字符串(replace)

替换字符串主要包括两个构造函数:

  • replace(str,searchStr,replaceStr):从指定字符串中查询,然后替换
  • replace(str,firstSearch,firstReplace,secondSearch,SecondReplace...):无限查询替换

代码示例如下:

1
2
3
4
5
var str1 = "Hello World, this is a nice function";      
var str2 = replace(str1,"World", "Folk");
writeToLog(str2);
var str2 = replace(str1,"World", "Folk", "nice","beautifull");
writeToLog(str2);

最终输出:

1
2
2019/08/19 11:10:21 - JavaScript代码.0 - Hello Folk, this is a nice function
2019/08/19 11:10:21 - JavaScript代码.0 - Hello Folk, this is a beautifull function

字符串右侧填充(rpad(string,char,length))

使用方法同lpad,只是一个是左侧,一个是右侧

去除空字符(右侧)(rtrim)

正则切分(str2RegExp)

出入一个正则表达式,对string字符串进行Split操作.代码如下:

1
2
3
4
5
6
7
8
9
10
11
12

var strToMatch = "info@proconis.de";
var strReg = "^(\\w+)@([a-zA-Z_]+?)\\.([a-zA-Z]{2,3})$";
var xArr = str2RegExp(strToMatch, strReg);
if ( xArr != null ) {
for(i=0;i<xArr.length;i++) {
writeToLog(xArr[i]);
}
}
else {
writeToLog("no match");
}

最终控制台输出:

1
2
3
2019/08/19 13:21:19 - JavaScript代码.0 - info
2019/08/19 13:21:19 - JavaScript代码.0 - proconis
2019/08/19 13:21:19 - JavaScript代码.0 - de

字符串截取(substr)

通过制定索引开始对字符串进行截取操作,主要有两个重构参数:

  • substr(string,from):指定from索引开始截取字符串
  • substr(string,from,to):指定开始和截止索引进行截取

代码示例:

1
2
3
4
5
6
var str1= "Hello Pentaho!";
var str2= substr(str1, 6);
var str3= substr(str1, 6, 7);
writeToLog("Input : " + str1);
writeToLog("From position 6: " + str2);
writeToLog("From position 6 for 7 long : " + str3);

控制台输出如下:

1
2
3
2019/08/19 13:31:20 - JavaScript代码.0 - Input : Hello Pentaho!
2019/08/19 13:31:20 - JavaScript代码.0 - From position 6: Pentaho!
2019/08/19 13:31:20 - JavaScript代码.0 - From position 6 for 7 long : Pentaho

去除左右空格(trim)

不转义HTML(unEscapeHtml(html))

针对以转义的HTML字符进行解密,代码如下:

1
2
3
4
5
6
7
var w='<h2>我是H2标题</h2>';

var esW=escapeHtml(w);
var unesw=unEscapeHtml(esW);

writeToLog(esW);
writeToLog(unesw);

最终控制台输出:

1
2
2019/08/19 13:38:16 - JavaScript代码.0 - &lt;h2&gt;&#25105;&#26159;H2&#26631;&#39064;&lt;/h2&gt;
2019/08/19 13:38:16 - JavaScript代码.0 - <h2>我是H2标题</h2>

解码转义XML(unEscapeXml )

字符串转大写(upper)

将传入字符串全部转大写.例如:

1
2
var str="Hello World";
writeToLog(upper(str));

浮点型的函数(Numeric Functions)

计算绝对值(abs(num))

计算一个数值的绝对值,代码示例:

1
2
3
4
var d1 = -1234.01;
var d2 = 1234.01;
writeToLog(abs(d1));
writeToLog(abs(d2));

最终控制台输出为:

1
2
2019/08/19 13:51:00 - JavaScript代码.0 - 1234.01
2019/08/19 13:51:00 - JavaScript代码.0 - 1234.01

最小双精度值(ceil(num))

返回最小的双精度值。该值将被四舍五入。代码示例:

1
2
3
4
var d1 = -1234.01;
var d2 = 1234.01;
writeToLog(ceil(d1));
writeToLog(ceil(d2));

最终控制台输出:

1
2
2019/08/19 13:52:40 - JavaScript代码.0 - -1234
2019/08/19 13:52:40 - JavaScript代码.0 - 1235

最大数值(floor(num))

返回最大数值,该值将被四舍五入,代码示例:

1
2
3
4
var d1 = -1234.01;
var d2 = 1234.01;
writeToLog(floor(d1));
writeToLog(floor(d2));

运行结果如下:

1
2
2019/08/19 13:55:13 - JavaScript代码.0 - -1235
2019/08/19 13:55:13 - JavaScript代码.0 - 1234

字符串转数值(str2num(var))

字符串转数值主要包含两个构造函数,分别是

  • str2num(str):传入数值字符串,进行格式化转换
  • str2num(str,format):通过指定格式进行数值转换

代码示例如下:

1
2
3
4
var str1 = "1.234,56";
var str2 = "12";
writeToLog((str2num(str1,"#,##0.00")));
writeToLog((str2num(str2)));

最终控制台输出:

1
2
2019/08/19 14:02:19 - JavaScript代码.0 - 1.234
2019/08/19 14:02:19 - JavaScript代码.0 - 12

截取数值(trunc)

1
trunc(1234.9); // 返回 1234

日期类型函数(Date Functions)

日期相加(dateAdd)

针对日期变量进行相应的添加时间,添加频率包括年、月、日、时、分、秒 等等

函数定义:dateAdd(date,format,plusNum)

  • date:日期对象
  • format:要加的类型
  • plusNum:加的数值

相加类型主要包括:

  • y:年
  • m:月
  • d:日
  • w:周
  • wd:工作日
  • hh:小时
  • mi:分钟
  • ss:秒

代码示例如下:

1
2
3
4
5
6
7
var d1 = new Date();

var fmt='yyyy-MM-dd HH:mm:ss';
writeToLog("当前时间:"+date2str(d1,fmt));
var py=dateAdd(d1,'y',1);
var fy=date2str(py,fmt);
writeToLog("加1年:"+fy);

最终控制台输出:

1
2
2019/08/19 14:17:41 - JavaScript代码.0 - 当前时间:2019-08-19 14:17:41
2019/08/19 14:17:41 - JavaScript代码.0 - 加1年:2020-08-19 14:17:41

日期比较(dateDiff)

两个日期相互比较

函数定义:dateDiff(startDate,endDate,type)

  • startDate:开始日期
  • endDate:截止日期
  • type:返回相差数值类型

类型主要包括:

  • y:年
  • m:月
  • d:日
  • w:周
  • wd:工作日
  • hh:小时
  • mi:分钟
  • ss:秒

获取指定日期数值(getDayNumber)

根据类型获取指定日期的数值

函数定义:getDayNumber(date,type)

  • date:当前日期实例
  • type:类别

类别主要分四类

  • y:获取当年的天数
  • m:获取当月的天数
  • w:获取本周的天数
  • wm:获取当月中本周的天数

代码示例:

1
2
3
4
5
var d1 = new Date();
writeToLog(getDayNumber(d1, "y"));
writeToLog(getDayNumber(d1, "m"));
writeToLog(getDayNumber(d1, "w"));
writeToLog(getDayNumber(d1, "wm"));

getFiscalDate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Returns the fiscal Date from the date value,
// based on a given offset.
//
// Usage:
// getFiscalDate(var);
// 1: Date - The Variable with the Date.
// 2: String - The Date/Month which represents
// the fiscal Start Offset. Format allways "dd.MM.".
//
// 2006-11-15
//
var d1 = new Date();
var str1 = "01.07.";
var str2 = "10.12.";
Alert(getFiscalDate(d1, str1));
Alert(getFiscalDate(d1, str2));

获取下一个工作日日期(getNextWorkingDay)

传入当前日期,获取该日期后面一个工作日日期

函数定义getNextWorkingDay(date)

代码示例如下:

1
2
3
4
5
6
7
8
9
10
var d1 = new Date();

// 周1
var d2=str2date('2019-08-19 16:36:00',fmt);
//周 6
var d3=str2date('2019-08-17 16:36:00',fmt);

writeToLog(date2str(getNextWorkingDay(d1),fmt));
writeToLog(date2str(getNextWorkingDay(d2),fmt));
writeToLog(date2str(getNextWorkingDay(d3),fmt));

我们这d2和d3变量中定义了不同的日期实例,分别是周1和周6,最终通过getNextWorkingDay能获取得到下一个工作日日期,控制台输出如下:

1
2
3
2019/08/19 16:37:38 - JavaScript代码.0 - 2019-08-20 16:37:38
2019/08/19 16:37:38 - JavaScript代码.0 - 2019-08-20 16:36:00
2019/08/19 16:37:38 - JavaScript代码.0 - 2019-08-19 16:36:00

获取当前月份数值(month(date))

获取当前日期的月份数值,需要注意的是,该值的月份是从0开始的,因此我们最终得到的结果应该+1才是我们的真实月份数值,代码示例:

1
2
var d1 = new Date();//2019/08/19
writeToLog(month(d1)); //最终输出为7

获取当前时间的季度值(quarter(date))

根据指定日期获取当前季度数值

1
2
var d1 = new Date();//2019/08/19
writeToLog(quarter(d1));//最终输出为3(代表第三季度)

字符串转日期(str2date)

字符串转日期和日期转字符串有点类似,只不过主体对换了一下,但是传入的格式参数都是一样的,主要有4个重载函数:

  • str2date(str):默认转换
  • str2date(str,format):传入format格式化参数
  • str2date(str,format,iso):根据iso编码及格式化参数进行转换
  • str2date(str,format,iso,timezone):根据不同时区的iso编码进行格式化转换

代码示例如下:

1
2
3
4
5
writeToLog(str2date("01.12.2006","dd.MM.yyyy"));
writeToLog(str2date("01.12.2006 23:23:01","dd.MM.yyyy HH:mm:ss"));
writeToLog(str2date("Tue.May.2006","E.MMM.yyyy","EN"));
writeToLog(str2date("22.02.2008 23:23:01","dd.MM.yyyy HH:mm:ss","DE"));
writeToLog(str2date("22.02.2008 23:23:01","dd.MM.yyyy HH:mm:ss","DE", "EST"));

截取日期(truncDate(date,type))

指定截取不同的日期部分,函数定义truncDate(date,type)

  • date:当前日期实例
  • type:截取类型

类型主要有6中,分别是整型,从0-5:

  • 5:截取月份
  • 4:截取天数
  • 3:截取小时
  • 2:截取分钟
  • 1:截取秒
  • 0:截取毫秒

代码示例 如下:

1
2
3
4
5
6
7
var dateTime = new Date();
var date0 = truncDate(dateTime, 0); // gives back today at yyyy/MM/dd HH:mm:ss.000
var date1 = truncDate(dateTime, 1); // gives back today at yyyy/MM/dd HH:mm:00.000
var date2 = truncDate(dateTime, 2); // gives back today at yyyy/MM/dd HH:00:00.000
var date3 = truncDate(dateTime, 3); // gives back today at yyyy/MM/dd 00:00:00.000
var date4 = truncDate(dateTime, 4); // gives back today at yyyy/MM/01 00:00:00.000
var date5 = truncDate(dateTime, 5); // gives back today at yyyy/01/01 00:00:00.000

获取当年的周数(week)

获取指定日期的周数,代码示例:

1
2
3
var d1 = new Date(); //2019/08/19

writeToLog(week(d1));// 返回34

获取年份(year)

获取传入日期的年份,代码示例:

1
2
3
var d1 = new Date(); //2019/08/19

writeToLog(year(d1));// 返回2019

逻辑判断型函数(Logic Functions)

isCodepage

判断字符串的codepage项,代码示例:

1
2
3
var xStr = "Réal";
writeToLog(isCodepage(xStr, "UTF-8"));// true
writeToLog(isCodepage(xStr, "windows-1250"));// true

是否日期(isDate(str))

判断当前字符串是否日期

1
2
3
4
var d1 = "Hello World";      
var d2 = new Date();
writeToLog(isDate(d1));//false
writeToLog(isDate(d2));//true

是否为空(isEmpty(str))

判断字符串是否为空

1
2
var d = "Hello World";      
Alert(isEmpty(d));//false

判断字符串是否为邮箱标准格式(isMailValid(str))

判断一个字符串是否是邮箱

判断是否是数值(isNum(str))

判断一个字符串是否是数值

1
2
3
4
var str1 = "Hello World";      
var str2 = 123456;
Alert(isNum(str1));//false
Alert(isNum(str2));//true

是否正则匹配(isRegExp)

判断给定的正则表达式是否匹配当前的字符串,主要有2个函数定义:

  • isRegExp(str,reg):给定正则判断字符串是否匹配
  • isRegExp(str,reg1,reg2,reg3…);可以递归判断正则匹配

最终返回的是匹配的次数数值,如果不匹配,返回-1

代码示例如下:

1
2
3
4
5
6
7
8
9
10
var email1 ="info@proconis.de";
var email2= "support@proconis.co.uk";
var email3= "HelloWorld@x";

var reg1="^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$";
var reg2="^[\\w-\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";

writeToLog(isRegExp(email1, reg1,reg2) + " Matches"); //1
writeToLog(isRegExp(email2, reg1,reg2) + " Matches"); //2
writeToLog(isRegExp(email3, reg1,reg2) + " Matches");// 1

是否工作日(isWorkingDay(date))

判断某日期是否是工作日,代码示例:

1
2
3
4
var d1 = new Date();//周1
var d2=str2date('2019-08-17','yyyy-MM-dd') //周六
writeToLog(isWorkingDay(d1));//true
writeToLog(isWorkingDay(d2));//false

特殊的函数(Special Functions)

弹框信息(Alert(msg))

在屏幕前弹出一个信息框

加载JavaScript文件(LoadScriptFile)

将一个javascript文件加载到实际的运行上下文中。应该从定义的StartScript调用此函数,否则,每次处理都会加载javascript文件行。

代码示例如下:

1
2
var xPfad = "F:/bak/Hello.js";
LoadScriptFile(xPfad);

此时,我们的外部JS文件仅仅是包含一句简单的输出,如下:

1
writeToLog("Hello LoadScriptFile,outSide JS File ");

最终运行时,控制台会打印出我们在外部JS中的输出行

从当前Tab栏加载JS并运行(LoadScriptFromTab)

如果我们在当前的JavaScript组件中通过模块化的方式编写了很多脚本代码,我们可以通过LoadScriptFromTab函数进行相互调用,这对于开发抽象来说是很好的,代码示例如下:

1
2
writeToLog("外部Tab加载JS-------------------------")
LoadScriptFromTab('Item_1');

有效卡号判断(LuhnCheck)

如果给定的是一个有效的卡号,则返回true

1
2
3
4
5
6
var str1 = "4444333322221111";      
writeToLog(str1 + ": " + LuhnCheck(str1)); //true

var str2 = "4444333322221110";
writeToLog(str2 + ": " + LuhnCheck(str2));//false

向文件中追加数据(appendToFile)

向指定文件中追加数据,如果文件不存在则创建文件

1
2
3
4
5
6
7

var file = "F:/bak/log.txt";

for(var i=0;i<100;i++){
appendToFile(file,'TEST'+i+"\r\n");
}

此时,该代码会向log.txt文件输出100条数据行

decode函数

decode函数有点类似于IF-THEN-ELSE语句,即表示通过给定查询的字符串是否存在,如果存在,即替换,否则返回默认值

代码示例:

1
2
3
4
5
6
var str1 = "Hallo";
writeToLog(decode(str1, "Hallo", "Hello"));
writeToLog(decode(str1, "1", "Mr", "2", "Mrs", "N/A"));
writeToLog(decode(str1, "1", "Mr", "2", "Mrs"));
str1 = "Mrs";
writeToLog(decode(str1, "1", "Mr", "2", "Mrs"));

控制台输出:

1
2
3
4
2019/08/19 17:39:01 - JavaScript代码.0 - Hello
2019/08/19 17:39:01 - JavaScript代码.0 - N/A
2019/08/19 17:39:01 - JavaScript代码.0 - Hallo
2019/08/19 17:39:01 - JavaScript代码.0 - Mrs

执行命令(execProcess)

代码如下:

1
2
var t=execProcess('ping www.baidu.com');
writeToLog(t)

调用命令行,ping百度的网址,最终输出返回数据

执行SQL语句(fireToDB)

通过获取数据库连接名称,传递SQL语句,以返回SQL查询的值,函数定义:

  • fireToDB(connectionName,SQL);第一个参数为数据库连接名称,我们在JNDI中定义的名称,第二个参数为SQL语句
1
2
3
var strConn = "MY Connection";
var strSQL = "SELECT COUNT(*) FROM ...";
var xArr = fireToDB(strConn, strSQL);

仅仅获取数值(getDigitsOnly)

在给定的字符串中仅仅筛选过滤得到数值,代码如下:

1
2
var str1 = "abc123cde";      
writeToLog(getDigitsOnly(str1));//返回123

获取Kettle环境变量的值(getEnvironmentVar)

获取在Kettle中的环境变量的值

1
2
writeToLog(getEnvironmentVar("user.dir"));
writeToLog(getEnvironmentVar("user.name"));

获取当前进程的受影响行数(getProcessCount(type))

根据类型获取当前进程的受影响行数,类型如下:

  • u:更新行数
  • i:插入行数
  • w:写入行数
  • r:读取行数
  • o:输出行数
1
2
writeToLog(getProcessCount("u"));
writeToLog(getProcessCount("r"));

获取当前转换名称(getTransformationName)

获取当前的转换名称

1
2
var xTranName = getTransformationName();
writeToLog(xTranName);

获取Kettle环境中的变量值(getVariable)

从当前的Kettle环境中获取指定的变量值,目前函数有2个重载:

  • getVariable(varName);根据变量名称获取变量值
  • getVariable(varName,defaultValue):根据变量名获取值,如果不存在则使用默认值
1
2
3
4
5
6
7
8
var strVarName="getVariableTest";
var strVarValue="123456";
Alert(getVariable(strVarName, ""));
setVariable(strVarName,strVarValue, "r");
Alert(getVariable(strVarName, ""));
strVarValue="654321";
setVariable(strVarName,strVarValue, "r");
Alert(getVariable(strVarName, ""));

控制台打印(println)

1
2
3
var str = "Hello World!";
print(str);

移除数值(removeDigits)

移除给定字符串中的数值,代码示例:

1
2
3
var str1 = "abc123cde"; 

writeToLog(removeDigits(str1));//返回abccde

发送邮件

设置环境变量(setEnvironmentVar)

通过在Script脚本组件中调用函数重新设置Kettle的环境变量

1
2
3
4
5
6
7
8
var strVarName="setEnvTest";
var strVarValue="123456";
Alert(getEnvironmentVar(strVarName));
setEnvironmentVar(strVarName,strVarValue);
Alert(getEnvironmentVar(strVarName));
strVarValue="654321";
setEnvironmentVar(strVarName,strVarValue);
Alert(getEnvironmentVar(strVarName));

设置变量(setVariable)

通过setVariable函数设置环境变量,该用途可以用于重新赋值Kettle环境中已经存在的变量值或者重新生成变量值

函数定义setVariable(key,value,level)

  • key:变量名称
  • value:变量值
  • level:级别,主要包括s(system)、r(root)、p(parent)、g(grandparent)四种类别

代码示例如下:

1
2
3
4
5
6
7
8
var strVarName="setEnvTest";
var strVarValue="123456";
Alert(getVariable(strVarName, ""));
setVariable(strVarName,strVarValue, "r");
Alert(getVariable(strVarName, ""));
strVarValue="654321";
setVariable(strVarName,strVarValue, "r");
Alert(getVariable(strVarName, ""));

写入日志(writeToLog)

打印并写入日志信息,该函数可能是我们用到的最多的函数,可以帮助我们调试信息,主要有两个重载:

  • writeToLog(msg):写入msg日志信息
  • writeToLog(level,msg):根据level基本写入msg信息

关于日志的级别,这里主要是简写的方式,主要如下:

  • d(Debug):调试模式
  • l(Detailed):详细
  • e(Error):错误
  • m(Minimal):最小日志
  • r(RowLevel):行级日志
1
2
writeToLog("Hello World!");
writeToLog("r", "Hello World!");

文件处理类函数(File Functions)

复制文件(copyFile)

复制一个文件到目标目录,函数定义如下:

copyFile(sourceFile,targetFile,overwrite)

  • sourceFile:源文件
  • targetFile:目标文件
  • overWrite:是否覆盖,如果目标文件存在的话,布尔类型
1
2
3
4
5
var file1 = "F:/bak/log.txt";

var targetFile="F:/bak/logTarget.txt";

copyFile(file1,targetFile,false)

创建文件夹(createFolder)

创建一个文件夹,代码示例如下:

1
2
var strFolder = "F:/bak/createFolder";
createFolder(strFolder);

删除文件(deleteFile)

删除一个文件(不能删除文件夹)

1
2
3
var targetFile="F:/bak/logTarget.txt";

deleteFile(targetFile);

判断文件是否存在(fileExists())

判断文件是否存在

1
2
3
var targetFile="F:/bak/logTarget.txt";

fileExists(targetFile);

获取文件扩展名(getFileExtension)

如果文件不存在,则返回null,代码示例

1
2
3
4
var file1 = "F:/bak/log.txt";

var ext=getFileExtension(file1);
writeToLog("扩展名:"+ext)

获取文件大小(getFileSize)

获取文件大小,结果是一个long类型的长整型数值

1
2
3
4
var file1 = "F:/bak/log.txt";

var ext=getFileSize(file1);
writeToLog("大小:"+ext)

获取文件最后修改日期(getLastModifiedTime)

获取文件最后修改日期,函数定义:

getLastModifiedTime(filePath,format)

  • filePath:文件路径
  • format:日期格式化
1
2
3
var file1 = "F:/bak/log.txt";

var ext=getLastModifiedTime(file1,"yyyy-MM-dd HH:mm:ss");

获取文件的父文件夹名称(getParentFoldername)

获取文件的父文件夹名称

1
2
var file1 = "F:/bak/log.txt";
var parentFolder=getParentFoldername(file1);

获取文件简称(getShortName)

获取文件简称

1
2
3
4
5

var file1 = "F:/bak/log.txt";
var shortName=getShortFilename(file1);

writeToLog("简单名称:"+shortName)//返回log.txt

判断是否是一个文件(isFile)

判断是否是一个文件

1
2
var file1 = "F:/bak/log.txt";
var flag=isFile(file1) //true

判断是否是一个文件夹(isFolder)

判断是否是一个文件夹

1
2
var file1 = "F:/bak/log.txt";
var flag=isFolder(file1) //false

加载一个文件的内容(loadFileContent)

从指定文件中加载内容,主要有两个重载函数:

  • loadFileContent(filePath):默认加载文件
  • loadFileContent(filePath,encoding):指定编码加载文件内容

代码示例:

1
2
3
4
var file1 = "F:/bak/log.txt";
var content=loadFileContent(file1);
var c1=loadFileContent(file1,"UTF-8")
writeToLog(content)

移动文件(moveFile)

移动指定文件,函数定义moveFile(source,target,overWrite)

  • source:源文件
  • target:目标文件
  • overWrite;是否覆盖,如果目标文件存在,布尔类型值
1
2
3
4
5
var file1 = "F:/bak/log.txt";
var targetFile="F:/bak/logTarget.txt";

moveFile(file1,targetFile,false)

创建一个空文件(touch)

创建一个空文件

1
2
var strFile = "F:/bak/log.txt";
touch(strFile);

总结

以上就是Kettle 8.3版本中的内置函数方法,方法很多,写这篇博客也是很累,算是全部都学习了一遍,脑子里已经记忆了一遍,但是我们也不需要死记硬背,就和我们学些Linux命令一样,如果你知道man命令,对某个命令不是很了解的话直接通过man命令学习即可.

Kettle也是如此,对于某个函数不是很了解的话,右键点击该函数,会出现sample字样菜单,点击该菜单即可弹出该函数的介绍和使用信息,里面包含了该函数的调用示例和函数详细介绍,也是很人性化的.

SQLite学习【一】

SQLite学习【一】

SQLite学习.sql
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
--创建数据库(数据库名可以是任何名字,文件类型始终为sqlite类型)
sqlite3 test.sqlite
sqlite3 other.db
sqlite3 db.sqlite3

--创建数据表 增删改查 SQLite

CREATE TABLE DEPARTMENT(
ID INT PRIMARY KEY NOT NULL,
DEPT CHAR(50) NOT NULL,
EMP_ID INT NOT NULL
);
CREATE TABLE Test(
ID INT PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);

SELECT * FROM sqlite_master WHERE type = 'table';

DROP TABLE DEPARTMENT;

SELECT * FROM sqlite_master WHERE type = 'table';

--增

INSERT INTO `Test` (ID, NAME, AGE, ADDRESS, SALARY) VALUES (1,'测试人员',35,'东北那旮瘩',5000);

INSERT INTO `Test` (ID, NAME, AGE, ADDRESS, SALARY)
VALUES (2,'测试人员',35,'东北那旮瘩',10000);

SELECT * from Test ;
--删
DELETE from `Test` where ID='1';
SELECT * from Test ;

--改
update `Test` set Name ='测试人员2' where ID=2;
SELECT * from `Test`;

DROP TABLE Test;
SELECT * FROM sqlite_master WHERE type = 'table';

C#穿透session隔离———Windows服务启动UI交互程序

C#穿透session隔离———Windows服务启动UI交互程序

写在前面

一开始是因为服务器经常会出现断电、系统崩溃的情况,导致一些正常运行的winform程序在系统故障重启后,每次都需要手动登录触发事件让程序自启。然后想利用windows服务在开启时就会自启来实现开机启动Winform程序。
但是因为从Vista 开始引入了 Session 0 隔离机制,导致windows服务无法直接进行界面交互操作。

注意:
使用CreateProcessAsUser与界面交互需要Session Id >0 ,用户会话的必须存在,如果存在服务器重启、注销后,重新开机导致系统只有Session 0存在,此时服务调用后的程序是不会显示界面的。
所以到头来还是没能实现我的想法。。。淦

Session 0 隔离原理

参考:

穿透Session 0 隔离(一)
摘录
用户界面特权隔离

在早期的Windows操作系统中,在同一用户下运行的所有进程有着相同的安全等级,拥有相同的权限。例如,一个进程可以自由地发送一个Windows消息到另外一个进程的窗口。从Windows Vista开始,当然也包括Windows 7、Windows 10,对于某些Windows消息,这一方式再也行不通了。进程(或者其他的对象)开始拥有一个新的属性——特权等级(Privilege Level)。一个特权等级较低的进程不再可以向一个特权等级较高的进程发送消息,虽然他们在相同的用户权限下运行。这就是所谓的用户界面特权隔离(User Interface Privilege Isolation ,UIPI)。

UIPI的引入,最大的目的是防止恶意代码发送消息给那些拥有较高权限的窗口以对其进行攻击,从而获取较高的权限等等,在计算机系统中,这却是一种维护系统安全的合适方式。

windows服务启动UI程序、

对于简单的交互,服务可以通过WTSSendMessage 函数,在用户Session 上显示消息窗口。对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨Session 通信,在桌面用户上创建一个应用程序界面。

解决思路是:window service创建一个和与当前登陆用户可以交互的进程,这个进程运行在admin权限下,能够调起应用程序的UI

具体的做法是:widow service复制winlogon.exe进程句柄,然后通过调用api函数CreateProcessAsUser()以winlogon.exe权限创建新进程,新创建的进程有winlogon.exe的权限(winlogon.exe运行在system权限下),负责调用程序。

作者原文:

First, we are going to create a Windows Service that runs under the System account. This service will be responsible for spawning an interactive process within the currently active User’s Session. This newly created process will display a UI and run with full admin rights. When the first User logs on to the computer, this service will be started and will be running in Session0; however the process that this service spawns will be running on the desktop of the currently logged on User. We will refer to this service as the LoaderService.

Next, the winlogon.exe process is responsible for managing User login and logout procedures. We know that every User who logs on to the computer will have a unique Session ID and a corresponding winlogon.exe process associated with their Session. Now, we mentioned above, the LoaderService runs under the System account. We also confirmed that each winlogon.exe process on the computer runs under the System account. Because the System account is the owner of both the LoaderService and the winlogon.exe processes, our LoaderService can copy the access token (and Session ID) of the winlogon.exe process and then call the Win32 API function CreateProcessAsUser to launch a process into the currently active Session of the logged on User. Since the Session ID located within the access token of the copied winlogon.exe process is greater than 0, we can launch an interactive process using that token.

参考:
交互式服务
C#开发Windows服务详细流程
C#穿透session隔离———Windows服务启动UI交互程序
Subverting Vista UAC in Both 32 and 64 bit Architectures

问题

windows服务启动winform程序不显示UI问题解决
原因:
xp系统的用户和window service运行在一个session下,在xp以后,windows系统改变了用户会话管理的策略,window service独立运行在session0下,依次给后续的登录用户分配sessionX(X =1,2,3…),session0没有权限运行UI。所以在window xp以后的系统下,window service调用有UI的application时只能看到程序进程但不能运行程序的UI。

参考:
C# windows服务启动winform程序不显示UI问题解决
How can a Windows service execute a GUI application?
穿透Session 0 隔离(二)

Win10中文语言下添加美式键盘

Win10中文语言下添加美式键盘

个人操作习惯使用Ctrl+Shift切换键盘布局,但是win10的更新会经常性导致输入法的问题出现。下面是一些平时收集到的方法。

阅读更多
服务器设置系统启动时程序自动运行(不需要登录)

服务器设置系统启动时程序自动运行(不需要登录)

写在前面

本方法不适用于winform、WPF等UI程序。涉及到windows系统的用户会话管理的策略。
Windows系统中存在一个特殊的 Session,其 Session ID 为 0。这通常称为 Session0。所有 Windows 服务都在 Session0 中运行,并且 Session0 是非交互式的。非交互式意味着无法启动 UI 应用程序;

1. 通过组策略设置脚本随服务器启动

打开组策略编辑器

  1. 找到 计算机配置->Windows设置->脚本(启动/关机)
  2. 选中启动
  3. 将下列脚本添加进去,配置好启动程序路径的参数:
start.bat
1
2
3
4
5
6
7
@echo off
REM 声明采用UTF-8编码
chcp 65001
@REM pause
set input=%1%
echo 自启动程序路径参数:%input%
start %input%

2. 使用任务计划程序

需要注意的时,在选择用户执行时,如果创建任务的用户为Administrator,需要注意执行时的用户账户,如果是需要在服务器启动时运行,就不应该为Administrator,而是应该为SYSTEM

参考资料:

碰见的问题:

界面显示问题:

According to Microsoft (emphasis added):

You can specify that a task should run even if the account under which the task is scheduled to run is not logged on when the task is triggered. To do this, select the radio button labeled Run whether user is logged on or not . If this radio button is selected, tasks will not run interactively. To make a task run interactively, select the Run only when user is logged on radio button.

Essentially, if you select ‘Run whether user is logged on or not’, the process will not start a UI.

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×