linux shell脚本编程

开场白

如果把shell命令比作盖房子的砖瓦,那么shell教程就是用一块一块砖瓦建立起来的房子。我们可以通过一些约定的格式将那些小巧的命令组合起来,实现更加自动化,更加智能的所谓shell脚本。
所谓的约定的格式,就是shell脚本的语法规则,就像c语言一样,将许多语句按照一定的规则组合起来形成一个程序。但这是要强调的是,c语言编写出来的程序需要经过编译器编译,生成另一个称为ELF格式的文件之后才能执行,但是shell脚本不需要编译而可以直接执行,这种脚本语言称为解释型语言。
要把shell命令放到一个“脚本”当中,有一个要求:脚本的第一行必须写成类似这样的

#!/bin/bash

聪明如你一定立即明白,这是给系统指定一款 Shell 解释器,来解释下面所出现的命令的。
比如,你的第一个最简单的脚本 helloworld.sh,也许是这样的:
在这里插入图片描述
执行shell脚本有三种方法:

  1.        ./helloworld.sh
    
  2.         /bin/bash helloworld.sh
    
  3.      .  helloworld.sh
    

这个脚本指定一款在/bin/下名字叫 bash 的 Shell 解释器,来解释接下来的任何命令。如
果你的系统用的是其他的解释器,就要将/bin/bash 改成相应的名字编辑器.

注意,脚本文件缺省是没有执行权限的,要使得脚本可以执行必须给他添加权限.

变量

shell脚本是一种弱类型语言,在脚本当中使用变量不需要也无法指定变量的“类型”。默认的情况下,shell脚本的变量都是字符串,即一连串的单词列表。
(1)变量的定义和赋值

  • myname=Michael Jackson
  • myname = ”Michael Jackson”
  • myname=‘Michael Jackson’

请严重注意:赋值号的两边没有空格!在 Shell 脚本中,任何时候要给变量赋值,赋值号两边一定不能有空格。
另外,变量名也有类似于 C 语言那样的规定:只能包含英文字母和数字,且不能以数字开头。

三个变量的区别主要是第二个和第三个的。下列有一个很好的例子:

gec@ubuntu:~$ cat helloworld.sh 
#!/bin/bash
var=1234
var1='${var}aa'
var2="${var}aa"

echo "${var}"
echo "${var1}"
echo "${var2}"

输出结果为:

gec@ubuntu:~$ ./helloworld.sh 
1234
${var}aa
1234aa


单引号的变量的引用是无效的,还是原封不定的保持着初始定义,没有引用变量概念。而双引号是对变量的引用是有效的。

(2)变量的引用
使用变量时,需要在变量的前面加一个美元符号:$myname 这表示对变量的引用,比如:
echo $myname
这样就把 myname 的值打印出来了。

变量的种类
变量的种类
Shell 脚本中有这么几种变量:
A 普通的用户自定义变量,比如上面的 myname。
B 系统预定义好的环境变量,比如 PATH。
C 命令行变量,比如$# 等。

系统的环境变量可以通过如下命令来查看:

gec@ubuntu:~$ env
CLUTTER_IM_MODULE=xim
LD_LIBRARY_PATH=/usr/local/arm/5.4.0/usr/lib:/usr/local/Qt-Embedded-5.7.0/lib:

可以看到,系统中的环境变量有很多,每个环境变量都用大写字母表示,比如 PATH。每个环境变量都有一个值,就是等号右边的字符串。根据环境变量的不同,它们各自的含义不同。
设置环境变量:以 PATH 环境变量为例子,如果想要将至修改为 dir/,只需执行以下命令:

gec@ubuntu:~$ export PATH=dir/

当然,PATH 环境变量的作用是保存系统中可执行程序或脚本的所在路径,因此它的值都是一些以分号隔开的目录,我们经常的使用办法是:不改变其原有的值,而给它再增加一个我们自己需要设置的目录 dir/,因此更有用的命令可能是类似于以下这样的:

gec@ubuntu:~$  export PATH=$PATH:dir/

还有,在命令行敲下如上的命令只会在当前的 Shell 中临时有效,如果要永久有效,就必须将命令 export PATH=$PATH:dir/ 写入~/.bashrc 中。然后执行以下命令使之生效:

gec@ubuntu:~$ source ~/.bashrc

而 Shell 脚本中的所谓命令行变量,指的是在脚本内部使用用户从命令行中传递进来的参数,例如:

gec@ubuntu:~$ ./helloworld.sh 1234 abc
1234
abc
gec@ubuntu:~$ cat helloworld.sh 
#!/bin/bash

echo "$1"
echo "$2"

1,$ # :代表命令行参数个数,即 2
2,$ * :代表所有的参数,即 abcd 1234
3,$ @ :同上
4,$ n :第 n 个参数,比如$1 即 abcd,而$2 就是 1234 。Shell 脚本中还有几个跟命令行变量形式很类似的特殊变量,他们是:

