大狗哥传奇

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

redis集群

发表于 2020-05-19 更新于 2020-06-30 分类于 redis

端口

redis 节点通常占用两个端口,一个服务端口,一个数据端口,服务端口默认为6379,数据端口则为服务端口+10000,确保这两个端口的防火墙为打开状态。

启动集群

  1. 建立多个节点的配置文件目录

    1
    2
    3
    mkdir cluster-test
    cd cluster-test
    mkdir 7000 7001 7002 7003 7004 7005
  2. 每个节点目录下新增redis.conf配置文件,以下为7000节点最小配置

    1
    2
    3
    4
    5
    6
    port 7000
    cluster-enabled yes
    cluster-config-file nodes-7000.conf
    cluster-node-timeout 5000
    appendonly yes
    daemonize yes
  3. 在 cluster-test 目录编写启动脚本

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash
    /usr/bin/redis-server 7000/redis.conf
    /usr/bin/redis-server 7001/redis.conf
    /usr/bin/redis-server 7002/redis.conf
    /usr/bin/redis-server 7003/redis.conf
    /usr/bin/redis-server 7004/redis.conf
    /usr/bin/redis-server 7005/redis.conf

    启动后我们可以通过ps -ef|grep redis看到所有 redis 节点都启动了

  4. 对于使用了redis 5以上版本,可以使用如下命令来分配hash槽

    1
    2
    #--cluster-replicas 1 使用一个节点作为从节点,那么这里至少需要6个节点,若不指定cluster-replicas则可以只使用三个节点
    redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1

    执行后可以得到输出

    [OK] All 16384 slots covered

    同时可以看到各个节点的相关运行时配置文件node.conf 我们有 3 个节点的 7000 端口的node.conf文件,我们可以看到hash槽的分布

    1
    2
    3
    4
    73a213f442c0299596d464a1b00b26c0eb433a63 127.0.0.1:7002@17002 master - 0 1589883608796 3 connected 10923-16383
    3c0f18ab3bcafd94486aab2264833d1a583a0b18 127.0.0.1:7001@17001 master - 0 1589883609703 2 connected 5461-10922
    1ebfb6ca239c3780fd76f599c0caace80391806b 127.0.0.1:7000@17000 myself,master - 0 0 1 connected 0-5460
    vars currentEpoch 3 lastVoteEpoch 0
  5. redis.conf配置bind ip,可以限定不能使用127.0.0.1去访问

分区

是指将将数据分别存储在不同 redis 节点,每个节点仅存储所有 key 的一部分。

优势

  • 可存储大量数据,不受单台服务器的物理内存限制
  • 分流

缺点

  • 涉及多个 key 的操作,若 key 在不同的节点上,则无法执行
  • 分区是基于 key 的,若一个 key 中大量数据,它是无法分配到不同的节点
  • 分区处理数据相对比较复杂,特别是处理 RDB/AOF 文件时
  • 调整容量比较复杂,容量调整时需要实时平衡各个节点的数据
  • 为了保证 redis 执行效率,redis 各个节点的数据使用异步的方式同步数据,因此 redis 不保证强一致性。若需要确保某个key的数据等待同步完返回,则可使用wait指令

分区方式

redis 使用 hash 函数将 key 转换为一个数字,然后根据节点数量进行模运算,根据余数将其分配到指定的节点。

  • hash 分配具有一致性,即同一个 key 计算出的 hash 值是固定的
  • 客户端分区 有调用端选择正确的节点读写数据
  • 代理服务分区 使用一个中间服务器,来转发请求
  • 请求路由 随机请求一个服务器,由服务器确认数据在哪个节点,当数据请求到错误的节点时,自动转发请求到正确的节点

redis 通过计算 key 的hash槽来将数据分配到指定的节点,每个节点占据固定长度的hash槽,这个就方便新增或者删除节点

1
2
3
4
5
#查看hash槽在节点上的分布
cluster slots

# 查看key的hash槽
cluster keyslot [key]

hash tag

当我们希望一些 key 分配到同一个节点时,我们可以使用hash tag,redis 仅会对 key 中的hash tag进行 hash 函数运算,hash tag是指第一个'{'到第一个'}'之间,不考虑嵌套关系,若第一个'{'和第一个'}'之间为空,则放弃hash tag 例如

