一、shell概述
- shell 的两层含义: 既是一种应用程序,又是一种程序设计语言
1.1 shell作为应用程序
- 解释、执行 用户输入的 命令,将用户的操作翻译成机器可以识别的语言,完成相应功能称之为 shell 命令解析器
- shell 是用户和 Linux 内核之间的接口程序,提供了用户操作内核的工具,shell包含多个接口,对应 用户终端输入的命令,当我们向终端输入指令时,一回车就会启动一个shell,shell把回车的那个字符串拿来运行,启动某个应用程序,这个应用程序去和内核产生一些结果。
- shell调用了系统核心的大部分功能来执行程序、并以 并行 的方式协调各个程序的运行,一个shell对应一个终端
- Linux 系统中提供了好几种不同的 shell 命令解释器,如 sh、ash、bash 等。一般默认使用 bash 作为默认的解释器。
shell本质是对内核起到保护作用,只有shell能够识别的命令才能够直接操作内核来控制硬件
1.2 shell作为一门语言
- shell定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支,用于简化我们对系统的管理与应用程序的部署称之为shell脚本
编译性语言和脚本语言的区别:
- 我们学过的 c/c++等语言,属于编译性语言(需经过编译器完成编译、汇编、链接等过程变为二进制代码方可执行)
- shell脚本是一种脚本语言,我们只需使用任意文本编辑器,按照语法编写相应程序,增加可执行权限,即可在安装了 shell 命令解释器的环境下执行
shell 脚本主要用于:
- 帮助开发人员或系统管理员将复杂而又反复的操作放在一个文件中,通过简单的 一步执行 操作完成相应任务,从而解放他们的负担
shell 脚本大体可以分为两类:
(1)系统进行调用
这类脚本无需用户调用,系统会在合适的时候调用,如:/etc/profile、~/.bashrc 等
- /etc/profile 此文件为系统的 每个用户 设置环境信息,当用户第一次登录时,该文件被执行,系统的公共环境变量在这里
设置开始自启动的程序,一般也在这里设置 - ~/.bashrc 登录时会自动调用,打开任意终端时也会自动调用
这个文件一般设置与个人用户有关的环境变量,如交叉编译器的路径等等
(2)用户编写,需要手动调用的
无论是系统调用的还是需要我们自己调用的,其语法规则都一样
二、shell 脚本的定义与执行
2.1shell语法
- 1、定义以开头:#!/bin/bash
#!用来声明脚本由什么 shell 解释,否则使用默认 shell - 2、单个"#"号代表注释当前行
- 3、执行: chmod + x test.sh ./test.sh 增加可执行权限后执行 bash test.sh 直接指定使用 bash
解释 test.sh . test.sh(source test.sh) 使用当前 shell 读取解释 test.sh
2.2shell脚本的代码编写流程
第一步:指定shell脚本文件,一般以.sh作为后缀名,也可以不加,因为Linux不以后缀作为文件区分,例如:test.sh
第二步:编写代码
#!/bin/bash
# #!用于指定当前脚本文件的shell解释器的类型,如果不写,则用默认的shel
# shell脚本是shell命令的有序集合,代码的构成就是命令
ls
pwd
echo "study~"
第三步:修改文件权限
chmod +x test.sh
第四步:执行脚本文件
./test.sh
或者 bash test.sh
或者 . test.sh(后两种方式不需要修改文件权限)
执行结果:
三种执行脚本的方式不同点:
- ./和 bash 执行过程基本一致,前者首先检测#!,使用#!指定的 shell,如果没有则使用默认的 shell,后者明确指定
bash 解释器去执行脚本,脚本中#!指定的解释器不起作用 - 用./和 bash 去执行会在后台启动一个新的 shell 去执行脚本 用.去执行脚本不会启动新的 shell,直接由当前的 shell
去解释执行脚本。
2.2 变量
2.2.1 自定义变量
- 定义变量 变量名=变量值 如:num=10
- 引用变量 $ + 变量名 如:i=$num 把变量 num 的值付给变量 i
- 显示变量 使用 echo 命令可以显示 单个 变量取值 echo $num
- 清除变量 使用 unset 命令清除变量 unset num
变量的其它用法:
- read string 从键盘输入一个字符串付给变量 string
- readonly var=100 定义一个只读变量,只能在定义时初始化,以后不能改变,不能被清除。
- export var=300 使用 export 说明的变量,会被导出为环境变量,其它 shell 均可使用 注意:此时必须使用source 2_var.sh 才可以生效
注意事项:
- 变量名只能包含英文字母下划线,不能以数字开头
1_num=10 错误
num_1=20 正确 - 等号两边不能直接接空格符,若变量中本身就包含了空格,则整个字符串都要用双引号、或单引号括起来;
name=aa bb //错误
name=“aa bb” //正确 - 双引号内的特殊字符可以保有变量特性,但是单引号内的特殊字符则仅为一般字符。
echo “$name is me” //输出:aa bb
is me echo ’ $name is me’ //输出: $name is me
代码示例:
#!/bin/bash
#定义变量
#shell脚本中没有数据类型可言,所以定义变量时不需要加数据类型
#shell脚本中,赋值操作时不能在等号两边加空格
#NUM = 100 #错误
NUM=100
#引用变量
# $变量名 取一个变量的值
i=$NUM
#显示变量
echo $NUM
echo $i
#清除变量
unset NUM
echo "*************"
echo $NUM
#使用read从终端读取数据保存在变量中
read str
echo "str = $str"
#使用readonly创建一个只读的变量
readonly n=999
echo "n = $n"
#指定变量初始化后不能再次修改变量的值,只能使用
#n=666
#echo "n = $n"
执行结果:
2.2.2 环境变量
- shell 在开始执行时就已经定义了一些和系统的工作环境有关的变量,我们在 shell 中可以直接使用$name 引用
定义:
- 一般在~/.bashrc 或/etc/profile 文件中(系统自动调用的脚本)使用 export 设置,允许用户后来更改
VARNAME=value
export VARNAME
传统上,所有环境变量均为大写
显示环境变量
使用 env 命令可以查看所有的环境变量。
清除环境变量
使用 unset 命令清除环境变量
常见环境变量:
-
HOME:用于保存注册目录的完全路径名。
-
PATH:用于保存用冒号分隔的目录路径名,shell 将按 PATH变量中给出的顺序搜索这些目录,找到的第一个与命 令名称一致的可执行文件将被执行。
PATH=$HOME/bin:/bin:/usr/bin;export PATH -
HOSTNAME:主机名
-
SHELL:默认的shell 命令解析器
-
LOGNAME:此变量保存登录名
-
PWD:当前工作目录的绝对路径名
…
设置环境变量:
方法1:临时设置
在终端执行命令
MYVAL=999
export MYVAL
方法2:永久设置
需要在配置文件(~/.bashrc或/etc/profile)中进行设置,设置完毕后需要通过 source命令配置文件立即生效
代码示例:使用环境变量,一般环境变量类似c语言中的全局变量,可以再shell脚本文件中任意使用
#!/bin/bash
echo "You are welcome to use bash"
echo "Current work dirctory is $PWD"
echo "the host name is $HOSTNAME"
echo "your home dir $HOME"
echo "Your shell is $SHELL"
echo "user env val is $MYVAL1"
执行结果:
2.2.3 预设变量
代码示例:
#! /bin/bash
#位置变量$0 - $9 保存从终端输入的每一个参数
#\用于转义$
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$5 = $5"
echo "\$6 = $6"
echo "\$7 = $7"
echo "\$8 = $8"
echo "\$9 = $9"
#如果超过9,需要加大括号
echo "\$10 = ${10}"
#$#:保存命令行传入的参数的个数,不包括$0
echo "\$# = $#"
#$@或者$*:保存所有的命令行传入的参数,但是不包括$0
echo "\$@ = $@"
echo "\$* = $*"
#$$:获取当前进程的进程号
echo "\$$ = $$"
//read NUM
# $?返回上一个命令执行的结果,如果执行成功,则$?的值为0,执行失败,则为非0
ls
echo "\$? = $?"
ls asdfasdf
echo "\$? = $?"
执行结果:
2.2.4 脚本变量的特殊用法
-
“”(双引号):包含的变量会被解释
-
‘’(单引号):包含的变量会被当做字符串解释
-
``(数字键1左面的反引号):反引号中的内容作为系统命令,并执行其内容,可以替换输出为 一个变量
例如:$ echo "today isdate
"
输出结果:today is 2012年07月29日星期日 12:55:21 CST -
\ 转义字符: 同c语言 \n \t \r \a等 echo命令需加-e转义
-
( 命令序列 ): 由子shell来完成,不影响当前shell中的变量
{ 命令序列 }: 在当前shell中执行,会影响当前变量
注意: “{”、“}”前后有一空格
代码示例:
#!/bin/bash
name="zhangsan"
#双引号里面的特殊字符,会使用其特殊含义
string1="good moring $name"
#单引号里面的特殊字符,都会失去特殊含义
string2='good moring $name'
echo $string1
echo $string2
#反引号:获取一个shell命令的输出结果,一般对有输出结果的命令经常去使用,也可以使用$()与反引号等价
echo "today is date"
echo "today is `date`"
echo 'today is `date`'
echo "My dir is $(ls)"
#转义字符:使用时,需要在echo后面加上-e选项
echo "**************"
echo "this \n is\ta\ntest"
echo -e "this \n is\ta\ntest"
echo "**************"
num=100
#由子shell来完成,不影响当前shell中的变量
( num=999;echo "1 $num" )
echo 1:$num
#在当前shell中执行,会影响当前变量
{ num=666;echo "2 $num"; }
echo 2:$num
执行结果:
2.3 条件测试语句
在写shell脚本时,经常遇到的问题就是判断字符串是否相等,可能还要检查文件状态或进 行数字测试,只有这些测试完成才能做下一步动作
test命令:用于测试字符串、文件状态和数字 test命令有两种格式: test condition 或 [ condition ]
使用方括号时,要注意在条件两边加上空格
shell脚本中的条件测试如下: 文件测试、字符串测试、数字测试、复合测试 测试语句一般与后面讲的条件语句联合使用
2.3.1 文件测试
- 按照文件类型
-e 文件名 文件是否存在
-s 文件名 是否为非空
-b 文件名 块设备文件
-c 文件名 字符设备文件
-d 文件名 目录文件
-f 文件名 普通文件
-L 文件名 软链接文件
-S 文件名 套接字文件
-p 文件名 管道文件 - 按照文件权限
-r 文件名 可读
-w 文件名 可写
-x 文件名 可执行 - 两个文件之间的比较
文件1 -nt 文件2 文件1的修改时间是否比文件2新
文件1 -ot 文件2 文件1的修改时间是否比文件2旧
文件1 -ef 文件2 两个文件的inode节点号是否一样,用于判断是否是硬链接
代码示例:
#! /bin/bash
echo "please input a filename >>> "
read FILE
test -e $FILE
echo "存在?$?"
test -s $FILE
echo "非空?$?"
[ -r $FILE ]
echo "可读?$?"
[ -w $FILE ]
echo "可写?$?"
[ -x $FILE ]
echo "可执行?$?"
test -b $FILE
echo "块设备文件?$?"
test -c $FILE
echo "字符设备文件?$?"
test -d $FILE
echo "目录文件?$?"
test -f $FILE
echo "普通文件?$?"
test -L $FILE
echo "软链接文件?$?"
test -S $FILE
echo "套接字文件?$?"
test -p $FILE
echo "管道文件?$?"
执行结果:
2.3.2 字符串测试
s1 = s2 测试两个字符串的内容是否完全一样
s1 != s2 测试两个字符串的内容是否有差异 -
z s1 测试s1 字符串的长度是否为0
-n s1 测试s1 字符串的长度是否不为0
代码示例:
#! /bin/bash
test "hello" = "hello"
echo "相等?$?"
test "hello" = "hella"
echo "相等?$?"
test "hello" != "hello"
echo "不相等?$?"
test "hello" != "hella"
echo "不相等?$?"
test -z "hello"
echo "长度是否为0?$?"
test -n "hello"
echo "长度是否不为0?$?"
执行结果:
2.3.3 数字测试
a -eq b 测试a 与b 是否相等
a -ne b 测试a 与b 是否不相等
a -gt b 测试a 是否大于b
a -ge b 测试a 是否大于等于b
a -lt b 测试a 是否小于b
a -le b 测试a 是否小于等于b
代码示例:
#! /bin/bash
echo "please input two numbers >>> "
read NUM1 NUM2
test $NUM1 -eq $NUM2
echo "相等?$?"
test $NUM1 -ne $NUM2
echo "不相等?$?"
test $NUM1 -gt $NUM2
echo "大于?$?"
test $NUM1 -ge $NUM2
echo "大于等于?$?"
test $NUM1 -lt $NUM2
echo "小于?$?"
test $NUM1 -le $NUM2
echo "小于等于?$?"
执行结果:
2.3.4 复合测试
第一种形式:命令执行控制
&&: command1 && command2 &&左边命令(command1)执行成功(即返回0)shell才执行&&右边的命令 (command2)
|| :command1 || command2 ||左边的命令(command1)未执行成功(即返回非0)shell才执行||右边的命令 (command2)
第二种形式:多重条件判定
代码示例:
#! /bin/bash
num=100
#判断 0 <= num <= 200
#test $num -ge 0 && test $num -le 200
test $num -ge 0 -a $num -le 200
echo "$?"
#判断 num <= 0 或者 num >= 200
test $num -le 0 || test $num -ge 200
echo "$?"
file="file.txt"
#判断文件是否不是普通文件
test ! -f $file
echo "$?"
#判断文件是否存在并且是否是目录文件
test -e $file && test -d $file
echo "$?"
执行结果:
2.4 控制语句
2.4.1 if语句
格式一:
if [ 条件1 ];then
执行第一段程序
else
执行第二段程序
fi
格式二:
if [ 条件1 ];then
执行第一段程序
elif [ 条件2 ];then
执行第二段程序
else
执行第三段程序
fi
代码示例:
#! /bin/bash
echo "please input a number >>> "
read NUM
#注意:赋值时,等号两边不能加空格
# []里面存放表达式时必须加空格
#一般形式
if [ $NUM -gt 50 ];then
echo "NUM > 50"
fi
if [ $NUM -gt 50 ]
then
echo "NUM > 50"
else
echo "NUM <= 50"
fi
echo "***********************"
#阶梯形式
if [ $NUM -gt 50 ]
then
echo "NUM > 50"
elif [ $NUM -lt 50 ]
then
echo "NUM < 50"
else
echo "NUM = 50"
fi
echo "***********************"
#嵌套形式
if [ $NUM -gt 50 ]
then
echo "NUM > 50"
elif [ $NUM -eq 50 ]
then
echo "NUM = 50"
else
echo "NUM < 50"
if [ $NUM -gt 30 ]
then
echo "NUM > 30"
else
echo "NUM <= 30"
fi
fi
执行结果:
2.4.2 case语句
case $变量名称 in
“第一个变量内容”)
程序段一
;;
“第二个变量内容”)
程序段二
;;
*)
其它程序段
;;
esac
代码示例一:
#!/bin/bash
echo "This script will print your choice"
case "$1" in
"one")
echo "your choice is one"
;;
"two")
echo "your choice is two"
;;
"three")
echo "Your choice is three"
;;
*)
echo "Error Please try again!"
;;
esac
echo "hello world"
执行结果一:
代码示例二:
#!/bin/bash
echo "Please input your choice:"
read choice
case "$choice" in
Y | yes | Yes | YES)
echo "It's right"
;;
# *是一个通配符,可以匹配任意字符并且数量不限
# N* 只要是N开头的,都会执行下面的命令
N* | n*)
echo "It's wrong"
;;
*)
exit 1
esac
执行结果二:
2.4.3 for语句
形式一:
for (( 初始值; 限制值; 执行步阶 ))
do
程序段
done
代码示例:
#!/bin/bash
#声明sum变量是一个整数变量,准备保存整数
declare -i sum
for (( i=1; i<=100; i++ ))
do
sum=sum+i
done
echo "The result is $sum"
执行结果:
形式二:
for var in con1 con2 con3 ...
do
程序段
done
第一次循环时,$ var的内容为con1
第二次循环时,$ var的内容为con2
第三次循环时,$ var的内容为con3
…
当in后面所有的值都赋值完毕并执行命令后,循环结束
代码示例一:
#!/bin/bash
#注意:for循环后的变量之前不能加$
for i in 1 2 3 4 5 6 7 8 9
do
echo $i
done
执行结果一:
代码示例二:
#!/bin/bash
for name in `ls`
do
if [ -f $name ];then
echo "$name is file"
elif [ -d $name ];then
echo "$name is directory"
else
echo "^_^"
fi
done
执行结果二:
2.4.4 while语句
while [ condition ]
do
程序段
done
当condition成立的时候进入while循环,直到condition不成立时才退出循环。
代码示例:
#!/bin/bash
declare -i i
declare -i s
while [ "$i" != "101" ]
do
s+=i;
i=i+1;
done
echo "The count is $s"
执行结果:
2.4.5 until语句
until [ condition ]
do
程序段
done
这种方式与while恰恰相反,当condition成立的时候退出循环,否则继续循环。
代码示例:
#!/bin/bash
declare -i i
declare -i s
#until循环,是循环条件不成立,执行命令体,如果条件为真,则循环结束
until [ "$i" = "101" ]
do
s+=i;
i=i+1;
done
echo "The count is $s"
执行结果:
2.4.6 break、continue
同c语言一样
代码示例:
#! /bin/bash
for ((num=1; num<=10; num++))
do
if [ $num -eq 5 ]
then
#break退出整个循环
#break
#continue退出本层循环,当执行到continue的位置时,循环下方的代码不在执行
#但是接着下个循环继续执行
continue
fi
echo "num = $num"
done
执行结果:
2.5 函数
有些脚本段间互相重复,如果能只写一次代码块而在任何地方都能引用那就提高了代码的可重用性。
shell 允许将一组命令集或语句形成一个可用块,这些块称为 shell 函数。
2.5.1 函数的定义和调用
定义函数的两种格式:
格式一:
函数名() {
命令 ...
}
格式二:
function 函数名() {
命令 ...
}
所有函数在使用前必须定义,必须将函数放在脚本开始部分
调用函数的格式为: 函数名 参数1 参数2……
代码示例:
#! /bin/bash
#定义一个函数
#由于shell中没有主函数一说,所以一般函数都需要定义在整个代码的最上边,下方对其进行调用
myfun()
{
echo "hello world"
echo "nihao beijing"
}
#函数的调用
myfun
myfun
myfun
执行结果:
2.5.2 函数传参
使用参数同在一般脚本中使用特殊变量 $1,$2 …$9一样
代码示例:
#! /bin/bash
myadd()
{
#shell函数传参
#shell函数中想要传参,不能在函数名后的括号里面定义
#而是使用$1 $2 ...保存外部传入的值
A=$1
B=$2
SUM=`expr $A + $B`
echo "$A + $B = $SUM"
}
#带参数的shell函数的调用
myadd 100 200
num1=666
num2=777
myadd $num1 $num2
执行结果:
2.5.3 函数返回值
函数可以使用return 提前结束并带回返回值
代码示例:
#! /bin/bash
myadd()
{
A=$1
B=$2
SUM=`expr $A + $B`
return $SUM
}
myadd 10 20
#函数的返回值一般通过$?可以获取到,但是$?获取到的最大值是255,如果超过这个值,会出错
echo "$?"
myadd 100 200
#echo "$?"
#在shell中,除了()中定义的变量,只要不做任何修饰,
#都可以认为是全局变量,可以再任意一个位置调用
echo "SUM = $SUM"
执行结果: