shell脚本从入门到精通,防坑必备

Linux系统.sh脚本文件的语法编辑规范

在Linux系统下,.sh文件是可执行的文件,其用法和Windows系统下的.bat文件类似,这篇文章可以帮助你阅读简单的.sh文件,你也可以动手试验其中的一些命令,编写一个属于自己的脚本文件。在观看时也可以找一个.sh文件进行对照。这样可以加深自己的了解,顺便读懂此文件。

注:如果自己有些Linux命令基础和编程基础再来阅读本文内容最好,但也要多练习,里面有多细节规范都是值得注意的

0、执行此类脚本文件

此类文件有个坑,可能有的朋友双击或者在命令行使用./文件名执行文件不能运行,报错权限不够。首先声明双击文件不能运行脚本!!!

正确姿势是:

  • ①右键属性,查看权限。

  • ②选中Allow executing files as program

  • ③右键打开终端。

  • ④使用./文件名执行文件。

收工!

接下来就正式开始了解脚本文件的语法规范了,大家上车了!!!

1、开头

.sh文件的开头必须声明(且放在文件的第一行):

#!/bin/sh

#! 是特殊的表示符,后面根的是此解释此脚本的shell的路径,是.sh文件的规范

/bin/sh 表示此脚本使用/bin/sh来解释执行,当然也有其他的解释路径。比如#!/usr/bin/env sh

当然,我也见过有的.sh文件没有这个开头声明(杠精劝退),可以是文件比较老,当时的程序员编写时没有什么规范,如果你是菜鸟,也请不要乱改,写自己脚本的时候注意即可

2、注释

注释,就是在程序执行的时候告诉编译器这一块代码不必执行。一个程序,注释在其中占据了非常重要的作用。既可以让自己以后阅读起来方面,也可以让别人看着方便。下文的代码中,也会经常使用注释对代码进行逐行进行解释,方便介绍。

.sh文件使用#注释,其这一行#后面的内容编译器都不会执行。

3、输入输出

一个程序,输入输出是非常必要的,可以显示出使用者与程序之间的交互,使用者也可以通过程序的输出情况来,分析出程序的运行情况,这也是对程序编辑者非常有帮助的事情。

其中输出(默认为终端)的语法是:

echo "这里是你想输出的内容"  #注意echo后面要加空格  # 其后面可以加 ">> 文件路径",意为输出到文件中

其中可以使用 -e 表示默认输出到屏幕上,由于默认情况下就算输出到屏幕上,因此可以不加。

获取输入的语法是:

read input   #read表示读入输入,而input表示将输入保存至这个变量之中

其中可以使用 -p 表示获取屏幕上的标准输入,由于默认情况下也是在控制台输入,因此也可以不加。

而在常见的情况下的输入都是有提示语,而使用上面两句的组合就显得比较复杂。因此shell脚本有一个简便的方式来进行提示输入:

read -p "此处时提示语" input   #read表示读入输入,而input表示将输入保存至这个变量之中
4、程序强制退出

在程序执行时,有时缺少某些必要条件或者在某些情况下,后面的程序不能正常运行(或者不需要执行),此时可以选择直接推出程序,其命令如下

exit 0

其中0表示告诉表编译器程序是正常退出的。当然,也有非0的情况,此时表示非正常退出。。不知道小伙伴注意到没有,一般我们自己编辑让程序退出的时候,肯定就是要正常退出了,因此exit 0在自己使用的情况比较多。

5、执行文件所附带的参数获取

在执行文件的时候,有时候需要在执行语句后面直接跟上脚本文件所需要调用的参数,以此作为脚本调用命令的格式。当我们在获取这些参数的时候,可以使用$1$2等等来进行获取值。当然参数多可以有$3$4,一样的道理。比如下方的例子(请着重注意$1$2的用法,其他的内容,在下文也会叙述)

n1=$1   #将参数1保存至变量n1中
n2=$2   #将参数1保存至变量n2中

if [ -z $n1 ]; then   #若没有输入第一个参数,则提示执行脚本的命令有误,提示出正确的格式
   echo "参数有误,正确表达式为:./test1.sh 参数1 参数2"
   exit 0    #参数没有输入,后面需要使用这个参数值的时候,程序就可以报错,为了防止报错,我们直接退出程序
fi

if [ -z $n2 ]; then   #若没有输入第二个参数,则提示执行脚本的命令有误,提示出正确的格式
   echo "参数有误,正确表达式为:./test1.sh 参数1 参数2"
   exit 0
