Shell编程学习

shell 概述

命令行解释器

硬件 -> Linux 内核 -> shell (cd, ls, …) -> 外层应用程序。

shell 解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
# Linux提供的shell解析器有:
[ec2-user@master bin]$ cat /bin/shell
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/usr/bin/tmux

[ec2-user@master bin]$ ll |grep bash
-rwxr-xr-x 1 root root 964608 Oct 31 2018 bash
lrwxrwxrwx 1 root root 10 Feb 26 2019 bashbug -> bashbug-64
-rwxr-xr-x 1 root root 6964 Oct 31 2018 bashbug-64
lrwxrwxrwx 1 root root 4 Feb 26 2019 sh -> bash

可以看出sh也用的bash解析器

shell 脚本入门

脚本格式,如下,开头指定解析器,不是注释

1
#!/bin/bash

初识shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[ec2-user@master test]$ touch helloworld.sh # 创建第一个shell脚本
[ec2-user@master test]$ vi helloworld.sh
#!/bin/bash
echo "hello world, ganshizhong"

## 执行脚本的三种方式
[ec2-user@master test]$ sh helloworld.sh
hello world, ganshizhong
[ec2-user@master test]$ bash helloworld.sh
hello world, ganshizhong
[ec2-user@master test]$ ./helloworld.sh
-bash: ./helloworld.sh: Permission denied
# 第三种要给权限,如下操作
[ec2-user@master test]$ ll
total 4
-rw-rw-r-- 1 ec2-user ec2-user 44 May 30 14:39 helloworld.sh
[ec2-user@master test]$ chmod 777 helloworld.sh # 赋予+x权限
[ec2-user@master test]$ ll
total 4
-rwxrwxrwx 1 ec2-user ec2-user 44 May 30 14:39 helloworld.sh
[ec2-user@master test]$ ./helloworld.sh
hello world, ganshizhong

例子

1
2
3
4
#!/bin/bash
cd /home/ec2-user/test/
touch test1.txt
echo "i love you" >> test1.txt

shell中的变量

1. 常用变量: $HOME, $PWD, $SHELL, $USER等

1
echo $HOME, $PWD, $SHELL, $USER

2. 自定义变量

  1. 基本语法
    • 定义变量:变量=值
    • 撤销变量:unset 变量
    • 声明静态变量: readonly变量,注意不能unset
  2. 变量定义规则
    • 变量可由字母、数字和下划线组成,但不能以数字开头,环境变量建议大写
    • 等号两侧不能有空格
    • 在bash中,变量默认类型都是字符串类型,无法直接进行数值计算
    • 变量的值如果有空格,需要使用双引号或单引号括起来
1
2
3
4
5
6
7
8
9
10
11
12
13
A=1
echo $A

unset A

readonly B=3
unset B # 不生效

D="I love gsz"
echo D

# 把变量提升到全局变量,供其他shell程序使用
export 变量名

3. 特殊变量

$n

  • 功能描述:n为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数需要用大括号,如 ${10}
1
2
3
4
5
6
7
8
9
10
[ec2-user@master test]$ vi parameter.sh
[ec2-user@master test]$ sh parameter.sh
#!/bin/bash
echo "$0"
echo "$1 $2 $3"
echo "$@"
echo "$*"
[ec2-user@master test]$ sh parameter.sh gsz a aa aaaa
parameter.sh
gsz a aa

$#

  • 功能描述:获取所有输入参数个数,常用循环

$*

  • 功能描述:表示命令行中所有的参数,并把所有参数看作一个整体

$@

  • 功能描述:表示命令行中所有的参数,但区分对待每个参数

$?

  • 功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0,则不正确。

运算符

基本语法

  • “$((运算式))” 或 “$[运算式]”
  • expr +,-,*,/,% 加减乘除取余

    expr运算符间要有空格

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [ec2-user@master test]$ expr 3 + 2
    5
    [ec2-user@master test]$ expr `expr 2 + 3` \* 4
    20
    [ec2-user@master test]$ expr `expr 2 + 3` \* 4
    20
    [ec2-user@master test]$ s=$[(2+3)*4]
    [ec2-user@master test]$ echo $s
    20

