大数据技术分享 大数据技术分享
  • 数学基础
  • 数学进阶
  • 编程入门
  • 电子基础
  • 编译原理
  • 数据结构与算法
  • 软件工程
  • 体系结构
  • 操作系统
  • 计算机网络
  • 计算机图形学
  • 设计模式
  • 并行与分布式系统
  • 计算机系统安全
  • Java基础
  • Java集合
  • Java并发
  • Java虚拟机
  • Linux
  • Shell
  • JDBC
  • Maven
  • Nginx
  • Git
  • Web开发
  • Docker
  • Zookeeper
  • Scala
  • Sqoop
  • Flume
  • Kafka
  • 数据清洗
  • HDFS
  • MySQL
  • HBase
  • Redis
  • ClickHouse
  • Hadoop
  • Hive
  • Spark
  • Flink
  • Java面试题
  • 大数据面试题
  • Java项目
  • 大数据项目
  • 推荐系统
  • 机器学习
  • 深度学习
  • 闲聊
  • 读书笔记
归档
  • 数学基础
  • 数学进阶
  • 编程入门
  • 电子基础
  • 编译原理
  • 数据结构与算法
  • 软件工程
  • 体系结构
  • 操作系统
  • 计算机网络
  • 计算机图形学
  • 设计模式
  • 并行与分布式系统
  • 计算机系统安全
  • Java基础
  • Java集合
  • Java并发
  • Java虚拟机
  • Linux
  • Shell
  • JDBC
  • Maven
  • Nginx
  • Git
  • Web开发
  • Docker
  • Zookeeper
  • Scala
  • Sqoop
  • Flume
  • Kafka
  • 数据清洗
  • HDFS
  • MySQL
  • HBase
  • Redis
  • ClickHouse
  • Hadoop
  • Hive
  • Spark
  • Flink
  • Java面试题
  • 大数据面试题
  • Java项目
  • 大数据项目
  • 推荐系统
  • 机器学习
  • 深度学习
  • 闲聊
  • 读书笔记
归档
  • Linux

  • Shell

    • 90%的Shell脚本,一篇搞定!
      • Shell概述
      • Shell脚本入门
        • 脚本格式
        • 执行方式
      • 变量和运算符
        • 系统变量
        • 自定义变量
        • 特殊变量
        • 运算符
      • 条件判断
      • 流程控制
        • if判断
        • case语句
        • for循环
        • while循环
      • 函数
        • read读取控制台输入
        • 系统函数
        • 自定义函数
      • Shell工具
        • cut工具
        • awk工具
        • sort工具
      • 正则表达式
        • 常规匹配
        • 常用特殊字符
        • 其他特殊字符
  • JDBC

  • Maven

  • Nginx

  • Git

  • Web开发

  • Docker

  • Zookeeper

  • Scala

  • 其他技术
  • Shell
高飞
2020-12-19
目录

90%的Shell脚本,一篇搞定!

今天来学习下shell脚本的写法和常用语法。

# Shell概述

Shell位于Linux和外层应用程序之间,是一个命令行解释器,接收应用程序或用户的命令去调用操作系统的内核,同时Shell编程语言易编写、灵活性强。

Shell解析器:

[Robofly@node1 ~]$ cat /etc/shells
/bin/sh
/bin/bash    # CentOS默认解析器是bash
/usr/bin/sh  # sh -> bash
/usr/bin/bash
/bin/tcsh
/bin/csh
1
2
3
4
5
6
7

# Shell脚本入门

# 脚本格式

脚本以#!/bin/bash开头。

# 直接用echo打印
[Robofly@node1 datas]$ touch helloworld.sh
[Robofly@node1 datas]$ vim helloworld.sh
#!/bin/bash
echo "Hello World!"
# 用echo追加内容到文件
[Robofly@node1 datas]$ touch batch.sh
[Robofly@node1 datas]$ vim batch.sh
#!/bin/bash
cd /home/datas
touch cls.txt
echo "I love cls" >>cls.txt
1
2
3
4
5
6
7
8
9
10
11
12

