一、Shell 种类与归属
- Unix与Linux常见的Shell脚本解释器有bash,sh,csh,ksh等(PS: bash 完全兼容sh)
- bash : linux 默认的shell
- sh : unix 默认的shell
- csh : 是linux中比较大的内核,命令指向/bin/tcsh的,可以认为csh为tcsh
- ksh : 兼容商业发行版
2. shell 本身是用C语言编写的程序,它是用户操作unix/linux的桥梁,用户的大部分工作是通过shell来完成的
shell 既是一种命令语言,也是一种程序设计语言,他有自己的编程结构,变量,参数和很多高级语言中的控制语句与循环语句
shell 两种交互: ① 交互型:用户输入一条,shell就执行一条 ② 批处理:用户写多条命令存在于一个shell脚本中,shell一次性完成
二、Shell 与其他语言区别
- shell 是一种解释性语言(运行时编译),而C、C++、Java 等高级语言是编译型语言(程序运行前,预编译)
- shell 相对于其他高级语言对文件处理更为方便快捷,但是运行的效率不如编译后的高级语言
三、不建议使用 Shell 场景
- 资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash等等)。
- 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算(这种情况一般使用C++或FORTRAN 来处理)。
- 有跨平台(操作系统)移植需求(一般使用C 或Java)。
- 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)。
- 对于影响系统全局性的关键任务应用。
- 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵、破解、恶意破坏等等。
- 项目由连串的依赖的各个部分组成。
- 需要大规模的文件操作。
- 需要多维数组的支持。
- 需要数据结构的支持,比如链表或数等数据结构。
- 需要产生或操作图形化界面 GUI。
- 需要直接操作系统硬件。
- 需要 I/O 或socket 接口。
- 需要使用库或者遗留下来的老代码的接口。
- 私人的、闭源的应用(shell 脚本把代码就放在文本文件中,全世界都能看到)。
四、简单 Shell 示例
[root@hadoop09-linux tmp]# cat firstShell.sh # .sh 是shell文件类型
#!/bin/bash # bash文件头 #!作为约定标记,告诉系统需要什么解释器运行
# Description: shell simple example # # 简述该shell文件功能作用,这里输出一句话 echo welcom to use shell # shell执行命令
[root@hadoop09-linux tmp]# sh firstShell.sh # 使用sh命令执行shell
welcom to use shell
[root@hadoop09-linux tmp]# ll # 查看shell运行权限
total 4
-rw-r--r--. 1 root root 76 Sep 7 18:22 firstShell.sh # 可以看到所有用户组都没有运行权限
[root@hadoop09-linux tmp]# chmod a+x firstShell.sh # 赋予权限
[root@hadoop09-linux tmp]# ./firstShell.sh # ./执行shell
welcom to use shell # 输出结果
再看一个例子:
功能:让用户选择Y 或N,如果使用者输入n 或N 时,就显示“Oh,interrupt”,如果不是Y/y/N/n 之内的其他字节,就显示“I don't know what your choice is”
[root@hadoop09-linux tmp]# cat secondShell.sh
#!/bin/bash
#------------------------------------------------
#当运行一个脚本的时候,这个脚本会让用户选择Y 或N ,
#如果使用者输入Y 或y 时,就显示“OK,continue”
#如果使用者输入n 或N 时,就显示“Oh,interrupt”
#如果不是Y/y/N/n 之内的其他字节,就显示“I don't know what your choice is”
#------------------------------------------------ echo "please input Y/y or N/n"
read words
if [ "$words" = "Y" -o "$words" = "y" ];then
echo "OK,continue"
elif [ "$words" = "N" -o "$words" = "n" ];then
echo "Oh,interrupt"
else
echo "I don't know what your choice is"
fi
[root@hadoop09-linux tmp]# sh secondShell.sh
please input Y/y or N/n
y
OK,continue
[root@hadoop09-linux tmp]# sh secondShell.sh
please input Y/y or N/n
n
Oh,interrupt
[root@hadoop09-linux tmp]# sh secondShell.sh
please input Y/y or N/n
a
I don't know what your choice is
五、Shell 注释
shell 中没有多行注释,需要注释时只需在行首加上#
六、Shell 变量
变量类型
- 局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量
- 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量
- shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
- 特殊变量:$0;$1,$2,$3...$9;$#;$*;$@;$?;$$
[root@hadoop09-linux tmp]# cat thirdShell.sh
#!/bin/bash
#Description...# num=6 # 定义变量,有点像javascript
echo "num:$num" # 没加{},{}可加可不加
num=8 # 重新给变量赋值,前面赋值将被覆盖
echo "num:${num}" # 加上{} 有时解释器会解释成 "num:$num" 加上花括号是为了方便系统识别变量边界,为变量加上{}是个好习惯
unset num # 删除变量
echo "num:${num}"
[root@hadoop09-linux tmp]# sh thirdShell.sh
num:6
num:8
num: # 删除变量后,变量为空
[root@hadoop09-linux tmp]# cat fourthShell.sh
#!/bin/bash
echo "File Name: $0" # $0 当前shell文件名
echo "First Parameter : $1" # $1 shell文件后跟的第一个参数
echo "First Parameter : $2" # $2 shell文件后跟的第二个参数
echo "Quoted Values: $@" # $@ 传递给shell的所有参数,参数作为最小单位输出
echo "Quoted Values: $*" # $* 传递给shell的所有参数 与$@有细微不同,参数作为整体输出
echo "Total Number of Parameters : $#" # $# 传递给shell的参数个数
echo "result : $?" # $? 上一个执行的命令结果-->0:成功;1:失败 有时一些命令返回其它值,表示不同类型的错误
echo "shell pid : $$" # $$ 当前shell进程ID
[root@hadoop09-linux tmp]# sh fourthShell.sh num1 num2
File Name: fourthShell.sh
First Parameter : num1
First Parameter : num2
Quoted Values: num1 num2
Quoted Values: num1 num2
Total Number of Parameters : 2
result : 0
shell pid : 3055
七、Shell 替换
- 特殊字符替换:使用 -e 替换字符串中的转移字符:\\(反斜杠);\a(警报);\b(退格键);\f(翻页);\n(换行);\r(换行);\t(水平制表符tab);\v(垂直制表符)
- 命令替换:预先执行命令 `command`(反引号)
- 变量替换:类似java三元运算符,或sql中替换函数,例 ${var:-word}
[root@hadoop09-linux tmp]# cat fifthShell.sh
#!/bin/bash
num=10
echo "this num is ${num} \n" # 未加 -e 转义,将输出原本模样
echo -e "this num is ${num} \n" # 将\n转义,将换行
Date=`date` # 预先执行date命令,将结果赋给Date
echo "this date is ${Date}"
str="string"
echo "this str is ${str}" # str原本模样
echo "this str is ${str:+"newString"}" # 表示str如果存在,则将newString替换输出
unset str # 删除变量str
echo "this str is ${str:-"newString"}" # 表示str如果不存在或为空,则将newString替换输出
echo "this str is ${str:?"str is empty"}" # 表示str如果不存在或为空,则报错,shell停止,报错内容为替换内容
[root@hadoop09-linux tmp]# sh fifthShell.sh
this num is 10 \n
this num is 10 this date is Wed Sep 7 19:48:16 PDT 2016
this str is string
this str is newString
this str is newString
fifthShell.sh: line 13: str: str is empty
八、Shell 运算符 (PS: if [] 中 [ ... ] 与 test ... 作用一样)
算数运算符 (最常使用运算工具:expr 2 + 2 ;注意运算时空格一定要有)
num=`expr 2 + 2`
num=`expr 4 - 2`
num=`expr 4 \* 2` # 乘法需要加\转义
num=`expr 4 / 2`
num='expr 4 % 2'
num=$num1
if [ $num1 == $num2 ]
if [ $num1 != $num2 ]
关系运算符
if [ $str1 -eq $str2 ] # 是否相等
if [ $str1 -ne $str2 ] # 是否不相等
if [ $str1 -gt $str2 ] # 是否大于>
if [ $str1 -lt $str2 ] # 是否小于<
if [ $str1 -ge $str2 ] # 是否大于等于>=
if [ $str1 -le $str2 ] # 是否小于等于<=
布尔运算符
if [ $str1 != $str2 ] # !非运算
if [ $str1 -eq $str2 -a $str3 -eq $str4 ] # -a and
if [ $str1 -eq $str2 -o $str3 -eq $str4 ] # -o or
字符串运算符
if [ $str1 = $str2 ] # 判断两个字符串是否相等
if [ $str1 != $str2 ] # 判断两个字符串是否不相等
if [ -z $str1 ] # 判断字符串长度是否为零,为0则true
if [ -n $str1 ] # 判断字符串长度是否不为零,不为0则true
if [ $str1 ] # 判断字符串是否不为空,不为空则true
文件测试运算符
if [ -r $file ] # 文件是否可读
if [ -w $file ] # 文件是否可写
if [ -x $file ] # 文件是否可执行
if [ -f $file ] # 是否是普通文件
if [ -d $file ] # 是否是目录目录文件
if [ -b $file ] # 是否是块设备文件
if [ -c $file ] # 是否是字符设备文件
if [ -s $file ] # 文件是否有内容
if [ -e $file ] # 文件(包括各类型文件)是否存在
九、Shell 数据
- 字符串
- 单引号
- 单引号内$无效,原文本输出
- 单引号内不能再出现单引号
- 双引号
- 双引号内可以有变量
- 可以出现转义字符
- 双引号内可以再次出现双引号
- 拼接字符串
- "words"$str
- "words$str"
- "words,"$str""
- "words,${str}"
- 获取字符串长度
- ${#str}
- 提取字符串
- ${str:1:4}
- 查找字符串
- str="hello linux"
- echo `expr index "${str}" ll` # 注意其中的双引号不能去掉
- 单引号
- 数组
- 定义数组
- arrays=(v1 v2 v3 v4 v5) # 以空格分隔
- arrays[0]=v1 # 也可以单个赋值
- 读取数组
- $arrays/${arrays} # 默认读取第一个值
- ${arrays[n]} # 读取第n+1个值
- ${arrays[*]}/${arrays[@]} # 读取数组中所有值
- 获取数组长度
- ${#arrays[n]} # 读取单个值的长度
- ${#arrays[*]}/${#arrays[@]} # 读取整个数组长度
- 定义数组
十、Shell echo与printf命令
echo
显示转义字符 # "\"words...\""
显示变量
显示换行 # 转义换行需要加 -e
显示不换行 # 转义不换行需要加 -e
显示结果重定向到文件 # "words..." > file 注:file若不存在则新建并录入
原样输出 # 使用单引号原样输出
显示命令执行结果 # echo `date`
printf
格式化输出,是echo的增强版,类似C语言中的printf 需要注意的是:printf并不会自动换行,需要自行加上\n
格式:printf format-string data
printf "%s %s \n" kk ll mm # 每隔一个单词格式化一次%s 代表一个单词,和awk命令中的print有些区别,awk有自己的格式化方式
十一、Shell if else 语句
if [ expression ]
then
Statement(s) to be executed if expression is true
fi if [ expression ];then
Statement(s) to be executed if expression is true
fi if [ expression ];then
Statement(s) to be executed if expression is true
else
Statement(s) to be executed if expression is false
fi if [ expression ];then
Statement(s) to be executed if expression is true
elif [ expression2 ];then
Statement(s) to be executed if expression2 is true
else
Statement(s) to be executed if expression1 and expression2 all false
fi
十二、Shell while 语句
while command
do
Statement(s) to be executed if command is true
done
十三、Shell until 循环
until command # 与while 判断正好相反
do
Statement(s) to be executed until command is true
done
十四、Shell for 循环
for 变量 in 列表
do
command1
command2
...
commandN
done
# 示例1 : 遍历1~5,并输出
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
# 示例2 : 遍历当前用户家目录下的所有以.bash开头的文件
for FILE in $HOME/.bash*
do
echo $FILE
done
十五、Shell case 语句
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac # 示例1
echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read aNum
case $aNum in
1) echo 'You select 1'
;;
2) echo 'You select 2'
;;
3) echo 'You select 3'
;;
4) echo 'You select 4'
;;
*) echo 'You do not select a number between 1 to 4'
;;
esac # 示例2
option="${1}"
case ${option} in
-f) FILE="${2}"
echo "File name is $FILE"
;;
-d) DIR="${2}"
echo "Dir name is $DIR"
;;
*)
echo "`basename ${0}`:usage: [-f file] | [-d directory]"
exit 1 # Command to come out of the program with status 1
;;
esac
十七、Shell 跳出循环
1. break 允许跳出所有循环
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done
PS:break n 表示跳出第n层循环
#!/bin/bash
for var1 in 1 2 3
do
for var2 in 0 5
do
if [ $var1 -eq 2 -a $var2 -eq 0 ]
then
break 2
else
echo "$var1 $var2"
fi
done
done
2.continue 允许跳出当前循环体
#!/bin/bash
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "Your number is $aNum!"
;;
*) echo "You do not select a number between 1 to 5!"
continue
echo "Game is over!"
;;
esac
done
运行发现echo "Game is over" 永远不会被执行
同break一样,continue后面跟一个数字表示跳出第几层循环
#!/bin/bash
NUMS="1 2 3 4 5 6 7"
for NUM in $NUMS
do
Q=`expr $NUM % 2`
if [ $Q -eq 0 ]
then
echo "Number is an even number!!-"$NUM
continue 2
fi
echo "Found odd number"
done
十八、Shell 函数/函数参数
function_name () {
list of commands
[ return value ]
}
# 或
function function_name () {
list of commands
[ return value ]
} # 示例1
Hello () { #方法定义
echo "Url is http://xxx/xxx/"
}
Hello #方法调用,必须先定义方法 # 示例2 带return
funWithReturn(){
echo "The function is to get the sum of two numbers..."
echo -n "Input first number: "
read aNum
echo -n "Input another number: "
read anotherNum
echo "The two numbers are $aNum and $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
# Capture value returnd by last command
ret=$?
echo "The sum of two numbers is $ret !" # 示例3 嵌套
number_one () {
echo "Url_1 is http://xxx/xxx/"
number_two
}
number_two () {
echo "Url_2 is http://xxx/xxx/"
}
number_one
删除方法定义
$unset .f function_name
函数传参
#!/bin/bash
funWithParam(){
echo "The value of the first parameter is $1 !" # 1
echo "The value of the second parameter is $2 !" # 2
echo "The value of the tenth parameter is $10 !" # 10
echo "The value of the tenth parameter is ${10} !" # 34
echo "The value of the eleventh parameter is ${11} !" # 73
echo "The amount of the parameters is $# !" # 参数个数 12
echo "The string of the parameters is $* !" # 传递给函数的所有参数 1 2 3 4 5 6 7 8 9 34 73
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
十九、Shell 输入输出重定向
1. 输出重定向
$ command > file # 示例
$ who > users # 文件不存在则新建,存在则覆盖 $ command >> file # 示例
$ echo line 2 >> users # 文件不存在则新建,存在则追加
$ cat users
line 1
line 2
$
2. 输入重定向
$ command < file # 示例
$ wc -l < users
2
$ $ command << delimiter
document
delimiter # 示例1
$wc -l << EOF
This is a simple lookup program
for good (and bad) restaurants
in Cape Town.
EOF
3
$ # 示例2
cat << EOF
This is a simple lookup program
for good (and bad) restaurants
in Cape Town.
EOF # 示例3
filename=test.txt
vi $filename <<EndOfCommands
i
This file was created automatically from
a shell script
^[
ZZ
EndOfCommands
如果执行某个命令,却不想让结果输出来,可以重定向到/dev/null
$ command > /dev/null
二十、Shell 文件包含
像其他语言一样,shell中也可以包含外部脚本
. filename
#或
source filename # 示例1 # 脚本1 1.sh 不必有执行权限
#!/bin/bash
url="http://xxx/xxx/xxx.html" #脚本2 2.sh 必须有执行权限
#!/bin/bash
. ./1.sh
echo $url