{user}100 -> user {user}{100}1 -> user {user{}10}0 -> user{ {}{user}100 -> {}{user}100

重分配

当需要新增或者移除节点时,redis 需要将 key 重新进行分配到剩余节点上。

redis管道

发表于 2020-05-19 更新于 2020-06-30 分类于 redis

redis 使用 cs 服务架构,执行一条命令的过程如下

  1. 客户端发送一个请求到服务器
  2. 服务器通过 socket 读取命令,通常以阻塞的方式读取
  3. 服务器通过read()指令将命令从内存加载到内核执行
  4. 核心执行命令将执行结果写回内存
  5. 将执行结果返回客户端

redis 执行命令的速度是较快的,当执行大量命令时,那么大部分时间将花费在请求返回,读取写回上。 redis 提供了管道命令,可以批量执行命令,依次执行,然后等执行完成后将执行结果打包返回给客户端。

当然也可以使用去执行大量命令,来达到批量执行的目的。

tomcat入门

发表于 2020-05-15 更新于 2020-06-30 分类于 java

类加载机制

概述

在 java 中,classloader 以 tree 的形式组织起来。通常情况下,当 classloader 加载一个特定的类或资源时,它会将请求委托给父类加载器去加载,若父类加载器中找不到该类,它才会尝试自己去加载。 tomcat入门_2020-05-15-02-22-01.png

  • Bootstrap jvm 提供的类加载器,一般是指($JAVA_HOME/jre/lib/ext)下的 jar 包
  • System tomcat 的核心类加载器,就$CATALINA_HOME/bin目录下的bootstrap.jar,tomcat-juli.jar和commons-daemon.jar
  • Common tomcat 的共享类加载器 包,在$CATALINA_BASE/lib目录下的 jar 包
  • WebappX 应用内部的类加载器,它加载应用/WEB-INF/classes和/WEB-INF/lib目录下的 class

类加载顺序

tomcat 的类加载模型违背了 jvm 的双亲委派模式,除了 Bootstrap 类加载器加载的类,它首先会尝试使用WebappX来加载类。 通常情况下 tomcat 的类加载顺序如下所述

  • Bootstrap classes of your JVM
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application
  • System class loader classes (described above)
  • Common class loader classes (described above)

若 tomcat 或者应用中在 context 中配置了<Loader delegate="true"/>,那么他的类加载顺序就会变成如下

  • Bootstrap classes of your JVM
  • System class loader classes (described above)
  • Common class loader classes (described above)
  • /WEB-INF/classes of your web application
  • /WEB-INF/lib/*.jar of your web application

扩展类加载器

当在 tomcat 目录下的conf/catalina.properties.中配置·server.loader或shared.loader,它就会启用Server或Shared类加载器 tomcat入门_2020-05-15-02-44-19.png

  • Server 是供 tomcat 使用的,对于应用是不可见的
  • Shared 所有 tomcat 应用共享的 jar 包,类似Common的类加载器

日志

  1. tomcat 内部使用tomcat-juli.jar作为日志组件,其使用配置为$CATALINA_HOME/conf/logging.properties
  2. 应用可以使用自定义日志组件。不同的 web 应用使用的日志组件是独立的,因为其 WebappX 类加载器是独立的,但是若日志组件在 Common 共享类加载器,或者 Share 类类加载器,那么他们使用的日志组件则是共享的

linux文件系统

发表于 2020-05-13 更新于 2020-12-02 分类于 linux

linux 使用虚拟目录来管理硬盘,第一个被加载的硬盘被视为 root 驱动器,root 驱动包含虚拟目录的核心部分,在 root 驱动器上,linux 创建一个特殊的目录被称为 mount points,用来挂载其他硬盘。使用虚拟目录可以将所有文件都存储在一起,尽管他们可能存储在不同的硬盘上,通常来说,系统的核心文件存储在 root 驱动器上。

链接

软链接,全称是软链接文件,英文叫作 symbolic link。这类文件其实非常类似于 Windows 里的快捷方式,这个软链接文件(假设叫 VA)的内容,其实是另外一个文件(假设叫 B)的路径和名称,当打开 A 文件时,实际上系统会根据其内容找到并打开 B 文件。

1
2
#软链接
ln -s [source] [link]

而硬链接,全称叫作硬链接文件,英文名称是 hard link。这类文件比较特殊,这类文件(假设叫 A)会拥有自己的 inode 节点和名称,其 inode 会指向文件内容所在的数据块。与此同时,该文件内容所在的数据块的引用计数会加 1。当此数据块的引用计数大于等于 2 时,则表示有多个文件同时指向了这一数据块。一个文件修改,多个文件都会生效。当删除其中某个文件时,对另一个文件不会有影响,仅仅是数据块的引用计数减 1。当引用计数为 0 时,则系统才会清除此数据块。

1
ln -n [source] [link]

软链接可以指向目录,而硬链接不可以

1
2
3
4
5
# 查看文件的inode节点编号,权限,创建日期,修改日期等
stat <file>

# 根据inode节点删除文件
find . -inum <inum> -exec rm -i {} \;

df

查看文件系统以及它们的相关信息

cp

复制文件

  • -v 显示复制的详情

tar

1
2
3
4
#压缩
tar -zcvf myfile.tgz file1 file2
# 解压
tar -xvf file.tar

rsync

同步命令,可用于备份应用。该命令做数据的同步备份,会对比数据源目录和数据备份目录的数据,并把不同的数据同步到备份目录。其也可以同步到其他服务器,用法类似 scp

  • -v 显示同步详情
  • -p 显示同步百分比
  • --delete 默认情况下 source 中被删除的文件不会同步到 target 中,需要加上该参数才会同步删除命令
  • --exclude 不同步符合[pattern]的文件,[pattern]表达式要匹配的相对路径下的文件名称,包含路径 例如

    1
    rsync -avp   --exclude='dir/*' /etc/ /data/etc/
  • --include 要配置--exclude一起使用,表示不执行符合[pattern]的--exclude 例如

    1
    2
    # 表示不同步/etc/log下的文件,除了/etc/log/important相关文件
    rsync -avp --include='/etc/*' --include='/etc/log/important*' --exclude='/etc/log/*' /etc/ /data/etc/

文件描述符

在 linux 中,对于每个进程(pid),所有打开的文件都是通过文件描述符引用的,文件描述符就是从 0 开始的小的非负整数,内核用以标识一个特定进程正在访问的文件。当打开一个文件或创建一个文件,就会在/proc/{pid}/fd生成一个可以访问该文件的文件描述符,通过该文件描述符,可以直接去访问该文件

我们可以在/proc/{pid}/fd中查看到当前进程相关的文件描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#self 代表当前进程
$ ll /proc/self/fd
total 0
lrwx------. 1 root root 64 2020-09-20 06:36:30 0 -> /dev/pts/0
lrwx------. 1 root root 64 2020-09-20 06:36:30 1 -> /dev/pts/0
lrwx------. 1 root root 64 2020-09-20 06:36:30 2 -> /dev/pts/0
lr-x------. 1 root root 64 2020-09-20 06:36:30 3 -> /proc/4781/fd


#查看当前文件描述
$ lsof -a -p $$

# 查看文件描述符0,1,2
$ lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 6145 li 0u CHR 136,1 0t0 4 /dev/pts/1
bash 6145 li 1u CHR 136,1 0t0 4 /dev/pts/1
bash 6145 li 2u CHR 136,1 0t0 4 /dev/pts/1

Linux 进程默认情况下会有三个缺省打开的文件描述符

  • 0(标准输入)stdin
  • 1(标准输出)stdout
  • 2(标准错误)stderr

我们可以看到0,1,2都执行/dev/pts/0,其代表当前登录的 bash 终端,也就是说默认情况下输入输出错误都是在终端的 bash 界面上操作的

1
2
$ tty
/dev/pts/0

当我们以另外一个用户登录并tail -f文件

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
$ ps -ef|grep tail
li 10570 7534 0 21:04 pts/2 00:00:00 tail -f 1.txt

# 我们查看其fd
$ ll /proc/10570/fd
total 0
lrwx------. 1 li li 64 2020-09-17 21:04:59 0 -> /dev/pts/2
lrwx------. 1 li li 64 2020-09-17 21:04:59 1 -> /dev/pts/2
lrwx------. 1 li li 64 2020-09-17 21:04:19 2 -> /dev/pts/2
lr-x------. 1 li li 64 2020-09-17 21:04:59 3 -> /home/li/1.txt
lr-x------. 1 li li 64 2020-09-17 21:04:59 4 -> anon_inode:inotify

# 我们向文件描述符写入一些msg
$ echo '0' > 0
$ echo '1' > 1
$ echo '2' > 2
$ echo '3' > 3

#我们可以在li这个终端上观察到

li$ tail -f 1.txt
0
1
2
3

#我们可以看到1.txt被写入了内容
li$ more 1.txt
3

proc 目录

Linux 内核提供了一种通过 proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc 文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。

  1. cmdline 启动时传递给 kernel 的参数信息
  2. cpuinfo cpu 的信息
  3. filesystems 内核当前支持的文件系统类型
  4. locks 内核锁住的文件列表
  5. meminfo RAM 使用的相关信息
  6. swaps 交换空间的使用情况
  7. version Linux 内核版本和 gcc 版本
  8. self 链接到当前正在运行的进程

proc 目录下一些以数字命名的目录,它们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在 proc 下,以进程的 PID 号为目录名,它们是读取进程信息的接口。而 self 目录则是读取进程本身的信息接口,是一个 link。

  1. /proc/[pid]/cmdline 该进程的命令及参数
  2. /proc/[pid]/comm 该进程的命令
  3. /proc/[pid]/cwd 进程工作目录
  4. /proc/[pid]/environ 该进程的环境变量
  5. /proc/[pid]/exe 该进程命令的实际命令地址
  6. /proc/[pid]/fd 该进程打开的文件描述符
  7. /proc/[pid]/maps 显示进程的内存区域映射信息
  8. /proc/[pid]/statm 显示进程所占用内存大小的统计信息 。包含七个值,度量单位是 page(page 大小可通过 getconf PAGESIZE 得到)。举例如下:

    1
    2
    $ cat statm
    26999 154 130 15 0 79 0
    • a)进程占用的总的内存
    • b)进程当前时刻占用的物理内存
    • c)同其它进程共享的内存
    • d)进程的代码段
    • e)共享库(从 2.6 版本起,这个值为 0)
    • f)进程的堆栈
    • g)dirty pages(从 2.6 版本起,这个值为 0)
  9. /proc/[pid]/status 包含进程的状态信息。

    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
    $ cat /proc/2406/status
    Name: frps
    State: S (sleeping)
    Tgid: 2406
    Ngid: 0
    Pid: 2406
    PPid: 2130
    TracerPid: 0
    Uid: 0 0 0 0
    Gid: 0 0 0 0
    FDSize: 128
    Groups: 0
    NStgid: 2406
    NSpid: 2406
    NSpgid: 2406
    NSsid: 2130
    VmPeak: 54880 kB
    VmSize: 54880 kB
    VmLck: 0 kB
    VmPin: 0 kB
    VmHWM: 34872 kB
    VmRSS: 10468 kB
    VmData: 47896 kB
    VmStk: 132 kB
    VmExe: 2984 kB
    VmLib: 0 kB
    VmPTE: 68 kB
    VmPMD: 20 kB
    VmSwap: 0 kB
    HugetlbPages: 0 kB
    Threads: 11
    SigQ: 0/31834
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000000000000
    SigCgt: fffffffe7fc1feff
    CapInh: 0000000000000000
    CapPrm: 0000003fffffffff
    CapEff: 0000003fffffffff
    CapBnd: 0000003fffffffff
    CapAmb: 0000000000000000
    Seccomp: 0
    Cpus_allowed: f
    Cpus_allowed_list: 0-3
    Mems_allowed: 00000000,00000001
    Mems_allowed_list: 0
    voluntary_ctxt_switches: 2251028
    nonvoluntary_ctxt_switches: 18031

mdls

查看文件的 meta 信息

linux常用命令

发表于 2020-05-13 更新于 2020-12-02 分类于 linux

echo

  • -e 对输出内容进行格式调整,命令格式echo -e "\033[字背景颜色;文字颜色m字符串\033[0m" 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    echo -e "\033[30m 黑色字 \033[0m"
    echo -e "\033[31m 红色字 \033[0m"
    echo -e "\033[32m 绿色字 \033[0m"
    echo -e "\033[33m 黄色字 \033[0m"
    echo -e "\033[34m 蓝色字 \033[0m"
    echo -e "\033[35m 紫色字 \033[0m"
    echo -e "\033[36m 天蓝字 \033[0m"
    echo -e "\033[37m 白色字 \033[0m"

    echo -e "\033[40;37m 黑底白字 \033[0m"
    echo -e "\033[41;37m 红底白字 \033[0m"
    echo -e "\033[42;37m 绿底白字 \033[0m"
    echo -e "\033[43;37m 黄底白字 \033[0m"
    echo -e "\033[44;37m 蓝底白字 \033[0m"
    echo -e "\033[45;37m 紫底白字 \033[0m"
    echo -e "\033[46;37m 天蓝底白字 \033[0m"
    echo -e "\033[47;30m 白底黑字 \033[0m"

sort

对输出内容进行排序,默认情况下sort仅比较ASCII字符。

  • -n 以数组大小来排序
  • -k, --key=KEYDEF 指定使用第几个字段进行排序
  • -t,--field-separator=SEP 使用 SEP 替换默认的空格作为间隔符
  • -r 反向排序
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
~$ cat 1.txt
2 c
10 a
300 b

~$ sort 1.txt
10 a
2 c
300 b

~$ sort -n 1.txt
2 c
10 a
300 b

~$ sort -k 2 1.txt
10 a
300 b
2 c

~$ cat 2.txt
2:c
10:a
300:b

~$ sort -t ':' -k 2 2.txt
10:a
300:b
2:c

read

read 命令用于从标准输入读取数值。

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

#这里默认会换行
echo "输入网站名: "
#读取从键盘的输入
read website
echo "你输入的网站名是 $website"
exit 0 #退出
  • -p 参数,允许在 read 命令行中直接指定一个提示
  • -s 选项能够使 read 命令中输入的数据不显示在命令终端上
  • -t 参数指定 read 命令等待输入的秒数,当计时满时,read 命令返回一个非零退出状态。
  • -n 参数设置 read 命令计数输入的字符。当输入的字符数目达到预定数目时,
  • -e 参数,以下实例输入字符 a 后按下 Tab 键就会输出相关的文件名(该目录存在的):

    1
    2
    3
    4
    $ read -e -p "输入文件名:" str
    输入文件名:a
    a.out a.py a.pyc abc.txt
    输入文件名:a

read 不能单独用在管道命令上

读取命令的输出,当指定多个变量时根据 IFS 规则分割变量

配合 while 使用

例如 while read line

read 通过输入重定向,把 file 的第一行所有的内容赋值给变量 line,循环体内的命令一般包含对变量 line 的处理;然后循环处理 file 的第二行、第三行。。。一直到 file 的最后一行。还记得 while 根据其后的命令退出状态来判断是否执行循环体吗?是的,read 命令也有退出状态,当它从文件 file 中读到内容时,退出状态为 0,循环继续惊醒;当 read 从文件中读完最后一行后,下次便没有内容可读了,此时 read 的退出状态为非 0,所以循环才会退出。

1
2
3
4
5
# line 仅仅是个变量名
while read line
do
echo $line
done < file

另一种也很常见的用法:

1
2
3
4
command | while read line
do
echo $line
done

如果你还记得管道的用法,这个结构应该不难理解吧。command 命令的输出作为 read 循环的输入,这种结构长用于处理超过一行的输出,当然 awk 也很擅长做这种事。

wall

向系统的全部在线用户发送信息 wall [n] [messege]

write

向其他用户发送信息`write [options] [ttyname]

命令可以作为参数传入 shell 脚本中

1
2
3
echo $1
echo $2
$1 $2 #直接执行

快速删除大文件

1
cat /dev/null > access.log

快速清空大文件内容

1
:> big.txt

清空文件内容

1
:> file.log

快速备份

逗号用来分割旧新文件后缀名

1
2
cp httpd.conf{,.bak}
cp demo.{txt,sh}

xargs 引用参数

1
2
3
4

ls *.jar|xargs -i echo {}
#或
ls *.jar|xargs -I {} echo {}

awk

awk可以用来快速切割文本,默认使用空格分隔符。'{}'中对每行都进行操作 $0表示当前行,$1表示切割的数组的第一个元素,'{print $1}'表示打印第一个元素

-F ’-‘ 增加切割符

查看内存信息

1
cat /proc/meminfo | grep MemTotal

apt-get国内镜像

修改配置文件 vim /etc/apt/sources.list

1
2
3
4
5
6
7
8
9
10
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse

然后执行apt-get update

查看java安装目录

1
2
3
4
5
6
han@ubuntu:/etc$ whereis java
java: /usr/bin/java /usr/share/java /usr/lib/jvm/java-8-openjdk-amd64/bin/java /usr/share/man/man1/java.1.gz
han@ubuntu:/etc$ ls -lrt /usr/bin/java
lrwxrwxrwx 1 root root 22 4月 2 15:54 /usr/bin/java -> /etc/alternatives/java
han@ubuntu:/etc$ ls -lrt /etc/alternatives/java
lrwxrwxrwx 1 root root 46 4月 2 15:54 /etc/alternatives/java -> /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java

离线安装 git

1
rpm2cpio git-1.7.9.6-1.e16.rfx.x86_64.rpm|cpio -idmv

离线安装 rpm 忽略依赖

1
rpm -ivu *.rpm --nodeps --force

然后在~/.bash_profile中配置一个alias即可,或者在PATH更新git的/usr/bin路径

定时任务

为当前用户创建 cron 服务

  1. 键入 crontab -e 编辑 crontab 服务文件

    例如 文件内容如下:

    1
    2
    */2 * * * * /bin/sh  /home/admin/jiaoben/buy/deleteFile.sh
    :wq #保存文件并并退出

    /bin/sh /home/admin/jiaoben/buy/deleteFile.sh 这一字段可以设定你要执行的脚本,这里要注意一下 bin/sh 是指运行 脚本的命令 后面一段时指脚本存放的路径

  2. 查看该用户下的 crontab 服务是否创建成功, 用 crontab -l 命令

  3. 启动 crontab 服务

    一般启动服务用 /sbin/service crond start 若是根用户的 cron 服务可以用 sudo service crond start, 这里还是要注意 下 不同版本 Linux 系统启动的服务的命令也不同 ,像我的虚拟机里只需用 sudo service cron restart 即可,若是在根用下直接键入 service cron start 就能启动服务

  4. 查看服务是否已经运行用 ps -ax | grep cron

  5. crontab 命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    cron服务提供crontab命令来设定cron服务的,以下是这个命令的一些参数与说明:

    crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数
    crontab -l //列出某个用户cron服务的详细内容
    crontab -r //删除某个用户的cron服务
    crontab -e //编辑某个用户的cron服务
    比如说root查看自己的cron设置:crontab -u root -l
    再例如,root想删除fred的cron设置:crontab -u fred -r
    在编辑cron服务时,编辑的内容有一些格式和约定,输入:crontab -u root -e
    进入vi编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt
    任务调度的crond常驻命令
    crond 是linux用来定期执行程序的命令。当安装完成操作系统之后,默认便会启动此任务调度命令。crond命令每分锺会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。
  6. cron 文件语法

    1
    2
    3
    4
    5
    6
    7
    8
    分     小时    日       月      星期     命令
    0-59 0-23 1-31 1-12 0-6 command (取值范围,0表示周日一般一行对应一个任务)

    记住几个特殊符号的含义:
    “*”代表取值范围内的数字,
    “/”代表”每”,
    “-”代表从某个数字到某个数字,
    “,”分开几个离散的数字

示例

1
0,1,2 * * * * sh ~/demo.sh

合并上下行

1
sed 'N;s/\n/ /' file.txt

清屏

clear 或者使用ctrl + l

查看当前是否 root

1
2
# 0既是root
echo $UID

合并多行

1
ls -1 | paste -sd "," -

显示当前 IP 的 hostname

1
2
3
4
echo $HOSTNAME

#显示当前ip
hostname -i

比较文件差异

1
2
3
# -y 左右对比
# --suppress-common-lines 忽略相同
diff -y alpha1.txt alpha2.txt --suppress-common-lines

显示 ASCII 表

1
man ascii

watch

watch 指令可以间歇性的执行程序,将输出结果以全屏的方式显示,默认是 2s 执行一次。watch 将一直运行,直到被中断。

  • -d | --differences 高亮显示差异部分
  • --cumulative 高亮显示“sticky”
  • -n  指定时间间隔
  • -t | --no-title 不显示日期时间以及间隔秒数

重复执行上次命令

  1. 使用上方向键,并回车执行。
  2. 按 !! 并回车执行。
  3. 输入 !-1 并回车执行。!-n执行上第 n 条命令
  4. 按 Ctrl+P 并回车执行。

可以使用sudo,以 root 用户执行上条命令 sudo !! sudo !-1

expr

expr 命令是一个手工命令行计数器,用于在 UNIX/LINUX 下求表达式变量的值,一般用于整数值,也可用于字符串。

1、计算字串长度

1
2
$ expr length “this is a test”
14

2、抓取字串

1
2
$ expr substr “this is a test” 3 5
is is

3、抓取第一个字符数字串出现的位置

1
2
$ expr index "sarasara" a
2

4、整数运算

1
2
3
4
5
6
7
8
9
10
11
12
$ expr 14 % 9
5
$ expr 10 + 10
20
$ expr 1000 + 900
1900
$ expr 30 / 3 / 2
5
$ expr 30 \* 3 #使用乘号时,必须用反斜线屏蔽其特定含义。因为 shell 可能会误解显示星号的意义
90
$ expr 30 \* 3
$ expr: Syntax error

更新系统时间

1
2
3
4
# 同步上海的时间
ntpdate ntp.api.bz
# 查看时区
date -R

查看文件格式

1
file xxx.txt

显示所有端口

1
netstat -tulpn

统计

1
2
3
4
5
6
$ wc freeswitch.md
# 行数 单词数 byte字节数
1081 2968 41953 freeswitch.md
$ wc -m freeswitch.md
# char字数
30744 freeswitch.md

sed

sed 命令是利用脚本来处理文本文件。sed 可依照脚本的指令来处理、编辑文本文件。 语法

1
sed [-hnV][-e<script>][-f<script 文件>][文本文件]

参数说明:

  • -e 指定脚本的表达式,不会修改源文件,仅将修改后的内容输出到控制台
  • -f 指定脚本的文件
  • -i 指定脚本的表示式,会直接修改源文件
  • -n 仅显示脚本所包含模板的行

其中脚本的指令支持

  • a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
  • c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
  • d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
  • i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
  • p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
  • s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!
  • g : 在行内进行全局替换
  • w 将所选的行写入文件
  • ! 对所选行以外的行执行
  • l 列出非打印字符

脚本的指令可以在指定行范围内执行,例如

1
2
3
4
5
6
7
8
9
10
11
# 第四行行尾  新行+newline
sed -e 4a\newline testfile
# 第一到第四行行尾 新行+newline
sed -e 1,4a\newline testfile
# 最后一行 新行+newline
sed -e $a\newline testfile


# 使用shell变量
var=hello
sed -e '1a'${var} testfile

与 grep 一样,sed 也支持特殊元字符,来进行模式查找、替换。不同的是,sed 使用的正则表达式是括在斜杠线"/"之间的模式。 如果要把正则表达式分隔符"/"改为另一个字符,比如 o,只要在这个字符前加一个反斜线,在字符后跟上正则表达式,再跟上这个字符即可。例如:sed -n '^Myop' datafile sed 的正则表达式语法支持功能比较少 | 元字符| 功能| | :---- | :---- | |^ |行首定位符| |$ |行尾定位符| |. |匹配除换行符以外的单个字符 | |* |匹配零个或多个前导字符 | |[] | 匹配指定字符组内的任一字符| |[^]| 匹配不在指定字符组内的任一字符| |& |保存查找串以便在替换串中引用 s/my/**&**/ 符号&代表查找串。my 将被替换为**my**| |< | 词首定位符 /<my/ 匹配包含以 my 开头的单词的行| |> | 词尾定位符 /my>/ 匹配包含以 my 结尾的单词的行| |x{m} | 连续 m 个 x| |x{m,}| 至少 m 个 x| |x{m,n}| 至少 m 个,但不超过 n 个 x|

特殊字符

  • \ 新行

示例

1
2
3
4
5
6
# nl 输出行号 搜索包含root的行并打印
nl testfile|sed -n '/root/p'
# nl 输出行号 搜索包含root的行并删除
nl testfile|sed -n '/root/d'
# 使用正则替换
sed 's/pattern/replace/g'

关闭自动退出

1
2
3
4
5
# vi /etc/profile

unset TMOUT
# 或者
TMOUT=0

当前用户

1
2
3
4
# 当前登录终端字符设备号
tty
# 当前所有登录的用户
w

tee

同时向某个文件写入内容

1
2
3
4
$ echo 123|tee 1.txt
123
$ cat 1.txt
123
  • -a 追加

iconv

转换文件编码

1
iconv -f gbk -t utf8  1.txt >  2.txt

linux搜索

发表于 2020-05-12 更新于 2020-12-02 分类于 linux

搜索命令

  • man -k 搜索命令,通常用于在只记得部分命令关键词的场合。在 man 的帮助手册中,将帮助文档分为了 9 个类别,对于有的关键字可能存在多个类别,我们就需要指定特定的类别来查看,一般 bash 命令在 1 类别中。如printf的三类别,可以使用man 3 printf来查看
  • whatis 命令的简要说明,whatis -w 'loca*'可以使用正则表达式来搜索应用
  • info 命令详细的介绍
  • which 命令安装目录

搜索文件

  1. locate <name>使用索引去查找文件,如果文件更新,需要定期执行更新命令(updatedb)来更新索引库.
  2. find 实时查找命令 命令语法find [path] [expression]

    • -cmin -n : 在过去 n 分钟内被修改过 -cmin +n : 在 n 分钟之前被修改过 例如

      1
      2
      3
      4
      #十分钟之内
      find . -cmin -10
      #十分钟之前
      find . -cmin +10
    • -ctime 与 -cmin 类似,表示天数 -ctime 和-mtime 的区别,-mtime 表示文件内容被修改,-ctime 表示文件内部或文件的 metadata(权限,所有者等)被更改

    • -delete 满足条件的文件删除

      1
      2
      # 删除十天前的文件
      find ./my_dir -mtime +10 -type f -delete
    • 根据文件名搜索,*是通配符 find . -name h* 若忽略大小写可以使用-iname 可使用正则表达式来搜索文件名,将搜索条件引号包含 find . -name '[regex]' 或者使用 find . -regex '[regex]' 需要注意的是,正则表达式匹配的是搜索的全名,而不是文件名。例如

      ~$ find . -regex '.*.txt' -maxdepth 1 ./1.txt

      若使用^1.*是搜索不出这个文件的

    • -maxdepth n : 搜索指定的深度

    • -not 反向搜索 例如: find . -not -name 'he'

    • -type :搜索指定类型的文件

      1. d: 目录
      2. c: 字型装置文件
      3. b: 区块装置文件
      4. p: 具名贮列
      5. f: 一般文件
      6. l: 符号连结
      7. s: socket

      查找指定扩展名的文件

      1
      2
      find -type f -regex '.*\.(jpg|png)'
      find -type f|egrep '.*\.(jpg|png)'

grep

grep 支持不同的匹配模式,比如默认的 BRE 模式,增强型的 ERE 模式,还有更强悍的 PRE 模式。普通情况下使用默认的 BRE(basic regular expression) 模式就可以了,这种方式的特点是支持的正则表达式语法有限。如果需要更进一步的正则表达式语法支持,可以使用 ERE(extended regular expression) 模式。如果要使用复杂的正则表达式语法,可以使用 PRE 模式,它支持 Perl 语言的正则表达式语法。

语法格式: grep [OPTIONS] PATTERN [FILE...]

  • -A n 可以输出匹配行后的 n 行
  • -B n 可以输出匹配行前的 n 行
  • -c 计算找到的符号行的次数
  • -C n 可以输出匹配行前后的 n 行
  • -e 使用多个规则
  • -f FILE 如果正则表达式太长,或者是需要指定多个正则表达式,可以把它们放在文件中。如果指定了多个正则表达式(每行一个),任何一个匹配到的结果都会被输出:
  • -i 忽略大小写
  • -l 仅显示文件名
  • -n 顺便输出行号
  • -o 只输出匹配到的部分(而不是整个行)
  • -R, -r, --recursive 会递归指定指定目录(可使用通配符)下的所有文件
  • -v 反向选择,即输出没有没有匹配的行
  • -w 表示匹配全词
  • --binary-files=without-match 不搜索二进制文件
  • --colour='auto' 是否高亮显示匹配词,可以为never, always or auto
  • --exclude-dir=[PATTERN] 排除一些目录(注意,这里设置的也是正则表达式),还可以同时指定多个表达式,例如grep -r --exclude-dir={.git,xgit} 'email' .
  • --include 仅搜索某些文件,可使用正则表达式,同时可以指定多个表达式 grep -rn 'stream' . --include='*.cpp'
  • -P 使用 perl 的正则引擎

grep 支持简单的正则表达式,

    其规则如下

    linux搜索_linux搜索_基础的正则表达式规则.png
    linux搜索_linux搜索_基础的正则表达式规则.png

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
~$ grep -i he 1.txt
Hello
hello

~$ grep -e a123 -e b123 1.log
a123456bcd
b1234aaeef

~$ grep -rn 'include' . --include='*.c' --exclude-dir={backup,temp}
./test/hello.c:1:#include <stdio.h>
./test/hello.c:2:#include <stdlib.h>

~$ grep -P '\d{6}' 1.log
a123456bcd

linux一切皆文件

发表于 2020-05-11 更新于 2020-12-02 分类于 linux

概述

linux一切都是基于文本的,许多操作文本的命令都可以对其他各种命令其作用,比如说grep,可以作用于top

uname

显示系统信息

  • -a 显示系统概要信息

which

在 PATH 变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用 which 命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。

1
2
3
$> which vi
alias vi='vim'
/usr/bin/vim

date

以指定格式查看时间

1
dt=`date +'%Y-%m-%d'`

cat

将文件内容连接后传输到标准输出,tac 和 cat 用法一致,但是从最后一行读取

  • -A : 显示回车尾行等特殊字符
  • -n(number) :从第一行开始对文件输出的所有行进行编号
  • -b :忽略空白行的编号

显示目录并给每个文件加上一个编号

1
ls |cat -n

ls

  • -F 目录以'/'结尾显示
  • -h 使用“-h”参数时,会根据文件的大小选择显示的单位是K、M还是G
  • -R 递归显示文件
  • -i 显示 inode 节点编号


设定ls的文件日期显示格式

    编辑全局配置文件:/etc/profile,使所有用户均显示该格式:

    1
    2
    #vi  /etc/profile
    export TIME_STYLE="+%Y-%m-%d %H:%M:%S"
1
2
3
4
ls -l
-rw-rw-r--. 1 li li 22 2020-10-16 22:14:31 1.log
drwxrwxr-x. 3 li li 269 2020-09-12 09:09:21 backup
lrwxrwxrwx. 1 li li 24 2020-05-22 10:44:52 redis -> /usr/share/redis/cluster

第一个字段用来描述文件或目录的权限,第一个字符确定文件的类型

  • - 文件
  • d 目录
  • l 链接
  • c 字符设备文件。一般位于/dev如键盘,最小传输单位为一个字节
  • b 块设备文件,一般至于/dev目录下,设备文件是普通文件和程序访问硬件设备的入口,最小传输单位为一个数据块(512 字节)
  • n

last

显示登录系统的用户信息

  • -a: 在最后一行显示主机名

head

head 命令输出文件开头部分,默认情况下显示文件的头 10 行。如果指定多个文件,每个文件前都有一个标题,给出文件名。如果没有指定文件,或当文件为-时,读取标准输入。

  • -c,--bytes=[-]K 显示文件前 K 字节。如果 K 前有-,则表示显示除最后 K 字节外的所有内容
  • -n,--lines=[-]K 显示前 K 行。如果 K 前有-,则表示显示除最后 K 行外的所有行

tail 命令与 head 用法一直,但输出文件尾部部分

free

显示系统内存状态

  • -s n:每 n 秒刷新一次查看内存使用状态
  • -h :根据内存的大小选择显示的单位是K、M还是G

lsof

lsof(list open files)是一个查看当前系统文件的工具。在 linux 环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,该文件描述符提供了大量关于这个应用程序本身的信息。

lsof 打开的文件可以是:

  1. 普通文件
  2. 目录
  3. 网络文件系统的文件
  4. 字符或设备文件
  5. (函数)共享库
  6. 管道,命名管道
  7. 符号链接
  8. 网络文件(例如:NFS file、网络 socket,unix 域名 socket)
  9. 还有其它类型的文件,等等

命令参数

  • -a 列出打开文件存在的进程
  • -c<进程名> 列出指定进程所打开的文件
  • -g 列出 GID 号进程详情
  • -d<文件号> 列出占用该文件号的进程
  • +d<目录> 列出目录下被打开的文件
  • +D<目录> 递归列出目录下被打开的文件
  • -n<目录> 列出使用 NFS 的文件
  • -i<条件> 列出符合条件的进程。(4、6、协议、:端口、 @ip )
  • -p<进程号> 列出指定进程号所打开的文件
  • -u 列出 UID 号进程详情
  • -h 显示帮助信息
  • -v 显示版本信息

无参数输出

lsof linux一切皆文件_2020-05-11-23-35-44.png

lsof 输出各列信息的意义如下:

  • COMMAND:进程的名称
  • PID:进程标识符
  • PPID:父进程标识符(需要指定-R 参数)
  • USER:进程所有者
  • PGID:进程所属组
  • FD:文件描述符,应用程序通过文件描述符识别该文件。如 cwd、txt 等:

    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
    (1)cwd:表示 current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
    (2)txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
    (3)lnn:library references (AIX);
    (4)er:FD information error (see NAME column);
    (5)jld:jail directory (FreeBSD);
    (6)ltx:shared library text (code and data);
    (7)mxx :hex memory-mapped type number xx.
    (8)m86:DOS Merge mapped file;
    (9)mem:memory-mapped file;
    (10)mmap:memory-mapped device;
    (11)pd:parent directory;
    (12)rtd:root directory;
    (13)tr:kernel trace file (OpenBSD);
    (14)v86 VP/ix mapped file;
    (15)0:表示标准输入
    (16)1:表示标准输出
    (17)2:表示标准错误
    一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u 等
    (1)u:表示该文件被打开并处于读取/写入模式
    (2)r:表示该文件被打开并处于只读模式
    (3)w:表示该文件被打开并处于
    (4)空格:表示该文件的状态模式为 unknow,且没有锁定
    (5)-:表示该文件的状态模式为 unknow,且被锁定
    同时在文件状态模式后面,还跟着相关的锁
    (1)N:for a Solaris NFS lock of unknown type;
    (2)r:for read lock on part of the file;
    (3)R:for a read lock on the entire file;
    (4)w:for a write lock on part of the file;(文件的部分写锁)
    (5)W:for a write lock on the entire file;(整个文件的写锁)
    (6)u:for a read and write lock of any length;
    (7)U:for a lock of unknown type;
    (8)x:for an SCO OpenServer Xenix lock on part of the file;
    (9)X:for an SCO OpenServer Xenix lock on the entire file;
    (10)space:if there is no lock.
  • TYPE:文件类型,如 DIR、REG 等,常见的文件类型:

    1
    2
    3
    4
    5
    6
    (1)DIR:表示目录
    (2)CHR:表示字符类型
    (3)BLK:块设备类型
    (4)UNIX: UNIX 域套接字
    (5)FIFO:先进先出 (FIFO) 队列
    (6)IPv4:网际协议 (IP) 套接字
  • DEVICE:指定磁盘的名称
  • SIZE:文件的大小
  • NODE:索引节点(文件在磁盘上的标识)
  • NAME:打开文件的确切名称

常用命令

  1. 查找某个文件相关的进程

    1
    lsof /bin/bash
  2. 列出某个用户打开的文件信息,-u 选项,u 是 user 的缩写

    1
    $lsof -u username
  3. 列出某个程序进程所打开的文件信息,-c 选项将会列出所有以 mysql 这个进程开头的程序的文件,其实你也可以写成 lsof | grep mysql, 但是第一种方法明显比第二种方法要少打几个字符;

    1
    lsof -c mysql
  4. 列出某个用户以及某个进程所打开的文件信息

    1
    lsof  -u test -c mysql
  5. 通过某个进程号显示该进程打开的文件

    1
    lsof -p 11968
  6. 命令查看当前目录下所有文件夹的大小 -d 指深度,后面加一个数值

    1
    du -d 1 -h

linux进程管理

发表于 2020-05-11 更新于 2020-06-30 分类于 linux

后台运行

shell 中执行命令若以&结尾,则程序会在后台执行,当用户退出时该后台程序会停止运行。若需要程序永久运行可以使用nohup 可使用jobs查看正在运行的作业,然后可以使用fg %n(n 为作业编号)将后台任务返回前台

Ctrl+c 是强制中断程序的执行。 Ctrl+z 的是将任务中断,但是此任务并没有结束,他仍然在进程中他只是维持挂起的状态。

Ctrl+z 的程序也可以使用fg将其返回前台

jobs -l显示pid

查询进程

  1. 查询正在运行的进程信息

    1
    ps -ef
  2. 查询归属于用户 colin115 的进程

    1
    2
    ps -ef | grep colin115
    ps -lu colin115
  3. 查询进程 ID(适合只记得部分进程字段)

    1
    pgrep <name>
  4. 以完整的格式显示所有的进程

    1
    ps -ajx
  5. 显示进程内存占用

    1
    ps -aux
  6. 查看端口占用的进程状态:

    1
    lsof -i:3306
  7. 查看用户 username 的进程所打开的文件

    1
    lsof -u username
  8. 查询 init 进程当前打开的文件

    1
    lsof -c init
  9. 查询指定的进程 ID(23295)打开的文件:

    1
    lsof -p 23295
  10. 查询指定目录下被进程开启的文件(使用+D 递归目录):

    1
    lsof +d mydir1/

杀进程

kill 命令可向进程发送指定的信号(默认发送的信号是 SIGTERM)。kill -l:显示信号的信息,我们常用的kill -9就是SIGKILL信号

分析进程

pstack

此命令可显示每个进程的栈跟踪,pstack <pid>,这个命令在排查进程问题时非常有用,比如我们发现一个服务一直处于 work 状态(如假死状态,好似死循环),使用这个命令就能轻松定位问题所在;可以在一段时间内,多执行几次 pstack,若发现代码栈总是停在同一个位置,那个位置就需要重点关注,很可能就是出问题的地方;

常用命令

快速杀进程

1
ps -ef|grep java|grep -v grep|awk '{print "kill -9 "$2}'|sh

linux环境变量

发表于 2020-05-11 更新于 2020-12-02 分类于 linux

配置文件加载

login shell

login shell是需要用户输入账号和密码进入shell,进行一个完整的登录流程。这种登录方式加载配置文件的方式如下

  1. 加载全局配置文件/etc/profile , 该脚本会将/etc/profile.d目录下的 sh 文件全部执行
  2. 加载用户配置文件,~/.bash_profile 或 ~/.bash_login 或~/.profile。上述三个文件只会读取一个,优先级依次降低 login shell 是说在取得 bash 时需要完整的登陆流程。什么时候取得 bash 呢?当然就是用户登陆的时候。当你在 tty1~tty6 登陆,需要输入账号和密码,此时取得的 bash 就是 login shell。

interactive shell

当使用非登录的方式启动一个 bash 时 ,就像你在桌面视图中用ctrl+alt+T启动的 shell 输入窗口就是non-login shell。还有就是你在 shell 窗口直接 su 切换的用户,都属于non-login shell。 non-login shell

  1. 加载全局配置文件/etc/bashrc
  2. 加载~/.bashrc

当使用su <user>切换用户的时,是以non-login shell的方式取得 bash 的,所以你的环境变量 PATH 等是不会改变的,如果你需要读取/etc/profile的话, 你需要用su - <user>的方式登录。su -就是su -l即su --login的意思 配置文件修改后需要使用source命令让它立即生效,例如source ~/.bashrc

Non-interactive shell

在脚本中执行的 shell

全局变量

1
printenv #打印所有全局变量

临时变量

1
2
3
set             # 打印所有临时变量
set hello=you # 设置临时变量
unset hello # 移除变量,当你尝试移除一个全局变量时,仅仅移除当前环境的变量,在退出当前执行进程后,该变量依然存在

一些常见变量

变量 描述
IFS 一系列用于区分不同属性的字符串集合,默认是空格
PATH shell 用来查找命令的目录,不同的目录用:分割
PS1 原始的 shell 提示字符串
PS2 提示继续进行输入的提示符
PWD 当前目录

相关配置

设定文件日期显示格式

1、编辑全局配置文件:/etc/profile,使所有用户均显示该格式:

1
2
#vi  /etc/profile
export TIME_STYLE="+%Y-%m-%d %H:%M:%S"

IFS

Shell 脚本中有个变量叫 IFS(Internal Field Seprator) ,内部域分隔符,shell 根据 IFS 的值,默认是 space, tab, newline 来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。

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

string="hello,shell,split,test"

#对IFS变量 进行替换处理
OLD_IFS="$IFS"
IFS=","
array=($string)
IFS="$OLD_IFS"

for var in ${array[@]}
do
echo $var
done

node入门

发表于 2020-05-08 更新于 2020-12-02 分类于 前端

npm

NPM 是随同 NodeJS 一起安装的包管理工具, nodejs的包管理器,用于 node 插件管理,包括安装,卸载,管理依赖等

用 npm 命令安装模块

npm 安装 Node.js 模块语法格式如下:

1
npm install <Module Name>

npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g 而已,比如

1
2
npm install express          # 本地安装
npm install express -g # 全局安装

本地安装

  1. 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
  2. 可以通过 require() 来引入本地安装的包。
  3. 可通过npm list查看项目安装的模块

全局安装

  1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
  2. 可以直接在命令行里使用。
  3. 可通过npm root -g查看所有全局安装的模块的目录:
  4. 可通过npm list -g查看全局安装的模块

使用 package.json

package.json 位于模块的目录下,用于定义包的属性

npm init 初始化,生成package.json

Package.json 属性说明

name - 包名。 version - 包的版本号。 description - 包的描述。 scripts 使用npm run command实际执行其配置的值。例如{"dev": "node build/dev-server.js"} homepage - 包的官网 url 。 author - 包的作者姓名。 contributors - 包的其他贡献者姓名。 dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。 repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。 main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。 keywords - 关键字

安装软件时可以将模块的依赖写入package.json的节点下

  1. -S -s --save 将安装包信息加入到dependencies
  2. -D -d --save --dev 将安装包信息加入devDependencies

配置

1
2
# 显示所有配置
npm config ls -l

事件驱动

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。 Node.js 单线程类似进入一个 while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数. node入门_2020-05-09-17-16-32.png

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

1
2
3
4
// 引入 events 模块
var events = require("events");
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();

以下程序绑定事件处理程序:

1
2
// 绑定事件及事件的处理程序
eventEmitter.on("eventName", eventHandler);

我们可以通过程序触发事件:

1
2
// 触发事件
eventEmitter.emit("eventName");

Stream(流)

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对 http 服务器发起请求的 request 对象就是一个 Stream,还有 stdout(标准输出)。 Node.js,Stream 有四种流类型:

  • Readable - 可读操作。
  • Writable - 可写操作。
  • Duplex - 可读可写操作.
  • Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  • data - 当有数据可读时触发。
  • end - 没有更多的数据可读时触发。
  • error - 在接收和写入过程中发生错误时触发。
  • finish - 所有数据已被写入到底层系统时触发。

从流中读取数据

创建 input.txt 文件,内容如下:

菜鸟教程官网地址:www.runoob.com 创建 main.js 文件, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var fs = require("fs");
var data = "";

// 创建可读流
var readerStream = fs.createReadStream("input.txt");

// 设置编码为 utf8。
readerStream.setEncoding("UTF8");

// 处理流事件 --> data, end, and error
readerStream.on("data", function (chunk) {
data += chunk;
});

readerStream.on("end", function () {
console.log(data);
});

readerStream.on("error", function (err) {
console.log(err.stack);
});

console.log("程序执行完毕");

以上代码执行结果如下:

程序执行完毕 菜鸟教程官网地址:www.runoob.com

写入流

创建 main.js 文件, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var fs = require("fs");
var data = "菜鸟教程官网地址:www.runoob.com";

// 创建一个可以写入的流,写入到文件 output.txt 中
var writerStream = fs.createWriteStream("output.txt");

// 使用 utf8 编码写入数据
writerStream.write(data, "UTF8");

// 标记文件末尾
writerStream.end();

// 处理流事件 --> data, end, and error
writerStream.on("finish", function () {
console.log("写入完成。");
});

writerStream.on("error", function (err) {
console.log(err.stack);
});

console.log("程序执行完毕");

以上程序会将 data 变量的数据写入到 output.txt 文件中。代码执行结果如下:

node main.js 程序执行完毕 写入完成。

查看 output.txt 文件的内容:

cat output.txt 菜鸟教程官网地址:www.runoob.com

管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。

设置 input.txt 文件内容如下:

菜鸟教程官网地址:www.runoob.com 管道流操作实例

创建 main.js 文件, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
var fs = require("fs");

// 创建一个可读流
var readerStream = fs.createReadStream("input.txt");

// 创建一个可写流
var writerStream = fs.createWriteStream("output.txt");

// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);

console.log("程序执行完毕");

代码执行结果如下:

node main.js 程序执行完毕

查看 output.txt 文件的内容:

cat output.txt 菜鸟教程官网地址:www.runoob.com

管道流操作实例

链式流 链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。 接下来我们就是用管道和链式来压缩和解压文件。 创建 compress.js 文件, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var fs = require("fs");
var zlib = require('zlib');

// 压缩 input.txt 文件为 input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));

console.log("文件压缩完成。");
代码执行结果如下:
$ node compress.js
文件压缩完成。
执行完以上操作后,我们可以看到当前目录下生成了 input.txt 的压缩文件 input.txt.gz。
接下来,让我们来解压该文件,创建 decompress.js 文件,代码如下:
var fs = require("fs");
var zlib = require('zlib');

// 解压 input.txt.gz 文件为 input.txt
fs.createReadStream('input.txt.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('input.txt'));

console.log("文件解压完成。");

代码执行结果如下:

node decompress.js 文件解压完成。

模块系统

模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

require

引入其他模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var http = require("http");
http
.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, { "Content-Type": "text/plain" });

// 发送响应数据 "Hello World"
response.end("Hello World\n");
})
.listen(8888);

