Shell 是用户使用操作系统的命令接口,Shell 接受用户以命令行方式输入的命令,也能解释执行由 shell 命令所编写的 shell 程序。用户可以将 Linux 的命令以编程的方式组合起来提交给 shell, shell 负责解释执行这种程序设计语言所编写的代码。这种程序设计语言构成的文件成为 shell script 或 shell 脚本。
Shell 脚本是包含一个或多个 shell 命令的集合,这些命令按照编写的顺序而被依次执行。建立 shell 脚本文件与建立普通文件一样,可以用 vi、emacs等来建立。
在 Bash shell 使用 中有 shell 变量、万用字符、数据流重导向、管线命令等内容,在此就不再介绍,只介绍一些其他的知识。
一、shell 基础知识
单引号、双引号与反引号
- 双引号(" "):将“$”、“"”、“`”和“\”进行替换,作为特殊字符;
- 单引号(' '):所有特殊字符均为普通字符;
- 反引号(` `)“:字符解释成命令,并返回执行结果。
运行脚本
运行 shell 脚本文件的三种方法:
- 把文件的权限设为可读、可执行,然后直接执行该文件;
- 使用shell 的启动命令来执行:bash 脚本名及其参数;
- 使用 bash 内部命令 source 或 . 来执行:source 脚本名及其参数 或 . 脚本名及其参数。
位置变量
位置变量是依据出现在命令行上的参数的位置来确定的变量。在调用 shell 程序的命令行中,参数的位置定义如下:
命令 参数1 参数2 ...
位置变量与命令行参数对应关系为:
$0:对应执行的命令名
$1:对应第一个参数
$2:对应第二个参数
... ...
$9:对应第九个参数
如果命令行上参数多余 9 个时,需要使用 shift 命令移动参数位置。每执行一次 shift 命令,变量 0 不变,从 1 开始位置参数左移一位。
数组变量
数组的索引从数字 0 开始,引用时需要使用中括号([])。
数组有两个特殊的索引:“*” 和 “@”,作用都是提取整个数组元素。但在加了双引号之后:
“@”表示原数组复制成一个新的数组,新数组与原数组一样;
“*”表示把原数组所有内容作为新数组的一个元素,新数组只有一个元素。
数组的个数:${ #name[*] }, name 为数组名。
数组元素的长度:${ #name[num] }, num 为数组下标。
name=("s1" "s2" "s3") #定义一个数组变量
echo ${ name[0] } #引用输出第一个变量
list=("${name[@]}") #list 数组元素为 "s1" "s2" "s3"
class=("${name[*]}") #class 为"s1 s2 s3"
echo ${#name[*]} #数组元素个数
echo ${#name[0]} #第一个元素的长度
特殊变量
shell中包含一些预定义的特殊变量,与环境变量类似。该类变量不能重新定义,均由“$”与另一个符号组成。常用的特殊变量:
- $# :实际位置参数个数(不包括脚本名)
- $* :所有位置参数组成的字符串
- $! :上一个后台命令对应的进程号
- $? :最近一条命令执行后的状态
- $$ :当前进程号 PID
算术运算
expr 执行简单的整数操作,可使用:+、-、\*、/、% 五种运算符,运算符前后要有空格。
let 不需要在变量之前加美元符号,但必须将单个或带有空格的表达式用双引号引起来。
a=3; b=6
x=`expr $b / $a` #整除,反引号
y=`expr $a + 1` #a 加 1
let x=x+1 #x 加 1
二、控制结构
if 结构
语法:if 表达式;then
命令表
[ else 命令表 ]
fi
else 可以省略,fi 表示 if 结束。if 结构还提供 elif 语句,相当于 else if。
#!/bin/bash #程序第一行,表示调用 bash 来执行脚本程序
#filename:ifcon #程序名
read -p "输入字符串1" str1
read -p "输入字符串2" str2
if [ $str1 = $str2 ] # 判断条件
then
echo "两个字符串相同"
else
echo "两个字符串不相同"
fi #结束 if
case 结构
语法:case 表达式 in
模式1[|模式12 ] ...) 命令表1;;
模式2[|模式22 ] ...) 命令表2;;
... ...
*) 命令表n;;
esac
说明:
- 每一个分支可以有多个模式,但必须以“)”结尾;
- 模式之间用“|”表示“或”的关系;
- 每个分支以“;;”表示分支结束;
- 分支 *)表示前面都不匹配时的处理,“esac” 表示 case 结束;
- 模式中可以使用万用字符。
#!/bin/bash
#filename:case_con
a=$1 #取第一个参数赋给变量 a
case $a in
S|s)
case $2 in
zou) echo "zou 同学,你好" ;;
han) echo "han 同学,你好" ;;
*) echo "未找到该名字" ;;
esac
;; #第一个大分支结束
T|t)
case $2 in
zhou) echo "zhou 老师,您好" ;;
zhang) echo "zhang 老师,您好" ;;
*) echo "未找到该名字" ;;
esac
;; #第二个大分支结束
*) echo "error input" ;;
esac
select 结构
语法:select 变量 in 列表
do
命令表
done
select 生成一个数字化的菜单,并提示用户选择,用户只需按下对应菜单项的数字即可完成选择。
select 总是与 case 一起使用,先让用户选择,再基于选择执行相应的命令。
#!/bin/bash
#filename:select_con
select N in 周五 周六 #菜单由“周五、周六”组成
do
case $N in #根据选择,执行相应的菜单功能
周五) echo "周五是工作日";;
周六) echo "周六可以休息";;
*) echo "选择错误"
break;;
esac
done
while 循环
通常在无法确定循环次数时,使用 while 循环;当确切知道循环次数时,使用 for 循环。
用法:while 表达式
do
命令表
done
for 循环
用法:for 变量 [ int 列表 ]
do
命令表
done
“列表”通常由一系列以空格为分隔的字符串组成,依次将“列表”中的字符串赋给“变量”,直到取完。
如果“in 列表”省略,则会将当前执行脚本中的位置参数作为“列表”。
break 和 continue 语句可以从循环结构中跳出来,如 while、for、select 等循环。break 和 continue 都可以在其后加上数字,指明要跳出的结构的层数。
#!/bin/bash
#filename:while_for
i=1
while [ $i -le 5 ] #“$i -le 5”为判断条件,当变量 i 小于等于 5 时循环
do
echo "while 第 $i 次循环,输出:$i"
i=$(($i+1)) #变量 i 加 1,也可用 let i=i+1 或 expr
done
for j in 1 2 3 4 5 #变量 j 依次取值 1、2、3、4、5
do
echo "for 第 $j 次循环,输出:$j"
let j=j+1;
done
三、条件测试
test 命令是 shell 编程中条件判断最常用的测试命令。test 命令的语法:test 表达式。test 命令也可以由方括号取代:[ 表达式 ]。
文件属性测试
常用的文件测试表达式格式:
#!/bin/bash
#filename:filetype
read -p "input the file name:" file
if test -c $file ; then #判断是否是字符设备
ls -l $file
elif test -d $file ; then #判断是否是目录
cd $file
else
echo "$file 不是字符设备,也不是目录"
fi
数值测试
常用的数值测试表达式:
#!/bin/bash
#filename:numbertest
read -p "input n1:" n1
read -p "input n2:" n2
if [ $n1 -eq $n2 ] ; then #判断两个变量的值是否相等
echo "n1 等于 n2"
elif [ $n1 -gt $n2 ] ; then #判断n1 是否大于 n2
echo "n1 大于 n2"
else
echo "n1 小于 n2"
fi
字符串测试
#!/bin/bash
#filename:stringtest
read -p "input s1:" s1
read -p "input s2:" s2
if [ -z $s1 ] ; then #判断字符串 s1 长度
echo "s1 长度为 0"
elif [ $s1 = $s2 ] ; then #判断 s1 与 s2 是否相等
echo "s1 与 s2 相等,且长度不相等"
fi
逻辑运算符
常用的逻辑运算符:
#!/bin/bash
#filename:logical_test
read -p "input n1:" n1
read -p "input n2:" n2
if [ $n1 -ge $n2 -o $n1 -eq $n2 ] ; then #判断 n1 是否大于等于 n2
echo "n1 大于或等于 n2"
elif [ $n1 -ge 10 -a $n1 -le 30 ] ; then #判断n1 大于 10 且小于 30
echo "10 <= $n1 <= 30"
fi
四、函数
shell 函数类似于 shell 脚本,里面存放了一系列指令。不同的是,shell 函数可以有返回值,而脚本没有。
函数定义: function 函数名
{
命令表
[return]
}
函数中的 return 用于返回函数中最后一个命令的退出状态或给定的参数值。
可以在函数中使用 exit 或 break 语句来中断函数执行。
可以使用命令“declare -f”显示定义的函数。
shell 函数定义可以放在“~/.bash_profile”文件中,也可以放在使用该函数的脚本中,还可以直接放在命令行。
无论在函数内外,没有用关键字 local 声明的变量都是全局变量。
#!/bin/bash
#filename:hellofunc
function hello #定义函数
{
s="static" #全局变量
local a="temp" #局部变量
if test $1 = "T" ; then
echo "hello teacher $2"
return 1
else
echo "error input"
return 0
fi
}
echo $s #输出 static
echo $a #变量 a 未声明,无输出
hello T zhou #执行函数,位置参数 T,zou
a=$? # 变量 a 的值为函数 return 返回值
如果需要使用的函数保存在其他脚本文件中,可以通过 “source”或“.” 命令将函数载入到内存中,供当前脚本使用。
#!/bin/bash
#filename:sourcefunc
source hello #使用 source 载入文件 hellofunc
read -p "input two para:" s1 s2
hello $s1 $s2 #调用 hello 函数,位置参数为 $s1 $s2