CompileMind

compile code, compile mind

0%

在Centos7版本之后,防火墙应用已经由从前的iptables转变为firewall这款应用了。但是,当今绝大多数的Linux版本(特别是企业中)还是使用的6.x以下的Centos版本,所以对iptables的了解还是很有必要的。此外,需要说明的是iptables自身并不具备防火墙的功能,它需要通过内核netfilter(网络过滤器)来实现,与firewalld一样,他们的作用都是用于维护规则,而真正使用规则干活的是内核的netfilter,只不过firewalld和iptables的结构以及使用方法不一样,他们都只是一个外壳应用罢了。打个比方,就好像有一本书,同样的内容,一种是纸质的,另一种是电子的,我们翻阅它的方式不同,给它做笔记的方式不同,但是内容(内核)一样。

早些时间心血来潮买过一个树莓派,但是当时只是玩一玩,买来按照网上的教程摆弄了一下就闲置了。最近毕业设计,做时序数据分析的相关的工作,刚好想起能够用到树莓派+Node-RED来生成模拟的时序数据。于是开始搭建相关的环境。特此记录一下。

树莓派搭建Node-RED环境

树莓派自2015年开始是默认就带NODE-RED的,但是如今已是2018年:)自带的版本已经很老了,可通过下面的命令进行自动升级NODE.JS和NODE-RED

在Linux下一切都是文件,无论是设备还是接口,亦或是网卡等均被抽象成了文件,并且有相关的内核代码进行调度。然而,在一切都是文件的前提下,最需要进行探讨的则是文件存储的根源:文件系统。文件系统的好坏能够更加完美的解决在一个操作系统中对于文件的管理。

如今我们大多数情况都会使用IDE来进行Java项目的开发,而一个如今众多优秀的IDE已经能够帮助我们自动的部署并调试运行我们的Java程序。然而在早期我们进行Java开始需要手动的建立逻辑包(package)与目录来管理我们的Java项目或是更高级一点的则是使用ant这样的构建工具。作为Javaer,对于Java的编译过程应当是熟悉的,这样即使脱离了IDE我们依然能够很好的理解Java的构建过程。

macOS下的Java与JDK的路径曾经困扰过我一段时间,今天稍有些忘记,故记下笔记,整理一下。Java与JDK的关系不在本文笔记之内,Javaer常识。

tr命令

tr命令可以将输入的数据中的某些字符做替换或者是作删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
tr [-ds] STR
d: 删除输入数据的中的STR
s: 替换重复的字符
#
last | tr '[a-z]' 'A-Z'
将last输出的数据中的所有小写字符替换为大写字符
SPPU UUZ1 TVF MBS 13 18:45 TUJMM MPHHFE JO
SFCPPU TZTUFN CPPU 3.10.0-693.17.1. TVF MBS 13 18:45 - 18:47 (00:01)
SPPU UUZ1 TVF MBS 13 10:55 - 13:15 (02:20)
SFCPPU TZTUFN CPPU 3.10.0-693.17.1. TVF MBS 13 10:54 - 18:47 (07:52)
SPPU UUZ1 MPO MBS 12 18:33 - 19:35 (01:02)
...

cat /etc/passwd | tr -d ':'
将cat /etc/passwd输出的数据中的':'全部删除
# output
rootx00root/root/bin/bash
binx11bin/bin/sbin/nologin
daemonx22daemon/sbin/sbin/nologin
admx34adm/var/adm/sbin/nologin
lpx47lp/var/spool/lpd/sbin/nologin
...

col命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
col [-xb]
-x: 将tab键替换为等长的空个
-b: 在文字内由反斜杠时,仅保留反斜杠后接的那个字符

cat -A ~/.bashrc
# 使用cat -A可以讲输出中所有的特殊按键
# output
...
# Source global definitions$
if [ -f /etc/bashrc ]; then$
^I. /etc/bashrc$
fi$
# 注意这里有个^I就是tab字符。

cat -A ~/.bashrc | col -x
# output
# Source global definitions$
if [ -f /etc/bashrc ]; then$
. /etc/bashrc$
# tab字符不再出现

join命令

用于对两个文件按照某一个字符或者字段进行按行连接

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
join [-ti12] file1 file2
-t: 选择分割字符,并且对比“第一个字段”的数据,如果两个文件相同,则将两条数据连成一行,并将第一个字段放在最前
-i: 忽略大小写
-1: 表示第一个文件
-2: 表示第二个文件

# 例1
head -n 3 /etc/passwd /etc/shadow
# 先查看这两个文件前三行数据
==> /etc/passwd <==
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

==> /etc/shadow <==
root:<密码太长,我忽略了方便查看>:17593:0:99999:7:::
bin:*:17110:0:99999:7:::
daemon:*:17110:0:99999:7:::

join -t ':' /etc/passwd /etc/shadow | head -n 3
# output
root:x:0:0:root:/root:/bin/bash:<密码太长,我忽略了方便查看>:17593:0:99999:7:::
bin:x:1:1:bin:/bin:/sbin/nologin:*:17110:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:17110:0:99999:7:::
# 我们可以看到,按照':'分割,并且默认一第一个字段进行连接

# 例2
我们知道/etc/passwd中第四个字段是GID,而/etc/group中第三个字段是GID,我们就可以像如下进行整合:
join -t ':' -1 4 /etc/passwd -2 3 /etc/group | head -n 3
# output
0:root:x:0:root:/root:/bin/bash:root:x:
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:
# 我们可以看到,将我们需要的字段提到了最前

paste命令

1
2
3
4
5
6
7
8
直接讲两个文件中的数据按行连接
paste [-d] file1 file2
-d: 设定每行数据连接的字符,默认为tab
paste /etc/passwd /etc/group | head -n 3
# output
root:x:0:0:root:/root:/bin/bash root:x:0:
bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:x:2:

expand命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
expand [-t] file
-t: 后面接数字,代表了将一个tab替换为多少个空格键
#
cat -A ~/.bashrc
# 使用cat -A可以讲输出中所有的特殊按键
# output
...
# Source global definitions$
if [ -f /etc/bashrc ]; then$
^I. /etc/bashrc$
fi$
注意看有个^I,是tab符号
cat -A ~/.bashrc | expand -t 10 -(标准输入) | cat -A
# output
...
# Source global definitions$
if [ -f /etc/bashrc ]; then$
. /etc/bashrc$
fi$
我们可以看到原先的tab变为了10个空格

数据流重定向

1
2
3
4
标准输入(stdin):代码为0,使用<或<<;
标准输出(stdout):代码为1,使用>或>>;
标准错误输出(stderr):代码为2,使用2>或2>>;
>:覆盖的方式,>>:追加的方式

如果想要一般输出与错误输出同时输入到某一个文件,如果采取如下的方式进行输出是错误的:

1
输出数据  1> list 2> list

如果按照上面的方式输出到list文件中时而没有采用特殊的语法,会因为两个输出进程的同步问题,导致正确的数据与错误的数据可能会交叉的输入到list文件中。正确的方式应该如下:

1
2
3
输出数据 > list 2>&1
# 或者是
输出数据 &> list

命令执行&& ||

1
2
3
4
5
6
7
cmd1 && cmd2
若cmd1执行完毕且正确执行($?==0),则执行cmd2
若cmd1执行完毕且错误执行($?!=0),则不执行cmd2

cmd1 || cmd2
若cmd1执行完毕且执行正确($?==0),则不执行cmd2
若cmd1执行完毕且执行错误($?!=0),则执行cmd2

cut命令

cut命令按行数据进行处理,常用的方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#参数 -d -f(组合使用)
输出数据 | cut -d '分个字符' -f fields
#
str=ni:hao:ma:?
echo $str | cut -d ':' -f 2
表示将echo出的str字符串按照':'字符分割,且取第2个字段
得到的结果是
hao
# 补充
-f 1,3 代表取第1和第3字段,输出 ni:man
-f 1-3 取1到3字段,输出 ni:hao:ma

# 参数 -c
输出数据 | cut -c 字符范围
#
str=hello
echo $str | cut -c 1
输出
h
# 补充
-c 1-,输出 hello
-c 1-3,输出 hel