// 终端打印如下信息
console.log("Server running at http://127.0.0.1:8888/");

引入 json 文件

1
var data = require("./data.json");

exports

创建 hello.js 文件,代码如下:

1
2
3
exports.world = function () {
console.log("Hello World");
};

则我们可以在代码中引用该模块的方法

1
2
var hello = require("./hello");
hello.world();

在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require('./hello') 加载这个模块,然后就可以直接访问 hello.js 中 exports 对象的成员函数了。 有时候我们只是想把一个对象封装到模块中,格式如下:

1
2
3
module.exports = function () {
// ...
};

模块接口的唯一变化是使用 module.exports = Hello 代替了 exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports

export 和 import

export 与 import 是 JavaScript 用来进行模块化管理的两个关键词

export 可以标记在任何变量、方法、类的声明前 例如

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
// 📁 say.js
export let months = ["Jan", "Feb"];

export const MODULES_BECAME_STANDARD_YEAR = 2015;

export function sayHi(user) {
alert(`Hello, ${user}!`);
}

export class User {
constructor(name) {
this.name = name;
}
}

function sayHi(user) {
alert(`Hello, ${user}!`);
}

function sayBye(user) {
alert(`Bye, ${user}!`);
}
//也可单独export变量
export { sayHi, sayBye };