1,$ ? :代表最后一个命令执行之后的返回值
2,$ $ :代表当前 Shell 的进程号 PID
(事实上以上变量前面的 是 变 量 的 引 用 符 , 他 们 的 真 正 的 名 字 是 紧 跟 是变量的引用符,他们的真正的名字是紧跟 后面的那个字符)

特殊符号们

Shell 脚本有好几种特殊的符号,各自有各自的神通,他们分别是:引号、竖杠(管道)、和大于小于号(重定向)。

第一:引号

引号有三种,他们是:双引号 “ ”、 单引号 ‘ ’、 反引号(抑音符)

双引号的作用就是将一些“单词”括起来形成单个的“值”。比如:
myname=“Michael Jackson”
在此变量的定义中如果没有双引号将会报错,因为这个字符串有两个单词,第二个单词会被认为是一个命令,但显然不对,因为 Jackson 不是命令而只是 myname 的一部分。

双引号所包含的内容还可以包括对变量的引用,比如:
fruit=apple
mytree=“$ftruit tree”
由于对别的变量进行了引用,因此 mytree 的最终值是 apple tree

双引号所包含的内容还可以是一个命令,比如:
today=“today is date
请注意:date 是一个 Shell 命令,用来获得系统的当前时间,因为此命令出现在双引
号内部,默认情况下脚本会把它当做一个普通的单词而不是命令,要让脚本识别出该命令必须用一对反引号()包含他。

单引号
以上对双引号的分析间接也澄清了单引号的作用了:如果一个字符串被单引号所包含,那么其内部的任何成分都将被视为普通的字符,而不是变量的引用或者命令,比如:
var=’$myname, today: date
这个变量 var 的值不会引用 myname 也不会执行 date。

反引号的作用就是在双引号中标识出命令
编写以下脚本,可以立即理解这三个引号的区别:
#!/bin/bash
var=calender
echo “var: date” # 直接打印出 var 和 date
echo "KaTeX parse error: Expected 'EOF', got '#' at position 14: var: `date`" #̲ 打印出变量 var 的值,以…var: date’ # 打印出$var: date

第二:竖杠 | (管道)

Shell 命令的一大优点是秉承了 Unix/Linux 的哲学:小而美。一个个小巧而精致的命
令,各自完成各自的功能,不罗嗦,不繁杂。但有时候,我们需要他们相互协作,共同完成任务,就像采购负责买菜,回来交给洗涮工加工,再交给厨师烹饪,再交给服务员端给客人,我们常常需要将一个命令所达成的结果,给到另一个命令进行再加工,这时候就需要用到管
道。例如:

gec@ubuntu:~$ ls -l | wc

管道就像水管一样,将前面的命令的执行结果输送给后面的命令。ls -l 负责收集当前目录下的文件的信息,然后将这些文件名作为结果输送到管道,wc 这个命令接着从管道中把他们读取出来,并计算出行数、单词个数和总字符数。
管道不仅可以连接两个命令,也可以连接多个命令,类似于这样:

gec@ubuntu:~$ cat /etc/passwd | awk -F=/'{print $1}'|wc

这就将三个命令连接起来,每个命令的输出都作为下一个命令的输入,连接起来就能完成强大的功能。

第三:大于号> 和小于号 <

每一个进程在刚开始运行的时候,系统都会为他们默认地打开了三个文件,他们分别是
标准输入、标准输出、标准出错,其文件描述符和对应设备关系如下图所示:

缺省打开的三个设备文件
这三个标准文件对应两个硬件设备:标准输入是键盘,标准输出和标准出错是显示器(是的,显示器设备被打开了两次,第一次打为行缓冲类型的标准输出,第二次打开为不缓冲类型的标准出错)。绝大多数的 Shell 命令,默认的输入输出都是这三个文件。

比如,当我们执行命令 ls 的时候,他会默认地将结果打印到显示器上,就是因为 ls 本来就被设计为将结果往 1 号描述符(即标准输出,当执行成功的时候)或者 2 号描述符(即标准出错,当执行失败的时候)。而当我们打开普通文件的时候,系统也会帮我们产生一系列后续的数字(文件描述符)来表示这些文件,比如我们紧跟着打开了文件 a.txt 和 b.doc,

在这里插入图片描述
此时进程的的描述符情况如下:
第一:假如需要将 ls 命令的成功的输出结果(本来会被默认地输送到 1 号文件描述符的信息)重定向到 a.txt 文件中去,方法如下:

vincent@ubuntu:~$ ls 1> a.txt

在这里插入图片描述
第二:假如需要将 ls 命令的失败的输出结果(本来会被默认地输送到 2 号文件描述符的信息)重定向到 a.txt 文件中去,方法如下:
vincent@ubuntu:~$ ls notexist 2> a.txt (notexist 是一个不存在的文件,所以 ls
命令执行会失败)
在这里插入图片描述
第三:重定向标准输入也是类似的,比如直接执行 echo 命令,他将会默认地从标准输入(即键盘)读取信息,然后打印出来。但是我们可以将标准输入重定向为 b.doc 文件:

vincent@ubuntu:~$ echo 0< b.doc

在这里插入图片描述
这样, echo 就将会从 b.doc 读取数据,而不是从键盘读取了。此处额外提一点,在
Shell 脚本中,在重定向符的右边,标准输入输出设备文件描述符要写成&0、&1 和&2。比如要将一句话输出到标准出错设备中去,语句是:

echo “hello world” 1>&2

注意:>和<方向,0<:进入 1/2>:输出

测试语句

有一个叫 test 的命令,专门用来实现所谓的测试语句,测试语句可以测试很多不同的
情形,比如:

gec@ubuntu:~$ test -e xxx
gec@ubuntu:~$ echo $?
1
gec@ubuntu:~$ test -e release.txt
gec@ubuntu:~$ echo $?
0
gec@ubuntu:~$ 

以上语句用以判断文件 file 是否存在,如果存在返回 0,否则返回 1。那么除了可以判断一个文件存在与否,还有没有别的功能呢?晒出以下列表给各位看官鉴赏:

在这里插入图片描述
在这里插入图片描述
test 命令的测试程序:

#1,检测字符串
a="abc" #变量的定义和赋值,=两边必须没有空格
b="aBc"

# 如果解释器是sh,必须使用单个等号来判断
#test $a = $b
#echo $? #问号?是一个变量,代表最近一个命令的执行结果

# 如果解释器是bash,使用单个等号或者两个等号都可以
test $a == $b
echo $? #问号?是一个变量,代表最近一个命令的执行结果

s="abc"
if test -z $s;then
	echo "字符串s为空"
else
	echo "字符串s不为空:$s"
fi

s1="abc"
s2="aBc"

#if test $s1>$s2
if [ $s1 \> $s2 ]
then
	echo "$s1大于$s2"
else
	echo "$s1小于$s2"
fi

#2,检测文件
read -p "请输入一个文件名:" filename
#if test -e $filename
if [ -e $filename ]
then
	echo "文件存在"
else
	echo "文件不存在"
fi

#3,检测数值
n=304
m=200

#if test $n -lt $m
if [ $n -lt $m ] # 方括号[ ] 等价于test命令,注意两边必须有空格
then
	echo "$n小于$m"
else
	echo "$n大于或等于$m"
fi

特别注意:方括号[ ] 等价于test命令,注意两边必须有空格

代码中的分支语句的语法要点有这么几处:
1,每一个 if 语句都有一个 fi(即倒过来写的 if)作为结束标记。
2,分支结构中使用 then 作为起始语句。
3,当且仅当 if 语句后面的语句执行结果为真(即为 0)时,then 以下的语句才会被执行。当然,if 语句还可以跟 else 配对使用,跟 C 语言类似,比如:

gec@ubuntu:~$ cat helloworld.sh 
#!/bin/bash

if [ -e file ]&&[ -r file ]
then
	cat file
elif [ -e file ]
then
     chmod u+x file
	cat file
else
touch file	
fi


注意:elif 而不是 else if,其后也要跟 if 一样紧随 then 语句。

数据处理

# 处理数值的办法1
declare -i n=100
n=$n+1
echo "$n"

# 处理数值的办法2(最常用)
m=100
m=$(($m+1))
echo "$m"

# 处理数值的办法3
k=100
k=`expr $k + 1`
echo "$k"

注意:在定义变量 n 前面加上 declare -i 表示该变量为数值

脚本函数

#特点:
#1. 可以返回数据但没有返回值类型,因为所有的数据都是字符串
#2. 可以接收但没有参数列表,因为所有的数据都是字符串
max()
{
	#传进来的参数分别是$1、$2、$3……
	echo $1
	echo $2

	return 88;
}

a=100
b=200

# 函数调用: 跟执行命令是一样的
max $a $b
echo $?

FOR循环

a="aaa bbb ccc xxx xyz vincent mike 888 666"

# for循环实质就是一个个列举$a中单词的过程
# 循环次数取决于列表中单词的个数
for m in $a
do
	echo "$m"
done

循环体用do和done包含起来

CASE语句

read -p "请输入一个数字:" n

case $n in
	1)
		echo "one"
		;;
	2)
		echo "two"
		;;
	3)
		echo "three"
		;;
	4|5)
		echo "four or five"
		;;
	"quit")
		echo "退出"
		exit
		;;
	*)
		echo "不认识!"
esac

1.每个分支都必须以双分号作为结束(最后一个分支除外)
2.星号*是 Shell 中的通配符,代表任意字符。

注意:第一,变量 n 的值实际上是字符串,因此上述代码中的 1) 也可写成 “1”)
第二,整个 case 结构必须 esac 作为结束。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值