WenLiangBaoBlog

个人博客


  • 首页

  • 标签7

  • 归档62

  • 搜索

configuartionProperties注解

发表于 2021-12-06 | 更新于 2019-04-29

@configuartionProperties

作用

​ 用于将properties 文件中的属性或yml属性文件中的属性 放入pojo中

使用案例

@configuartionProperties + @Component

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "me.user")
@Data
public class User {

private String name;

private String password;

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.healthengine.medpro.fileSystem.FileSystemApplication;
import com.healthengine.medpro.fileSystem.controller.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileSystemApplication.class)
public class ConfigurationProperties {
@Autowired
private User user;

@Test
public void configuration(){
System.out.println(user);
}


}

配置文件设置

1
2
3
4
me:
user:
name: baowengliang
password: 123456

测试结果

1
User{name='baowengliang', password='123456'}

@Bean + @ConfigurationProperties

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lombok.Data;

@Data
public class User {

private String name;

private String password;

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}

启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication()
public class FileSystemApplication {

public static void main(String[] args){
SpringApplication.run(FileSystemApplication.class,args);
}

@Bean
@ConfigurationProperties(prefix = "me.user")
public User user(){
return new User();
}
}

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.healthengine.medpro.fileSystem.FileSystemApplication;
import com.healthengine.medpro.fileSystem.controller.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileSystemApplication.class)
public class ConfigurationProperties {
@Autowired
private User user;

@Test
public void configuration(){
System.out.println(user);
}


}

配置文件设置

1
2
3
4
me:
user:
name: baowengliang
password: 123456

测试结果

1
User{name='baowengliang', password='123456'}

@EnableConfigurationProperties + @ConfigurationProperties

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "me.user")
public class User {

private String name;

private String password;

@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}

启动类

1
2
3
4
5
6
7
8
9
@SpringBootApplication()
@EnableConfigurationProperties(User.class)
public class FileSystemApplication {

public static void main(String[] args){
SpringApplication.run(FileSystemApplication.class,args);
}

}

配置文件

1
2
3
4
me:
user:
name: baowengliang
password: 123456

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.healthengine.medpro.fileSystem.FileSystemApplication;
import com.healthengine.medpro.fileSystem.controller.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileSystemApplication.class)
public class ConfigurationProperties {
@Autowired
private User user;

@Test
public void configuration(){
System.out.println(user);
}


}

测试结果

1
User{name='baowengliang', password='123456'}

English-语法

发表于 2021-12-06 | 更新于 2020-05-08

主语

定义: 一个句子所要描述的对象就是主语. 比如我是中国人中的我就是主语

定语

简介:修饰或限定名词的词语叫做定语。也就是说定语只跟名词有关系

chinese example:

注:下划线部分为定语

  • 我的书
  • 美丽善良的姑娘
  • 昨天晚上你买的书

前置定语

简介:放在名词前面的定语叫做前置定语

English example:

注:下划线部分为定语

  • my book
  • your house
  • Her house
  • His house
  • Its house
  • Our house
  • Their house

名词(n)

定义:世界万物的名称可以称为名词

普通名词

可数名词

不可数名词

专有名词

数词(num)

代词

简介: 代词是为了避免重复使用名词时用来替代名词的词语。

代词的分类(9类):

人称代词

简介:人称代词就是代替人或有生命的名词,如我你它

主格:

I You She He It We They
我 你 她 他 它 我们 他们/她们/它们

宾格:

Me You Her Him It Us Them
我 你 她 他 它 我们 他们/她们/它们

English example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
She is happy

They are excited

We learn French

I like music

He is a student

They are teachers

It is cheap

做宾格的例子 example

I like her

I support them

He teaches us English

They belive me

人称代词的作用

可以做主语(主格)、宾语(宾格)、表语

主格和宾格都可以作表语如:

  • Who is it

  • It is me/I

物主代词

简介:表示所有关系的代词叫物主代词

形容词性物主代词

My Your Her His Its Our Their
我的 你的/你们的 她的 他的 它的 我们的 他们的/她们的/它们的

名词性物主代词

Mine Yours Hers His Its Ours Theirs
我的 你的/你们的 她的 他的 它的 我们的 他们的/她们的/它们的

形容词性物主代词和名词性物主代词的区分:

Chinese example:

注:下划线部分为定语

  • 1.这是我的书 其中的”我的”在中文里是定语,修饰名词”书”。所以这里的”我的”翻译成英语就是一个形容词性物主代词 This is my book
  • 2.你的书比我的贵,这里的”我的”在中文的含义是”我的书”实际上是指”书”,它属于名词的意义。所以这里的我的翻译成英语就是一个名词性物主代词 Your books is more expensive than mine

形容词性物主代词的用法

​ 做前置定语,所以它的后面会跟一个名词

English example:

注:下划线部分为定语

  • My house
  • Your house
  • Her house
  • His house
  • Its house
  • Our house
  • Their house
  • Her boyfriend
  • Your cars
  • My water

注意事项:做前置定语时,中间不能加数词、冠词

English example:

  • My two books
  • My the book
  • My a book

名词性物主代词的用法

名词性物主代词相当于一个名词,所以可以作主语、表语、宾语。但是只能在前面提到或者避免重复的时候使用。

English example:

Tom: My car is very good

Tony: Mine is better

Tom: My car is very good

Tony: I want yours

Tom : Whose is this house?

Tony: It is hers

My computer is better then yours

注意:名词性物主代词不能做定语

反身代词

相互代词

指示代词

单数 复数
This 这个(作主语be动词用is) These 这些(作主语be动词用are)
That 那个(作主语be动词用is) Those 那些(作主语be动词用are)

上表中的这些单词就是指示代词的全部,它们可以作:主语、宾语、表语、定语。其中作主语和定语最常见。

作定语举例(example)

This house.

This computer.

That boy.

That company.

These houses.

Those houses.

作主语举例(example)

This is my book.

Those are her pencils.

That is her sister.

These are his books.

作宾语举例(example)

I want this.

I like that.

I will buy These.

作表语举例(example)

It is this.

不定代词

疑问代词

连接代词

关系代词

nginx

发表于 2021-12-06 | 更新于 2019-05-22

nginx 的安装

ubuntu 下安装

1
apt install nginx

nginx 虚拟主机配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo vim /etc/nginx/nginx.conf

在 http {}中添加

server{
listen 80; //监听端口
server_name www.xuecheng.com; //服务名
ssi on; //是否开启ssi
ssi_silent_errors on;
location / {
alias /home/spiderbao/webStrom_dev/xc-ui-pc-static-portal/;//主机映射的路径
index index.html;
}
}

python_argparse

发表于 2021-12-06 | 更新于 2019-05-02

argparse由来

​ 原文:

The argparse module is a command line parsing library which provides more functionality than the existing command line parsing modules in the standard library, getopt [2] and optparse [3]. It includes support for positional arguments (not just options), subcommands, required options, options syntaxes like “/f” and “+rgb”, zero-or-more and one-or-more style arguments, and many other features the other two lack.

The argparse module is also already a popular third-party replacement for these modules. It is used in projects like IPython (the Scipy Python shell) [4], is included in Debian testing and unstable [5], and since 2007 has had various requests for its inclusion in the standard library [6] [7] [8]. This popularity suggests it may be a valuable addition to the Python libraries.

个人理解:

argparse 是一个命令行分析库,它比标准库中的getopt optparse要强大。argparse属于目前流行的第三方命令分析库。

命令解析模块必知的一些基础概念

位置参数

如 ls /home/XXX  其中的 ‘/home/XXX’就称之为位置参数

可选参数

如 ls -l 其中的 ‘-l’ 就称之为可选参数

帮助文档

如 ls --help 

入门dome1

1
2
3
4
import argparse

parse = argparse.ArgumentParser()
parse.parse_args()

用来创建一个基础的命令程序

1
2
3
4
5
6
import argparse

parse = argparse.ArgumentParser()
parse.add_argument('echo',help='回显你输入的字符串')
args = parse.parse_args()
print(args.echo)

当用户使用命令行python 文件名 –help 会查看到
positional arguments:
echo 回显你输入的字符串

positional arguments:

what is positional arguments?

通常它代表命令中的坐标信息,并且它一个必选项参数。

下面的dome演示如何构建及其调用
1
2
3
4
5
6
7
8
9
import argparse

parse = argparse.ArgumentParser()
parse.add_argument('echo',help='回显你输入的字符串')
parse.add_argument('square',help='计算输入数的平方',type=int)

args = parse.parse_args()
print(args.echo)
print(args.square**2)

当用户使用命令行python 文件名 –help 会查看到
positional arguments:
echo 回显你输入的字符串
square 计算输入数的平方

optional arguments:
-h, –help show this help message and exit

optional arguments

what is optional arguments?

它可以代表命令中的一些附件信息,如下面的2个dome一个是表示选择只有一个参数,一个表示选项可以有多个参数

多参数

1
2
3
4
5
6
7
8
9
import argparse

parse = argparse.ArgumentParser()

parse.add_argument('--verbosity',help="increase output verbosity")
args = parse.parse_args()

if args.verbosity:
print('verbosity turned on')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py --version
usage: argparse_study.py [-h] [--verbosity VERBOSITY]
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -h
usage: argparse_study.py [-h] [--verbosity VERBOSITY]

optional arguments:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -verbosity 1
usage: argparse_study.py [-h] [--verbosity VERBOSITY]
argparse_study.py: error: unrecognized arguments: -verbosity 1
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py --verbosity 1
verbosity turned on
spiderbao@spiderbao-CW65S:~/python/study/code$

单参数

1
2
3
4
5
6
7
8
9
import argparse

parse = argparse.ArgumentParser()

parse.add_argument('--verbosity',help="increase output verbosity",action='store_true')
args = parse.parse_args()

if args.verbosity:
print('verbosity turned on')
1
2
3
4
5
6
7
8
9
10
11
12
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py --verbosity
verbosity turned on
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py --verbosity 1
usage: argparse_study.py [-h] [--verbosity]
argparse_study.py: error: unrecognized arguments: 1
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -h
usage: argparse_study.py [-h] [--verbosity]

optional arguments:
-h, --help show this help message and exit
--verbosity increase output verbosity
spiderbao@spiderbao-CW65S:~/python/study/code$

短选项

1
2
3
4
5
6
7
import argparse
parse = argparse.ArgumentParser()
parse.add_argument('-v','--verbosity',help='increase output verbosity',action='store_true')
args = parse.parse_args()

if args.verbosity:
print('verbosity turned on')

命令行输入

1
2
3
4
5
6
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -v 1
verbosity turned on
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -v
verbosity turned on
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py --verbosity
verbosity turned on

组合Positional和Optional参数

1
2
3
4
5
6
7
8
9
10
11
parse = argparse.ArgumentParser()
parse.add_argument('-v','--verbosity',help='显示输出详情',action='count',default=1)
parse.add_argument('square',help='计算输入数的平方',type=int)
args = parse.parse_args()
answer = args.square**2
if args.verbosity>=1:
print('%s^2=%s' %(args.square,answer))
if args.verbosity>=2:
print('runing %s'%__file__)
if args.verbosity<1:
print(answer)

命令行输入显示

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
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -h 
usage: argparse_study.py [-h] [-v] square

positional arguments:
square 计算输入数的平方

optional arguments:
-h, --help show this help message and exit
-v, --verbosity 显示输出详情
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -h
usage: argparse_study.py [-h] [-v] square

positional arguments:
square 计算输入数的平方

optional arguments:
-h, --help show this help message and exit
-v, --verbosity 显示输出详情
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4
4^2=16
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 -v
4^2=16
runing argparse_study.py
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 -vv
4^2=16
runing argparse_study.py