//export 可重命名
export { sayHi as hi, sayBye as bye };

我们可以通过 import 导入其他模块的变量,语法为import {...}

1
2
3
4
5
// 📁 main.js
import { sayHi, sayBye } from "./say.js";

sayHi("John"); // Hello, John!
sayBye("John"); // Bye, John!

也可以给 import 的对象命名

1
2
3
4
5
6
7
8
9
10
11
12
// 📁 main.js
import * as say from "./say.js";

say.sayHi("John");
say.sayBye("John");

//可以为具体成员变量重新命名
// 📁 main.js
import { sayHi as hi, sayBye as bye } from "./say.js";

hi("John"); // Hello, John!
bye("John"); // Bye, John!

当使用export default时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 📁 user.js
export default class User {
// just add "default"
constructor(name) {
this.name = name;
}
}

//export default 可以导出匿名函数或类,一个模块只能有一个default
export default class {
constructor() { ... }
}
export default function(){
}


// 📁 main.js
import User from "./user.js"; // not {User}, just User

new User("John");

// 也可以使用非default的方式导入,默认将其视为名为default的变量

import {default as User,sayHi} from './user.js'
export default export
export class User {...} export default class User {...}
import {User} from ... import User from ...

模块的加载过程

由于 Node.js 中存在 4 类模块(原生模块和 3 种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示: node入门_2020-05-12-20-57-08.png

从文件模块缓存中加载

尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块。

从原生模块加载

原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名之后,优先检查模块是否在原生模块列表中。以 http 模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json 文件,require("http") 都不会从这些文件中加载,而是从原生模块中加载。 原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

从文件加载

当文件模块缓存中不存在,而且不是原生模块的时候,Node.js 会解析 require 方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。 require 方法接受以下几种参数的传递:

  • http、fs、path 等,原生模块。
  • ./mod 或../mod,相对路径的文件模块。
  • /pathtomodule/mod,绝对路径的文件模块。
  • mod,非原生模块的文件模块。 在路径 Y 下执行 require(X) 语句执行顺序:
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
1. 如果 X 是内置模块
a. 返回内置模块
b. 停止执行
2. 如果 X 以 '/' 开头
a. 设置 Y 为文件根路径
3. 如果 X 以 './' 或 '/' or '../' 开头
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. 抛出异常 "not found"

LOAD_AS_FILE(X)
1. 如果 X 是一个文件, 将 X 作为 JavaScript 文本载入并停止执行。
2. 如果 X.js 是一个文件, 将 X.js 作为 JavaScript 文本载入并停止执行。
3. 如果 X.json 是一个文件, 解析 X.json 为 JavaScript 对象并停止执行。
4. 如果 X.node 是一个文件, 将 X.node 作为二进制插件载入并停止执行。

LOAD_INDEX(X)
1. 如果 X/index.js 是一个文件, 将 X/index.js 作为 JavaScript 文本载入并停止执行。
2. 如果 X/index.json 是一个文件, 解析 X/index.json 为 JavaScript 对象并停止执行。
3. 如果 X/index.node 是一个文件, 将 X/index.node 作为二进制插件载入并停止执行。

LOAD_AS_DIRECTORY(X)
1. 如果 X/package.json 是一个文件,
a. 解析 X/package.json, 并查找 "main" 字段。
b. let M = X + (json main 字段)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS

路由

路由简单来说是一个请求的 uri 地址与相对应的 function 的映射表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var http = require("http");
var url = require("url");

function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
// true 返回dict
var paramertes = url.parse(request.url, true).query;
console.log("Request for " + pathname + " received.");

route(pathname);

response.writeHead(200, { "Content-Type": "text/plain" });
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}

exports.start = start;

路由中可以根据pathname调用对应的函数去处理

1
2
3
4
5
function route(pathname) {
console.log("About to route a request for " + pathname);
}

exports.route = route;
1
2
3
4
var server = require("./server");
var router = require("./router");

server.start(router.route);

错误问题

npm 安装软件时 报错 reason: getaddrinfo EAI_AGAIN

关闭代理软件即可

1…678…15

lijun

与其感慨路难行,不如马上就出发
145 日志
30 分类
117 标签
© 2019 – 2020 lijun