sort命令

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
head -4 /etc/passswd
# output
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
我们可以看到并没有按照首字母排序

head -4 /etc/passwd | sort
# output
adm:x:3:4:adm:/var/adm:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
root:x:0:0:root:/root:/bin/bash
我们可以看到已经按照首字母排序了

同样,我们可以指定想按照哪一个字段来排序,
head /etc/passwd | sort -t ':' -k 3
# 不看前4行了,准备输出所有行
# 将输出按照类型':'分割(-t ':'),并且取第3个字段(-k 3)
# 然而此时的字段依然是按照字符进行,如本测试机上输出的结果注意看第二行:
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 注意第三个字段,11跑到了2前面去了,因为字符串11排在2前面
此时我们需要加上 -n 参数提示按照数字进行
head /etc/passwd | sort -t ':' -k 3 -n

last命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 该命令用来列出目前与过去登录系统的用户相关信息
last
# output
root tty1 Mon Mar 12 18:33 still logged in
reboot system boot 3.10.0-693.17.1. Mon Mar 12 18:33 - 19:02 (00:29)
root tty1 Sat Mar 10 20:18 - 20:18 (00:00)
reboot system boot 3.10.0-693.17.1. Sat Mar 10 20:18 - 20:18 (00:00)
root tty1 Fri Mar 9 19:10 - 20:50 (01:40)
...
其中:
第一列:用户名
第二列:终端位置。(pts/0通过ssh或者telnet远程连接的用户,tty:直接连接到计算机或者本地用户)
第三列:登陆IP或者内核(看到0.0或者什么都没有,意味着用户通过本地终端连接,除了重启,内核版本会显示在状态中)

第四列:开始时间(如:sun apr 3 :四月三号星期天)
第五列:结束时间(still login in 还未退出,down:直到正常关机,crash:直到强制关机)
第六列:持续时间

uniq命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
last | cut -d ' ' -f 1 | sort | uniq
# 先取用户名,然后排序,最后去重
# output
reboot
root
wtmp
zhen
# 加上 -c 显示统计
last | cut -d ' ' -f 1 | sort | uniq -c
# output
1
27 reboot
26 root
1 wtmp
3 zhen

务必注意,uniq命令是通过叠加去重相邻的字符串,如果你不首先进行排序,那么会出现下面的情况:

1
2
3
4
5
6
7
8
9
10
11
12
      1 root
1 reboot
1 root
1 reboot
1 root
1 reboot
1 root
1 reboot
1 zhen
1 root
1 reboot
...

wc命令

1
2
3
4
5
6
7
8
wc [-lwm]
-l: 仅列出行
-w: 仅列出多少个英文单词
-m: 仅列出多少个字符
head /etc/passwd | wc
# output
10 10 385
# 分别代表行数,词数,字符数(这里10个“词”应该是因为每一行没有空格的原因,wc统计是按空格来分词的)

tee双向重定向

由前面的数据流我们可以知道,我们在将数据定向时,如果不采取特殊的操作,数据要么输出到屏幕,要么输出到文件或者是设备中,没有办法,既输出到屏幕有输出到文件中;又或者是,我们想要对数据进行处理存放到一个文件中,但是同时对原始数据又存到另一个文件中。使用tee命令,我们就可以做到。

例如,我们使用last命令首先要把数据存放到last.log中,同时要对用户去重并输出到屏幕上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
last | tee [-a 追加] last.log | cut -d ' ' -f 1 | sort | uniq
# output

reboot
root
wtmp
zhen
# 同时我们打开last.log文件可以看到没有做任何处理的原始数据
root tty1 Mon Mar 12 18:33 still logged in
reboot system boot 3.10.0-693.17.1. Mon Mar 12 18:33 - 19:29 (00:56)
root tty1 Sat Mar 10 20:18 - 20:18 (00:00)
reboot system boot 3.10.0-693.17.1. Sat Mar 10 20:18 - 20:18 (00:00)
root tty1 Fri Mar 9 19:10 - 20:50 (01:40)
...

