shell脚本学习

# 一、shell基础1

## 1.shell是什么

- Shell是一个命令行解释器,它为用户提供 了一个向Linux内核发送请求以便运行程 序的界面系统级程序,用户可以用Shell来 启动、挂起、停止甚至是编写一些程序。

- Shell还是一个功能相当强大的编程语言, 易编写,易调试,灵活性较强。Shell是解释执行的脚本语言,在Shell中可以直接调 用Linux系统命令。

Linux支持的Shell

/etc/shells

/bin/sh

/bin/bash

/usr/bin/sh

/usr/bin/bash

/bin/ksh

/bin/rksh

## 2.脚本的执行方式

### 1.echo输出命令

`echo 'hello word'`

选项  -e  支持反斜线字符转换;

| 控制字符 | 作用                                                        |

| :------- | ------------------------------------------------------------ |

| \\      | 输出\本身                                                    |

| \a      | 输出警告音                                                  |

| \b      | 退格键,也就是向左删除键                                    |

| \c      | 取消输出行末的换行符。和“-n”选项一致                        |

| \e      | ESCAPE键                                                    |

| \f      | 换页符                                                      |

| \n      | 换行符                                                      |

| \r      | 回车键                                                      |

| \t      | 制表符,也就是tab键                                          |

| \v      | 垂直制表符                                                  |

| \0nnn    | 按照八进制ASCII码表输出字符。其中0为数字零,nnn是三位八进制数 |

| \xhh    | 按照十六进制ASCII码表输出字符。其中hh是两位十六进制数        |

`echo -e "a\tb\tc\nd\te\tf"`

输出:

a b c

d e f 制表符与换行符

`echo -e “\x61\t\x62\t\x63\n\x64\t\x65\t\x66”`

输出:

a b c

d e f 按照十六进制ASCII码也同样可以输出

`echo -e "\e[1;31m abcd \e[0m"`

输出:

红色的abcd

