# Shell概述

Shell是一个命令解释器,它在操作系统的最底层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户。这种对话方式可以是交互的方式(从键盘输入命令,可以立即得到shell的回应),或非交互(脚本)的方式。
命令行解释器:使用户得以与内核进行沟通的“翻译官”

# Shell解析器

  1. Linux提供的Shell解析器有:
[root@hadoop101 ~]$ cat /etc/shells 
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh
1
2
3
4
5
6
7
  1. bash和sh的关系 bash 是 Bourne Again Shell 是Linux标准的默认shell ,它基于Bourne shell,吸收了C shell和Korn shell的一些特性。bash完全兼容Bourne shell,也就是说用Bourne shell的脚本不加修改可以在bash中执行。
    sh 是Bourne shell 这个是UNIX标准的默认shell,对它评价是concise简洁 compact紧凑 fast高效 有AT&T编写,属于系统管理shell。
[root@hadoop101 bin]$ ll | grep bash
-rwxr-xr-x. 1 root root 941880 511 2016 bash
lrwxrwxrwx. 1 root root    4 527 2017 sh -> bash
1
2
3
  1. Centos默认的解析器是bash
[root@hadoop102 bin]$ echo $SHELL

/bin/bash
1
2
3

# Shell 脚本入门

# Shell使用

  1. 脚本格式
    脚本以#!/bin/bash开头(指定解析器)
  2. 第一个Shell脚本:helloworld
    1)需求:创建一个Shell脚本,输出 helloworld
    2)案例实操:
[root@hadoop101 datas]$ touch helloworld.sh
[root@hadoop101 datas]$ vi helloworld.sh
1
2

在helloworld.sh中输入如下内容

#!/bin/bash
echo "helloworld"
1
2

3)脚本的常用执行方式
第一种:采用bash或sh+脚本的相对路径或绝对路径(不用赋予脚本 +x 权限)
sh+脚本的相对路径

[root@hadoop101 datas]$ sh helloworld.sh 
Helloworld
1
2

sh+脚本的绝对路径

[root@hadoop101 datas]$ sh /home/root/datas/helloworld.sh 
helloworld
1
2

bash+脚本的相对路径

[root@hadoop101 datas]$ bash helloworld.sh 
Helloworld
1
2

bash+脚本的绝对路径

[root@hadoop101 datas]$ bash /home/root/datas/helloworld.sh 
Helloworld
1
2

第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x) (a)首先要赋予helloworld.sh 脚本的+x权限

[root@hadoop101 datas]$ chmod 777 helloworld.sh
1

(b)执行脚本 相对路径

[root@hadoop101 datas]$ ./helloworld.sh 
Helloworld
1
2

绝对路径

[root@hadoop101 datas]$ /home/root/datas/helloworld.sh 

Helloworld
1
2
3

注意:第一种执行方法,本质是bash解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
3. 第二个Shell脚本:多命令处理 1)需求
在/home/root/目录下创建一个banzhang.txt,在banzhang.txt文件中增加“I love cls”。
2)案例实操

[root@hadoop101 datas]$ touch batch.sh
[root@hadoop101 datas]$ vi batch.sh
1
2

在batch.sh中输入如下内容

#!/bin/bash
cd /home/root
touch cls.txt
echo "I love cls" >>cls.txt
1
2
3
4

# echo命令

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

echo string
1

您可以使用echo实现更复杂的输出格式控制。

  1. 显示普通字符串
echo "It is a test"
1

这里的双引号完全可以省略,以下命令与上面实例效果一致:

echo It is a test
1
  1. 显示转义字符
echo "\"It is a test\""
1

结果将是:

"It is a test"
1

同样,双引号也可以省略
3. 显示变量
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

#!/bin/sh
read name 
echo "$name It is a test"
1
2
3

以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:

[root@www ~]# sh test.sh
OK                     #标准输入
OK It is a test        #输出
1
2
3
  1. 显示换行
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
1
2

输出结果:

OK!
It is a test
1
2
  1. 显示不换行
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"
1
2
3

输出结果:

OK! It is a test
1
  1. 显示结果定向至文件
echo "It is a test" > myfile
1
  1. 原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\"'
1

输出结果:

$name\"
1
  1. 显示命令执行结果
echo `date`
1