选参互斥异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
parse = argparse.ArgumentParser(description='calculate X to the power of Y')
group = parse.add_mutually_exclusive_group();
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
#group 添加互斥项,相当于usage: argparse_study.py [-h] [-v | -q] x y 中的[-v | -q] 只能选取一个
parse.add_argument("x", type=int, help="the base")
parse.add_argument("y", type=int, help="the exponent")
args = parse.parse_args()
answer = args.x**args.y

if args.quiet:
print(answer)
elif args.verbose:
print("{} to the power {} equals {}".format(args.x, args.y, answer))
else:
print("{}^{} == {}".format(args.x, args.y, answer))

命令行输入显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py -h
usage: argparse_study.py [-h] [-v | -q] x y

calculate X to the power of Y

positional arguments:
x the base
y the exponent

optional arguments:
-h, --help show this help message and exit
-v, --verbose
-q, --quiet
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 2
4^2 == 16
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 2 -v
4 to the power 2 equals 16
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 2 -q
16
spiderbao@spiderbao-CW65S:~/python/study/code$ python3 argparse_study.py 4 2 -vq
usage: argparse_study.py [-h] [-v | -q] x y
argparse_study.py: error: argument -q/--quiet: not allowed with argument -v/--verbose

以上内容参考https://docs.python.org/3/howto/argparse.html?highlight=argparse

Java—日志框架解析-转载

发表于 2021-12-06 | 更新于 2019-06-10

导语

作为Java程序员,幸运的是,Java 拥有功能和性能都非常强大的日志库;不幸的是,这样的日志库有不止一个
相信每个人都曾经迷失在JUL(Java Util Log), JCL(Commons Logging), Log4j, SLF4J, Logback,
Log4j2等等的迷宫中。在我见过的绝大多数项目中,都没有能够良好的配置和使用日志库。这篇文章先讲述Java
常见日志库的历史和关系,后续会讲日志使用的最佳实践。让我们从头(Java Util Log)开始说起吧。

Java Util Log

简称JUL,是JDK 中自带的log功能。虽然是官方自带的log lib,JUL的使用确不广泛。主要原因:

1.JUL从JDK1.4 才开始加入(2002年),当时各种第三方log lib已经被广泛使用了
2.JUL早期存在性能问题,到JDK1.5上才有了不错的进步,但现在和Logback/Log4j2相比还是有所不如
3.JUL的功能不如Logback/Log4j2等完善,比如Output Handler就没有Logback/Log4j2的丰富,有
时候需要自己来继承定制,又比如默认没有从ClassPath里加载配置文件的功能

Log4j 1.x

Log4j 是在 Logback 出现之前被广泛使用的 Log Lib, 由 Gülcü 于2001年发布,后来成为Apache
基金会的顶级项目。Log4j 在设计上非常优秀,对后续的 Java Log 框架有长久而深远的影响,也产生了
Log4c, Log4s, Log4perl 等到其他语言的移植。Log4j 的短板在于性能,在Logback 和 Log4j2
出来之后,Log4j的使用也减少了。

Commons Logging

简称JCL,是Apache下面的项目。JCL 是一个Log Facade,只提供 Log API,不提供实现,然后有 Adapter
来使用 Log4j 或者 JUL 作为Log Implementation。

就像之前所说,JDK现在带了自己的JUL,然后又有第三方的Log4j等日志库存在,不同的项目可能各自使用了
不同的日志库。如果你的项目依赖的其他lib各自使用了不同的日志库,你想控制日志行为,就需要针对每个日
志库都写一个配置文件,是不是很酸爽?

然后这个时候 JCL 就出现了。在程序中日志创建和记录都是用JCL中的接口,在真正运行时,会看当前ClassPath
中有什么实现,如果有Log4j 就是用 Log4j, 如果啥都没有就是用 JDK 的 JUL。

这样,在你的项目中,还有第三方的项目中,大家记录日志都使用 JCL 的接口,然后最终运行程序时,可以按照自
己的需求(或者喜好)来选择使用合适的Log Implementation。如果用Log4j, 就添加 Log4j 的jar包进去,然
后写一个 Log4j 的配置文件;如果喜欢用JUL,就只需要写个JUL的配置文件。如果有其他的新的日志库出现,也
只需要它提供一个Adapter,运行的时候把这个日志库的 jar 包加进去。

到这个时候一切看起来都很简单,很美好。接口和实现做了良好的分离,在统一的JCL之下,不改变任何代码,就可以
通过配置就换用功能更强大,或者性能更好的日志库实现。

这种简单美好一直持续到SLF4J出现。

SLF4J/Logback

SLF4J(The Simple Logging Facade for Java)和Logback也是Gülcü创立的项目,其创立主要是为了提供更
高性能的实现。其中,SLF4j 是类似于JCL 的Log Facade,Logback 是类似于Log4j 的 Log Implementation。

之前已经说过,Apache 有了个JCL,用来做各种Log lib统一的接口,如果 Gülcü 要搞一个更好的Log实现的话,
直接写一个实现就好了,为啥还要搞一个和SLF4J呢?

原因是Gülcü 认为 JCL 的 API 设计得不好,容易让使用者写出性能有问题的代码。

比如在用 JCL 输出一个 debug 级别的 log:

1
logger.debug("start process request, url:" + url);

这个有什么问题呢?一般生产环境 log 级别都会设到 info 或者以上,那这条 log 是不会被输出的。然而不管会不会输出,
这其中都会做一个字符串连接操作,然后生产一个新的字符串。如果这条语句在循环或者被调用很多次的函数中,就会多做很多
无用的字符串连接,影响性能。

所以 JCL 的最佳实践推荐这么写:

1
2
3
if (logger.isDebugEnabled()) {
logger.debug("start process request, url:" + url);
}

然而开发者常常忽略这个问题或是觉得麻烦而不愿意这么写。所以SLF4J提供了新的API,方便开发者使用:

1
logger.debug("start process request, url:{}", url);