因为\e[1 表示开启颜色区别 \e[0m 表示结束颜色区别 31m表示红色 还有其他:

30m=黑色,31m=红色,32m=绿色,33m=黄色,34m=蓝色,35m=洋红,36m=青色,37m=白色

- 脚本格式:

第一行#号不是注释,是声明使用的解释器  `#!/bin/bash`

```shell

vim test.sh

#!/bin/bash

#程序注释要完整可读

echo -e "Hello Word!"

```

脚本执行的两种方式:

1.授权可执行权限后用绝对路径或相对路径执行;

2.bash 脚本执行(无需权限,不常用);

## 3.bash的基本功能

### 1.历史命令与命令补全

history 历史命令补全

history  [选项]  [历史命令保存文件]

-c:清空历史命令(慎用)

-w:把缓存中的历史命令写入文件

默认存储位置: ~/bash_history(该次登陆的命令不会在该文件中被记录,需要-w才能保存),历史命令默认保存1000条,可以在/etc/profile中修改;

使用上下箭头调用历史命令

!n:重复执行第n条命令;

!!:重复执行上一条命令;

"!字符串":重复执行最后一条以该命令开头的命令;

`````shell

[root@localhost ~]# !net

netstat -tlnp|grep 1521

`````

### 2.tab键补全命令

在bash中,不管是命令还是文件都可以使用tab键补全

## 2.别名与快捷键

### 别名

别名查询:alias

定义方式:alias 别名='原命令'

`alias  vi='vim'`

除非确实需要,否则不建议用自定义别名覆盖现有命令;

用命令行定义的别名是临时生效的,要想永久生效需要修改家目录下的.bashrc文件;

删除别名:unalias 别名

### bash常用快捷键

| 快捷键    | 作用                                                        |

| ---------- | ------------------------------------------------------------ |

| Ctrl+A    | 把光标移动到命令行开头,如果我们输入的命令过长,想要把光标移动到开头使用; |

| ctrl+E    | 把光标移动到命令行结尾                                      |

| **ctrl+C** | 强制终止当前命令                                            |

| **ctrl+L** | 清屏,相当于clear命令                                        |

| **ctrl+U** | 删除或剪切光标之前的命令                                    |

| ctrl+K    | 删除或剪切光标之后的内容                                    |

| **ctrl+Y** | 粘贴ctrl U 或者ctrl K 剪切的内容                            |

| **ctrl+R** | 在历史命令中搜索,按下后就会出现搜索界面,搜索到以后直接回车可以执行 |

| **ctrl+D** | 退出当前终端                                                |

| ctrl+Z    | 暂停,并进入后台。牵扯工作管理中的内容,后续讲              |

| ctrl+S    | 暂停屏幕输出                                                |

| ctrl+Q    | 恢复屏幕输出                                                |

加粗的为常用命令

## 3.bash输入输出重定向

### 1.标准输入输出

| 设备  | 设备文件名  | 文件描述符 | 类型        |

| ------ | ----------- | ---------- | ------------ |

| 键盘  | /dev/stdin  | 0          | 标准输入    |

| 显示器 | /dev/sdtout | 1          | 标准输出    |

| 显示器 | /dev/sdterr | 2          | 标准错误输出 |

### 2.输出重定向

| 类型              | 符号        | 作用                                                      |

| ------------------ | ----------- | ---------------------------------------------------------- |

| 标准输出重定向    | 命令>文件  | 以覆盖的方式,把命令的正确内容输出到到指定的文件或设备中; |

| 标准输出重定向    | 命令>>文件  | 以追加的方式,把命令的正确内容输出到到指定的文件或设备中; |

| 标准错误输出重定向 | 命令2>文件  | 以覆盖的方式,把命令的错误内容输出到到指定的文件或设备中; |

| 标准错误输出重定向 | 命令2>>文件 | 以追加的方式,把命令的错误内容输出到到指定的文件或设备中; |

不在将命令执行的结果显示在命令行中,而是输出在文件中,就叫命令的输出重定向;

通常工作中这种分开输出的方式不常用:

```shell

date >>  test.txt #将正确命令追加到test

dateaaa 2>> test.txt #将错误命令追加到test

```

常用方式为正确和错误命令同时输出:

|                      |                                                          |

| --------------------- | -------------------------------------------------------- |

| 命令 > 文件 2>&1      | 以覆盖方式,把正确输出和错误输出都保存到同一个文件当中; |

| 命令>> 文件2>&1      | 以追加方式,把正确输出和错误输出都保存到同一个文件当中; |

| 命令&>文件            | 以覆盖方式,把正确输出和错误输出都保存到同一个文件当中; |

| 命令&>>文件          | 以追加方式,把正确输出和错误输出都保存到同一个文件当中; |

| 命令>>文件1  2>>文件2 | 以追加方式,把正确的输出到文件1,错误的输出到文件2;    |

例如:

```shell

dateaaa &>> test.txt

date &>> test.txt

```

正确和错误的输出都会追加到test.txt文件中

执行命令不保存输出结果:

`ls &>/dev/null`

/dev/null类似垃圾箱,无法查询内容。

### 3.输入重定向

`wc [选项] [文件名]`

-c 统计字节数

-w 统计单词数

-l 统计行数

用的情况不多,统计命令或文件中的信息。

## 4.多命令顺序执行和管道符

### 1.多命令顺序执行

| 多命令执行符 | 格式          | 作用                                                        |

| ------------ | -------------- | ------------------------------------------------------------ |

| ;            | 命令1;命令2    | 多个命令顺序执行,命令之间没有任何逻辑联系                  |

| &&          | 命令1&&命令2  | 逻辑与<br />当命令1正确执行,则命令2才会执行<br />当命令1执行不正确,则命令2不会执行 |

| \|\|        | 命令1\|\|命令2 | 逻辑或<br />当命令1执行不正确,则命令2才会执行<br />当命令1正确执行,则命令2不会执行 |

举例:

```shell

ls;date;cd /asdf; cd #命令之间不互相干扰

ll && echo yes || echo no  #判断命令是否正确执行,ll执行成功后逻辑与&&生效,然后执行echo yes,逻辑或||失效,不执行echo no;

```

### 2.管道符

命令格式:

命令1 | 命令2

#命令1的正确输出作为命令2的操作对象,例如:

```shell

ll -a /etc | more

netstat -tlnp|grep 1521

```

grep作用:

grep  【选项】 "搜索内容"  文件名

`grep "root" /etc/passwd`

选项:

-i:忽略大小写

-n:输出行号

-v:反向查找

--color=auto:搜索出的内容用颜色显示

## 5.通配符和其他特殊符号

### 1.通配符

| 通配符 | 作用                                                        |

| ------ | ------------------------------------------------------------ |

| ?    | 匹配任意一个字符                                            |

| *      | 匹配0个或任意多个字符,也就是匹配                            |

| []    | 匹配括号中任意**一个**字符,例如[abc]代表一定匹配一个字符,或是a、或是b、或是c; |

| [-]    | 匹配括号中任意**一个**字符,-代表一个范围。例如[a-z] 代表匹配任意一个小写字母; |

| [^]    | 逻辑非,表示匹配不是括号里的**一个**字符。例如:`[^0-9]`代表匹配一个不是数字的字符。 |

中括号只能匹配一个字符;

### 2.bash中的其他特殊符号

| 符号 | 作用                                                        |

| ---- | ------------------------------------------------------------ |

| ‘’  | 单引号。在单引号中所有的特殊符号,如“$“和”·“(反引号)都没有特殊含义; |

| ”“  | 双引号。在双引号中特殊符号都没有特殊含义,但是“$“和”·“和\都是例外,拥有调用变量的值、引用命令、转义符的特殊含义 |

| ··  | 反引号。反引号括起来的内容是系统命令,在bash中会先执行它,和$()作用一样,不过推荐使用$(),反引号容易看错。 |

| $()  | 引用系统命令                                                |

| #    | shell脚本中#号开头的是注释                                  |

| $    | 用于调用变量的值,如果需要调用name变量的值时,需要用$name调用 |

| \    | 转义符,跟在\后的特殊符号将失去特殊含义,变为普通字符。<br />如输出`\$`将输出$,而不是变量的引用。 |

例如:

```shell

#单引号和双引号的区别

name=eajon

echo $name

echo '$name'

echo "$name"

#反引号和$()的作用:

abc=`date`

echo $abc

abcd=$(date)

echo $abcd

```

# 二、shell基础2

## 1.bash变量

变量分类:

用户自定义变量

环境变量:这种变量主要保存的是系统和操作环境相关的数据;

位置参数变量:这种变量主要是用来向脚本中传递参数或数据的,变量名不能自定义,变量作用是固定的;

预定义变量:是bash中已经定义好的变量,变量名不能自定义,作用也是固定的;

### 1.用户自定义变量

变量设置规则:

可以由字母数字和下划线组成,但是不能以数字开头,比如2name是个错误的变量名;

在bash中,变量的默认类型都是字符串类型的,如果要进行数字运算,则必须指定变量类型为数值型;

变量由等号连接值,等号左右两侧不能有空格;

变量的值有空格,需要用单引号或者双引号将这个值包含;

在变量的值中,可以使用\ 转义符;

如果需要增加变量的值,可以进行变量的叠加,不过需要用双引号包含"$变量名"或用${变量}包含;

可以把命令的结果作为值,$(命令);

环境变量名建议大写,便于区分;

```shell

aa=123 #变量的定义

echo $aa #变量调用

aa=${aa}456 #变量的叠加

aa="$aa"789 #变量的叠加

echo $aa

set  #查看系统所有变量

unset 变量名 #删除变量

```

### 2.环境变量

用户自定义变量只在当前的shell中生效,而环境变量会在当前shell和这个shell所有的子shell当中生效;如果把环境变量写入配置文件,那么这个环境变量会在所有的shell中生效。

环境变量的设置:

```shell

export 变量名=变量值

env #查询变量

unset 删除变量

```

系统常见环境变量:

PATH:系统查找命令的路径,tab的补全功能也是在path中搜索补全的;

PS1:定义系统提示符的变量

### 3.位置参数变量

| 位置参数变量 | 作用                                                        |

| ------------ | ------------------------------------------------------------ |

| $n          | n为数字,$0代表命令本身,$1~$9代表第一个到第九个参数,10以上的需要用大括号括起来,如:${10} |

| $*          | 这个变量代表命令行中的所有参数,$*把所有参数看做一个整体;  |

| $@          | 这个变量也代表命令行中的所有参数,不过$@把每个参数区分对待  |

| $#          | 这个变量代表命令行中所有参数的个数                          |

```shell

#!/bin/bash

echo $0

echo $1

echo $2

echo $3

./canshu.sh  11  12  13 #调用位置参数

======================================

#!/bin/bash

echo $@

echo $#

echo $*

#传入参数调用

./canshu2.sh 11 22 33  44  666

5

11 22 33 44 666

11 22 33 44 666

```

### 4.预定义变量

| 预定义变量 | 作用                                                        |

| ---------- | ------------------------------------------------------------ |

| $?        | 最后一次执行命令的返回状态。如果这个变量值为0,证明上一个命令正确执行;<br />如果这个变量的值非0,则证明上一个命令执行不正确; |

| $$        | 当前进程号(PID)                                            |

| $!        | 后台运行的最后一个进程的进程号(PID)                        |

多命令执行中的逻辑与或者逻辑非就是通过$?来判断的;

接收键盘输入:

read [选项]  [变量名]

选项:

-p: 提示信息,在等待read输入时,输出提示信息;

-t:read命令会一直等待用户输入,使用此选项可以指定时间;

-n:字符数:read命令只要接受指定的字符数,就会执行;

-s:隐藏输入的数据,适用于机密信息的输入

## 2.数值运算与运算符

### 1.declare声明变量类型

declare [+/-]  [选项]  变量名

选项:

-:给变量设定类型属性

+:取消变量的类型属性

-i:将变量声明为整数型

-x:将变量声明为环境边浪

-p:显示指定变量的被声明类型

### 2.数值运算的方法:

```shell

declare -i cc=$aa+$bb #使用declare声明

dd=$(expr $aa + $bb) #使用expr,加号两端必须有空格

ff=$(($aa+$bb)) #使用双括号括起来,最常用最直观

```

运算优先级类似数学,可以通过小括号调整

### 3.运算符

shell支持的运算符:

优先级越高的越先执行,可以通过()调整

| 运算符 | 优先级      | 说明                              |

| ------ | ----------- | ---------------------------------- |

| 13    | -,+        | 单目负、单目正                    |

| 12    | !,~      | 逻辑非、按位取反或补码            |

| 11    | *  /  %    | 乘、除、取模                      |

| 10    | +, -        | 加、减                            |

| 9      | << ,  >>    | 按位左移、按位右移                |

| 8      | <= ,>=, <,> | 小于或等于、大于或等于、小于、大于 |

| 7      | ==,!=      | 等于、不等于                      |

| 6      | &          | 按位与                            |

| 5      | ^          | 按位异或                          |

| 4      | \|          | 按位或                            |

| 3      | &&          | 逻辑与                            |

| 2      | \|\|        | 逻辑或                            |

| 1      | =,+=,-=    | 赋值,运算且赋值                  |

## 3.变量测试与内容替换

### 变量置换参考

| 变量置换方式 | 变量y没有设置            | 变量y为空值              | 变量y设置了值    |

| ------------ | ------------------------ | ------------------------ | ----------------- |

| x=${y-新值}  | x=新值                  | x为空                    | x=$y              |

| x=${y:-新值} | x=新值                  | x=新值                  | x=$y              |

| x=${y+新值}  | x为空                    | x=新值                  | x=新值            |

| x=${y:+新值} | x为空                    | x为空                    | x=新值            |

| x=${y=新值}  | x=新值<br />y=新值      | x为空<br />y值不变      | x=$y<br />y值不变 |

| x=${y:=新值} | x=新值<br />y=新值      | x=新值<br />y=新值      | x=$y<br />y值不变 |

| x=${y?新值}  | 新值输出到<br />错误输出 | x为空                    | x=$y              |

| x=${y:?新值} | 新值输出到<br />错误输出 | 新值输出到<br />错误输出 | x=$y              |

## 4.环境变量

### 1.环境变量配置文件简介

#### 1.source命令

source 配置文件或  . 配置文件(点是source的缩写)

使修改了环境变量的配置文件生效

#### 2.环境变量配置文件简介:

环境变量配置文件中主要是定义对系统的操作环境生效的系统默认环境变量,比如PATH、HISTSIZE、PS1、HOSTNAME等默认环境变量。

```shell

/etc/profile

/etc/profile.d/*.sh

~/.bash_profile

~/.bashrc

/etc/bashrc

```

etc下配置的环境变量每个用户登录都有效,家目录下的环境变量只对自己用户生效;

### 2.环境变量配置文件作用

# 三、shell编程

## 1.正则表达式

正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配。grep、awk、sed等命令可以支持正则表达式;

基础正则表达式:

| 元字符    | 作用                                                        |

| --------- | ------------------------------------------------------------ |

| *        | 前一个字符匹配0次或任意多次;<br />匹配0次无意义            |

| .        | 匹配除了换行符的任意一个字符(相当于通配符的?);<br />s..d 代表s和d中间有两个字符 |

| ^        | 匹配行首。例如:^hello会匹配以hello开头的行                  |

| $        | 匹配行尾。例如hello$会匹配以hello结尾的行                    |

| []        | 匹配括号中的一个字符,只匹配一个字符。<br />例如:`[0-9][a-z]`可以匹配一个数字和一个字母构成的两位字符 |

| [^]      | 匹配除括号的字符以外的一个字符。<br />例如:`[^0-9]`匹配一位非数字字符 |

| \        | 转义符,用于取消特殊符号的含义                              |

| `\{n\}`  | 表示前面的字符刚好出现n次。<br />例如:`[1][3-9][0-9]\{9\}`匹配手机号码。 |

| `\{n,\}`  | 表示其前面的字符出现不小于n次。<br />例如:`[0-9]\{3\}`表示三位及以上的数字 |

| `\{n,m\}` | 表示前面的字符至少出现n次,至多出现m次。<br />例如:`[a-z]\{2,8\}`匹配2-8位的小写字母 |

常用:

```shell

.*: a.*b 代表匹配任何a开头 b结尾的字符串;

^$: “^$” 代表匹配空白行,通常加-n选项附带行号,方便区分

grep  -n "^$" test.txt

```

## 2.字符截取命令

### 1.cut字段提取命令

只支持提取的列是由制表符,单个符号分隔的,进行简单的分隔:

```shell

cut [选项] 文件名

-f  列号 :  提取第几列

-d  分隔符 : 按照指定的分隔符分隔

例如:

cut -f 3,5  -d  , #取第三列和第五列,列与列之间以逗号分隔

```

### 2.printf命令

格式化打印命令

```shell

printf `输出类型和格式`  输出内容

输出类型:

%ns:    输出字符串,n是指输出几个字符;

%ni:  输出整数,n代表输出几个数字;

%m,nf  输出浮点数,m和n是数字,指输出的整数位数和小数位数:如 %8.2f代表共输出8位数,其中6位整数,2位小数

例如:printf  '%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n'  $(netstat -tlnp|grep 1521)

```

在awk命令的输出中支持print和printf命令

print:print会在每个输出之后自动加入一个换行符(Linux默认没有print命令)

printf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要手工加入换行符

### 3.awk命令

语法:

通过判断条件执行动作

```shell

awk '条件1{动作1} 条件2 {动作2}' ...  文件名

```

- 条件(pattern):

  一般使用关系表达式作为条件:

  x > 10 判断变量是否大于10

- 动作 (action):

  格式化输出;

  流程控制语句

```shell

awk与命令使用管道符结合使用:

netstat -tlnp|grep 1521 | awk '{print $7}'

awk与文件结合使用:

awk '{print $7}' test.txt

查找指定进程pid流程

netstat -tlnp|grep 1521  | awk '{print $7}' |cut -f 1  -d /

杀指定pid的进程

kill -9 $(netstat -tlnp|grep 8080 | awk '{print $7}'| cut -f 1 -d /)

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 最近开始系统学习shell脚本的相关知识,为了对已经学习的知识及时巩固,也为别人提供参考的资料,这里会对自...
    涤除而玄览阅读 271评论 0 0
  • shell脚本编程 shell脚本 协程,协程可以同时做两件事。它在后台生成一个子shell,并在这个子shell...
    Ileo_km阅读 271评论 0 0
  • [TOC] 简介: Shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言...
    黄海滨_x阅读 831评论 0 2
  • 学习shell脚本前先对linux的基本常用知识点介绍一下。 linux系统介绍linux由四个部分组成,分别是l...
    涤除而玄览阅读 326评论 0 0
  • 上一节的内容都是从上到下执行的顺序语句,本节介绍shell中的分支语句,满足一些逻辑判断的使用。shell中分支语...
    涤除而玄览阅读 320评论 0 0