条件判断

  1. 基本语法

    • [ condition ]

      注意前后要有空格,条件非空即为true,[atguigu]返回true,[]返回false.

  2. 常用判断条件

    • 两个整数之间比较
      • =字符串比较
      • -lt 小于(less than)
      • -le 小于等于(less equal)
      • -eq 等于(equal)
      • -gt 大于(greater than)
      • -ge 大于等于(greater equal)
      • -ne 不等于(not equal)
    • 按照文件权限进行判断
      • -r 有读写权限(read)
      • -w 有写的权限(write)
      • -x 有执行的权限(execute)
    • 按照文件类型进行判断
      • -f 文件存在且是一个常规的文件(file)
      • -e 文件存在(existence)
      • -d 文件存在并且是一个目录(directory)
    • 多条件判断
      • && 表示前面一条执行成功,才执行后一条命令
      • || 表示上一条命令执行失败后,才执行下一条命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[ec2-user@master test]$ [23 -ge 22]
-bash: [23: command not found
[ec2-user@master test]$ [ 23 -ge 22 ]
[ec2-user@master test]$ echo $?
0
[ec2-user@master test]$ [ 23 -le 22 ]
[ec2-user@master test]$ echo $?
1
[ec2-user@master test]$ [ -w helloworld.sh ]
[ec2-user@master test]$ echo $?
0
[ec2-user@master test]$ [ 23 -ge 22 ] && echo ok
ok
[ec2-user@master test]$ [ 23 -ge 22 ] && [ ] || echo notok
notok

流程控制(重点)

if 判断

  1. 基本语法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    if [ 条件判断式子 ];then
    程序
    elif
    fi
    # 或
    if [ 条件判断式 ]
    then
    程序
    if

    if后必须有空格

  2. 例子
    1
    2
    3
    4
    5
    6
    #!/bin/bash
    if [ $1 -eq 1 ];then
    echo "ganshizhong is handsome"
    elif [ $1 -eq 2 ];then
    echo "ganshizhong"
    if

case 语句

  1. 基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
casein
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
  1. 例子
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
case $1 in
1)
echo "print 1"
;;
2)
echo "print 2"
;;
*)
echo "print other"
esac

for 循环

  1. 基本语法
1
2
3
4
for ((初始值;循环控制条件;变量变化))
do
程序
done
  1. 例子
1
2
3
4
5
6
7
#!/bin/bash
s=0
for ((i=1;i<=100;i++))
do
s=$[$s+$i]
done
echo $s
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
s=0
for i in $*
do
echo $i
done

[ec2-user@master test]$ sh for2.sh 1 2 23
1
2
23
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
s=0
for i in "$@"
do
echo $i
done

[ec2-user@master test]$ sh for2.sh "1 2 23"
1
2
23

此处应该注意’$*’和’$@’之间的区别

while循环

  1. 基本语法
1
2
3
4
while [ 条件判断式 ]
do
程序
done
  1. 例子
1
2
3
4
5
6
7
8
9
#/bin/bash
s=0
i=1
while [ $i -le 100 ]
do
s=$[$i+$s]
i=$[$i+1]
done
echo $s

条件中 只能用 -le 不能用 <=

read读取控制台输入

  1. 基本语法
1
2
3
4
5
6
read(选项)(参数)
选项:
-p: 指定读取值时的提示符
-t: 指定读取值时等待的时间(秒)
参数:
变量:指定读取值的变量名
  1. 例子
1
2
3
4
5
6
7
#/bin/bash
read -t 7 -p "Enter your name in 7 seconds: " NAME
echo $NAME

[ec2-user@master test]$ sh read.sh
Enter your name in 7 seconds: ganshizhong
ganshizhong

函数

  1. 系统函数basename 和 dirname
1
2
3
4
5
basename [string/pathname] [suffix] #获取文件名
[ec2-user@master test]$ basename ./read.sh
read.sh
[ec2-user@master test]$ basename ./read.sh .sh
read

中括号都是可选参数

1
2
3
4
dirname 文件绝对路径 # 获取路径
[ec2-user@master test]$ dirname /home/ec2-user/test/read.sh
/home/ec2-user/test

  1. 自定函数
1
2
3
4
5
6
[ function ] funname[()]
{
action;
[return int;]
}
funname

1)必须在调用之前声明函数,shell是逐行运行的,不会先编译

2)函数返回值,只能通过$?系统变量获得,可以显示加:return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)

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

function sum()
{
s=0;
s=$[$1+$2]
echo $s
}
read -p "input your parameter: " p1
read -p "input your parameter: " p2
sum $p1 $p2