fi

当然,也有一些比较其他的用法,大家可以灵活使用,下文也些例子,帮助大家理解。

$0			# 程序本身
$#			# 执行程序所附带的参数个数
$@			# 执行程序所附带的所有参数
6、变量
(1)变量的命名规则

和大多数的编程语言一样,.sh脚本文件的命名规则也是:变量名必须是以字母或下划线字符“_”开头,后面跟字母、数字或下划线字符。其也是区分大小写的,即varVAR是不同的变量。

(2)变量的申明与使用

由于.sh语言变量时弱类型,因此申明变量时必须要给变量赋值。例如上文中的n1n2

变量的使用时,可以通过$变量名来进行获取其值。与其他语言不同的是,$变量名不仅可以单独使用,也可以使用在双引号包裹的字符串中,都是可以正常使用的。但是有几种问题需要注意:

①字符串中使用时,变量名紧跟后面的单词。例如

var="apple"        # ※※※※请不要在等号两边多加空格,否则会导致程序报错
echo "There are a lot of $vars here."

此时编译器查找变量时,会找vars,无法找到,便会报错,因此可以使用${}来指定变量名,从而使变量正常使用

var="apple"        # ※※※※请不要在等号两边多加空格,否则会导致程序报错
echo "There are a lot of ${var}s here."

②当字符串中需要打印$这个字符的时候,但是由于编译器会找到$后面的变量获取值,因此可能无法得出想要结果。因此可以通过转义字符\来将其进行转义。例如

echo "A total price of \$30"

其中对于变量还有比较深入的用法,其中有兴趣可以参考大佬的文章对于变量总结的文章传送门

7、Linux命令

./sh最主要的功能就是帮我我们执行一些Linux命令。因此,这一部分也是最为关键的一部分。Linux命令众多,这里也不做过多的赘述,大家可以自行百度各个命令及其用法。这里简单叙述一下比较常用的命令,可以帮助你阅读一些简单的脚本文件:

cd			:跳转当前工作路径
ls			:显示当前目录下的文件
cp			:复制文件(copy)
mv			:移动文件(move)
rm			:删除文件(remove)
mkdir		:创建文件夹
grep		:管道符,查找文件里符合条件的字符串
touch		:创建文件,若文件存在,则修改其时间属性
chmod		:查看或修改文件权限
git			:代码管理工具
ln			:创建链接文件(同window的快捷方式)
ps			:显示所有进程状态
su			:切换用户
tar			:打包文件(打包而已,并没有进行压缩)
8、流程控制语句
(1)if…else判断

其语法为

if [ 判断条件1 ] ; then
	代码块1
elif [ 判断条件2 ] ; then
	代码块2
else 
	代码块3
fi				# if判断结束的标志	

对于有过编程基础的小伙伴应该对if分支都有所了解,在各个代码里面,if...else判断都是大同小异。对于if...else...if的使用以及其嵌套使用都比较简单,下面就通过简单的例子来展示其用法。

read -p "请输入你的成绩:" score
if [ $score \< 60 ] ; then			# 小于60为不及格,由于小于号是特殊字符,因此需要进行转义
	echo "你的成绩不合格"
elif [ $score \< 70 ] ; then		# 由于小于60已经进入上面的分支,因此此判断为60到70的区间
	echo "你的成绩及格了"
elif [ $score \< 90 ] ; then
	echo "你的成绩良好"
elif [ $score \<= 100 ] ; then
	echo "你太优秀了"
else
	echo "你的分数太高了,是不是错了"
fi
(2)case…esac判断

其语法为

case $变量名 in					# 若变量的值为下列的某个一个值的时候,执行其对应的代码块
	变量可能的值1)
		所执行的程序块1
		;;
	变量可能的值2)
		所执行的程序块2
		;;
	变量可能的值3)
		所执行的程序块3
		;;
	*)
		其他默认所执行的程序块
		;;
esac							# case结束的标志		

对于有过编程基础的小伙伴一下子能够看懂,case...esac的用法和其他编程语言中的switch...case其用法是相同的。在使用情况也和其类似,当变量的值只有几种情况时,使用其效率比if...else更快,但是对于变量的值处在某个范围内的话,还是需要使用if...else更方便一些,下面就通过简单的例子来展示其用法。