这样的话,在不输出 log 的时候避免了字符串拼接的开销;在输出的时候需要做一个字符串format,代价比手工拼接字符串大一些,
但是可以接受。

而 Logback 则是作为 Log4j 的继承者来开发的,提供了性能更好的实现,异步 logger,Filter等更多的特性。

现在事情变复杂了。我们有了两个流行的 Log Facade,以及三个流行的 Log Implementation。Gülcü是个追求完美的人,
他决定让这些Log之间都能够方便的互相替换,所以做了各种 Adapter 和 Bridge 来连接:

可以看到甚至 Log4j 和 JUL 都可以桥接到SLF4J,再通过 SLF4J 适配到到 Logback!

在这里需要注意不能搞出循环的桥接,比如下面这些依赖就不能同时存在:

1.jcl-over-slf4j 和 slf4j-jcl
2.log4j-over-slf4j 和 slf4j-log4j12
3.jul-to-slf4j 和 slf4j-jdk14

总感觉事情在变得更麻烦呢!

Log4j2

现在有了更好的 SLF4J 和 Logback——你会想事情到这里总该了解了吧,让他们慢慢取代JCL 和 Log4j 好了。

然而维护 Log4j 的人不这样想,他们不想坐视用户一点点被 SLF4J /Logback 蚕食,继而搞出了 Log4j2。

Log4j2 和 Log4j1.x 并不兼容,设计上很大程度上模仿了 SLF4J/Logback,性能上也获得了很大的提升。

Log4j2 也做了 Facade/Implementation 分离的设计,分成了 log4j-api 和 log4j-core。

现在好了,我们有了三个流行的Log 接口和四个流行的Log实现,如果画出桥接关系的图来回事什么样子呢?

是不是感觉有点晕呢?同样,在添加依赖的时候,要小心不要搞成循环依赖。对于如今的局面我的日志使用方案是

个人推荐日志搭配方案

1. 总是使用Log Facade,而不是具体Log Implementation

正如之前所说的,使用 Log Facade 可以方便的切换具体的日志实现。而且,如果依赖多个项目,使用了不同的Log Facade,
还可以方便的通过 Adapter 转接到同一个实现上。如果依赖项目使用了多个不同的日志实现,就麻烦的多了。

具体来说,现在推荐使用 Log4j-API 或者 SLF4j,不推荐继续使用 JCL。

2. 只添加一个 Log Implementation依赖

毫无疑问,项目中应该只使用一个具体的 Log Implementation,建议使用 Logback 或者Log4j2。如果有依赖的项目中,
使用的 Log Facade不支持直接使用当前的 Log Implementation,就添加合适的桥接器依赖。具体的桥接关系可以看
上一篇文章的图。

3. 具体的日志实现依赖应该设置为optional和使用runtime scope

在项目中,Log Implementation的依赖强烈建议设置为runtime scope,并且设置为optional。例如项目中使用了 SLF4J
作为 Log Facade,然后想使用 Log4j2 作为 Implementation,那么使用 maven 添加依赖的时候这样设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

设为optional,依赖不会传递,这样如果你是个lib项目,然后别的项目使用了你这个lib,不会被引入不想要的Log
Implementation 依赖;

Scope设置为runtime,是为了防止开发人员在项目中直接使用Log Implementation中的类,而不适用Log Facade中的类。

4. 如果有必要, 排除依赖的第三方库中的Log Impementation依赖

这是很常见的一个问题,第三方库的开发者未必会把具体的日志实现或者桥接器的依赖设置为optional,然后你的项目继承了这些
依赖——具体的日志实现未必是你想使用的,比如他依赖了Log4j,你想使用Logback,这时就很尴尬。另外,如果不同的第三方依
赖使用了不同的桥接器和Log实现,也极容易形成环。

这种情况下,推荐的处理方法,是使用exclude来排除所有的这些Log实现和桥接器的依赖,只保留第三方库里面对Log Facade的依赖。

比如阿里的JStorm就没有很好的处理这个问题,依赖jstorm会引入对Logback和log4j-over-slf4j的依赖,如果你想在自己的项目
中使用Log4j或其他Log实现的话,就需要加上excludes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>com.alibaba.jstorm</groupId>
<artifactId>jstorm-core</artifactId>
<version>2.1.1</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>

5. 避免为不会输出的log付出代价(减少对不输出日志的成本)

Log库都可以灵活的设置输出界别,所以每一条程序中的log,都是有可能不会被输出。这时候要注意不要额外的付出代价。(字符拼接代价)

先看两个有问题的写法:

1
2
logger.debug("start process request, url: " + url);
logger.debug("receive request: {}", toJson(request));

第一条是直接做了字符串拼接,所以即使日志级别高于debug也会做一个字符串连接操作;第二条虽然用了SLF4J/Log4j2中的懒
求值方式来避免不必要的字符串拼接开销,但是toJson()这个函数却是都会被调用并且开销更大。

推荐的写法如下:

1
2
3
4
5
6
logger.debug("start process request, url:{}", url); // SLF4J/LOG4J2
logger.debug("receive request: {}", () -> toJson(request)); // LOG4J2
logger.debug(() -> "receive request: " + toJson(request)); // LOG4J2
if (logger.isDebugEnabled()) { // SLF4J/LOG4J2
logger.debug("receive request: " + toJson(request));
}

6. 日志格式中最好不要使用行号,函数名等字段

原因是,为了获取语句所在的函数名,或者行号,log库的实现都是获取当前的stacktrace,然后分析取出这些信息,而获取
stacktrace的代价是很昂贵的。如果有很多的日志输出,就会占用大量的CPU。在没有特殊需要的情况下,建议不要在日志中
输出这些这些字段。

7. log中不要输出稀奇古怪的字符!

部分开发人员为了方便看到自己的log,会在log语句中加上醒目的前缀,比如:

1
logger.debug("========================start process request=============");

