是时候整理一番shell了。先说说shell的特点与适用:
Shell有两种执行命令的方式:
交互式(Interactive):解释执行用户的命令,用户输入一条命令,Shell就解释执行一条。
批处理(Batch):用户事先写一个Shell脚本(Script),其中有很多条命令,让Shell一次把这些命令执行完,而不必一条一条地敲命令。
Shell是一种脚本语言,那么,就必须有解释器来执行这些脚本。Unix/Linux上常见的Shell脚本解释器有bash、sh、csh、ksh等,习惯上把它们称作一种Shell。我们常说有多少种Shell,其实说的是Shell脚本解释器。
bash是Linux标准默认的shell,本教程也基于bash讲解。bash由Brian Fox和Chet Ramey共同完成,是BourneAgain Shell的缩写,内部命令一共有40个。Linux使用它作为默认的shell是因为它有诸如以下的特色:
- 可以使用类似DOS下面的doskey的功能,用方向键查阅和快速输入并修改命令。
- 自动通过查找匹配的方式给出以某字符串开头的命令。
- 包含了自身的帮助功能,你只要在提示符下面键入help就可以得到相关的帮助。
因为Shell似乎是各UNIX系统之间通用的功能,并且经过了POSIX的标准化。因此,Shell脚本只要“用心写”一次,即可应用到很多系统上。因此,之所以要使用Shell脚本是基于:
- 简单性:Shell是一个高级语言;通过它,你可以简洁地表达复杂的操作。
- 可移植性:使用POSIX所定义的功能,可以做到脚本无须修改就可在不同的系统上执行。 开发容易:
- 在短时间内完成一个功能强大又妤用的脚本。
但是,考虑到Shell脚本的命令限制和效率问题,下列情况一般不使用Shell:
- 资源密集型的任务,尤其在需要考虑效率时(比如,排序,hash等等)。
- 需要处理大任务的数学操作,尤其是浮点运算,精确运算,或者复杂的算术运算(这种情况一般使用C++或FORTRAN 来处理)。
- 有跨平台(操作系统)移植需求(一般使用C 或Java)。 复杂的应用,在必须使用结构化编程的时候(需要变量的类型检查,函数原型,等等)。
- 对于影响系统全局性的关键任务应用。 对于安全有很高要求的任务,比如你需要一个健壮的系统来防止入侵、破解、恶意破坏等等。
- 项目由连串的依赖的各个部分组成。 需要大规模的文件操作。 需要多维数组的支持。 需要数据结构的支持,比如链表或数等数据结构。
- 需要产生或操作图形化界面 GUI。 需要直接操作系统硬件。 需要 I/O 或socket 接口。 需要使用库或者遗留下来的老代码的接口。
- 私人的、闭源的应用(shell 脚本把代码就放在文本文件中,全世界都能看到)。
再说说shell的语法:我感觉就是语法弱化版的c,但是可以很方便地执行各种命令,这是c不能简单做到的
#!/bin/bash
echo "Hello World !"
“#!” 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。echo命令用于向窗口输出文本。
chmod +x ./test.sh #使脚本具有执行权限
./test.sh #执行脚本
读入:
echo "What is your name?"
read PERSON
echo "Hello, $PERSON"
注释:顶格写#之后的就是注释,只有单行注释
如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果
变量:
定义变量时,变量名不加美元符号
,变量名和等号之间不能有空格,使用一个定义过的变量,只要在变量名前面加美元符号
即可,已定义的变量,可以被重新定义,使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。使用 unset 命令可以删除变量。
shell运行时存在3种变量类型:
运行shell时,会同时存在三种变量:
1) 局部变量
局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2) 环境变量
所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量
shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
变量名只能包含数字、字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量。
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$# 传递给脚本或函数的参数个数。
$* 传递给脚本或函数的所有参数。
$@ 传递给脚本或函数的所有参数。
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。
$? 上个命令的退出状态,或函数的返回值。
$$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。
转义字符:
\\ 反斜杠
\a 警报,响铃
\b 退格(删除键)
\f 换页(FF),将当前位置移到下页开头
\n 换行
\r 回车
\t 水平制表符(tab键)
\v 垂直制表符
echo 命令的 -E 选项禁止转义,默认也是不转义的;使用 -n 选项可以禁止插入换行符。
Shell可以先执行命令,将输出结果暂时保存,在适当的地方输出:
`command`
注意是反引号,不是单引号,这个键位于 Esc 键下方。
变量替换:
${var} 变量本来的值
${var:-word} 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。
${var:=word} 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。
${var:?message} 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。
${var:+word} 如果变量 var 被定义,那么返回 word,但不改变 var 的值。
Bash 支持很多运算符,包括算数运算符、关系运算符、布尔运算符、字符串运算符和文件测试运算符。
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。expr相关见本文末。
关系运算符:
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 true。
-ne 检测两个数是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
字符串运算符:
**= 检测两个字符串是否相等,相等返回 true**。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否为0,不为0返回 true。 [ -z $a ] 返回 true。
str 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。
注意这里的相等并不是我们认为的 == 而是=就行了
文件测试运算符:
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是具名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。
单引号:
单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
双引号:
双引号的优点:
双引号里可以有变量
双引号里可以出现转义字符
字符串操作:
#拼接字符串
your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
#获取字符串长度
string="abcd"
echo ${#string} #输出 4
#提取子字符串
string="alibaba is a great company"
echo ${string:1:4} #输出liba
数组:
array_name=(value0 value1 value2 value3)
使用@ 或 * 可以获取数组中的所有元素:
${array_name[*]}
${array_name[@]}
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}
printf:
printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。printf 由 POSIX 标准所定义,移植性要比 echo 好。
printf 不像 echo 那样会自动换行,必须显式添加换行符(\n)
结构化语句:
if xxx
then xxx
elif xxx
then xxx
elif xxx
then xxx
else xxx
fi
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
例如写一个执行命令判断选项的:
#!/bin/bash
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
for循环:
#!/bin/bash
for FILE in $HOME/.bash*
do
echo $FILE
done
while:
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
echo "Yeah! great film the $FILM"
done
until:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done
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
在嵌套循环中,break 命令后面还可以跟一个整数,表示跳出第几层循环。
continue同c语言一样,仅仅跳出本次,continue 后面也可以跟一个数字,表示跳出第几层循环。
函数:
定义:
function function_name () {
list of commands
[ return value ]
}
注意点归纳如下:
1、Shell 函数必须先定义后使用
2、函数名前加上关键字 function 是可选的
3、函数返回值,可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值。
4、Shell 函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败。如果 return 其他数据,比如一个字符串,往往会得到错误提示:“numeric argument required”。
如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值
5、调用函数只需要给出函数名,不需要加括号。
6、像删除变量一样,删除函数也可以使用 unset 命令,不过要加上 .f 选项:
unset.f fun_name
7、在Shell中,调用函数时可以向其传递参数。在函数体内部,通过
n的形式来获取参数的值,例如,
1表示第一个参数,
2表示第二个参数...注意,第10个参数要写成:
{10}的形式
8、函数内部特殊的参数
$# 传递给函数的参数个数。
$* 显示所有传递给函数的参数。
$@ 与$*相同,但是略有区别,请查看Shell特殊变量。
$? 函数的返回值。
输入输出重定向:
#输入重定向
command > file
#输出重定向
command < file
command > file 将输出重定向到 file。
command < file 将输入重定向到 file。
command >> file 将输出以追加的方式重定向到 file。
n > file 将文件描述符为 n 的文件重定向到 file。
n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m 将输出文件 m 和 n 合并。
n <& m 将输入文件 m 和 n 合并。
<< tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。
重定向方式——嵌入文档:
command << delimiter
document
delimiter
它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。开始的delimiter前后的空格会被忽略掉。
包含外部脚本:
2种方法:
. filename
#注意点 跟 filename之间有个空格
或者:
source filename
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
下面简单介绍expr命令:
用途
求表达式变量的值。
语法
expr Expression
描述
expr 命令读入 Expression 参数,计算它的值,然后将结果写入到标准输出。
您必须对 Expression 参数应用以下规则:
- 用空格隔开每个项。
- 用 / (反斜杠) 放在 shell 特定的字符前面。
- 对包含空格和其他特殊字符的字符串要用引号括起来。
用法:
1,关系或数学运算,
- Expression1 /| Expression2 返回 Expression1 如果它不是空值或者 0 值的话,否则返回 Expression2 。
- Expression1 /& Expression2 返回 Expression1 如果两个表达式都不是空值或者 0 值,否则返回一个 0 值。
- Expression1 { =, />, />=, /<, /<=, != } Expression2 如果两个表达式都是整数,返回整数比较的结果;否则它返回的是字符串比较的结果。
- Expression1 {+, - } Expression2 整数值变量的加或者减。
- Expression1 { /*, /, % } Expression2 整数值变量的乘、除或者提供除法的余数。
2、匹配字符串
Expression1 : Expression2
将 Expression1 的运算得到的字符串与 Expression2 的运算结果的正则表达式模式进行比较。正则表达式语法与 ed 命令相同,除了所有的模式固定到字符串的开始之外(也就是说,只有以字符串的第一个字符开始的序列才被正则表达式匹配)。因此 ^ (插入符号) 在这种情况下就不是特殊字符。
一般地,匹配运算符返回匹配的字符个数(失败的时候返回 0)。
如果模式包含了一个子表达式,也就是:
/( Expression /),则会返回包含了实际匹配的字符的字符串。
expr match String1 String2 #与 Expression1 : Expression2 相同。
expr length String1 #返回 String1 的长度。
expr index String1 String2 #返回 String1 中包含 String2 中任意字符的第一个位置。
expr substr String1 StartPosition Length
返回一个以 StartPosition 的字符开始的在 String1 中的字符串,并且是 Length 长度的字符串。
例如:
要想返回 $SHELL 环境变量 /usr/bin/ksh 的长度,请输入:
expr length $SHELL
显示以下内容:
12
要想返回 "de" 字符串中的任何一个字符在 "abcdef" 中出现的第一个位置,请输入:
expr index abcdef de
显示以下内容:
4
/usr/bin/expr 包含了 expr 命令。