[ec2-user@master test]$ sh sumfunc.sh
1
2
3

shell工具(重点)

cut

在文件中负责剪切数据。cut命令从文件的每一行剪切字节、字符和字段并输出。

  1. 基本用法
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
cut [选项参数] filename # 
-f: 列号,提取第几列
-d: 分隔符,按照指定分隔符分割列
[ec2-user@master test]$ cat cut.txt
a b
c d
ee ff
gg hh
[ec2-user@master test]$ cut -d " " -f 1,2 cut.txt
a b
c d
ee ff
gg
[ec2-user@master test]$ cat cut.txt |grep ff |cut -d " " -f 1
ee
## 切割环境变量
[ec2-user@master ~]$ echo $PATH
/opt/java/jdk1.8.0_271/bin:/opt/hadoop/hadoop-3.2.3/bin:/opt/hadoop/hadoop-3.2.3/sbin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/java/jdk1.8.0_271/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin
[ec2-user@master ~]$ echo $PATH| cut -d : -f 3-
/opt/hadoop/hadoop-3.2.3/sbin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/java/jdk1.8.0_271/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin
## 切割ip
[ec2-user@master ~]$ ifconfig eth0 |grep "inet "
inet 192.168.1.4 netmask 255.255.255.0 broadcast 192.168.1.255
[ec2-user@master ~]$ ifconfig eth0 |grep "inet "|cut -d "t" -f 2
192.168.1.4 ne
[ec2-user@master ~]$ ifconfig eth0 |grep "inet "|cut -d "t" -f 2| cut -d" " -f2
192.168.1.4

缺点,cut只能切一个字符,如果存在很多空格,就会很麻烦

sed

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

  1. 基本用法
1
2
3
4
5
6
7
sed [选项参数] "command" filename 
选项参数:
-e: 直接在指令列模式上进行sed的动作编辑
命令:
a: 新增,a的后面可以接字串,在下一行出现
d: 删除
s:查找并替换
  1. 案例
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
## 将"xiao ming"这个单词插入sed.txt第二行,打印。
[ec2-user@master ~]$ cat sed.txt
gan shizhong
g sz
[ec2-user@master ~]$ sed "1a xiao ming" sed.txt
gan shizhong
xiao ming
g sz
[ec2-user@master ~]$ cat sed.txt
gan shizhong
g sz
## 删除"g sz"
[ec2-user@master ~]$ sed "/sz/d" sed.txt
gan shizhong
[ec2-user@master ~]$ cat sed.txt
gan shizhong
g sz
## 替换"g sz" 为 "xiao ming"
[ec2-user@master ~]$ sed "s/g sz/xiao ming/g" sed.txt # g表示
gan shizhong
xiao ming
## 删除第1行,并将"g sz" 替换为"gan sz"
[ec2-user@master test]$ sed -e "1d" -e "s/g sz/gan sz/g" sed.txt
gan sz

awk

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

  1. 基本用法
    1
    2
    3
    4
    5
    6
    awk [选项参数] "pattern1{action1} pattern2{action2} ..." filename
    pattern: 表示AWK在数据中查找的内容,就是匹配模式
    action: 在找到匹配内容时所执行的一系列命令,基本用print
    选项参数
    -F: 指定输入文件分析分隔符
    -v: 复制一个用户定义变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo cp /etc/passwd ./
sudo chown ec2-user:ec2-user passwd
## 搜索passwd文件以root关键字开头的所有行,并输出改行的第七列
[ec2-user@master test]$ awk -F : '/^root/{print $7}' passwd
/bin/bash
[ec2-user@master test]$ awk -F : '/^root/{print $1,$7}' passwd
root /bin/bash
[ec2-user@master test]$ awk -F : '/^root/{print $1 $7}' passwd
root /bin/bash
[ec2-user@master test]$ awk -F : '/^root/{print $1","$7}' passwd
root,/bin/bash
## 只显示第一列和第七列,以逗号隔开,且在所有行前面添加列名user,shell在最后一行添加"gsz,/bin/gsz"
[ec2-user@master test]$ awk -F : 'BEGIN{print "user,shell"} {print $1","$7} END{print "gsz,/bin/gsz"}' passwd
## passwd中用户id增加数值1并输出
[ec2-user@master test]$ awk -F : -v i=1 '{print $3+i}' passwd

此处要用单引号,单引号里的内容不可转义

两个斜杠之间时正则表达式