虽然对于自己来说是方便了,但是如果所有人都这样来做的话,那log输出就没法看了!正确的做法是使用grep(linux 中的shell命令) 来看只自己关心的日志。

转载处:https://zhuanlan.zhihu.com/p/24272450

git

发表于 2021-12-06 | 更新于 2020-06-09

为什么使用git

这个问题的答案就在于,集中式版本控制和分布式版本控制的的区别。

那么怎样算得上集中式版本控制呢?

比如说SVN就是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就郁闷了。

svn-active-stream

集中式版本控制存在如下缺陷: 依赖网络性强,由于只使用一个中央服务器,所以会有单点问题。

以上说了集中式版本控制,那么分布式版本控制工具又是怎么样的呢?

分布式版本控制的代表就是git

Git 是分布式版本控制系统,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,如果是自己先push上传,别想要push时远程库就会阻止并提示他更新最新的远程版本库中的内容后再push。下图就是分布式版本控制工具管理方式:

git-active-stream

分布式版本控制:解决了集中式版本控制的单点问题,和网络依赖性问题,适用于互联网多地区协同开发,但是在只是在局域网下开发,个人认为还是使用svn会更方便,git虽然性能优越但是使用上没有SVN简便。

git 和svn等系统的存储差异

从概念上来说,其他大部分系统以文件变更列表的方式存储信息。这类系统将它们存储的信息看作是一组基本文件和每个文件随时间逐步积累的差异。(被称作基于差异的版本控制)

如下图:
checking-over-time

Figure . 存储每个文件与初始版本的差异.

git对待数据的方式与上图不同,而git是把每个文件看做一个小的文件系统然后为其生成快照,从而管理一系列快照。在git中,每当你提交更新或保存项目的状态时,它基本上对待当前项目的文件创建一份快照,并保存这个快照指向的索引。为了效率,如果文件没有修改,git不再更新存储该文件,而是只保留一个链接指向之前存储的文件。git对待数据更像是一个快照流

checking-over-time-git

Figure.存储项目随时间改变的快照.

git简介

同生活中的许多伟大事件一样,Git 诞生于一个极富纷争大举创新的年代。Linux 内核
开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保
存归档的繁琐事务上(1991-2002 年间)。到 2002 年,整个项目组开始启用分布式版本
控制系统 BitKeeper 来管理和维护代码。
到 2005 年的时候,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结
束,他们收回了免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux 的
缔造者 Linus Torvalds )不得不吸取教训,只有开发一套属于自己的版本控制系统才不至于
重蹈覆辙。他们对新的系统订了若干目标:
• 速度
• 简单的设计
• 对非线性开发模式的强力支持(允许上千个并行开发的分支)
• 完全分布式
• 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

工作流程

一般工作流程如下:
1.从远程仓库中克隆 Git 资源作为本地仓库。
2.从本地仓库中 checkout 代码然后进行代码修改
3.在提交前先将代码提交到暂存区。
4.提交修改。提交到本地仓库。本地仓库中保存修改的各个历史版本。
5.在修改完成后,需要和团队成员共享代码时,可以将代码 push 到远程仓库。
下图展示了 Git 的工作流程:

git-active-stream

git 的工作目录介绍

git-active-stream

其中 ./git 文件夹下就是git的本地仓库

git的使用

创建仓库

1
git init 要初始的文件夹路径

添加文件到暂存区

1
git add 要加入的文件

将暂存区文件添加到本地仓库,并将本地仓库推送到远程仓库中

将缓存区域文件添加到本地仓库

1
git commit

将本地仓库推送到远程仓库中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spiderbao@spiderbao-CW65S:~/git_study$ git push ssh://git@39.xxx.171.29/home/git/git_study
fatal: 当前分支 master 没有对应的上游分支。
为推送当前分支并建立与远程上游的跟踪,使用

git push --set-upstream ssh://git@39.XXX.171.29/home/git/git_study master

spiderbao@spiderbao-CW65S:~/git_study$ git push --set-upstream ssh://git@39.XXX.171.29/home/git/git_study master
git@39.XXX.171.29's password:
Permission denied, please try again.
git@39.XXX.171.29's password:
对象计数中: 6, 完成.
Delta compression using up to 4 threads.
压缩对象中: 100% (3/3), 完成.
写入对象中: 100% (6/6), 523 bytes | 523.00 KiB/s, 完成.
Total 6 (delta 0), reused 0 (delta 0)
To ssh://39.XXX.171.29/home/git/git_study
* [new branch] master -> master
分支 'master' 设置为跟踪来自 'ssh://git@39.XXX.171.29/home/git/git_study' 的远程分支 'master'。

查看修改历史

1
2
3
4
5
6
7
8
9
10
11
12
spiderbao@spiderbao-CW65S:~/git_study$ git log
commit 4cb2b4f87b03b6cf3301b000a3e14c44027e3f42 (HEAD -> master)
Author: spiderbao <13593265401@163.com>
Date: Fri May 17 14:28:44 2019 +0800

第二次修改

commit 1910c01353a8f12304e2a923164f69e33925a03a
Author: spiderbao <13593265401@163.com>
Date: Fri May 17 14:21:19 2019 +0800

first commit

还原修改

reset 还原,还原后历史版本不存在

1
2
spiderbao@spiderbao-CW65S:~/git_study$ git reset --hard  1910c01353a8f12304e2a923164f69e33925a03a
HEAD 现在位于 1910c01 first commit

删除文件

1
2
spiderbao@spiderbao-CW65S:~/git_study$ git rm test 
rm 'test'

忽略文件或文件夹

在文件目录下创建gitignore 文件

1
2
touch .gitignore
vim .gitignore