# 执行方式

  1. 采用bash或sh+脚本的相对或绝对路径执行,此方式不用赋予脚本执行权限。
# sh脚本的相对路径,前提是进入脚本所在的目录
[Robofly@node1 datas]$ sh helloworld.sh
# sh脚本的绝对路径
[Robofly@node1 ~]$ sh /home/Robofly/datas/helloworld.sh
# bash脚本的相对路径,前提是进入到脚本所在的目录
[Robofly@node1 datas]$ bash helloworld.sh
# bash脚本的绝对路径
[Robofly@node1 ~]$ bash /home/Robofly/datas/helloworld.sh
1
2
3
4
5
6
7
8
  1. 采用脚本的相对或绝对路径执行,此方式必须具有可执行权限。
# 先赋予脚本执行权限
[Robofly@node1 ~]$ chmod +x helloworld.sh
# 相对路径执行脚本
[Robofly@node1 datas]$ ./helloworld.sh
# 绝对路径执行脚本
[Robofly@node1 ~]$ /home/Robofly/datas/helloworld.sh
1
2
3
4
5
6

注:第一种执行方法本质是bash解析器帮你执行脚本,所以脚本本身不需要执行权限,而第二种执行方法本质是脚本需要自己执行,所以需要执行权限。

# 变量和运算符

# 系统变量

# 常用的系统变量:$HOME $PWD $SHELL $USER等
[Robofly@node1 ~]$ echo $HOME # 查看系统变量的值
[Robofly@node1 ~]$ set # 显示当前Shell中所有的变量
1
2
3

# 自定义变量

[Robofly@node1 ~]$ A=5 # 定义变量A
[Robofly@node1 ~]$ A=8 # 给变量A重新赋值
[Robofly@node1 ~]$ unset A # 撤销变量A
[Robofly@node1 ~]$ readonly B=2 # 声明静态的变量B=2,不能unset
# bash中变量默认为字符串类型无法直接进行数值运算
[Robofly@node1 ~]$ C=1+2 
[Robofly@node1 ~]$ echo $C
1+2
# 变量的值如果有空格需要用双引号或单引号括起来
[Robofly@node1 ~]$ D="I love you"
# export+变量名可将变量提升为全局环境变量供其他shell程序使用
[Robofly@node1 ~]$ export B
1
2
3
4
5
6
7
8
9
10
11
12

# 特殊变量

# $n($0表示该脚本名称,$1-$9表示第一到第九个参数,10以上的参数需要用大括号包含${10})
[Robofly@node1 ~]$ touch para.sh
[Robofly@node1 ~]$ vim para.sh
#!/bin/bash 
echo "$0 $1 $2" # 双引号表示取变量的值,单引号不取变量的值,反引号(``)执行引号中的命令
[Robofly@node1 ~]$ chmod +x para.sh
[Robofly@node1 ~]$ ./para.sh cls xz
./para.sh cls xz
# $#(获取所有输入参数个数,常用于循环)
[Robofly@node1 ~]$ vim para2.sh
#!/bin/bash
echo "$0 $1 $2"
echo $# # 加不加双引号一样可输出参数个数,但加单引号依旧是输出字符
[Robofly@node1 ~]$ chmod +x para2.sh
[Robofly@node1 ~]$ ./para2.sh cls xz
./para2.sh cls xz 
2
# $*(命令行中所有的参数,把所有的参数看成一个整体)
# $@(也代表命令行中所有的参数,但是它把每个参数区分对待)
[Robofly@node1 ~]$ vim para3.sh
#!/bin/bash
echo "$0 $1 $2"
echo $#    echo $*    echo $@
[Robofly@node1 ~]$ bash para3.sh 1 2 3
./para3.sh 1 2
3    1 2 3    1 2 3
# $?(最后一次执行的命令的返回状态,如果这个变量的值为0表示上次命令正确执行,非0则表示上次命令执行不正确)
[Robofly@node1 datas]$ ./helloworld.sh
hello world
[Robofly@node1 datas]$ echo $?
0
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

