shell脚本语言
shell是一个命令解析器,我们可以把很多要执行的命令以某种语言的方式组织起来交给shell去执行,这种命令的组织方式称为shell脚本语言。
xxx.sh
是一个多个命令的组织文件,即shell脚本文件;shell脚本文件是一个普通文件,是shell基本语言的方式。
./xxx.sh
把这些命令全部交给shell命令解析器去执行。命令解析器其实就是一个程序,如下所示:
/bin/bash
/bin/sh
/bin/dash
#都是shell解析程序
1. shell脚本文件
一个最简单的shell脚本文件: test.sh
#!/bin/dash #指定shell的解析程序
echo "hello world" #打印输出hello world
首先vi test.sh
,然后编写程序,此后可能需要改变脚本文件test.sh
的权限即chmod +x test.sh
,最后运行脚本文件即./test.sh
2. shell变量
在shell脚本文件中,我们可以定义变量。shell变量没有类型的概念,或者说理解为全部都是字符串类型,shell变量不需要定义,直接用赋值就行。我们大致可以把shell变量分为4种。
变量赋值格式:变量名=值 即 name=value
变量的引用格式:$变量名 或者 ${变量名}
例如:
num = 520
k1=$num
k2=${num}
2.1 shell自定义变量
如:
num=520
data=`cmd`
#`反撇号引用里面的cmd表示的那个命令或者说执行程序或者脚本的输出结果
data=`ls -l`
echo $data
2.2 shell位置变量
指的是传递给脚本文件或者函数的参数,如:
./test.sh 123 abc
在shell脚本或者shell函数中:
$0 #就表示第0个参数,即命令行程序名(./test.sh)
$1 #就表示第1个参数123
$2 $3···#表示的就是第2个参数到第···个参数的名字
$# #表示命令行参数的个数(不包括脚本名字)
$@ #包含所有的命令行参数
#$@ = $1 $2 $3···
$* #包含所有的命令行参数
#$* = $1C$2C$3···
#C是IFS的第一个字符
#IFS:内部域分隔符,即Internal Field Separetor
#默认的值 空格 Tab 换行符
#IFS="\t\n"
$? #表示前面一个命令或者程序的退出码(返回值exti值)
2.3 shell环境变量
环境变量:就是在同一个终端下面所有的进程都可以共享的变量
HOME:用户主目录的路径,如:
echo "$HOME"
PATH:命令或者可执行程序的搜索路径
例子:
gcc 1.c -o 1
./1
为什么需要./1
执行程序,而不是直接1就可以执行 ;因为需要找到1这个可执行程序 。./1
就是相当于告诉计算机在我当前目录下来找 ,如果没有告诉的话,则计算机就会去PATH指定的路径下查找1这个可执行文件。
可以通过echo "$PATH"
来查看默认的搜索路径,PATH的值为 dir1 : dir2 : dir3 … (每个路径之间用:隔开)。那么如果我们想直接敲1就可以执行程序的话 ,我们就应该把当前的路径添加到PATH中去
修改环境变量的值。
比如:
#export 环境变量名=新值
PATH=$PATH:/home/china
如果仅在终端修改环境变量, 这个环境变量的新值只在这个终端有效。那么如果想要全局也就是整个系统都有效的话 ,需要修改配置文件:/etc/profile
2.4 shell特殊变量
3. shell数组
语句格式:数组名[下标]=value
下标即0,1,2,3,4,…
引用单个数组元素格式:${数组名[下标]}
${数组名}
–>在bash/dash中代表引用的是数组的第一个元素,数组名相当于首地址。
引用整个数组(引用数组中所有的元素)元素格式:
${数组名[*]}
或者${数组名[@]}
4. shell脚本语句
4.1说明性语句(注释)
以#
开头的行就是注释行
4.2 功能性语句
1)任意的操作系统的命令(如echo
ls
cd
date
clear
…)
2)shell内部命令,自编程序 。如:read
在shell中表示从终端获取数据 ,如:
read a b #这个代码就是说从终端获取两个字符串。 第一个字符串保存在变量a中,第二个字符串就保存在变量b中。
4.2.1重定向问题
1)输入重定向
命令的输入请求通常是想标准输入设备(如键盘 )提交请求,我们也可以很方便地把输入重定向到一个设备/文件 ,这种情况我们称之为输入重定向。在获取输入的命令后添加 < filename
,该命令所有的输入都来自原filename指定的文件。
2)输出重定向
命令的输出通常提交给标准输出设备(如:终端)。也可以很方便地转向输出到另外一个设备/文件 ,则叫输出重定向。在输出命令的后面添加 > filename
,>
先把filename文件的内容清空,再把数据输出到filename指定的文件中。在输出命令的后面添加 >> filename
,>>
并不会清空filename原有的数据 ,而是以追加的形式把输出的数据输出到filename指定文件的末尾。
4.2.2 expr 算术运算命令
expr: 算术运算命令,expr主要用于进行简单的整数运算
包括: +
-
*
/
%
如: expr 1 + 2
//操作符和操作数之间一定要有一个空格!!!
练习: 写一个shell 计算两个整数的和,这两个整数通过参数传入or终端输入。
#!/bin/bash
sum=`expr $1 + $2`
echo $sum
read a b
sum=`expr $a + $b`
echo $sum
4.2.3 test测试命令
test: 测试命令
test语句可以测试三种对象 :字符串、 整数 、文件。
test测试成功(满足条件) 返回0
test测试不成功(条件不满足) 返回1
a.字符串测试
=
测试两个字符串的内容是否完全一样
test "abc" = "abc"
echo $?
!=
测试两个字符串内容是否 不一样
test "abc" != "abcd"
echo $?
-z zero
测试字符串是否为空
test -z ""
echo $?
-n not null
测试字符串 是否不为空
test -n "abc"
echo $?
以前的版本:
str=
test -z $str #==>test -z
会认为少了一个操作数,报语法错误 ,在测试字符串的时候 需要防止字符串为空做法:引用字符串 ,后面额外增加一个字符${str}x
如:判断变量a和变量b相不相等,test ${a}x = ${b}x
b.整数测试
a -eq b
equal即测试两个整数是否相等
如:test 1 -eq 2
a -ne b
not equal测试两个整数是否不相等
a -gt b
greadter than >
a -ge b
greadter or equal >=
a -lt b
litter than <
a -le b
litter or equal <=
c.文件测试
-d filename
测试filename是否为一个目录(directory)
test -d 1.c
echo $?
-f filename
测试filename是否为一个文件(file)
-L filename
测试filename是否为一个链接文件(Link)
-r filename
测试filename是否存在并且可读(read)
-w filename
测试filename是否存在并且可写(write)
-x filename
测试filename是否存在并且可执行(exculte)
-s filename
测试filename是否存在并且长度不为0(size)
f1 -nt f2
测试文件f1是否比f2更新,newer than
f1 -ot f2
测试文件f1是否比f2更旧 ,older than
最后 test
命令可以用[]
来代替
test expression
<==> [ expression ]
如: test "abc" = "abc"
相当于 [ "abc" = "abc" ]
4.2.4 test复合表达式
组合两个或者两个以上的表达式称之为复合表达式 ,为了使用复合表达式,你可以用test([])
内置的操作符,也可以用条件操作符。
1)使用test内置的操作符来创建复合表达式
-a
and 并且 (与)
-o
or 或者 (或)
例子: 我要测试从参数传入的第一个文件($1)是一个普通文件,并且他要比从参数传入的第二个文件($2)更新。
test -f $1 -a $1 -nt $2
例子: 5>4>3
test 5 -gt 4 -a 4 -gt 3
2)使用条件操作符来创建复合表达式
&&
||
!
例子:
[ 5 -gt 4 ] && [ 4 -gt 3 ]
4.3结构性语句(分支结构、循环结构)
4.3.1 单路分支结构if
if command ; then ... 语句列表 else #else和后面的语句可以不要,只要一个if语句,不是省略的意思 ... 语句列表 fi
例子:
从键盘输入一个变量 //read
如果这个字符串是hello的话 //if test =
那么我就输出good //echo
如果不是,我就输出nogood
read a
if [ $a = "hello" ] ; then
echo "good"
else
echo "nogood"
fi
练习: 从键盘输入一个整数 判断这个整数是不是奇数
read a
i=`expr $a % 2`
if [ $i -eq 1 ];then
echo "jishu"
else
echo "oushu"
fi
4.3.2 多路分支结构case
case 字符串变量 in 模式1) ...语句列表 ;; #类似于C语言中的break 但是shell中;;一定需要写的 模式2) ...语句列表 ;; ... 模式n) ...语句列表 ;; esac
case语句真正的强大之处在于他可以使用模式而不是固定的字符来匹配。一个模式是由一串正规字符和特殊的通配符(* ?)
组成的字符串。该模式可以是正则表达式(有限支持 )
*
shell通配符,代表任意多个(也可以是0个)字符。
?
shell通配符, 代表任意一个字符。
例子: 从键盘输入一个文件名 判断该文件是什么类型。
read filename
case $filename in
*.c)
echo "C source file"
;;
*.h)
echo "Header file"
;;
esac
4.3.3 循环结构for
for 变量名 in 单词表 do ...循环体语句 done
类似于C语言的for
for(i=0;i<3;i++ ) { ...循环体语句 }
“单词表”:以空白符分隔开的字符串列表
如:a b c abc 123
for执行的次数就是“单词表”中单词的个数 ,并且每次执行时 “变量”的值就取下一个单词的值。
例子:
for i in a b c abc
do
echo $i
done
for可以写成与C风格类型的循环
sum=0
for((i=0;i<100;i++)) //步长为1
do
sum=`expr $sum + $i`
done
echo $sum
练习:用shell求100之内满足3的倍数的数之和
sum=0
for((i=0;i<100;i+=3))//步长为3
do
sum=`expr $sum + $i`
done
echo $sum
4.3.4循环结构while和until
while 命令或者表达式 do 循环体语句 done
相当于C语言中
while(表达式) { 循环体语句 }
until 命令或表达式 do 循环体语句 done
until
和while
的功能相似 ,不同的是unitl
只有当测试的命令或者表达式值为假的时候才会执行循环体语句。当条件成立 ,跳出循环 ,这一点正好跟while是相反的。
练习:请你把下面这个while的例子改成until的用法
#!/bin/dash
i=0
while [ $i -le 100 ]
do
xxx;
done
#改成
until [ $i -gt 100 ]
do
xxx;
done
4.3.5 break 和 continue
5. shell函数
5.1函数的定义:
function_name()
{
//函数的内部具体实现;
}
function_name
:你要定义的函数名字, 名字的取法和C语言类似,
做到“名如其意” “大驼峰”或者“小驼峰”。
5.2函数的调用:
function_name arg1 arg2 arg3 ...
在函数的内部
arg1
--> $1
arg2
--> $2
arg3
--> $3
5.3获取函数的返回值
通常有两种方式
1.ret=function_name arg1 arg2 arg3 ...
例子: 执行函数function_name时的标准输出的内容
#!/bin/dash
sum()
{
a=$1
b=$2
s=`expr $a + $b`
echo $s
}
ret=`sum 1 2`
2.function_name arg1 arg2 arg3 ... ret=$?
$? 表示前面一个命令或者程序的退出码(返回值 exti值)
例子:
#!/bin/dash
sum()
{
a=$1
b=$2
s=`expr $a + $b`
return $s
}
sum 1 2 #调用函数,参数为1和2
echo $?
练习: 写一个函数来判断一个从终端输入的数是不是质数
//C语言
void is_prime(int x)
{
for(i=2;i<x;i++)
{
if(x % i == 0)
{
printf("not zhishu")
}
}
printf("is zhishu")
}
int main()
{
int x;
scanf("%d",&x);
is_prime(x);
}
#shell
is_prime()
{
i=2
while [ $i -lt $1 ]#$1是函数调用时传递的第一个参数
do
ret=`expr $1 % $i`
if [ $ret -eq 0 ];then
echo "notzhishu"
return 0
fi
i=`expr $i + 1`
done
echo "iszhishu"
}
read n
is_prime $n