忽略语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
空行或是以 # 开头的行即注释行将被忽略。
可以在前面添加正斜杠 / 来避免递归,下面的例子中可以很明白的看出来与下一条的区别。
可以在后面添加正斜杠 / 来忽略文件夹,例如 build/ 即忽略 build 文件夹。
可以使用 ! 来否定忽略,即比如在前面用了 *.apk ,然后使用 !a.apk ,则这个 a.apk 不会
被忽略。
* 用来匹配零个或多个字符,如 *.[oa] 忽略所有以".o"或".a"结尾, *~ 忽略所有以 ~ 结尾
的文件(这种文件通常被许多编辑器标记为临时文件); [] 用来匹配括号内的任一字符,
如 [abc] ,也可以在括号内加连接符,如 [0-9] 匹配 0 至 9 的数; ? 用来匹配单个字符。
看了这么多,还是应该来个栗子:
# 忽略 .a 文件
*.a
# 但否定忽略 lib.a, 尽管已经在前面忽略了 .a 文件
!lib.a
# 仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO
/TODO
# 忽略 build/ 文件夹下的所有文件
build/
# 忽略 doc/notes.txt, 不包括 doc/server/arch.txt
doc/*.txt
# 忽略所有的 .pdf 文件 在 doc/ directory 下的
doc/**/*.pdf

解决文件冲突

编辑有冲突后的文件,再次提交即可

文件更新或拉取

1
2
3
4
5
6
7
8
9
10
11
12
spiderbao@spiderbao-CW65S:~/cloneToGit$ git clone ssh://git@39.xxx.xxx.29/home/git/git_study
正克隆到 'git_study'...
git@39.xxx.xxx.29's password:
remote: Counting objects: 13, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 13 (delta 0), reused 0 (delta 0)
接收对象中: 100% (13/13), 完成.

spiderbao@spiderbao-CW65S:~/cloneToGit$ cd git_study/
spiderbao@spiderbao-CW65S:~/cloneToGit/git_study$ git pull
git@39.xxx.xxx.29's password:
已经是最新的。

git分支管理

为了真正理解 Git 处理分支的方式,我们需要回顾一下 Git 是如何保存数据的。

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照

在进行提交操作时,git会保存一个提交对象(commit object)。该提交对象会包含一个指向暂存快照的指针。但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象

案例:

我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和(使用我们在 起步 中提到的 SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中 (Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交

1
2
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。

现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个 树 对象 (记录着目录结构和 blob 对象索引)以及一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。

branch-1

Figure 首次提交对象及其树结构

做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。

branch-1

Figure 提交对象及其父对象

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。 master 分支会在每次提交时自动向前移动。

Note: Git 的 master 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。

branch-3

创建分支

1
git branch 新分支名称

选择分支

1
git checkout 分支名称

合并分支

1
2
3
4
5
6
spiderbao@spiderbao-CW65S:~/git_study$ git merge mybatis 
更新 8c65eb0..458de41
Fast-forward
xiaoli | 1 +
1 file changed, 1 insertion(+)
create mode 100644 xiaoli

git 贮藏(stash)

个人认为此功能用来间当前缓存区中的信息暂时贮藏,便于切换别的分支去处理事情。等处理完后再切换到当前分支并恢复贮藏信息并继续当前分支的开发。

使用场景:

由于分支的切换需要干净的暂存区(stage,也是就是这个区域内不能有信息)。所以导致想要切换版本就必须面临提交当前工作区信息到版本库中或者不提交。而这两者都具有弊端,前者能保存当前工作区中的内容,但是会对提交历史产生影响。后者虽然不会对仓库的历史产生影响,但是当前工作区中的信息也不会保存。使用git的贮藏功能可以兼备以上所期望的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
* commit 394e7afc1077df0bf4d399c8d6d1e6821eb7f49d (HEAD -> experiment)
| Author: spiderbao <13593265401@163.com>
| Date: Thu Jun 4 14:29:01 2020 +0800
|
| test
|
| modified test
|
* commit 49fd47ed235a96ebe7a353c016aa4602c3b8dfb5
| Author: spiderbao <13593265401@163.com>
| Date: Wed Jun 3 10:05:10 2020 +0800
|
| add experiment
|
* commit 214cbe6f166becc20309a70f35bbc43ff60ec7eb (master)
| Author: spiderbao <13593265401@163.com>
| Date: Wed Jun 3 10:06:25 2020 +0800
|
| add test
|
* commit 252d42a731d129478ecfc2882d691ab3ee6e5deb
Author: spiderbao <13593265401@163.com>
Date: Wed Jun 3 10:02:57 2020 +0800

add README.md

当用户在experiment开发时,接到了临时的紧急通知需要修改master分支上的几处BUG。

1.使用git status 查看当前项目的情况,将要保存的信息放入暂存区(stage)处

1
2
3
4
5
6
7
8
9
10
11
位于分支 experiment
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)

新文件: index.html
新文件: lib/simplegit.rb

未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)

TODO
1
$  git add TODO
1
2
3
4
5
6
7
位于分支 experiment
要提交的变更:
(使用 "git reset HEAD <文件>..." 以取消暂存)

新文件: TODO
新文件: index.html
新文件: lib/simplegit.rb
  1. 将暂存区中的内容通过stash 放入特定的一个栈中

    1
    2
    $ git stash 
    保存工作目录和索引状态 WIP on experiment: 394e7af test
  1. 切换分支工作,解决BUG并提交

    1
    2
    3
    $ git checkout master
    切换到分支 'master'
    ......
  1. 切换分支并恢复贮藏信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ git checkout experiment 
    切换到分支 'experiment'
    $ git stash apply stash@{0}
    位于分支 experiment
    要提交的变更:
    (使用 "git reset HEAD <文件>..." 以取消暂存)

    新文件: TODO
    新文件: index.html
    新文件: lib/simplegit.rb
  1. 清理贮藏区中的信息

    1
    2
    3
    4
    $ git stash list
    stash@{0}: WIP on experiment: 394e7af test
    $ git stash drop stash@{0}
    丢弃了 stash@{0} (b1cb57a6cb76585027a27eed6779bcab6ca4f400)

树莓派-系统篇

发表于 2021-12-06 | 更新于 2019-11-22

shell程序开机启动

核心文件是 /etc/profile

1
2
3
4
vim /etc/profile

#将需要开机自启动的文件路径加入
/home/pi/text.sh

获取系统位数

1
getconf LONG_BIT

jwt

发表于 2021-12-06 | 更新于 2019-04-29

什么是jwt

​ JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的
信息。在Java世界中通过JJWT实现JWT创建和验证。

jjwt快速尝鲜

  1. 引入坐标

    1
    2
    3
    4
    5
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.6.0</version>
    </dependency>

    上面使用的坐标是基于apache2.0 下的jwt实现

  2. 使用并测试

    生成token

    1
    2
    3
    4
    5
    6
    7
    8
    JwtBuilder jwt = Jwts.builder()
    .setId("123")
    .setSubject("老王")
    .setIssuedAt(new Date())//发布时间
    .setExpiration(new Date(System.currentTimeMillis()+6000))//到期时间
    .signWith(SignatureAlgorithm.HS384,"XXXXX");//设置密钥
    String compact = jwt.compact();//签约生成token
    System.out.println(compact);

    解析token

    1
    2
    3
    4
    Claims cla = Jwts.parser()
    .setSigningKey("XXXXX")//设置密钥
    .parseClaimsJws(key)//设置token
    .getBody();

    当token认证失败时会抛出ExpiredJwtException异常

    自定义claims

    生成

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //为了方便测试,我们将过期时间设置为1分钟
    long now = System.currentTimeMillis();//当前时间
    long exp = now + 1000*60;//过期时间为1分钟
    JwtBuilder builder= Jwts.builder().setId("888")
    .setSubject("小白")
    .setIssuedAt(new Date())
    .signWith(SignatureAlgorithm.HS256,"itcast")
    .setExpiration(new Date(exp))
    .claim("roles","admin") //自定义claims存储数据
    .claim("logo","logo.png");
    System.out.println( builder.compact() );

    解析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    String
    compactJws="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiOjE1MjM0MT
    czMjMsImV4cCI6MTUyMzQxNzM4Mywicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJsb2dvLnBuZyJ9.b11p4g4rE94r
    qFhcfzdJTPCORikqP_1zJ1MP8KihYTQ";
    Claims claims =
    Jwts.parser().setSigningKey("itcast").parseClaimsJws(compactJws).getBody();
    System.out.println("id:"+claims.getId());
    System.out.println("subject:"+claims.getSubject());
    System.out.println("roles:"+claims.get("roles"));
    System.out.println("logo:"+claims.get("logo"));
    SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
    System.out.println("过期时间:"+sdf.format(claims.getExpiration()));
    System.out.println("当前时间:"+sdf.format(new Date()) );

bash-初识

发表于 2021-12-06 | 更新于 2020-04-21

什么是bash

简单的说,bash是一个命令解释工具

命令的语法以及规范

变量

变量和java中的变量所表示的含义大致相同,都是用一个引用表示地址值中的数据。

待验证:bash的变量只能存储文本。

变量的赋值方式和Java中变量赋值类似。下面列举几个例子,看后自会明白。

$var=word 注意等号的左右不能留有空格

$var=’hello word’

$var=”hello word”
上面两种都可以表示文本中包含空格的输入。不同之处在于,双引号中可以掺入变量,系统会放入变量引用的值。而单引号却只能打印所有输入的文本内容,变量引用也会当做文本。

$v1=word
$var=”hello ${v1}test” ${}中放入的变量可以中缺的被标识并识别,如果不使用${} 表达式的含义将会变为引用v1!的变量此时不会输出 hello Wordtest 而是 hello

$now=date 注意中可以使用命令。查看now echo $now 这时会输出时间

$date=$now 变量之间也可以互相传值

$read name 这时Linux会等待用户输入,输入完成后会把输入的值赋值给name

数学运算

bash中的数学计算类似上学时的数学运算,只要吧运算表达式写入$(())中即可

加法运算 $((3+2))

减法运算 $((3-2))

乘法运算 $((3*2))

除法运算 $((3/2))

求余运算 $((2%3))

乘方运算 $((2**3))

返回代码

Linux命令执行完后会放回一个值,返回0表示这段命令执行成功
$? 可以查看当前命令执行的结果

;的使用

命令中使用;可以在一行命令中执行多个命令。如ls;$? 表示打印目录信息并回响命令返回值

短路与或

短路与,在多个命令中,后面命令的执行与否取决于之前命令的返回值,当返回值为0时之后的命令才会执行

如: rm dome.file && echo “remove dome.file sucess” 当删除过文件dome.file后会输出文本信息,失败后则不输出。

短路或,短路或正好是短路与的反向解释。当之前的命令失败后后面的命令才会执行,否则不执行

如:rm dome.file && echo “remove dome.file fail” 当没有删除文件dome.file后会输出文本信息,否则不会输出任何信息。

bash script

first bash file

1
2
3
4
5
6
7
8
#!/bin/bash

user=`whoami`
echo "Information of ${user} computer" >> log

lscpu >> log
uname -a >> log
free -h >> log

这是一个简单的bash脚本,刚刚解释了什么是bash他是一个命令解释器,所以bash脚步就是集多条bash命令的一个文本。
这样做的好处是我们可以记录使用命令的逻辑,并多次复用。

bash中传入参数

bash 中的参数使用 $0 $1 …表示

使用例子:

测试流程 1编辑bashscript 2运行 3显示结果

1 编辑script

1
2
3
4
5
#!/bin/bash

echo $0
echo $1
echo $2

2 运行

1
spiderbao@spiderbao-CW65S:~/my-bash$ ./test_arg.bash hello word

3.显示结果

1
2
3
./test_arg.bash
hello
word

函数

个人认为函数是对bash脚本的一次封装,通过内部定义函数可以将命令功能分块包装。从而达到脚本内函数的复用

无参函数示例

测试流程 1编写脚本 2运行 3展示结果

1 编写脚本

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

function meInfo(){
date >> log
lscpu >> log
uname -a >> log
free -h >> log
}