read -p "请输入今天第一个来的同学名字:" name
case $name in			# 若变量的值为下列的某个一个值的时候,执行其对应的代码块
	"xiaoming")
		echo "今天又是小明第一个来,大家鼓励"
		;;
	"xiaoli")
		echo "今天时小丽第一个来,大家加油"
		;;
	*)
		echo "今天${name}先来"
		;;
esac
(3)while…do…done循环

其语法为

while [ 判断条件 ]			# 循环执行的条件,请注意格式,中括号左右都要有空格
do							# 循环体执行的开始
	循环体代码块				# 循环执行的循环体
done						# 循环体执行的结束

对于有过编程基础的小伙伴一下子能够看懂,while...do...done的用法和其他编程语言中的while循环的用法是相同的。都是先进行一次判断,再执行循环体的代码块,其比较适用于循环次数不固定,循环次数时根据循环体的不同而改变。下面就通过简单的例子来展示其用法。

# 当输入n的时候退出程序,输入其他则接着循环
while [ "$input" != "n" -a "$input" != "N" ]		# 中括号和判断符两侧都要有空格
do
	read -p "你确定要继续吗?(y/n)" input
done
(4)until…do…done循环

其语法为

until [ 判断条件 ]			# 循环执行的条件,请注意格式,中括号左右都要有空格
do							# 循环体执行的开始
	循环体代码块				# 循环执行的循环体
done						# 循环体执行的结束

乍一看,until...do...done循环while...do...done循环差不多一样,其实却是是这样的。两个唯一之处就是until...do...done循环是直到某个条件满足时才退出,当满足判断条件时就退出;而while...do...done循环是当条件成立时就执行循环体,当不满足判断条件了就退出了。因此两者正是相反的。下面将while...do...done循环的例子替换为util...do...done循环,大家来进行比较分析。

# 当输入n的时候退出程序,输入其他则接着循环
until [ "$input" == "n" -o "$input" == "N" ]		# 中括号和判断符两侧都要有空格
do
	read -p "你确定要继续吗?(y/n)" input
done