# 运算符

# expr运算符间要有空格,没有空格作为字符串输出
[Robofly@node1 ~]$ expr 2 + 3 
# expr +,-,\*,/,% 加减乘除取余
[Robofly@node1 ~]$ expr `expr 2 + 3` \* 4
# $((运算式))或$[运算式]
[Robofly@node1 ~]$ s=$[(2+3)*4]
[Robofly@node1 ~]$ echo $s
1
2
3
4
5
6
7

# 条件判断

  1. 基本语法

[ 条件 ]:条件前后必须有空格。条件非空即为true,否则返回false。

常用判断条件:

-z:判断变量的值是否为空,空则返回0为true,非空返回1为false。

-n:判断变量的值是否为空,空则返回1为false,非空返回0为true。

  • 两个整数之间比较:

    -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. 举例
# 23是否大于等于22
[Robofly@node1 ~]$ [ 23 -ge 22 ]
[Robofly@node1 ~]$ echo $?
0
# helloworld.sh是否具有写权限
[Robofly@node1 ~]$ [ -w helloworld.sh ]
[Robofly@node1 ~]$ echo $?
0
# 目录中的文件是否存在
[Robofly@node1 ~]$ [ -e /home/Robofly/cls.txt ]
[Robofly@node1 ~]$ echo $?
1
# 多条件判断(&&表示前一条命令执行成功时才执行后一条命令,||表示前一条命令执行失败时才执行后一条命令)
[Robofly@node1 ~]$ [ condition ] && echo ok || echo notok
ok
[Robofly@node1 ~]$ [ condition ] && [  ] || echo notok
notok
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 流程控制

# if判断

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

注:中括号内的条件判断前后必须有空格,且if后必须要有空格。

  1. 举例
[Robofly@node1 ~]$ touch if.sh
[Robofly@node1 ~]$ vim if.sh
#!/bin/bash
if [ $1 -eq "1" ]
then
	echo "第一个参数为1"
elif [ $1 -eq "2" ]
then 
	echo "第一个参数为2"
fi
[Robofly@node1 ~]$ chmod +x if.sh
[Robofly@node1 ~]$ ./if.sh 1
1
2
3
4
5
6
7
8
9
10
11
12

# case语句

  1. 基本语法
case $变量名 in
"值1")
​    程序1
;;
"值2")
​    程序2
;;
*)
​    都不是执行此程序
;;
esac
1
2
3
4
5
6
7
8
9
10
11

注:case行尾必须为in,每一个模板必须以)结束,;;相当于break,*)相当于default。

  1. 举例
[Robofly@node1 ~]$ vim case.sh
#!/bin/bash
case $1 in
"1")
	echo "参数为1"
;;
"2")
	echo "参数为2"
;;
*)
	echo "参数即不为1也不为2"
esac
[Robofly@node1 ~]$ chmod +x case.sh
[Robofly@node1 ~]$ ./case.sh 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# for循环

  1. 基本语法
# 语法1
for((初始值;循环控制条件;变量变化))
	​do
		程序
​	done
# 语法2
for 变量 in 值1 值2 值3 ...
	do
		程序
	done
1
2
3
4
5
6
7
8
9
10
  1. 举例
# 从1加到100
[Robofly@node1 ~]$ vim for1.sh
#!/bin/bash
s=0
for((i=0;i<=100;i++))
	do
		s=$[$s+$i]
	done
echo $s
[Robofly@node1 ~]$ chmod +x for1.sh
[Robofly@node1 ~]$ ./for1.sh
# 打印所有输入参数
[Robofly@node1 ~]$ vim for2.sh
#!/bin/bash
# $*和$@都表示所有参数,不被""包含时都以$1$2...的形式输出所有参数
# 当它们被""包含时$*将参数作为一个整体以"$1$2..."输出,$@将参数分开以"$1""$2""..."形式输出
for i in $*
	do
		echo "第$i个参数"
	done