meInfo

2 运行

1
spiderbao@spiderbao-CW65S:~/my-bash$ ./test_function.bash

3 显示结果

1
2
3
4
5
6
7
8
spiderbao@spiderbao-CW65S:~/my-bash$ cat log 
2019年 08月 26日 星期一 13:52:10 CST
xxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxx
xxxxxxxxxxxxx
x

带参函数示例

测试流程 1编写脚本 2运行 3展示结果

1.编写脚本

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

function meInfo(){
date >> $1
echo 'hello' >> $1
echo 'word' >> $1
}

meInfo melog
meInfo melog2

2 运行

1
spiderbao@spiderbao-CW65S:~/my-bash$ ./test_function.bash

3 显示结果

1
2
3
4
5
6
7
8
spiderbao@spiderbao-CW65S:~/my-bash$ cat melog
2019年 08月 26日 星期一 14:14:37 CST
hello
word
spiderbao@spiderbao-CW65S:~/my-bash$ cat melog2
2019年 08月 26日 星期一 14:14:37 CST
hello
word

跨脚本函数调用

首先跨脚本函数调用是基于脚本之上的操作,从此也可以看出函数封装的好处。

跨脚本函数调用主要使用source关键字

测试流程 1编写脚本 2 运行 3 展示结果

1 编写脚本

test_function.bash

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

function meInfo(){
date >> $1
echo 'hello' >> $1
echo 'word' >> $1
}

meInfo melog
meInfo melog2

app.bash

1
2
3
4
5
6
#!/bin/bash

source test_function.bash

meInfo melog3
meInfo log

2 运行

1
spiderbao@spiderbao-CW65S:~/my-bash$ ./app.bash

3 展示结果

1
2
3
4
5
6
7
8
9
10
spiderbao@spiderbao-CW65S:~/my-bash$ cat melog melog2 melog3
2019年 08月 26日 星期一 14:30:22 CST
hello
word
2019年 08月 26日 星期一 14:30:22 CST
hello
word
2019年 08月 26日 星期一 14:30:22 CST
hello
word

可以看到source 引入的test_function.bash 脚本被执行了一遍,注意是整个脚本不单是脚本中的一个函数而是整个脚本。

逻辑判断

逻辑判断为的是判断一个函数式运行的真假或一个命令的真假

案例

1
2
spiderbao@spiderbao-CW65S:~/my-bash$ test 3 -gt 2 ;echo $?
0

因为3大于2(-gt表示大于)所以命令的返回值为0

关于数值的判断符

大于 -gt
小于 -lt
等于 -eq
不等于 -ne
大于等于 -ge
小于等于 -le

关于文本的判断符

文本相同 =
文本不相同 !=
文本按字典排序一个文本在另一个文本之前 >
文本按字典排序一个文本在另一个文本之后 <

关于文件判断符

检查文件是否存在 -e
检查文件是否存在并且是普通文件 -f
检查目录是否存在 -d
检查软连接是否存在 -l
检查文件是否可读 -r
检查文件是否可写 -w
检查文件是否可以执行 -x

与或非

&& || !

注意 && || ! 不能联合使用

选择结构

if

if 选择结构,类似java中的流程控制语句if

案例

1
2
3
4
5
6
7
#!/bin/bash

user=`whoami`
if [ $user = 'root' ]
then
echo "你是超级用户!"
fi

需要注意语法格式

if else

if else ,类似java中的流程控制语句 if else

案例

1
2
3
4
5
6
7
8
9
#!/bin/bash

user=`whoami`
if [ $user = 'root' ]
then
echo "你是超级用户!"
else
echo "你不是超级用户!"
fi

需要注意语法格式

if else 嵌套使用,类似java流程控制语句中的if else if else

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

user=`whoami`
if [ $user = 'root' ]
then
echo "你是超级用户!"
else if [ $user = 'laowang' ]
then
echo "你是老王用户!"
else if [ $user = 'spiderbao' ]
then
echo '你是spidermen的朋友spiderbao!'
else
echo '我也不知道你是谁!'
fi
fi
fi

格式样式模仿Java的if else if else ,唯一不同处在于结尾fi使用了多少个if就需要添加多少个fi来结尾。毕竟这是一个嵌套语句。

case

case 类似java流程控制语句中的switch

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

user=`whoami`

case $user in
root)
echo "你是超级管理员用户!"
;;
laowang)
echo "你是老王用户!"
;;
spiderbao)
echo '你是spiderbao用户!'
;;
*)
echo '我也不知道你是谁!'
;;
esac

需要注意的是 )中可以填写文本以及通配符
通配符

  • 表示任意个数的任意字符
    ? 表示一个字符的任意字符
    [] 类似正则表达式中的[],表示可选范围内的字符,另外[]只占一个字符位 。例如[1-5][a-x] 1a,2b 均匹配

Linux-wget

发表于 2021-12-06 | 更新于 2019-07-03

简介:

来自 https://www.jianshu.com/p/2e2ba8ecc22a 用户的摘录:

1
2
3
4
5
6
wget是linux上的命令行的下载工具。这是一个GPL许可证下的自由软件。wget支持HTTP和FTP协议,支持代理服务器和断点续传功能,能够自动递归远程主机的目录,找到合乎条件的文件并将其下载到本地硬盘上;如果必要,wget将恰当地转换页面中的超级连接以在本地生成可浏览的镜像。由于没有交互式界面,wget可在后台运行,截获并忽略HANGUP信号,因此在用户推出登录以后,仍可继续运行。通常,wget用于成批量地下载Internet网站上的文件,或制作远程网站的镜像。

作者:JeffYU
链接:https://www.jianshu.com/p/2e2ba8ecc22a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
1234…7
WenLiangBao

WenLiangBao

个人技术总结。(*^_^*)温故而知新,可以为师矣

62 日志
7 标签
Creative Commons
© 2024 WenLiangBao
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Mist v7.0.0