当执行上面的例子的时候,可能有的小伙伴会报错 [: unexpected operator ,这是正常情况(我一开始也这样报错了),这是由于 Linux默认sh链接到dash的,和bash不兼容(两个都是相似的脚本。具体两者的区别可以自行百度)

对于个问题,只需要在终端输入

sudo dpkg-reconfigure dash

在弹出框中选择No,就可以了。再次运行程序就正常了

(5)for…do…done循环

对于for...do...done循环,有两种格式,和其他大多数语言类似,其也有 简单的for循环 格式和 for...each循环 格式(只是比较类似,大家可以这么去记),以下就以这两个名字来做简单的叙述:

for...each循环 格式的语法为

for 变量名 in 变量值1 变量值2 变量值3 		# 循环的变量名依次为"in"后面的变量值时,执行下文的循环体
do											# 循环体执行的开始
	循环体代码块								# 循环执行的循环体,有几个变量值,就循环几次
done										# 循环体执行的结束

写过其他语言的for...each循环的,可能一下子能够想要in后面加数组的效果会更好。因此我们可以配合前文的 $@ 来进行使用,从而依次读取出每调用函数的每一个参数。下面来举个例子来展示其用法。

index=1
for var in $@
do
	echo "运行程序所带的第$index个参数为$var"
	let index=($index+1)		# let表示此运算是整数运算,不写的话,第一次之后index="1+1"
	# ((index=$index+1))		# 也可以使用双括号来做整数运算,其效果同上
done

上面这个程序调用之后的运行结果为:

简单的for循环格式的语法为

for ((初始值;限制条件;步长))		# 循环的变量名依次为"in"后面的变量值时,执行下文的循环体
do								# 循环体执行的开始
	循环体代码块					# 循环执行的循环体,有几个变量值,就循环几次
done							# 循环体执行的结束

其用法也和程序中的for循环类似了,只不过需要注意的是:for后面需要两个括号,这也是写这个循环时最容易范的错。下面可以通过这个例子来做一个简单的了解,也可以通过这个来查看for循环中括号内三个条件的运行规则,做进一步的加深了解。

for ((index=1;index<10;index++))
do
	echo "当前index的值为$index"
done

上面这个程序调用之后的运行结果为:

以上就对5种条件判断叙述完了,对于有其他编程语言基础的,可以通过其他的语言中对应的语法来进行记忆。

9、条件判断

对于条件判断,上面流程控制语句中大家也见过了,其大部分的使用位置便是在这些流程控制中。而在上文中我大都只是使用了比较简单的大于小于来进行判断,现在就正式进入条件判断,梳理那些比较复杂的条件判断,以及比较常用的用法。

(1)简单比较判断
大于:" > ""-gt"					#greater than
大于等于:" >= "" -ge "			#greater equal
小于:" \< ""-lt"				#less than       由于小于号是特殊字符,因此需要进行转义
大于等于:" \<= "" -le "			#less equal
等于:" == "" = ""-eq"		#equal
不等于:" != ""-ne"				#no equal
与:" && "" -a "					# 左右两边都为真才为真
或:" || "" -o "					# 左右两边都为假才为假
非:" ! "							# 非真即假、非假即真

这几个是条件判断中最常见的判断符了,上文中也有简单的使用,对于有编程的小伙伴应也非常熟了,这里就不过多介绍了。

(2)字符串的判断
-z $变量名				# (zero)判断字符串是否为空,字符串为空时返回true
-n $变量名				# (与上面相反)判断字符串是否不为空(全为空格时也不为空),字符串不为空时返回true
(3)文件的类型判断

.sh脚本文件里面,更多的是对文件的操作。因此接下来主要针对文件进行判断

-e 文件路径字符串		# 判断文件是否存在
-d 文件路径			  	# 判断文件是否存在,且文件类型是目录(dir)
-f 文件路径			  	# 判断文件是否存在,且文件类型是文件(file)
-b 文件路径			  	# 判断文件是否存在,且文件类型是块设备文件(block)
-c 文件路径			  	# 判断文件是否存在,且文件类型是字符设备文件(character)
-S 文件路径			  	# 判断文件是否存在,且文件类型是socket文件(注意是大写的S)
-p 文件路径			  	# 判断文件是否存在,且文件类型是FIFO文件
-L 文件路径			  	# 判断文件是否存在,且文件类型是连接文件(注意是大写的L)(link)
(4)文件权限的判断
-s 文件路径			  # 判断文件是否存在,且是一个非空白文件(注意是小写的S)
-r 文件路径			  # 判断文件是否存在,且具有读权限
-w 文件路径			  # 判断文件是否存在,且具有写权限
-x 文件路径			  # 判断文件是否存在,且具有可执行权限
-u 文件路径			  # 判断文件是否存在,且具有"SUID"属性
-g 文件路径			  # 判断文件是否存在,且具有"SGID"属性
-k 文件路径			  # 判断文件是否存在,且具有"sticky bit"属性
(5)两个文件的比较
文件1 -nt 文件2			# 文件1是否比文件2新(newer than)
文件1 -ot 文件2			# 文件1是否比文件2旧(older than)
文件1 -ef 文件2			# 文件1和文件2是否是同一个文件(equal file)
10、函数

其函数编写的语法为

# 函数的声明
function 函数名(){
	函数体				# 可以使用"$1","$2"来获取调用函数的参数
	return;			# 最后的"return"可有可无,需要的使用即可
}

# 调用
函数名 参数1 参数2     	# 函数名后面加参数即可

先说两点:

①在函数中的参数使用也和程序的参数使用是一样的,,也可以使用$0$#等,其也是意思也就成为了自己函数的参数数量,其实可以将程序本身理解为一个大的函数。

$1$2$0$#等的这些用法的使用是独立的。在函数A和函数B中的两者使用是相互独立的,函数中和程序里的使用也是相互独立的,都是互不影响的。

下面举一个简单的例子来说明:

function function_add(){
	echo "$1+$2=$(($1+$2))"				# 双括号表示计算,再加 $ 可以将计算的值显示出来。
}
function_add 3 4
11、其他
(1)使用两个"`"包裹Linux命令可以将命令的返回值赋值给变量,例如
var=`ls`								# 键盘tab键上方的键,在英文状态下即可输入 ` 
for v in $var							# 遍历变量的值
do
	echo "当前文件夹下的文件有:$v"		# 即可以打印每一个文件的名字
done
(2)单个if时候的简写

当程序只有一个if判断分支的时候,可以使用简便的方式进行书写,例如

[ "$#" -lt 1 ] && echo "执行文件时请添加参数" && exit 0		# 必须写为一行
echo "执行文件时带了参数,正确"

当某个条件成立时,需要执行某些语句 。。。这种情况的时候,使用这个用法就比较简单了。注意:执行的语句为多条时,使用" && "来进行连接,且必须为一行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值