摘要:Linux
,Shell脚本
Shell 是一个用 C语言
编写的程序,它是用户使用 Linux 的桥梁
,用户通过Shell访问操作系统内核的服务。
编写和运行Shell脚本
创建一个文件扩展名为 sh(sh代表shell),扩展名并不影响脚本执行
# vim aaa.sh
#!/bin/bash
echo 1
#!
是一个约定的标记,置于解释器之前,/bin/bash
是解释器Bash的路径
有两种运行Shell脚本的方式,一种是将Shell脚本作为sh/bash
的命令行参数
或者解释器参数
,另一种是将Shell脚本作为具有执行权限
的可执行文件
。
直接作为命令行参数运行
root@ubuntu:~# bash aaa.sh
1
在ubuntu下sh是dash
的链接,dash和bash的略有不同
将Shell脚本赋予可执行权限,使用./Shell文件名
或者绝对路径Shell文件名
运行
root@ubuntu:~# chmod a+x aaa.sh
root@ubuntu:~# ./aaa.sh
1
root@ubuntu:~# /home/gp/aaa.sh
1
Linux文件权限详解:
- linux文件调用权限分为三级 :
文件所有者
(Owner)、用户组
(Group)、其它用户
(Other Users - 每一级分别由rwx来标示,称为
符号模式
,分别代表可读
,可写
,可执行
,相当于标示了用户和权限的交叉组合的每一种情况的值,有该权限则标为对应字母,无该权限则标为-
- 在三级之前有
d
或者-
,分别标示文件夹
和文件
- 只有文件的
所有者
和超级用户
可以修改文件或者目录的权限 - 除了rwx字母,还可以使用
数字权限
(8进制,从0到7),r=4
,w=2
,x=1
,如果赋予全部可读可写可执行权限,则数字为4+2+1=7,如果没有任何权限数值就是0,以此类推,比如下图分别用字母和数字模式表示该文件对于所有者可读可写可执行,对于用户组可读可执行,对于所有其他用户可读,数字模式分别是754
chmod修改权限
chmod修改文件权限分为三部分,对哪个用户
进行修改,增加还是取消
权限,增加或是取消什么权限
。
- 哪个用户:可选参数为
u
,g
,o
,a
,分别代表所有者
,所有者组
,其他用户
,所有用户
- 增加还是取消:可选参数是
+
,-
,=
,表示增加
权限,取消
权限,唯一设定
权限 - 什么权限:可选参数是
r
,w
,x
,分别标示可读,可写,可执行
也可以直接使用数字模式
,指定三个数字分别代表所有者,所有者组,其他用户,如果设定数字不足,先以右边为主,缺省的就是没有任何权限,即一个数字代表其他用户,两个数字代表所有者组和其他用户,直接设置数字相当于重置覆盖,不需要配合+,-使用
先用普通用户新建一个文件,文件初始对于所有者只有可读可写权限,对于其他用户和组内其他用户只有可读权限
# 新建一个文件
gp@ubuntu:~$ touch bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rw-r--r-- 1 gp gp 0 2月 10 17:41 bbb.sh
增加权限,使得其他所有用户都有可执行权限,此处a可以省略,+x
默认是对所有用户
gp@ubuntu:~$ chmod a+x bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rwxr-xr-x 1 gp gp 0 2月 10 17:41 bbb.sh*
使用数字模式赋予文件所有用户所有权限
gp@ubuntu:~$ chmod 777 bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rwxrwxrwx 1 gp gp 0 2月 10 17:41 bbb.sh*
赋予全部权限也可以使用符号模式,使用a=rwx
设定
gp@ubuntu:~$ chmod a=rwx bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rwxrwxrwx 1 gp gp 0 2月 10 17:45 bbb.sh*
取消其他用户的可写,可执行权限
gp@ubuntu:~$ ll bbb.sh gp@ubuntu:~$ chmod o-wx bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rwxrwxr-- 1 gp gp 0 2月 10 17:51 bbb.sh*
也可以直接使用数字模式,直接覆盖修改
gp@ubuntu:~$ chmod 774 bbb.sh
gp@ubuntu:~$ ll bbb.sh
-rwxrwxr-- 1 gp gp 0 2月 10 17:41 bbb.sh*
-R
参数:以递归的方式对目前目录下
的所有档案
与子目录
进行相同的权限变更,如果不加-R只有该文件夹修改,目录下的子目录权限没有修改
# 创建一个文件夹,再撞见2层子文件夹
drwxr-xr-x 3 gp gp 4096 2月 10 19:49 a/
gp@ubuntu:~$ tree a/
a/
└── aa
└── aaa
将文件夹下和所有子文件夹都设置为777权限
gp@ubuntu:~$ chmod -R 777 a
再修改回来
gp@ubuntu:~$ chmod -R 744 a
Shell变量
定义变量
Shell是弱类型
编程语言,不需要在定义变量的时候指定数据类型,默认数据类型都是字符串
,Shell 支持以下三种定义变量的方式:
variable=value
variable='value'
variable="value"
- 使用不带任何引号的方式,适合value中没有
任何空格
,tab
- 使用单引号或者双引号,适合value中存在空格tab的情况,其中单引号内的内容为
纯串
,不会对其中的命令,变量进行解析,而双引号会对其中的变量和命令
进行先解析替换
,再输出 - =号左右不能有空格
- 变量名由数字、字母、下划线组成,必须由字母或者下划线开头,不能带有Shell的关键字
使用变量
使用变量需要在变量名前面加美元符号$即可,变量名外面的花括号{ }
是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,和${}
类似的是$()
,前者用变量值替换变量名,后者用命令执行结果替换变量名,后者还可以使用反引号。
gp@ubuntu:~/shell$ cat test.sh
#!/bin/bash
a=qwe
b='as a'
c="se${b}"
d='se${b}'
e="se`pwd`"
f='se`pwd`'
g="se${b}aaa"
h="se$baaa"
echo $a
echo $b
echo $c
echo $d
echo $e
echo $f
echo $g
echo $h
打印结果
qwe #没有空格tab可以不加引号
as a
seas a
se${b} # 单引号是能使纯串,不能替换变量
se/home/gp/shell # 命令需要加反引号``,也要使用双引号才能替换
se`pwd`
seas aaaa
se # 没有花括号,会把后面的整个内容当成变量,但是变量找不到定义输出为空
只读变量readonly
已经赋值的变量可以直接调用变量名重新赋值修改覆盖,但是如果加了readonly
修饰就是只读变量。修改会报错
gp@ubuntu:~/shell$ vim test.sh
#!/bin/bash
a=1
a=2
readonly a
a=3
echo $a
gp@ubuntu:~/shell$ ./test.sh
./test.sh: line 5: a: readonly variable
2
第五行报错,不能重新赋值变量
删除变量unset
已经赋值的变量可以使用unset
删除
gp@ubuntu:~/shell$ vim test.sh
#!/bin/bash
a=1
unset a
echo $a
gp@ubuntu:~/shell$ ./test.sh
打印结果为空,变量已删除
Shell字符串
Shell中定义的变量默认的都是字符串形式,可以使用单引号,双引号,也可以不使用,字符串拼接可以使用以下几种方式
gp@ubuntu:~/shell$ a=123
gp@ubuntu:~/shell$ b="你好${a}" # 你好123
gp@ubuntu:~/shell$ c="你好"$a""
gp@ubuntu:~/shell$ e='你好'$a''
可以使用${#}
获取字符串长度
gp@ubuntu:~/shell$ d=${#a}
gp@ubuntu:~/shell$ echo $d # 输出字符串长度3
可以使用索引提取子串
,索引从0开始,如果超出越界了只输出到最后一个字符
gp@ubuntu:~/shell$ f=${a:1:3}
gp@ubuntu:~/shell$ echo $f # 输出23
Shell 数组
在 Shell 中,用圆括号
来表示数组,数组元素用空格
符号分割开,数组只支持一维数字
,不限定数组的大小,比如
gp@ubuntu:~/shell$ a=(1 2 aa 2.2 p_p)
可以使用[下标]
获取数组的值,使用[*]
或者[@]
获取数组所有的值
gp@ubuntu:~/shell$ echo ${a[2]} # aa
gp@ubuntu:~/shell$ echo ${a[*]} # 1 2 aa 2.2 p_p
获取数组长度的方法和字符串一样,使用{#}
gp@ubuntu:~/shell$ echo ${#a[*]} # 5
修改数组元素使用下标访问直接修改
gp@ubuntu:~/shell$ a[2]=cc
gp@ubuntu:~/shell$ echo ${a[*]}
1 2 cc 2.2 p_p
同理可以定义一个空数组,使用下标设定每个位置的值
vim a.sh
#!/bin/bash
a=()
a[0]=a
a[1]=b
a[2]=c
echo ${a[*]}
gp@ubuntu:~/shell$ chmod +x a.sh
gp@ubuntu:~/shell$ ./a.sh
a b c
可以使用元素的内置方法/
删除元素
gp@ubuntu:~/shell$ a=(1 2 3)
gp@ubuntu:~/shell$ echo ${a[*]/2}
1 3
这个方法可以用来判断数组中是否包含某个元素
gp@ubuntu:~/shell$ if [[ ${a[*]/2} != ${a[*]} ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [[ ${a[*]/5} != ${a[*]} ]]; then echo 1; else echo 2; fi;
2
也可以使用=~
来判断是否包含某元素
gp@ubuntu:~/shell$ if [[ ${a[*]} =~ 2 ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [[ ${a[*]} =~ 1 ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [[ ${a[*]} =~ 3 ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [[ ${a[*]} =~ 4 ]]; then echo 1; else echo 2; fi;
2
Shell脚本注释
对于单行注释使用#
,对于多行注释使用:>>
,前后增加一个任意的字符即可,比如BLOCK,!,EOF等,比如
:<
# vim a.sh
#!/bin/bash
a=()
a[0]=a
a[1]=b
#a[2]=c
echo ${a[*]}
a[2]=c
:<
gp@ubuntu:~/shell$ ./a.sh
a b
a b c
向Shell脚本传递外部参数
外部参数直接挂在启动Shell脚本命令的后面,多个参数用空格隔开
,自带空格的变量用单引号或者双引号,在Shell脚本内使用$+索引
直接访问
vim a.sh
#!/bin/bash
echo "file name is $0"
echo "first is ${1}"
echo "second is $2"
echo "thrid is $3"
gp@ubuntu:~/shell$ chmod +x a.sh
gp@ubuntu:~/shell$ ./a.sh 1 2 "3 4"
file name is ./a.sh
first is 1
second is 2
thrid is 3 4
同样可以使用$#
获取传递的参数个数,$*
获取所有传递进来的参数字符串
echo "params count is $#" # params count is 3
echo "params string is $*" # params string is 1 2 3 4
Shell运算符
Shell支持的运算符如下:
- 算数运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
算数运算符
Bash不支持数学运算,需要使用expr
运算符实现,expr写在反引号
中,之后接数学运算,其中数字和符号空格隔开
,expr只能针对整数
和字符串
,对于小数等其他则报错。
gp@ubuntu:~/shell$ a=`expr 2 + 2`
gp@ubuntu:~/shell$ echo $a
4
也可以使用$[]
的方式
gp@ubuntu:~/shell$ a=$[ 2 + 2 ]
gp@ubuntu:~/shell$ echo $a
4
也可以使用let
关键字,let后面不需要对计算符号间留空格,let后面跟变量不需要$
gp@ubuntu:~/shell$ a=0
gp@ubuntu:~/shell$ let a++
gp@ubuntu:~/shell$ echo $a
1
gp@ubuntu:~/shell$ a=0
gp@ubuntu:~/shell$ let a=a+1
gp@ubuntu:~/shell$ echo $a
1
gp@ubuntu:~/shell$ let a=3+4
gp@ubuntu:~/shell$ echo $a
7
支持的运算符+
,-
,*
,/
,%
,=
,==
,!=
,其中乘号*需要加转义\
。
gp@ubuntu:~/shell$ cat test.sh
#!/bin/bash
a=3
b=10 # 只能对整数和字符串
c=`expr $a + $b`
echo "a+b=$c"
echo "a+b=$[ a + b]"
echo "a+b=$(expr $a + $b)" # 使用$(expr)
echo "a+b=`expr $a + $b`" # 使用反引号
echo "a==b?:`expr $a == $b`"
echo "a*b=`expr $a \* $b`"
echo "a%b=`expr $a % $b`"
输出计算结果
a+b=13
a+b=13
a+b=13
a+b=13
a==b?:0
a*b=30
a%b=3
比较运算符
算术比较符,使用字母配合[ ]
括号,或者使用比较符配合(())
双括号,在[ ]
出现的一般比较符只有=
和!=
都是用来比较字符串的,不可用于整数比较,不能出现>
,<
等
-
-eq
等于,如,[ $a -eq $b ] -
-ne
不等于,如,[ $a -ne $b ] -
-gt
大于,如,[ $a -gt $b ] -
-ge
大于等于,如,[ $a -ge $b ] -
-lt
小于,如,[ $a -lt $b ] -
-le
小于等于,如,[ $a -le $b ] -
<
小于,(($a < $b)) -
<=
小于等于,(($a <= $b)) -
>
大于,(($a > $b)) -
>=
大于等于,(($a >= $b)) -
==
等于,(($a == $b))
gp@ubuntu:~/shell$ a=2
gp@ubuntu:~/shell$ b=1
gp@ubuntu:~/shell$ if (("$a" == "$b")); then echo 1; else echo 2; fi;
2
gp@ubuntu:~/shell$ if (("$a" > "$b")); then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [ $a -gt $b ]; then echo 1; else echo 2; fi; # [ ]内部左右都要空一格,否则报错
1
字符串比较符
-
==
等于,或者=
,[ $a == $b]],字符串相等 -
!=
大于,[ $a > $b] -
>
大于,[ $a > $b] -
<
大于,[ $a > $b]
gp@ubuntu:~/shell$ a="abc"
gp@ubuntu:~/shell$ b="abc"
gp@ubuntu:~/shell$ if [[ $a == $b ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ b="abd"
gp@ubuntu:~/shell$ if [[ $a == $b ]]; then echo 1; else echo 2; fi;
2
gp@ubuntu:~/shell$ a=2020-12-13
gp@ubuntu:~/shell$ b=2019-12-30
gp@ubuntu:~/shell$ if [[ $a > $b ]]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ b=2021-12-30
gp@ubuntu:~/shell$ if [[ $a > $b ]]; then echo 1; else echo 2; fi;
2
布尔运算符
包含与,或,非,连接在多个表达式中间,用空格隔开,[[]]
和[]
在不同情况下语法不同,双中括号比中括号更加通用,可以防止语句错误,比如&&
,||
,>
,<
等可以出现在双中括号中,但是在单中括号内报错
其中
-o
和||
等价,-a
和&&
等价,但是||
和&&
必须在[[]]
中使用
gp@ubuntu:~/shell$ a=5
gp@ubuntu:~/shell$ b=3
gp@ubuntu:~/shell$ if [ $a -lt $b -o $b -le 3 ]; then echo "1"; else echo "2"; fi;
1
gp@ubuntu:~/shell$ if [[ $a -lt $b || $b -le 3 ]]; then echo "1"; else echo "2"; fi;
1
# 多个条件的组合逻辑
gp@ubuntu:~/shell$ if [[ $a -lt $b || $b -le 2 || $a -ge 5 ]]; then echo "1"; else echo "2"; fi;
1
针对字符串比较
gp@ubuntu:~/shell$ if [ $a == $b -o $a == "abc" ]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [[ $a == $b || $a == "abc" ]]; then echo 1; else echo 2; fi;
1
&&
,||
可以配合布尔值输出符号前后的变量,a && b,如果a为False输出a,否则输出b,a || b,如果a为False输出b,否则输出a
gp@ubuntu:~/shell$ [ $a -gt $b ] && echo 'a > b' || echo 'a < b';
a < b
文件测试运算符
主要有-r
,-w
,-x
可读可写可执行,-s
是否为空(大小为0),true代表不为空,-e
是否存在,-d
是否是个目录
gp@ubuntu:~/shell$ file="/home/gp/shell/test.sh"
gp@ubuntu:~/shell$ if [ -e $file ]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [ -s $file ]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [ -d $file ]; then echo 1; else echo 2; fi;
2
gp@ubuntu:~/shell$ if [ -r $file ]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [ -w $file ]; then echo 1; else echo 2; fi;
1
gp@ubuntu:~/shell$ if [ -x $file ]; then echo 1; else echo 2; fi;
1
# 撤销用户的执行权限
gp@ubuntu:~/shell$ chmod u-x test.sh
gp@ubuntu:~/shell$ ll
total 12
drwxr-xr-x 2 gp gp 4096 2月 12 17:03 ./
drwxr-xr-x 40 gp gp 4096 2月 12 09:27 ../
-rw-r-xr-x 1 gp gp 139 2月 12 09:25 test.sh*
gp@ubuntu:~/shell$ if [ -x $file ]; then echo 1; else echo 2; fi;
2
Shell的echo和printf命令
echo和printf都是用于Shell打印变量的命令,使用printf可以输出更规则更格式化的结果。它引用于C语言的printf命令,使用printf可以指定字符串的宽度
、实现左对齐
(使用减符号-)、右对齐
(默认的)、格式化小数输出
等。
echo的单引号,双引号,反引号
单引号是纯串,双引号是变量替换,反引号是命令结果替换
gp@ubuntu:~/shell$ a=3
gp@ubuntu:~/shell$ echo "$a"; echo "ls" # 变量替换
3
ls
gp@ubuntu:~/shell$ echo '$a'; echo 'ls' # 纯字符串
$a
ls
gp@ubuntu:~/shell$ echo `$a`; echo `ls` # 命令结果替换
3: command not found
test.sh
echo的转义和换行
echo -e
识别转义和特殊意义的符号
,如换行符、n、制表符\t、转义符\等
gp@ubuntu:~/shell$ echo 'Hello World!\n';echo "Hello World"!
Hello World!\n
Hello World!
gp@ubuntu:~/shell$ echo -e 'Hello World!\n';echo "Hello World"! # 识别\n
Hello World!
Hello World!
echo默认每行结尾自带\n,如果取消换行需要加-n
gp@ubuntu:~/shell$ echo 'Hello World!'; echo 'Hello World!';
Hello World!
Hello World!
gp@ubuntu:~/shell$ echo -n 'Hello World!'; echo 'Hello World!';
Hello World!Hello World!
echo的颜色输出
echo输出的字符串带有颜色和背景颜色,有固定的写法,-e[xx;xxm...\e[0m
,因为要识别特殊符号,所以必须使用-e
,且用双引号
,详情如下图
gp@ubuntu:~/shell$ echo -e "my name is \e[31;45mgp\e[0m"
my name is gp
printf的使用
printf默认不在结尾加换行符,它不像echo一样,所以要手动加\n换号
gp@ubuntu:~/shell$ cat test.sh
#!/bin/bash
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
gp@ubuntu:~/shell$ ./test.sh
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
- %s %c %d %f 都是
格式替代符
,%s 输出一个字符串,%d整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出 - %-10s 指一个
宽度为 10
个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来 - %-4.2f 指
格式化为小数
,其中 .2 指保留2位小数。
Shell控制语句与循环
Shell的控制流的基本语句为
if condition
then
command1
else
command2
fi
或者
if condition1
then
command1
elif condition2
then
command2
else
command3
fi
也可以写成一行
if condition; then command1; else command2; fi;
举几个例子:
#!/bin/bash
# 判断数字相等
a=3
b=10
if [ $a -gt $b ]
then
echo 'a > b'
else
echo 'a
输出结果
gp@ubuntu:~/shell$ ./test.sh
a
for循环
for循环的一般语法为
for var in item1 item2 ... itemN
do
command
done
for (( ; ; ))
参考如下,循环对象为字符串,使用in
作为关键字,以空格
作为一个独立的循环对象之一,也可以设置+1循环设置最大停止条件for (( ; ; ))
,也可以对命令执行结果进行循环
gp@ubuntu:~/shell$ cat test2.sh
#!/bin/bash
for i in 0 1 2 3
do
echo "当前值为$i"
done
j=10
for (( i=5; i<=$j; i++ ))
do
echo "当前值为$i"
done
for i in `ls`
do
echo "当前值为$i"
done
gp@ubuntu:~/shell$ ./test2.sh
当前值为0
当前值为1
当前值为2
当前值为3
当前值为5
当前值为6
当前值为7
当前值为8
当前值为9
当前值为10
当前值为test2.sh
当前值为test.sh
for循环对数组元素进行循环
gp@ubuntu:~/shell$ cat test2.sh
#!/bin/bash
a=( a b c d )
for i in ${a[*]}
do
echo "当前元素是$i"
done
for i in ${a[@]}
do
echo "当前元素是$i"
done
gp@ubuntu:~/shell$ ./test2.sh
当前元素是a
当前元素是b
当前元素是c
当前元素是d
当前元素是a
当前元素是b
当前元素是c
当前元素是d
while循环
while循环的基本用法如下
while condition
do
command
done
实例参考如下,使用let
改变每次循环的状态作为停止判断条件,let操作的变量不需要使用$
gp@ubuntu:~/shell$ cat test3.sh
#!/bin/bash
a=1
while (( $a <= 3))
do
echo "当前值是$a"
let a++
done
gp@ubuntu:~/shell$ ./test3.sh
当前值是1
当前值是2
当前值是3
可以使用(())
双括号作为判断条件,或者使用单中括号使用专用的判断符号
#!/bin/bash
a=1
while [ $a -le 3 ]
do
echo "当前值是$a"
let a++
done
echo $a
输出如下
当前值是1
当前值是2
当前值是3
4
可以使用while实现死循环
#!/bin/bash
while true
do
echo 1
done
until循环
until循环和while循环相反,当条件为False时执行循环,为True跳出循环
gp@ubuntu:~/shell$ cat test3.sh
#!/bin/bash
a=0
until (( $a >= 3))
do
echo "当前值为$a"
let a++
done
gp@ubuntu:~/shell$ ./test3.sh
当前值为0
当前值为1
当前值为2
break和continue跳出循环
break结束这个循环,continue跳出本次循环
gp@ubuntu:~/shell$ cat test4.sh
#!/bin/bash
for i in 1 2 4 5 6
do
if (( $i > 4 ))
then
break
fi
echo $i
done
echo --------------
for i in 2 4 1 10 3
do
if (( $i > 4 ))
then
continue
fi
echo $i
done
gp@ubuntu:~/shell$ ./test4.sh
1
2
4
--------------
2
4
1
3