Linux Shell
文章目录
1.Shell概述
Shell是一个命令行解释器,他接受应用程序/用户命令,然后调用操作系统内核。
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell还是一个功能强大的编程语言,易编写、易调试、灵活性强。
2.Shell脚本
- 脚本格式
脚本以**#!/bin/bash**开头(指定解析器)
- 第一个Shell脚本
#!/bin/bash
echo "hello.world"
-
脚本的常用执行方式
- 采用bash或者sh 加脚本的相对路径或者绝对路径(脚本不用x权限)本质是bash解析器帮助执行
-
直接输入脚本的绝对路径或者相对路径。本质自己执行,所以需要权限
- 这个需要赋予x权限
- 在脚本的路径前面加".“或者是"source”。
前两种方法是通过打开一个子Shell来执行脚本,当脚本内容结束,则子Shell关闭,回到付父Shell中去。第三种不需要打开子Shell。子Shell开否,区别在于环境变量的继承关系。
3.变量
(1)系统预定义变量
- 常用系统变量
$HOME --当前家目录
$PWD --当前工作目录
$SHELL --当前编辑器
$USER --当前用户
-
案例
- 查看系统变量的值
- 系统所有全局变量 --env | less或者是 printenv | less
- 显示当前Shell中的所有变量
(2)用户自定义变量
定义变量
your_name="mashibing"
注意,变量名和等号之间不能有空格,这和其他的编程语言不太一样。同时,变量名的命名须遵循如下规则:
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
- 在bash里,变量默认类型是字符串类型,无法直接进行数值运算。
- 局部变量升级全局变量 使用(export 变量名)
注意: 变量的值里如果有空格,要用单引号或者双引号。
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可。 推荐给所有变量加上花括号,这是个好的编程习惯。
只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
删除变量
语法:unset variable_name 删除变量名。 注意(变量被删除后不能再次使用,unset 命令不能删除只读变量。)
(3)特殊变量
- $n
功能描述: n为数字,$O代表该脚本名称,$1- 9 代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如 9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如 9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如{10}。
- $#
功能描述: 获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性)。
- $@, $*
-
$ * 功能描述: 这个变量代表命令行中所有的参数,*$把所有的参数看成一个整体。
-
$ @ 功能描述: 这个变量也代表命令行中所有的参数,不过**$@把每个参数区分对待**。
- $?
功能描述: 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。
4.运算符
- 基本语法
$((运算式)) 或者 $[运算式]
eg1: 计算2+3*4,并赋值给s
eg2:设计脚本计算两个变量
-
类型
- 算数运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
注意: 原生bash是不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用
expr 是一款表达式计算工具,使用它能完成表达式的求值操作
例如,两个数相加(注意使用的是反引号 ` 而不是单引号 ')
[root@localhost ~]# vim test.sh
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : $val"
[root@localhost ~]# ./test.sh
两数之和为 : 4
注意:
表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
完整的表达式要被 ` ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
算术运算符
环境如下:变量 a 为 10,变量 b 为 20
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | expr $a + $b 结果为 30。 |
- | 减法 | expr $a - $b 结果为 -10。 |
* | 乘法 | expr $a \* $b 结果为 200。 |
/ | 除法 | expr $b / $a 结果为 2。 |
% | 取余 | expr $b % $a 结果为 0。 |
= | 赋值 | a=$b 将把变量 b 的值赋给 a。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $a != $b ] 返回 true。 |
(注意: 条件表达式要放在方括号之间,并且要有空格,例如: [ a = = a== a==b] 是错误的,必须写成 [ $a == $b ])
注意:乘号(*)前边必须加反斜杠()才能实现乘法运算,也是转义字符
关系型运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
环境如下:变量 a 为 10,变量 b 为 20
运算符 | 说明 | 举例 |
---|---|---|
-eq (equal) | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne (not equal) | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt (greater than) | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt (less than) | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge (greater equal) | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
-le (less equal) | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
布尔运算符
环境如下:变量 a 为 10,变量 b 为 20
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
逻辑运算符
环境如下:变量 a 为 10,变量 b 为 20
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑的 OR | [[ $a -lt 100 |
字符串运算符
环境如下:变量 a 为 "abc",变量 b 为 "efg"
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n “$a” ] 返回 true。 |
$ | 检测字符串是否为空,不为空返回 true。 | [ $a ] 返回 true。 |
文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] 返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
-S: 判断某文件是否 socket。
-L: 检测文件是否存在并且是一个符号链接。
5.条件判断
- 基本语法
(1) test + 语句 (判断是true 为 0 ,false为1,与其他语言正好相反)
(2) [ condition ] –condition前后要有空格
注意: [ ]中内容非空即为true
- eg1:判断数值是否相等
- eg2 :判断文件的权限
- eg3:判断文件(-e 判断文件是否存在 -f 判断文件是否为普通文件file -d 判断文件是否为目录)
6.流程控制
sh的流程控制不可为空,在sh/bash里如果else分支没有语句执行,就不要写这个else。
(1)if else
if 的 语法:
#!/bin/bash
if condition
then
command1
command2
...
commandN
fi
# 也可以一行(适用于终端命令提示符)
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
if else 的 语法:
#!/bin/bash
if [condition]
then
command1
command2
...
commandN
else
command
fi
if else-if else 语法格式:
#!/bin/bash
if [condition1]
then
command1
elif [condition2]
then
command2
else
commandN
fi
- if单条件判断
(2)case
1)基本语法
case $变量名 in
"值 1")
│如果变量的值等于值1,则执行程序1
;;
"值 2")
如果变量的值等于值2,则执行程序2
;;
...省略其他分支...
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
注意事项:
(1) case行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。
(2)双分号“;;”表示命令序列结束,相当于java中的break。
(3)最后的“*)”表示默认模式,相当于java中的 default。
eg:
(3)for循环
- 基本语法1
for ((初始值;循环控制条件;变量变化))
do
程序
done
eg:从1加到100的和
- 基本语法2
for 变量 in 值1 值2 值3
do
程序
done
eg1:
eg2: $* 与 $@ 的区别
-
*$把所有的参数看成一个整体。
-
$@把每个参数区分对待。
(4)while循环
- 基本语法
while [ 条件判断式 ]
do
程序
done
eg:
7.read读取控制台输入
- 基本语法
read (选项) (参数)
选项 | |
---|---|
-p | 指定读取值时的提示符; |
-t | 指定读取值时等待的时间(秒)如果-t不加表示一直等待; |
参数 | |
---|---|
变量:指定读取时的变量名; |
8.函数
(1)系统函数
-
basename
-
基本语法
basename [string / pathname] [suffix]
功能描述: basename命令会删掉所有的前缀包括最后一个(/')字符,然后将字符串显示出来。
-
basename 可以理解为取路径里的文件名称。
选项:
suffix为后缀,如果suffix被指定了, basename会将pathname或string 中的suffix去掉。
eg:
使用函数前:
使用函数后:
- dirname
基本语法
dirname 文件绝对路径
功能描述: 从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)
dirname可以理解为取文件路径的绝对路径名称
eg:
(2)自定义函数
基本函数语法如下:
#!/bin/bash
[ function ] funname [()]
{
action;
[return int;]
}
参数说明:
1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、必须在调用函数地方之前,先声明函数,shell脚本是逐行运行。不会像其它语言一样先编译。
3、函数返回值,只能通过$?系统变量获得,可以显示加: return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值 n(O-255)。
eg:
9.正则表达式
正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在 Linux 中,grep,sed,awk 等文本处理工具都支持通过正则表达式进行模式匹配。
- 常规匹配
一串不包含特殊字符的正则表达式匹配它自己,例如
[root@localhost scripts]# cat /etc/passwd | grep fantianyu
fantianyu:x:1000:1000:fantianyu:/home/fantianyu:/bin/bash
就会匹配所有包含fantianyu的行。
- 常用特殊字
- 特殊字符 ^
匹配一行的开头,例如
[root@localhost scripts]# cat /etc/passwd | grep ^a
adm:x:3:4:adm:/var/adm:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
- 特殊字符 $
匹配一行的结尾,例如
[root@localhost scripts]# cat /etc/passwd | grep bash$
root:x:0:0:root:/root:/bin/bash
fantianyu:x:1000:1000:fantianyu:/home/fantianyu:/bin/bash
- 特殊字符 “.”
. 匹配任意一个字符,例如 r…t的意思是匹配r与t之间只有两个字符的数据
[root@localhost scripts]# cat daily_archive.sh | grep r..t
DEST=/root/archive/$FILE
[root@localhost scripts]# cat /etc/passwd | grep r..t
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
4.特殊字符’*’
不单独使用,和上一个字符连用,表示匹配上一个字符0次或多次,例如 ro*t --表示o出现任意多次
[root@localhost scripts]# cat /etc/passwd | grep ro*t
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
5.字符区间(中括号)[ ]
[ ] | 表示匹配某个范围内的一个字符 |
---|---|
[6,8] | 匹配6或者8 |
[0-9] | 匹配一个 0-9的数字 |
[0-9]* | 匹配任意长度的数字字符串 |
[a-z] | 匹配一个a-z之间的字符 |
[a-z]* | 匹配任意长度的字母字符串 |
[a-c,e-f] | 匹配a-c或者e-f之间的任意字符 |
[root@localhost scripts]# cat /etc/passwd | grep r[a,b]t
operator:x:11:0:operator:/root:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
[root@localhost scripts]# echo "rbtsdfsdfsd" | grep r[a,b]t
rbtsdfsdfsd
[root@localhost scripts]# echo "123123rbtsdfsdfsd" | grep r[a,b]t
123123rbtsdfsdfsd
[root@localhost scripts]# echo "123123rbtsdfsdfsd" | grep r[ab]t
123123rbtsdfsdfsd
[root@localhost scripts]# cat /etc/passwd | grep r[a-z]*t
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
libstoragemgmt:x:998:996:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin[root@localhost scripts]# cat /etc/passwd | grep r[a-z]*t
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
libstoragemgmt:x:998:996:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
setroubleshoot:x:994:991::/var/lib/setroubleshoot:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin6.
6.特殊字符 \
\ 表示转义,并不会单独使用。由于所有特殊字符都有其特定匹配模式,当我们想匹配某一特殊字符本身时(例如,我想找出所有包含’S’的行),就会碰到困难。此时我们就要将转义字符和特殊字符连用,来表示特殊字符本身,例如
[root@localhost scripts]# cat daily_archive.sh | grep '\$'
if [ $# -ne 1 ]
if [ -d $1 ]
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)
DATE=$(date +%y%m%d)
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE
tar -czf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]
echo "归档文件为:$DEST"
[root@localhost scripts]# cat daily_archive.sh | grep '/\$'
DEST=/root/archive/$FILE
tar -czf $DEST $DIR_PATH/$DIR_NAME
10.文本处理工具
- cut
cut的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。
基本用法
cut [选项参数] filename
说明:默认分隔符是制表符
选项参数 | 功能 |
---|---|
-f | 列号,提取第几列 |
-d | 分隔符,按照指定分隔符分隔列,默认是制表符”\t“ |
-c | 按字符进行切割 后加 n 表示取第几列 比如 -c 1 |
- eg:
- 数据准备 (vi cut.txt)
- 切割cut.txt第1列
- 切割第2,3列
- 指定切割
- 切割ifconfig 后打印的IP地址
- awk
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
- 基本用法
awk [选项参数] ‘/patern1/{actionl} /pattern2/ {action2}...’ filename
pattern:表示 awk在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
- 选项参数
选项参数 | 功能 |
---|---|
-F | 指定输入文件分隔符 |
-v | 赋值一个用户定义变量 |
- eg
- 搜索passwd文件以root关键字开头的所有行,并输出该行的第7列。
- 搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分割。
- 只显示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名user,shell在最后一行添加"dahaige,/bin/zuishuai’%。
[root@localhost scripts]# cat /etc/passwd | awk -F ":" 'BEGIN{print "user,shell"}{print $1","$7} END{print "end of file"}'
user,shell
root,/bin/bash
bin,/sbin/nologin
daemon,/sbin/nologin
adm,/sbin/nologin
lp,/sbin/nologin
sync,/bin/sync
shutdown,/sbin/shutdown
halt,/sbin/halt
mail,/sbin/nologin
operator,/sbin/nologin
games,/sbin/nologin
ftp,/sbin/nologin
nobody,/sbin/nologin
systemd-network,/sbin/nologin
dbus,/sbin/nologin
polkitd,/sbin/nologin
libstoragemgmt,/sbin/nologin
colord,/sbin/nologin
rpc,/sbin/nologin
saned,/sbin/nologin
saslauth,/sbin/nologin
abrt,/sbin/nologin
setroubleshoot,/sbin/nologin
rtkit,/sbin/nologin
pulse,/sbin/nologin
chrony,/sbin/nologin
unbound,/sbin/nologin
radvd,/sbin/nologin
tss,/sbin/nologin
usbmuxd,/sbin/nologin
geoclue,/sbin/nologin
qemu,/sbin/nologin
gluster,/sbin/nologin
gdm,/sbin/nologin
rpcuser,/sbin/nologin
nfsnobody,/sbin/nologin
gnome-initial-setup,/sbin/nologin
sshd,/sbin/nologin
avahi,/sbin/nologin
postfix,/sbin/nologin
ntp,/sbin/nologin
tcpdump,/sbin/nologin
fantianyu,/bin/bash
end of file
- 将passwd文件中的用户id增加数值1并输出
[root@localhost scripts]# cat /etc/passwd | awk -F ":" '{print $3}'
0
1
2
3
~~
89
38
72
1000
[root@localhost scripts]# cat /etc/passwd | awk -F ":" '{print $3+1}'
1
2
3
~~
90
39
73
1001
[root@localhost scripts]# cat /etc/passwd | awk -v i=2 -F ":" '{print $3+i}'
2
3
4
5
~~
91
40
74
1002
- awk内置变量
变量 | 说明 |
---|---|
FILENAME | 文件名 |
NR | 已读的记录数(行号) |
NF | 浏览记录的域的个数(切割后,列的个数) |
11.综合应用案例
- 归档文件
实际生产应用中,往往需要对重要数据进行归档备份。
需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/)﹐将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive下。
这里用到了归档命令: tar
后面可以加上-c选项表示归档,加上-z选项表示同时进行压缩,得到的文件后缀名为.tar.gz。
- 每天2点的时候归档备份
- 发送消息
我们可以利用Linux自带的mesg 和 write工具,向其它用户发送消息。
需求:实现一个向某个用户快速发送消息的脚本,输入用户名作为第一个参数,后面直接跟要发送的消息。脚本需要检测用户是否登录在系统中、是否打开消息功能,以及当前发送消息是否为空。
#!/bin/bash
# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $login_user ]
then
echo "$1 不在线!"
echo "脚本退出.."
exit
fi
# 查看用户是否开启消息
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本退出.."
exit
fi
# 确认是否有消息发送
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本退出.."
exit
fi
if [ -z $login_user ]
then
echo "$1 不在线!"
echo "脚本退出.."
exit
fi
#!/bin/bash
# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $login_user ]
then
echo "$1 不在线!"
echo "脚本退出.."
exit
fi
# 查看用户是否开启消息
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本退出.."
exit
fi
# 确认是否有消息发送
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本退出.."
exit
fi
# 从参数中获取要发送的消息
whole_msg=$(echo $* | cut -d " " -f 2-)
# 获取用户登录的终端
user_terminal=$(who grep -i -m 1 $1 | awk '{print $2}')
# 写入要发送的消息
echo $whole_msg | write $login_user $user_terminal
if [ $? != 0 ]
then
echo "发送失败!"
else
echo "发送成功!"
fi
exit