BEGIN 是所有行前,END是所有行后

  1. awk 内置变量
1
2
3
FILENAME 文件名
NR 已读的记录数
NF 浏览记录的个数(切割后,列的个数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 统计passwd文件中,每行的行号,每行的列数
[ec2-user@master test]$ 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
...
## 切割IP
[ec2-user@master test]$ ifconfig eth0 |grep "inet "|awk -F t '{print $2}'|awk -F " " '{print$1}'
192.168.1.4
## 查询sed.txt文件空行所在的行号
[ec2-user@master test]$ cat sed.txt
gan shizhong

g sz
[ec2-user@master test]$ awk '/^$/{print NR}' sed.txt
2

sort

sort命令实在Linu非常有用,他将文件进行排序,并将排序结果标准输出

  1. 基本语法
1
2
3
4
5
sort (选项) (说明)
-n: 依照数值大小排序
-r: 以相反的顺序来排序
-t: 设置排序时所用的分隔字符
-k: 指定排序的列
1
2
3
4
5
6
7
8
9
10
11
12
[ec2-user@master test]$ cat sort.sh
bb:40:5.1
bd:30:4.3
xz:50:2.3
cls:10:3.5
ss:25:1.6
[ec2-user@master test]$ sort -t : -nrk 2 sort.sh
xz:50:2.3
bb:40:5.1
bd:30:4.3
ss:25:1.6
cls:10:3.5

企业真实面试题(重点)

1
2
3
4
5
6
7
# 求列和
[ec2-user@master test]$ cat chengji.txt
张三 40
李四 50
王五 60
[ec2-user@master test]$ cat chengji.txt | awk -F " " '{sum+=$2} END{print sum}'
150
1
2
3
4
5
6
7
8
# 检查文件是否存在
#!/bin/bash
if [ -f file.txt ]
then
echo "存在"
else
echo "不存在"
fi
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
# 排序
[ec2-user@master test]$ cat sort.txt
9
1
3
5
7
2
4
3
8
4
3
1
[ec2-user@master test]$ sort -n sort.txt |awk '{a+=$0;print$0}END{print "SUM="a}'
1
1
2
3
3
3
4
4
5
7
8
9
SUM=50

awk里的变量可以添加可以不添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查找当前文件夹中,所有的文本文件内容包含“gsz”的文件名称
[ec2-user@master test]$ grep -r "sz" /home/ec2-user/test
/home/ec2-user/test/sed.txt:g sz
[ec2-user@master test]$ grep -r "shizhong" /home/ec2-user/test
/home/ec2-user/test/helloworld.sh:echo "hello world, ganshizhong"
/home/ec2-user/test/if.sh: echo "ganshizhong is handsome"
/home/ec2-user/test/if.sh: echo "ganshizhong"
/home/ec2-user/test/sed.txt:gan shizhong
[ec2-user@master test]$ grep -r "shizhong" ./
./helloworld.sh:echo "hello world, ganshizhong"
./if.sh: echo "ganshizhong is handsome"
./if.sh: echo "ganshizhong"
./sed.txt:gan shizhong
[ec2-user@master test]$ grep -r "shizhong" /home/ec2-user/test |cut -d ":" -f 1
/home/ec2-user/test/helloworld.sh
/home/ec2-user/test/if.sh
/home/ec2-user/test/if.sh
/home/ec2-user/test/sed.txt

正则表达语法

字符 说明
\ 转义
^ 开始
$ 结尾
* 0次或多次匹配
+ 一次或多次匹配

shell编程题

Linux运维之批量下载指定网站的100个图片文件,并找出大于200KB的文件

1
2
3
4
5
6
#!/bin/bash
for i in {1..100}
do
wget http://down.fengge.com/img/$i.png
done
find ./ -name "*.png" -size +200K

一个文本文件info.txt的内容如下: aa,201 zz,502 bb,1 ee,42 每行都是按照逗号分隔,其中第二列都是数字,请对该文件按照第二列数字从大到小排列。

1
sort -t "," -k 2 info.txt -rn

查看当前Linux服务器是否监听80端口,如果在监听,请找出其进程ID,并结束该进程。

1
2
3
4
lsof -i:端口号
kill -9 pid
# 或
netstat -tunlp

curl命令最常用的参数就是-I,仅返回头部信息,使用HEAD请求,获取的结果如下

1
curl -I http://192.168.100.115