1、shell脚本的创建与执行
[root@net10 mnt]# vim first.sh
-----------------------------
#!/bin/bash
echo "hello"
-----------------------------
[root@net10 mnt]# chmod +x first.sh #使文件可执行
[root@net10 mnt]# sh first.sh
hello
(1)#!/bin/bash ,指定命令解释器
[root@net10 mnt]# echo $SHELL #系统变量 $SHELL 可以获取当前系统默认的shell
/bin/bash
当用户执行程序时,当前的Shell会载入该程序的代码,如果发现有 “#!”标识,则表示当前程序指定了解释并执行它的shell.
然后会读取标识符后面的内容,搜寻解释器的绝对路径。如果发现了它指定的解释器,则会创建一个关于该解释器的进程,解释并执行当前脚本的语句。
(2)执行shell程序的三种方式
- 授予用户执行该脚本文件的权限: chmod +x filename 然后
./ filename - 通过调用shell脚本解释器来执行:
/bin/bash filename或sh filename
将脚本文件作为参数传递给解释器;这种执行方式,只要用户拥有读取该文件的权限即可 - 通过source命令来执行:
source filename或 . filename
source是shell内部的命令,其功能是读取指定的shell程序文件,并且一次执行其中的所有语句。该命令与前面两种方式的区别在于只是简单的读取脚本里面的语句,并且依次在当前的shell里面执行,并没有创建新的子shell进程。脚本里面所创建的变量都会保存到当前的shell里面。
source,通常用于重新执行刚修改的初始化文件,使之立即生效
使用source或“.”可以将自身脚本中的变量值或函数等的返回值传递到当前父脚本中使用。这是它和其他几种方法最大的区别。
2、变量
(1)普通变量
shell中的变量不区分数据类型的,统一按照字符串处理。但根据变量的上下文环境,允许程序执行一些不同的操作,如字符串的比较和整数的加减法
[root@net10 mnt]# y=abc12;let y+=1;echo y=$y
y=1
[root@net10 mnt]# n="";echo n=$n;let n+=1;echo n=$n
n=
n=1
[root@net10 mnt]# echo m=$m;let m+=1;echo m=$m
m=
m=1
为了能够执行加法运算,shell会自动进行数据类型的转换,如果遇到含有非数字的字符串,空串,以及没有定义的变量,则变量的值都会被转换为整数0。
(2)变量声明
为了更好的控制变量的相关属性,bash提供了一个declare的命令来声明变量。
declare attribute variale
-i 整数变量
-p 显示所有变量的值
-f 显示所有自定义函数
-a 数组变量
-x 环境变量
[root@net10 ~]# x=6/3;echo $x
#shell中的所有数据都看做是字符串来处理的
6/3
[root@net10 ~]# declare -i x;x=6/3;echo $x
# x被声明为整数,可以直接参与算数运算
2
[root@net10 ~]# declare +i x;x=6/3;echo $x
# 取消 x 的整数属性
6/3
(3)用户变量
用户变量就是用户在shell编程过程中定义的变量,分为全局变量和局部变量。
<3.1 >全局变量
在脚本中定义的变量为全局变量,不仅可以在脚本中直接使用,还可以在函数内部直接使用;
在函数内部定义的变量默认也是全局变量,其作用域从函数调用时执行变量定义的地方开始,一直到shell脚本结束或是被显式的删除为止。
[root@net10 ~]# vim global
#!/bin/bash
func()
{
echo $v
v=200
}
v=100
func
echo $v
[root@net10 ~]# sh global
100
200
<3.2> 局部变量
在shell中,可以在函数内部通过local关键字定义局部变量,另外,函数的参数也是局部变量。
函数外部的全局变量与函数内部的局部变量在出现同名的情况下,函数内部的局部变量会被优先使用。
[root@net10 ~]# vim local
#!/bin/bash
func()
{
echo global x is $x
local x=5
echo local x is $x
}
x=2
func
echo global x is $x
尽管在函数内部修改了变量x的值,但这只影响到局部变量,所以在函数外部输出的仍然是全局变量的值。
[root@net10 ~]# sh local
global x is 2
local x is 5
global x is 2
<3.3> 用户变量的设置
vim .bash_profile 声明用户级变量,只针对当前设定的用户有效
source .bash_profile 修改文件后,重新读取文件
(4)系统变量
主要在对参数的判断和命令返回值判断时使用,包括脚本和函数的参数,以及脚本和函数的返回值。
<4.1> 常用的系统变量
$n
n是一个整数,从1开始,表示参数的位置,如:$1
表示第一个参数
$#
命令行参数的个数
$0
当前shell脚本的名称
$?
前一个命令或函数的返回状态码
$*
以“参数1 参数2 …” 的形式将所有的参数通过一个字符串返回
$@
以 “参数1”“ 参数2 ”… 的形式返回每个参数
$$
返回本程序的进程ID
[root@net10 ~]# vim enviroment
#!/bin/bash
echo "\$0--- the script name is $0"
echo "\$#---the number of parameter is $#"
echo "\$?---the status of last command is $?"
echo "\$*---the parameters are $*"
echo "\$@---the every parameter is $@"
echo "\$n---first is \$1=$1,ten is \$10=$10"
echo "\$\$---ID is $$"
[root@net10 ~]# chmod +x enviroment
[root@net10 ~]# ./enviroment a b c d e
$0---the script name is ./enviroment
$#---the number of parameter is 5
$?---the status of last command is 0
#成功的命令返回0,不成功的命令返回非0值。
$*---the parameters are a b c d e
$@---the every parameter is a b c d e
$n---first is $1=a,ten is $10=a0
#使用 $n 获取位置参数时,shell通常的变量名只是一位数字,即0~9。
$$---ID is 2425
<4.2>系统级变量的设置
vim /etc/profile
vim .bashrc
(5)环境变量
export 声明当前环境的变量,当环境变化时,变量的值就会发生变化
也可以在文件中声明变量,变量读取时,先从环境中读取,当环境中的变量与文件中的变量不一样时,文件中的变量优先
[ling@net10 ~]$ y=3
[ling@net10 ~]$ echo $y
3
[ling@net10 ~]$ vim num #在脚本文件中令y=5
[ling@net10 ~]$ sh num
5
3、引用,转义和替换
引用和转义在shell解析字符串时用于去除字符串中特殊字符或保留词语的特殊含义。这会导致按字面处理字符串,而不是展开变量或将其部分内容视作具有特殊含义。
引用有三种类型:
(1)弱引用(部分引用)
将字符串放置在 双引号 中,保留字符串中所有字符的字面意义,
"$", "`","\"和"!"
字符除外。
换言之,若引用中,“$”、“`”、“\”和“!”字符仍拥有特殊的含义。
[root@net10 ~]# echo "$(date +%T)"
20:23:27
(2)强引用
将字符串放置在单引号中,所有字符都将被解释为普通的字符。
[root@net10 ~]# echo '$(date +%T)'
$(date +%T)
(3)转义
\是转义字符。当反斜线后面的一个字符具有特殊的意义时,反斜线将屏蔽该字符的特殊意义,使得shell按照的字符的字面意义来解释。
[root@net10 ~]# echo "make \$$ fast"
make $$ fast
[root@net10 ~]# echo "make \$$$$ fast"
make $1942$ fast
#当没有转义字符时,$就被解释为特殊的意义
[root@net10 ~]# echo "make \$\$\$\$ fast"
make $$$$ fast
(4)命令替换(“)
命令替换,是指在shell程序中,将某个shell命令的执行结果赋给某个变量。可以分别使用反引号 `和圆括号()进行命令替换。
[root@net10 ~]# v=`pwd`;echo $v
/root
[root@net10 ~]# v=`pw`;echo $v
-bash: pw: command not found
#shell将反引号中的字符串当作shell命令,在执行时,shell首先执行该命令,并以它的标准输出结果取代整个反引号。如果输入错误命令,则会
提示command not found
[root@net10 ~]# v=$(pwd);echo $v
/root
4、条件测试
(1)语法
test:使用test进行条件测试的时候,如果没有指定绝对路径,则使用的都是内部命令。
[ ]:条件表达式和左右方括号之间都必须有一个空格。左方括号 [ 是一个shell命令,命令与参数之间必须保留一个空格。
(2)字符串测试
字符串运算符:
string 判断字符串是否为空
-n string 判断string是否是非空串(nonzero)
-z string 判断字符串是否是空串(zero)
在测试运算符的左右两边,一定要含有一个空格
[root@net10 ~]# a="hello"
[root@net10 ~]# test $a;echo $?
0
[root@net10 ~]# test -n $a;echo $?
0
[root@net10 ~]# test -z $a;echo $?
1
[root@net10 ~]# [ "$a" = "$b" ];echo $?
1
[root@net10 ~]# test "$a" != "$b";echo $?
0
(3)整数测试
整数运算符:
-eq 等于,equal
-ne 不等于,not equal
-lt 小于,less than
-le 小于等于,less equal
-ge 大于等于,greater equal
(4)文件测试
-b 文件是否存在,且为块文件
-c 文件是否存在,且为字符设备
-d 文件是否存在,且为目录
-e 文件是否存在
-f 文件是否存在,且为常规文件
-w 文件是否存在且可写
-L 文件是否存在,且为符号链接
-r 文件是否存在,且可读
-x 文件是否存在,且可执行
-nt 某个指定的文件修改时间是否比另一个晚
-ot 某个指定的文件修改时间是否比另一个早
-ef 两个文件是否有相同的设备和节点数
[root@net10 ~]# ls -l
rw-r--r-- 1 root root 55 Mar 11 13:52 so
-rw-r--r-- 1 root root 79 Mar 11 10:57 test
[root@net10 ~]# test -a so ;echo $?
0
[root@net10 ~]# [ -x so ];echo $?
1
(5)逻辑操作符
! 逻辑非
-o 逻辑或
-a 逻辑与
5、条件判断
(1)if语句
shell 没有提供{}表示代码块,所以需要用 fi 关键字来表示if结构的结束。
[root@net10 ~]# vim if
#!/bin/bash
if [ "$(whoami)" = "root" ]
then
echo "you are root"
fi
[root@net10 ~]# sh if
you are root
也可以使用 && 操作符代替if语句,但需要用exit语句退出程序。
[root@net10 ~]# vim if
#!/bin/bash
[ "$(whoami)" = "root" ] && ( echo "you are root")
exit 1
在shell中,空命令 “:”不做任何事情,但它的退出状态永远是0.
(2)if else语句
[root@net10 ~]# vim else
#!/bin/bash
if [ "$*" = "apple" ];then
echo "banana"
else
if [ "$*" = "banana" ];then
echo "apple"
else
echo "erro"
fi
fi
[root@net10 ~]# ./else banana
apple
使用if else 语句,会使整个程序看起来不清楚,经常会出现漏掉fi的情况。使用if elif则会使程序可读性大大增强。
[root@net10 ~]# vim elif
#!/bin/bash
if [ "$*" = "apple" ];then
echo "banana"
elif [ "$*" = "banana" ];then
echo "apple"
else
echo "erro"
fi
[root@net10 ~]# ./ifeslse apple
banana
(3)case语句
基本语法:每个case子句中的测试部分,都以又括号“)”结束;
每个case子句都以一对分号“;;”作为结束符;
如果没有一个值与case 子句中的值相匹配,则执行 * 后面的一组语句;
case 语句结构以esac结尾。
[root@net10 ~]# vim case
#!/bin/bash
echo "hit a key:"
read key
case $key in
[[:lower:]])
echo "lowercase letter";;
[[:upper:]])
echo "uppercase letter";;
[0-9])
echo "digit number";;
* )
echo "other";;
esac
[root@net10 ~]# ./case
hit a key:
5
digit number
6、运算符
(1)算术运算符
+ , - ,* ,/ ,% , **
使用算数运算符时,一定要注意运算符左右两边的空格,否则会得出错误的结果。
执行算数运算的四种方式:
使用expr外部程序,(进行运算时需要将括号转义,expr不能进行幂运算)
使用 $((…)) , (写法自由,无需转义处理)
使用 $[…] ,(与使用两个圆括号的语法相同)
- 使用 let ,(变量名无需使用 $ ,表达式中的空格和特殊字符必须引用起来)
[root@net10 ~]# vim operator
#!/bin/bash
echo *******************expr*****************
x1=`expr 5 + 1`
echo "x1=$x1"
x2=`expr 1+4` #要有运算符左右两边的空格
echo "x2=$x2"
x3=`expr \( 3 - 1 \) \* 2`
echo "x3=$x3"
x4=`expr (3-1)*2` #进行运算时需要将括号转义
echo "x4=$x4"
echo *******************'$(())'*****************
s1=$(( 5 + 1 ))
echo "s1=$s1"
s2=$((5+1))
echo "s2=$s2"
s3=$(( (3-1) * 2))
echo "s3=$s3"
s4=$(((3-1)*2))
echo "s4=$s4"
echo *******************let*****************
let l1=5+1
echo "l1=$l1"
[root@net10 ~]# sh operator
*******************expr*****************
x1=6
x2=1+4
x3=4
operator: command substitution: line 9: syntax error near unexpected token `3-1'
operator: command substitution: line 9: `expr (3-1)*2'
x4=
*******************$(())*****************
s1=6
s2=6
s3=4
s4=4
*******************let*****************
l1=6
(2)自增/自减运算符
仅针对变量而言,不能对常量或表达式执行自增/自减操作
7、循环结构
(1)for循环
数字列表作为循环条件的列表
#!/bin/bash
for i in 1 2 3 4 5
#for ((i=1;i<=5;i++)) #使用类似C语言风格的写法
#for i in {1..5} 使用省略的写法表示某个范围
do
echo "the number is $i"
done
for m in {1..10..2} #用户指定for语句的步长
# for i in $( seq 2 2 10 ) #此种写法也可以
do
echo "$m"
done
ls命令的输出结果作为 for循环的执行条件,用于对文件进行批处理操作。
[root@net10 ~]# vim ls
#!/bin/bash
for file in $(ls) #$() 的作用是将其中的字符串作为shell命令来执行
do
mv "$file" "$file.$(date +%T)" #给所有的文件加上时间
done
[root@net10 ~]# sh ls
[root@net10 ~]# ls
anaconda-ks.cfg.17:58:06 if.17:58:06
case.17:58:06 ling.db.17:58:06
casescore.17:58:06 local.17:58:06
(2)until语句
当表达式的值不为0时,执行do 和done 之间的语句;当表达式的值为0 ,将退出until循环结构。
在使用until循环语句时,一定要注意循环变量的改变。
[root@net10 ~]# vim squar
#!/bin/bash
i=1
until [ "$i" -gt 5 ]
do
let "square=i*i"
echo "$square=$i*$i"
let i++
done
[root@net10 ~]# sh squar
1=1*1
4=2*2
9=3*3
16=4*4
25=5*5