前情提要,如果系统中存在两个都实现了同一接口的类,Spring在进行@Autowired自动装配的时候,会选择哪一个?如下:

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
// 一下两个类均被标记为bean
@Component
public class CD implements Playable {
@Override
public void play() {
System.out.println("CD is playing...");
}
}
@Component
public class Video implements Playable {
@Override
public void play() {
System.out.println("Video is playing...");
}
}

//配置类仅打开自动扫描
@Configuration
@ComponentScan(basePackages = "zhen"
public class MyConfig {
}

//测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfig.class)
public class MyConfigTest {
@Autowired
Playable playable;
@Test
public void checkNULL() {
Assert.assertNotNull(playable);
}
}

此时再次运行测试类会发现,FAILD并且报错:

Unsatisfied dependency expressed through field ‘playable’; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘zhen.Playable’ available: expected single matching bean but found 2: CD,video // 找到了两个都bean都能匹配

自动装配歧义性问题

上面的异常就是出现了歧义性。Spring为我们扫描了我们代码中的bean(这个部分是没有问题的),但是,在自动装配的过程中却由于歧义性而报错,并且,造成这样的歧义性还有由于Autowired这个注解仅仅按照类型进行装配——上面的CD与Video都实现了Playable接口,Autowired注解仅告诉Spring在测试类中的playable接受一个Playable类型的对象但是这里有两个bean:CD、video都是Playable类型的,所以Spring不知道。

为了解决这个问题,我们需要通过一定的手段来限定:

1
2
声明首选的bean
限定自动转配的bean

声明首选的bean

根据名字我们很容易理解,就是声明在有歧义性情况下,Spring到底选择哪一个bean来装配。方式就是在bean组件下添加@Primary注解,例如在原先的CD的@Component下加上首选注解,再次运行测试代码,PASS。但是,这种方式通常只在同类型bean较少的或者是系统简单的情况使用,而且还存在一个情况:假如目前有两位开发人员,在各自的环境编写bean,他们都希望自己的bean是Primary的,都加该注解,实际上还是会报错,因为系统现在同样有两个Primary bean,Spring还是不能判断选择哪一个bean注入。

限定自动装配的bean——@Qualifier注解

首先,我们可以通过在@Component中加入字符串来更明确的指定bean id而不是使用Spring的默认bean id策略。就像如下:

1
2
3
4
5
6
7
8
@Component("myCD")
public class CD implements Playable {
// ...
}
@Component("myVideo")
public class Video implements Playable {
// ...
}

当这样指定以后,我们在自动转配的地方,使用@Qualifier(“指定id”)来限定我们要注入的确定的bean:

1
2
3
4
5
...
@Autowired
@Qualifier("myCD")
Playable playable;
...

再次运行不会报错。

关于@Qualifier,最佳的情形应该是来标记bean特性。但是,如果多个bean都有相同的特性,都是用了相同的标记的@Qualifier注解,那么同样又会出现歧义性问题。所以我们又要添加新的@Qualifier注解来进一步限定,这样做没有问题,但是Java语法规定,不允许在同一条目上重复出现相同类型的多个注解。你不能这么做:

1
2
3
4
5
6
// 编译器会报错
@Qualifier("myCD")
@Qaulifier("JayChou")
public class CD implements Playable {
...
}

为了结局这样的问题,我们可以创建自己的注解:

1
2
3
4
5
6
7
8
9
10
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.METHOD}) //字段注解  
@Retention(RetentionPolicy.RUNTIME) //在运行期保留注解信息
@Qualifier // 需要使用@Qualifier注解来限定
public @interface MyCD {
}
@Target({ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface JayChou {
}

如此定义了注解以后,我们就可以在原先的@Component下如下定义:

1
2
3
4
5
6
@Component
@MyCD
@JayChou
public class CD implements Playable {
...
}

并且在测试类下如下声明:

1
2
3
4
@Autowired
@MyCD
@JayChou
Playable playable;

测试通过!

Spring中的Bean是一个很重要的概念。Spring作为一个Bean容器,它可以管理对象和对象之间的依赖关系,我们不需要自己建立对象,把这部分工作全部转交给容器完成,具有低耦合,对代码没有侵略性。