注意: 这里使用的是反引号 `, 而不是单引号 '
结果将显示当前日期

Thu Jul 24 10:08:46 CST 2014
1

# printf 命令

printf 命令模仿 C 程序库(library)里的 printf() 程序。

printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。

printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf 命令的语法:

printf  format-string  [arguments...]
1

参数说明:

  • format-string: 为格式控制字符串
  • arguments: 为参数列表。

实例

$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\n"
Hello, Shell
1
2
3
4

接下来,我来用一个脚本来体现 printf 的强大功能:

实例

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
1
2
3
4

执行脚本,输出结果如下所示:

姓名     性别   体重kg
郭靖     男      66.12
杨过     男      48.65
郭芙     女      47.99
1
2
3
4

%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。

%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中 .2 指保留2位小数。

实例

# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"

# 没有引号也可以输出
printf %s abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def

printf "%s\n" abc def

printf "%s %s %s\n" a b c d e f g h i j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

执行脚本,输出结果如下所示:

1 abc
1 abc
abcdefabcdefabc
def
a b c
d e f
g h i
j  
 and 0
1
2
3
4
5
6
7
8
9

printf 的转义序列

序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

实例

$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>

$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>

$ printf "www.runoob.com \a"
www.runoob.com $                  #不换行
1
2
3
4
5
6
7
8
9

# Shell中的变量

# 系统变量

  1. 常用系统变量
    $HOME、$PWD、$SHELL、$USER等
  2. 案例实操
    1)查看系统变量的值
[root@hadoop101 datas]$ echo $HOME
/home/root
1
2

2)显示当前Shell中所有变量:set

[root@hadoop101 datas]$ set
BASH=/bin/bash
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
1
2
3
4
5

# 自定义变量

  1. 基本语法
    1)定义变量:变量=值
    2)撤销变量:unset 变量
    3)声明静态变量:readonly变量,注意:不能unset
    4)除了显式地直接赋值,还可以用语句给变量赋值,如:
for file in `ls /etc`for file in $(ls /etc)
1
2
3
  1. 变量类型
    运行shell时,会同时存在三种变量:
    1). 局部变量
    局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
    2).环境变量
    所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。
    3). shell变量
    shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的运行。
  2. 变量定义规则
    1)变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
    2)等号两侧不能有空格
    3)在bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
    4)变量的值如果有空格,需要使用双引号或单引号括起来。
  3. 案例实操
    1)定义变量A
[root@hadoop101 datas]$ A=5
[root@hadoop101 datas]$ echo $A 或者 ${A}
5
1
2
3

2)给变量A重新赋值

[root@hadoop101 datas]$ A=8
[root@hadoop101 datas]$ echo $A
8
1
2
3

3)撤销变量A

[root@hadoop101 datas]$ unset A
[root@hadoop101 datas]$ echo $A
1
2

4)声明静态的变量B=2,不能unset

[root@hadoop101 datas]$ readonly B=2
[root@hadoop101 datas]$ echo $B
2
[root@hadoop101 datas]$ B=9
-bash: B: readonly variable
1
2
3
4
5

5)在bash中,变量默认类型都是字符串类型,无法直接进行数值运算

[root@hadoop102 ~]$ C=1+2
[root@hadoop102 ~]$ echo $C
1+2
1
2
3

6)变量的值如果有空格,需要使用双引号或单引号括起来

[root@hadoop102 ~]$ D=I love banzhang
-bash: world: command not found
[root@hadoop102 ~]$ D="I love banzhang"
[root@hadoop102 ~]$ echo $A
I love banzhang
1
2
3
4
5

7)可把变量提升为全局环境变量,可供其他Shell程序使用 export 变量名

[root@hadoop101 datas]$ vim helloworld.sh 
1

在helloworld.sh文件中增加echo $B

#!/bin/bash
echo "helloworld"
echo $B
1
2
3
[root@hadoop101 datas]$ ./helloworld.sh 
Helloworld
1
2

发现并没有打印输出变量B的值。

[root@hadoop101 datas]$ export B
[root@hadoop101 datas]$ ./helloworld.sh 
helloworld
2
1
2
3
4

# 特殊变量表

参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令 (opens new window)功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

# 特殊变量:$n

  1. 基本语法
    $n (功能描述:n为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10})
  2. 案例实操
    1)输出该脚本文件名称、输入参数1和输入参数2 的值
[root@hadoop101 datas]$ touch parameter.sh 
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0  $1  $2"
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls  xz
./parameter.sh  cls  xz
1
2
3
4
5
6
7

# 特殊变量:$#

  1. 基本语法
    $# (功能描述:获取所有输入参数个数,常用于循环)
  2. 案例实操
    1)获取输入参数的个数
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0  $1  $2"
echo $#
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls  xz
parameter.sh cls xz 
2
1
2
3
4
5
6
7
8

# 特殊变量:$*、$@

  1. 基本语法
    $* (功能描述:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体)
    $@ (功能描述:这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待)
  2. 案例实操
    1)打印输入的所有参数
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0  $1  $2"
echo $#
echo $*
echo $@
[root@hadoop101 datas]$ bash parameter.sh 1 2 3
parameter.sh  1  2
3
1 2 3
1 2 3
1
2
3
4
5
6
7
8
9
10
11

# 特殊变量:$?

  1. 基本语法
    $? (功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。)
  2. 案例实操
    1)判断helloworld.sh脚本是否正确执行
[root@hadoop101 datas]$ ./helloworld.sh 
hello world
[root@hadoop101 datas]$ echo $?
0
1
2
3
4

# Shell 字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。

  1. 单引号
str='this is a string'
1

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
  1. 双引号
your_name='runoob'
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str
1
2
3

输出结果为:

Hello, I know you are "runoob"! 
1

双引号的优点:

  • 双引号里可以有变量
  • 双引号里可以出现转义字符
  1. 拼接字符串
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting  $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2  $greeting_3
1
2
3
4
5
6
7
8
9

输出结果为:

hello, runoob ! hello, runoob !
hello, runoob ! hello, ${your_name} !
1
2

4.获取字符串长度

string="abcd"
echo ${#string} #输出 4
1
2

5.提取子字符串
以下实例从字符串第 2 个字符开始截取 4 个字符:

string="runoob is a great site"
echo ${string:1:4} # 输出 unoo
1
2

提示

第一个字符的索引值为 0

  1. 查找子字符串
    查找字符 io 的位置(哪个字母先出现就计算哪个):
string="runoob is a great site"
echo `expr index "$string" io`  # 输出 4
1
2

提示

以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。


# Shell 数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

  1. 定义数组
    在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
数组名=(值1 值2 ... 值n)
1

例如:

array_name=(value0 value1 value2 value3)
1

或者

array_name=(
value0
value1
value2
value3
)
1
2
3
4
5
6

还可以单独定义数组的各个分量:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
1
2
3

可以不使用连续的下标,而且下标的范围没有限制。
2. 读取数组
读取数组元素值的一般格式是:

${数组名[下标]}
1

例如:

valuen=${array_name[n]}
1

使用 @ 符号可以获取数组中的所有元素,例如:

echo ${array_name[@]}
1
  1. 获取数组的长度
    获取数组长度的方法与获取字符串长度的方法相同,例如:
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
1
2
3
4
5
6

# shell 注释

shell有单行注释和多行注释

# 单行注释

单行注释使用#号开头。

# 多行注释

多行注释可以使用以下格式:

:<<EOF
注释内容...
注释内容...
注释内容...
EOF
1
2
3
4
5

EOF 也可以使用其他符号:

:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!
1
2
3
4
5
6
7
8
9
10
11

# shell 运算符

  1. 基本语法
    1)“$((运算式))”或“$[运算式]”
    2)expr + , - , *, /, % 加,减,乘,除,取余
    注意:expr运算符间要有空格
  2. 案例实操:
    1)计算3+2的值
[root@hadoop101 datas]$ expr 2 + 3
5
1
2

2)计算3-2的值

[root@hadoop101 datas]$ expr 3 - 2 
1
1
2

3)计算(2+3)X4的值
a)expr一步完成计算

[root@hadoop101 datas]$ expr `expr 2 + 3` \* 4
20
1
2

b)采用$[运算式]方式

[root@hadoop101 datas]# S=$[(2+3)*4]
[root@hadoop101 datas]# echo $S
1
2

# shell 条件判断

  1. 基本语法
    [ condition ](注意condition前后要有空格)
    注意:条件非空即为true,[ root ]返回true,[] 返回false。
  2. 常用判断条件
    1)两个整数之间比较
    | 参数 | 说明 | | :--- | :------------- | | -eq | 等于则为真 | | -ne | 不等于则为真 | | -gt | 大于则为真 | | -ge | 大于等于则为真 | | -lt | 小于则为真 | | -le | 小于等于则为真 | 2)按照文件权限进行判断
-r 有读的权限(read)			-w 有写的权限(write)
-x 有执行的权限(execute)
1
2

3)按照文件类型进行判断

参数 说明
-e 文件名 如果文件存在则为真
-r 文件名 如果文件存在且可读则为真
-w 文件名 如果文件存在且可写则为真
-x 文件名 如果文件存在且可执行则为真
-s 文件名 如果文件存在且至少有一个字符则为真
-d 文件名 如果文件存在且为目录则为真
-f 文件名 如果文件存在且为普通文件则为真
-c 文件名 如果文件存在且为字符型特殊文件则为真
-b 文件名 如果文件存在且为块特殊文件则为真

4)字符串条件判断

参数 说明
= 等于则为真
!= 不相等则为真
-z 字符串 字符串的长度为零则为真
-n 字符串 字符串的长度不为零则为真

5)Shell test 命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。 3. 案例实操
1)23是否大于等于22

[root@hadoop101 datas]$ [ 23 -ge 22 ]
[root@hadoop101 datas]$ echo $?
0
1
2
3

2)helloworld.sh是否具有写权限

[root@hadoop101 datas]$ [ -w helloworld.sh ]
[root@hadoop101 datas]$ echo $?
0
1
2
3

3)/home/demo/cls.txt目录中的文件是否存在

[root@hadoop101 datas]$ [ -e /home/root/cls.txt ]
[root@hadoop101 datas]$ echo $?
1
1
2
3

4)多条件判断(&& 表示前一条命令执行成功时,才执行后一条命令,|| 表示上一条命令执行失败后,才执行下一条命令)

[root@hadoop101 ~]$ [ condition ] && echo OK || echo notok
OK
[root@hadoop101 datas]$ [ condition ] && [ ] || echo notok
notok
1
2
3
4

5)字符串测试(结合 test 命令)

num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
    echo '两个字符串相等!'
else
    echo '两个字符串不相等!'
fi
1
2
3
4
5
6
7
8

# shell 流程控制

# if 判断

  1. 基本语法
    if 语句语法格式:
if [ 条件判断式 ];then
 程序
fi

或者

if [ 条件判断式 ]
 then
  程序
fi
1
2
3
4
5
6
7
8
9
10

if else 语法格式:

if [ 条件判断式 ]

then
   程序 
else
   程序
fi
1
2
3
4
5
6
7

if else-if else 语法格式:

if [ 条件判断式 ]

then
    程序
elif [ 条件判断式 ]

then 
    程序   
else
    程序
fi
1
2
3
4
5
6
7
8
9
10
11

注意事项:
1)[ 条件判断式 ],中括号和条件判断式之间必须有空格
2)if后要有空格

  1. 案例实操
    1)输入一个数字,如果是1,则输出banzhang zhen shuai,如果是2,则输出cls zhen mei,如果是其它,什么也不输出。
[root@hadoop101 datas]$ touch if.sh
[root@hadoop101 datas]$ vim if.sh
#!/bin/bash
if [ $1 -eq "1" ]
then
    echo "banzhang zhen shuai"
elif [ $1 -eq "2" ]
then
    echo "cls zhen mei"
fi

[root@hadoop101 datas]$ chmod 777 if.sh 
[root@hadoop101 datas]$ ./if.sh 1
banzhang zhen shuai
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# case 语句

  1. 基本语法
case $变量名 in 
 "值1")
  如果变量的值等于值1,则执行程序1 
  ;; 
 "值2") 
  如果变量的值等于值2,则执行程序2
  ;;
 …省略其他分支…
 *)
  如果变量的值都不是以上的值,则执行此程序
  ;;
esac
1
2
3
4
5
6
7
8
9
10
11
12

注意事项:

  1. case行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。
  2. 双分号“****;;****”表示命令序列结束,相当于java中的break。
  3. 最后的“*)”表示默认模式,相当于java中的default。
  1. 案例实操
    1)输入一个数字,如果是1,则输出banzhang,如果是2,则输出cls,如果是其它,输出renyao。
[root@hadoop101 datas]$ touch case.sh
[root@hadoop101 datas]$ vim case.sh
!/bin/bash
case $1 in
"1")
   echo "banzhang"
;;
"2")
   echo "cls"
;;
*)
   echo "renyao"
;;
esac

[root@hadoop101 datas]$ chmod 777 case.sh
[root@hadoop101 datas]$ ./case.sh 1
banzhang
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# for 循环

  1. 基本语法1
for (( 初始值;循环控制条件;变量变化 )) 

do
  程序
done
1
2
3
4
5
  1. 案例实操
    1)从1加到100
[root@hadoop101 datas]$ touch for1.sh
[root@hadoop101 datas]$ vim for1.sh
#!/bin/bash
s=0
for((i=0;i<=100;i++))
do
   s=$[$s+$i]
done
echo $s

[root@hadoop101 datas]$ chmod 777 for1.sh 
[root@hadoop101 datas]$ ./for1.sh 
"5050"
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 基本语法2
for 变量 in 值1 值2 值3… 
 do 
  程序 
 done
1
2
3
4
  1. 案例实操
    1)打印所有输入参数
[root@hadoop101 datas]$ touch for2.sh
[root@hadoop101 datas]$ vim for2.sh
#!/bin/bash
#打印数字
for i in $*
 do
   echo "ban zhang love $i "
 done

[root@hadoop101 datas]$ chmod 777 for2.sh 
[root@hadoop101 datas]$ bash for2.sh cls xz bd
ban zhang love cls
ban zhang love xz
ban zhang love bd
1
2
3
4
5
6
7
8
9
10
11
12
13
14

2)比较$*和$@区别
a)$*和$@都表示传递给函数或脚本的所有参数,不被双引号“”包含时,都以$1 $2 …$n的形式输出所有参数。

[root@hadoop101 datas]$ touch for.sh
[root@hadoop101 datas]$ vim for.sh
#!/bin/bash 
for i in $*
do
      echo "ban zhang love $i "
done

for j in $@
do      
        echo "ban zhang love $j"
done

[root@hadoop101 datas]$ bash for.sh cls xz bd
ban zhang love cls 
ban zhang love xz 
ban zhang love bd 
ban zhang love cls
ban zhang love xz
ban zhang love bd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

b)当它们被双引号“ ”包含时,“$*”会将所有的参数作为一个整体,以“$1 $2 …$n”的形式输出所有参数;“$@”会将各个参数分开,以“$1” “$2”…”$n”的形式输出所有参数。

[root@hadoop101 datas]$ vim for.sh
#!/bin/bash 

for i in "$*" 
#$*中的所有参数看成是一个整体,所以这个for循环只会循环一次 
do 
	echo "ban zhang love $i"
done 

for j in "$@" 
#$@中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次 
do 
	echo "ban zhang love $j" 
done

[root@hadoop101 datas]$ chmod 777 for.sh
[root@hadoop101 datas]$ bash for.sh cls xz bd
ban zhang love cls xz bd
ban zhang love cls
ban zhang love xz
ban zhang love bd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# while 循环

  1. 基本语法
while [ 条件判断式 ] 
do 
    程序
done
1
2
3
4

无限循环语法格式:

while :
do
    程序
done
1
2
3
4

或者

while true
do
    程序 
done
1
2
3
4

或者

for (( ; ; ))
1
  1. 案例实操
    1)从1加到100
[root@hadoop101 datas]$ touch while.sh
[root@hadoop101 datas]$ vim while.sh
#!/bin/bash

s=0
i=1
while [ $i -le 100 ]
do
   s=$[$s+$i]
   i=$[$i+1]
done
echo $s

[root@hadoop101 datas]$ chmod 777 while.sh 
[root@hadoop101 datas]$ ./while.sh 
5050
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# until 循环

until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until 语法格式:

until  [ 条件判断式 ] 
do
     程序
done
1
2
3
4

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
以下实例我们使用 until 命令来输出 0 ~ 9 的数字:

a=0

until [ ! $a -lt 10 ]
do
   echo $a
   a=`expr $a + 1`
done
1
2
3
4
5
6
7

运行结果:

0
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
10

# read 读取控制台输入

1.基本语法

read(选项)(参数)
选项:
-p:指定读取值时的提示符;
-t:指定读取值时等待的时间(秒)。
参数
   变量:指定读取值的变量名
1
2
3
4
5
6
  1. 案例实操
    1)提示7秒内,读取控制台输入的名称
[root@hadoop101 datas]$ touch read.sh
[root@hadoop101 datas]$ vim read.sh
#!/bin/bash

read -t 7 -p "Enter your name in 7 seconds " NAME
echo $NAME

[root@hadoop101 datas]$ ./read.sh 
Enter your name in 7 seconds xiaoze
xiaoze
1
2
3
4
5
6
7
8
9
10

# shell 函数

# 系统函数

# basename

  1. 功能
    返回完整路径最后/的后面部分,常用于获取文件名。
  2. 基本语法
    basename [string / pathname] [suffix]
    选项:
    suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉。
  3. 案例实操
    1)截取该/home/root/banzhang.txt路径的文件名称
[root@hadoop101 datas]$ basename /home/root/banzhang.txt 
banzhang.txt
[root@hadoop101 datas]$ basename /home/root/banzhang.txt .txt
banzhang
1
2
3
4

# dirname

  1. 功能
    从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)
  2. 基本语法
    dirname 文件绝对路径
  3. 案例实操
    获取banzhang.txt文件的路径
[root@hadoop101 ~]$ dirname /home/root/banzhang.txt 
/home/root
1
2

# 自定义函数

  1. 基本语法
[ function ] funname[()]
{
	Action;
[return int;]
}
funname
1
2
3
4
5
6
  1. 经验技巧
    1)必须在调用函数地方之前,先声明函数,shell脚本是逐行运行。不会像其它语言一样先编译。
    2)函数返回值,只能通过$?系统变量获得,可以显示加:return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)
    3.案例实操
    1)计算两个输入参数的和
[root@hadoop101 datas]$ touch fun.sh
[root@hadoop101 datas]$ vim fun.sh
#!/bin/bash

function sum()
{
    s=0
    s=$[ $1 + $2 ]
    echo "$s"
}

read -p "Please input the number1: " n1;
read -p "Please input the number2: " n2;
sum $n1 $n2;

[root@hadoop101 datas]$ chmod 777 fun.sh
[root@hadoop101 datas]$ ./fun.sh 
Please input the number1: 2
Please input the number2: 5
7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
带参数的函数示例:

funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
1
2
3
4
5
6
7
8
9
10

输出结果:

第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
1
2
3
4
5
6
7

# Shell 输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
重定向命令列表如下:

命令 说明
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

# shell 重重定向

# shell 输入重定向

和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:

command1 < file1
1

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
注意:输出重定向是大于号(>),输入重定向是小于号(< 实例:
接着以上实例,我们需要统计 users 文件的行数,执行以下命令:

$ wc -l users
       2 users
1
2

也可以将输入重定向到 users 文件:

$  wc -l < users
       2 
1
2

注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

command1 < infile > outfile
1

同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。

# shell 输出重定向

重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

command1 > file1
1

上面这个命令执行command1然后将输出的内容存入file1。
注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
实例:
执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):

$ who > users
1

执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
你可以使用 cat 命令查看文件内容:

$ cat users
_mbsetupuser console  Oct 31 17:35 
tianqixin    console  Oct 31 17:35 
tianqixin    ttys000  Dec  1 11:33 
1
2
3
4

输出重定向会覆盖文件内容,请看下面的例子:

$ echo "数媒实验室" > users
$ cat users
数媒实验室
$
1
2
3
4

如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:

$ echo "数媒实验室" >> users
$ cat users
数媒实验室
数媒实验室
$
1
2
3
4
5

# shell 重定向深入理解

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
    默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
    如果希望 stderr 重定向到 file,可以这样写:
$ command 2>file
1

如果希望 stderr 追加到 file 文件末尾,可以这样写:

$ command 2>>file
1

提示

2 表示标准错误文件(stderr)。

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

$ command > file 2>&1
或者
$ command >> file 2>&1
1
2
3

如果希望对 stdin 和 stdout 都重定向,可以这样写:

$ command < file1 >file2
1

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

# Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
它的基本的形式如下:

command << delimiter
    document
delimiter
1
2
3

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:

  • 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
  • 开始的delimiter前后的空格会被忽略掉。

实例:
在命令行中通过 wc -l 命令计算 Here Document 的行数:

$ wc -l << EOF
    欢迎
    来到
	数媒实验室
EOF
3          # 输出结果为 3 行
$
1
2
3
4
5
6
7

我们也可以将 Here Document 用在脚本中,例如:

#!/bin/bash

cat << EOF
欢迎来到
数媒实验室
EOF
1
2
3
4
5
6

执行以上脚本,输出结果:

欢迎来到
数媒实验室
1
2

# /dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null
1

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1
1

**注意:**0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。 这里的 2> 之间不可以有空格,2> 是一体的时候才表示错误输出。

# Shell工具

# cut

cut的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。

  1. 基本用法
cut [选项参数]  filename
1

说明:默认分隔符是制表符
2. 选项参数说明

选项参数 功能
-f 列号,提取第几列
-d 分隔符,按照指定分隔符分割列
  1. 案例实操
    1)数据准备
[root@hadoop101 datas]$ touch cut.txt
[root@hadoop101 datas]$ vim cut.txt
dong shen
guan zhen
wo  wo
lai  lai
le  le
1
2
3
4
5
6
7

2)切割cut.txt第一列

[root@hadoop101 datas]$ cut -d " " -f 1 cut.txt 
dong
guan
wo
lai
le
1
2
3
4
5
6

3)切割cut.txt第二、三列

[root@hadoop101 datas]$ cut -d " " -f 2,3 cut.txt 
shen
zhen
 wo
 lai
 le
1
2
3
4
5
6

4)在cut.txt文件中切割出guan

[root@hadoop101 datas]$ cat cut.txt | grep "guan" | cut -d " " -f 1
guan
1
2

5)选取系统PATH变量值,第2个“:”开始后的所有路径:

[root@hadoop101 datas]$ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/root/bin

[root@hadoop102 datas]$ echo $PATH | cut -d: -f 2-
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/root/bin
1
2
3
4
5

6)切割ifconfig 后打印的IP地址

[root@hadoop101 datas]$ ifconfig eth0 | grep "inet addr" | cut -d: -f 2 | cut -d" " -f1
192.168.1.102
1
2

# sed

sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的
内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。

  1. 基本用法
sed [参数]  'command'  filename
1
  1. 参数说明 | 选项参数 | 功能 | | -------- | ------------------------------------- | | -e | 直接在指令列模式上进行sed的动作编辑。 |
  2. 命令功能描述 | 命令 | 功能描述 | | ---- | ------------------------------------- | | a | 新增,a的后面可以接字串,在下一行出现 | | d | 删除 | | s | 查找并替换 |
  3. 案例实操 1)数据准备
[root@hadoop102 datas]$ touch sed.txt
[root@hadoop102 datas]$ vim sed.txt
dong shen
guan zhen
wo  wo
lai  lai

le  le
1
2
3
4
5
6
7
8

2)将“mei nv”这个单词插入到sed.txt第二行下,打印。

[root@hadoop102 datas]$ sed '2a mei nv' sed.txt 
dong shen
guan zhen
mei nv
wo  wo
lai  lai

le  le
[root@hadoop102 datas]$ cat sed.txt 
dong shen
guan zhen
wo  wo
lai  lai

le  le
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

注意:文件并没有改变
3)删除sed.txt文件所有包含wo的行

[root@hadoop102 datas]$ sed '/wo/d' sed.txt
dong shen
guan zhen
lai  lai

le  le
1
2
3
4
5
6

4)将sed.txt文件中wo替换为ni

[root@hadoop102 datas]$ sed 's/wo/ni/g' sed.txt 
dong shen
guan zhen
ni  ni
lai  lai

le  le
1
2
3
4
5
6
7

注意:‘g’表示global,全部替换
5)将sed.txt文件中的第二行删除并将wo替换为ni

[root@hadoop102 datas]$ sed -e '2d' -e 's/wo/ni/g' sed.txt 
dong shen
ni  ni
lai  lai

le  le
1
2
3
4
5
6

# awk

一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。

  1. 基本用法 awk [参数] ‘pattern1{action1} pattern2{action2}...’ filename
    pattern:表示AWK在数据中查找的内容,就是匹配模式
    action:在找到匹配内容时所执行的一系列命令
  2. 参数说明 | 选项参数 | 功能 | | -------- | -------------------- | | -F | 指定输入文件折分隔符 | | -v | 赋值一个用户定义变量 |
  3. 案例实操 1)数据准备
[root@hadoop102 datas]$ sudo cp /etc/passwd ./
1

2)搜索passwd文件以root关键字开头的所有行,并输出该行的第7列。

[root@hadoop102 datas]$ awk -F: '/^root/{print $7}' passwd 
/bin/bash
1
2

3)搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分割。

[root@hadoop102 datas]$ awk -F: '/^root/{print $1","$7}' passwd 
root,/bin/bash
1
2

注意:只有匹配了pattern的行才会执行action
4)只显示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名user,shell在最后一行添加"dahaige,/bin/zuishuai"。

[root@hadoop102 datas]$ awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
user, shell
root,/bin/bash
bin,/sbin/nologin
。。。
root,/bin/bash
dahaige,/bin/zuishuai
1
2
3
4
5
6
7

注意:BEGIN 在所有数据读取行之前执行;END 在所有数据执行之后执行。
5)将passwd文件中的用户id增加数值1并输出

[root@hadoop102 datas]$ awk -v i=1 -F: '{print $3+i}' passwd
1
2
3
4
1
2
3
4
5
  1. awk的内置变量 | 变量 | 说明 | | -------- | -------------------------------------- | | FILENAME | 文件名 | | NR | 已读的记录数 | | NF | 浏览记录的域的个数(切割后,列的个数) |

  2. 案例实操
    1)统计passwd文件名,每行的行号,每行的列数

[root@hadoop102 datas]$ awk -F: '{print "filename:"  FILENAME ", linenumber:" NR  ",columns:" NF}' passwd 
filename:passwd, linenumber:1,columns:7
filename:passwd, linenumber:2,columns:7
filename:passwd, linenumber:3,columns:7
1
2
3
4

2)切割IP

[root@hadoop102 datas]$ ifconfig eth0 | grep "inet addr" | awk -F: '{print $2}' | awk -F " " '{print $1}' 
192.168.1.102
1
2

3)查询sed.txt中空行所在的行号

[root@hadoop102 datas]$ awk '/^$/{print NR}' sed.txt 
5
1
2

# sort

sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出。

  1. 基本语法
    sort(选项)(参数)
    | 选项 | 说明 | | ---- | ------------------------ | | -n | 依照数值的大小排序 | | -r | 以相反的顺序来排序 | | -t | 设置排序时所用的分隔字符 | | -k | 指定需要排序的列 | 参数:指定待排序的文件列表
  2. 案例实操
    1)数据准备
[root@hadoop102 datas]$ touch sort.sh
[root@hadoop102 datas]$ vim sort.sh 
bb:40:5.4
bd:20:4.2
xz:50:2.3
cls:10:3.5
ss:30:1.6
1
2
3
4
5
6
7

2)按照“:”分割后的第三列倒序排序。

[root@hadoop102 datas]$ sort -t : -nrk 3  sort.sh 
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6
1
2
3
4
5
6

# 正则表达式

# 正则表达式简介

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。
许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。
正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。
支持正则表达式的程序如:locate |find| vim| grep| sed |awk

# 正则元字符一栏表

元字符:在正则中,具有特殊意义的专用字符,如: 星号(*)、加号(+)等
前导字符:元字符前面的字符叫前导字符

元字符 功能 示例
* 前导字符出现0次或者连续多次 ab* abbbb
. 除了换行符以外,任意单个字符 ab. ab8 abu
.* 任意长度的字符 ab.* adfdfdf
[] 括号里的任意单个字符或一组单个字符 [abc][0-9][a-z]
[^] 不匹配括号里的任意单个字符或一组单个字符 [^abc]
[3] 匹配以括号里的任意单个字符开头 [4]
^[^] 不匹配以括号里的任意单个字符开头
^ 行的开头 ^root
$ 行的结尾 bash$
^$ 空行
{n}和{n} 前导字符连续出现n次 [0-9]{3}
{n,}和{n,} 前导字符至少出现n次 [a-z]{4,}
{n,m}和{n,m} 前导字符连续出现n-m次 go{2,4}
<> 精确匹配单词 <hello>
() 保留匹配到的字符 (hello)
+ 前导字符出现1次或者多次 [0-9]+
? 前导字符出现0次或者1次 go?
| ^root|^ftp
() 组字符 (hello|world)123
\d perl内置正则 grep -P \d+
\w 匹配字母数字下划线

# 正则表达式名词

  • 元字符
    指那些在正则表达式中具有特殊意义的专用字符,如:点(.) 星(*) 问号(?)等
  • 前导字符 位于元字符前面的字符. abc* aooo.

# 常用元字符

元字符 功能 备注
. 匹配除了换行符以外的任意单个字符
* 前导字符出现0次或连续多次
.* 任意长度字符 ab.*
^ 行首(以...开头) ^root
$ 行尾(以...结尾) bash$
^$ 空行
[] 匹配括号里任意单个字符或一组单个字符 [abc]
[^] 匹配不包含括号里任一单个字符或一组单个字符 [^abc]
[1] 匹配以括号里任意单个字符或一组单个字符开头 [2]
^[^] 匹配不以括号里任意单个字符或一组单个字符开头 ^[^abc]
  • 示例文本
# cat 1.txt
ggle
gogle
google
gooogle
goooooogle
gooooooogle
taobao.com
taotaobaobao.com

jingdong.com
dingdingdongdong.com
10.1.1.1
Adfjd8789JHfdsdf/
a87fdjfkdLKJK
7kdjfd989KJK;
bSKJjkksdjf878.
cidufKJHJ6576,

hello world
helloworld yourself
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 举例说明

1

# 其他元字符

元字符 功能 备注
< 取单词的头
> 取单词的尾
< > 精确匹配
{n} 匹配前导字符连续出现n次
{n,} 匹配前导字符至少出现n次
{n,m} 匹配前导字符出现n次与m次之间
( ) 保存被匹配的字符
\d 匹配数字(grep -P [0-9]
\w 匹配字母数字下划线(grep -P [a-zA-Z0-9_]
\s 匹配空格、制表符、换页符(grep -P [\t\r\n]

举例说明

需求:将10.1.1.1替换成10.1.1.254

1)vim编辑器支持正则表达式
# vim 1.txt
:%s#\(10.1.1\).1#\1.254#g 
:%s/\(10.1.1\).1/\1.254/g 

2)sed支持正则表达式【后面学】
# sed -n 's#\(10.1.1\).1#\1.254#p' 1.txt
10.1.1.254

说明:
找出含有10.1.1的行,同时保留10.1.1并标记为标签1,之后可以使用\1来引用它。
最多可以定义9个标签,从左边开始编号,最左边的是第一个。


需求:将helloworld yourself 换成hellolilei myself

# vim 1.txt
:%s#\(hello\)world your\(self\)#\1lilei my\2#g

# sed -n 's/\(hello\)world your\(self\)/\1lilei my\2/p' 1.txt 
hellolilei myself

# sed -n 's/helloworld yourself/hellolilei myself/p' 1.txt 
hellolilei myself
# sed -n 's/\(hello\)world your\(self\)/\1lilei my\2/p' 1.txt 
hellolilei myself

Perl内置正则:
\d      匹配数字  [0-9]
\w      匹配字母数字下划线[a-zA-Z0-9_]
\s      匹配空格、制表符、换页符[\t\r\n]

# grep -P '\d' 1.txt
# grep -P '\w' 2.txt
# grep -P '\s' 3.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
35
36
37

# 扩展类常用元字符

  • grep你要用我,必须加 -E 或者 让你兄弟egrep来找我
  • sed你要用我,必须加 -r
扩展元字符 功能 备注
+ 匹配一个或多个前导字符 bo+ 匹配boo、 bo
? 匹配零个或一个前导字符 bo? 匹配b、 bo
| 匹配a或b
() 组字符(看成整体) (my|your)self:表示匹配myself或匹配yourself
{n} 前导字符重复n次
{n,} 前导字符重复至少n次
{n,m} 前导字符重复n到m次

举例说明:

# grep "root|ftp|adm" /etc/passwd
# egrep "root|ftp|adm" /etc/passwd
# grep -E "root|ftp|adm" /etc/passwd

# grep -E 'o+gle' test.txt 
# grep -E 'o?gle' test.txt 

# egrep 'go{2,}' 1.txt
# egrep '(my|your)self' 1.txt

使用正则过滤出文件中的IP地址:
# grep '[0-9]\{2\}\.[0-9]\{1\}\.[0-9]\{1\}\.[0-9]\{1\}' 1.txt 
10.1.1.1
# grep '[0-9]{2}\.[0-9]{1}\.[0-9]{1}\.[0-9]{1}' 1.txt 
# grep -E '[0-9]{2}\.[0-9]{1}\.[0-9]{1}\.[0-9]{1}' 1.txt 
10.1.1.1
# grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' 1.txt 
10.1.1.1
# grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' 1.txt 
10.1.1.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

第二类正则

表达式 功能 示例
[:alnum:] 字母与数字字符 [[:alnum:]]+
[:alpha:] 字母字符(包括大小写字母) [[:alpha:]]{4}
[:blank:] 空格与制表符 [[:blank:]]*
[:digit:] 数字 [[:digit:]]?
[:lower:] 小写字母 [[:lower:]]{4,}
[:upper:] 大写字母 [[:upper:]]+
[:punct:] 标点符号 [[:punct:]]
[:space:] 包括换行符,回车等在内的所有空白 [[:space:]]+
[root@server shell05]# grep -E '^[[:digit:]]+' 1.txt
[root@server shell05]# grep -E '^[^[:digit:]]+' 1.txt
[root@server shell05]# grep -E '[[:lower:]]{4,}' 1.txt
1
2
3

正则表达式总结

  1. 我要找什么?
    • 找数字 [0-9]
    • 找字母 [a-zA-Z]
    • 找标点符号 [[:punct:]]
  2. 我要如何找?看心情找
    • 以什么为首 ^key
    • 以什么结尾 key$
    • 包含什么或不包含什么 [abc] ^[abc] [^abc] ^[^abc]
  3. 我要找多少呀?
    • 找前导字符出现0次或连续多次 ab==*==
    • 找任意单个(一次)字符 ab==.==
    • 找任意字符 ab==.*==
    • 找前导字符连续出现几次 {n} {n,m} {n,}
    • 找前导字符出现1次或多次 go==+==
    • 找前到字符出现0次或1次 go==?==