[Robofly@node1 ~]$ chmod +x for2.sh
[Robofly@node1 ~]$ ./for2.sh 1 2 3 4 5 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# while循环

  1. 基本语法
while [ 条件判断 ]
	do
		程序
	​done
1
2
3
4
  1. 举例
[Robofly@node1 ~]$ vim while.sh
# 从1加到100
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
	do
		s=$[$s+$i]
		i=$[$i+1]
	done
echo $s
[Robofly@node1 ~]$ chmod +x while.sh
[Robofly@node1 ~]$ ./while.sh
1
2
3
4
5
6
7
8
9
10
11
12
13

注:凡是条件判断的都要在[ ]内的条件判断前后加空格。

# 函数

# read读取控制台输入

  1. 基本语法
read (选项) (参数)
# 选项:-p指定读取值时的提示符;-t指定读取值时等待的时间(秒)
# 参数:变量-指定读取值的变量名
1
2
3
  1. 举例
[Robofly@node1 ~]$ vim read.sh
#!/bin/bash
read -t 7 -p "请在7秒内输入你的名字:" NAME
echo $NAME
[Robofly@node1 ~]$ chmod +x read.sh
[Robofly@node1 ~]$ ./read.sh
1
2
3
4
5
6

# 系统函数

# basename [string/pathname][suffix]截取路径的文件名称
# suffix为后缀,如果suffix被指定了则basename会将string或pathname中的suffix去掉
[Robofly@node1 ~]$ basename /home/Robofly/para.sh
para.sh
[Robofly@node1 ~]$ basename /home/Robofly/para.sh .sh
para
# dirname+文件绝对路径来获取文件的路径
[Robofly@node1 ~]$ dirname /home/Robofly/para.sh
/home/Robofly
1
2
3
4
5
6
7
8
9

# 自定义函数

  1. 基本语法
[ function ] funname[()]
{ 
action
[ return int; ]
}
1
2
3
4
5

注:必须在调用函数之前声明函数,因shell脚本是逐行运行的。函数返回值只能通过$?系统变量获得,可以显式的加return 返回,如果不加将以最后一条命令运行结果作为返回值,return后跟数值n(0-255)。

  1. 举例
[Robofly@node1 ~]$ vim fun.sh
#计算两个输入参数之和
#!/bin/bash
function sum()
{
	s=0
	s=$[$1+$2]
	echo "$s"
}
# 下面的语句可不加分号,但如果写成单行需要用分号进行区分,如果像现在这样写成块则相当于用换行符替代了分号,故可不加分号
read -p "请输入第一个参数:" n1; 
read -p "请输入第二个参数:" n2;
sum $n1 $n2;
[Robofly@node1 ~]$ chmod +x fun.sh
[Robofly@node1 ~]$ ./fun.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Shell工具

# cut工具

  1. 基本语法
# cut命令从文件的每一行剪切字节、字符和字段并将其输出
cut [ 选项参数 ] filename 
# 选项参数:-f:列号表提取第几列;-d:分隔符表按指定分隔符分割列;-c:指定具体的字符
1
2
3

注:默认的分隔符是制表符。

  1. 举例
# 数据准备
[Robofly@node1 ~]$ vim cut.txt
gao hang
fei zhou
wo  wo
lai  lai
le  le
# 按空格切割并提取出第一列
[Robofly@node1 ~]$ cut -d " " -f 1 cut.txt # 按空格切可理解为去掉一个空格后取第n列
# 从文件中切割出fei(从前往后执行的)
[Robofly@node1 ~]$ cat cut.txt | grep "fei" | cut -d " " -f 1
# 切出PATH值中第二个:开始后的所有路径
[Robofly@node1 ~]$ echo $PATH | cut -d : -f 3-
# 切出IP地址
[Robofly@node1 ~]$ ifconfig ens33 | grep netmask | cut -d "n" -f 2 | cut -d " " -f 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# awk工具

  1. 基本语法
# awk将文件逐行的读入,以空格为默认的分隔符将每行切开
awk [ 选项参数 ] 'pattern1{action1} pattern2{action2}...' filename
# 选项参数:-F:指定输入文件的分隔符;-v:赋值一个用户定义变量
# pattern:在数据中要查找的内容
# action:在找到匹配内容时所执行的一系列命令
# awk的内置变量:FILENAME-文件名,NR-已读的记录数,NF-浏览记录的域的个数(切割后的列个数)
1
2
3
4
5
6
  1. 举例
# 数据准备
[Robofly@node1 ~]$ sudo cp /etc/passwd ./
# 对passwd文件按:进行分割,找到以root开头的行并打印该行的第7列
[Robofly@node1 ~]$ awk -F : '/^root/{print $7}' passwd
# 对passwd文件按:进行分割,找到以root开头的行并打印该行的第1列和第7列且以','分割
[Robofly@node1 ~]$ awk -F : '/^root/{print $1","$7}' passwd # 只有匹配了pattern的行才会执行action
# 对passwd文件按:切割,找到第1列和第7列以","隔开,且在所有行前加user,shell在最后一行加end,/bin/hello
[Robofly@node1 ~]$ awk -F : 'BEGIN{print "user,shell"} {print $1","$7} END{print "end,/bin/hello"}' passwd # BEGIN在所有数据读取行之前执行,END在所有数据执行之后执行
# 将passwd文件中的用户id增加数值1并输出
[Robofly@node1 ~]$ awk -v i=1 -F : '{print $3+i}' passwd
# 统计passwd的文件名,每行的行号,每行的列数
[Robofly@node1 ~]$ awk -F : '{print "filename:"FILENAME ",linenumber:"NR ",columns:"NF}' passwd
1
2
3
4
5
6
7
8
9
10
11
12

# sort工具

  1. 基本语法
# sort命令在Linux里非常有用,它将文件进行排序并将排序结果标准输出。
sort (选项) (参数)
# 选项:-n:依照数值的大小排序;-r:以相反的顺序来排序;-t:设置排序时所用的分隔字符;-k:指定需要排序的列
# 参数:指定待排序的文件列表
1
2
3
4
  1. 举例
# 数据准备
[Robofly@node1 datas]$ vim sort.sh
aa:23:5.4
bb:67:4.3
cc:89:2.6
dd:12:3.8
ee:90:1.5
# 按照":"分割后的第三列倒序排序
[Robofly@node1 datas]$ sort -t : -nrk 3 sort.sh
aa:23:5.4
bb:67:4.3
dd:12:3.8
cc:89:2.6
ee:90:1.5
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 正则表达式

# 常规匹配

# 匹配所有包含root的行
[Robofly@node1 ~]$ cat passwd | grep root
1
2

# 常用特殊字符

[Robofly@node1 ~]$ cat passwd | grep ^a # 匹配出所有以a开头的行
[Robofly@node1 ~]$ cat passwd | grep t$ # 匹配出所有以t结尾的行
[Robofly@node1 ~]$ cat passwd | grep r..t # .匹配一个任意的字符
[Robofly@node1 ~]$ cat passwd | grep ro*t # 和上一个字符连用表示匹配上一个字符0次或多次
[Robofly@node1 ~]$ cat passwd | grep r[a,b,c]*t # 匹配[]中字符0次或多次
[Robofly@node1 ~]$ cat passwd | grep a\$b # 匹配所有包含a$b的行
1
2
3
4
5
6

# 其他特殊字符

太多不便列出,需要的公众号后台回复“正则表达式”获取。

以上就是今天的内容分享,下篇见。

🎁 公众号

小伙伴们大家好,上方扫码关注公众号「大数据技术开发」,与你分享我的成长历程与技术感悟~

Linux常用命令
JDBC连接数据库总结

← Linux常用命令 JDBC连接数据库总结→

最近更新
01
知识体系总览
02-27
02
读书笔记
02-21
03
深度学习
02-20
更多文章>
Theme by Vdoing | Copyright © 2019-2023 码农高飞 | 豫ICP备2021025399号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式