在Linux系统下,.sh文件是可执行的文件,其用法和Windows系统下的.bat文件类似,这篇文章可以帮助你阅读简单的.sh文件,你也可以动手试验其中的一些命令,编写一个属于自己的脚本文件。在观看时也可以找一个.sh文件进行对照。这样可以加深自己的了解,顺便读懂此文件。
注:如果自己有些Linux命令基础和编程基础再来阅读本文内容最好,但也要多练习,里面有多细节规范都是值得注意的
此类文件有个坑,可能有的朋友双击或者在命令行使用./文件名执行文件不能运行,报错权限不够。首先声明双击文件不能运行脚本!!!
正确姿势是:
①右键属性,查看权限。
②选中Allow executing files as program。
③右键打开终端。
④使用./文件名执行文件。
收工!
接下来就正式开始了解脚本文件的语法规范了,大家上车了!!!
.sh文件的开头必须声明(且放在文件的第一行):
#!/bin/sh#! 是特殊的表示符,后面根的是此解释此脚本的shell的路径,是.sh文件的规范
/bin/sh 表示此脚本使用/bin/sh来解释执行,当然也有其他的解释路径。比如#!/usr/bin/env sh
当然,我也见过有的.sh文件没有这个开头声明(杠精劝退),可以是文件比较老,当时的程序员编写时没有什么规范,如果你是菜鸟,也请不要乱改,写自己脚本的时候注意即可
注释,就是在程序执行的时候告诉编译器这一块代码不必执行。一个程序,注释在其中占据了非常重要的作用。既可以让自己以后阅读起来方面,也可以让别人看着方便。下文的代码中,也会经常使用注释对代码进行逐行进行解释,方便介绍。
.sh文件使用#注释,其这一行#后面的内容编译器都不会执行。
一个程序,输入输出是非常必要的,可以显示出使用者与程序之间的交互,使用者也可以通过程序的输出情况来,分析出程序的运行情况,这也是对程序编辑者非常有帮助的事情。
其中输出(默认为终端)的语法是:
echo "这里是你想输出的内容" #注意echo后面要加空格 # 其后面可以加 ">> 文件路径",意为输出到文件中其中可以使用 -e 表示默认输出到屏幕上,由于默认情况下就算输出到屏幕上,因此可以不加。
获取输入的语法是:
read input #read表示读入输入,而input表示将输入保存至这个变量之中其中可以使用 -p 表示获取屏幕上的标准输入,由于默认情况下也是在控制台输入,因此也可以不加。
而在常见的情况下的输入都是有提示语,而使用上面两句的组合就显得比较复杂。因此shell脚本有一个简便的方式来进行提示输入:
read -p "此处时提示语" input #read表示读入输入,而input表示将输入保存至这个变量之中在程序执行时,有时缺少某些必要条件或者在某些情况下,后面的程序不能正常运行(或者不需要执行),此时可以选择直接推出程序,其命令如下
exit 0其中0表示告诉表编译器程序是正常退出的。当然,也有非0的情况,此时表示非正常退出。。不知道小伙伴注意到没有,一般我们自己编辑让程序退出的时候,肯定就是要正常退出了,因此exit 0在自己使用的情况比较多。
在执行文件的时候,有时候需要在执行语句后面直接跟上脚本文件所需要调用的参数,以此作为脚本调用命令的格式。当我们在获取这些参数的时候,可以使用$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 # 程序本身 $# # 执行程序所附带的参数个数 $@ # 执行程序所附带的所有参数和大多数的编程语言一样,.sh脚本文件的命名规则也是:变量名必须是以字母或下划线字符“_”开头,后面跟字母、数字或下划线字符。其也是区分大小写的,即var和VAR是不同的变量。
由于.sh语言变量时弱类型,因此申明变量时必须要给变量赋值。例如上文中的n1、n2。
变量的使用时,可以通过$变量名来进行获取其值。与其他语言不同的是,$变量名不仅可以单独使用,也可以使用在双引号包裹的字符串中,都是可以正常使用的。但是有几种问题需要注意:
①字符串中使用时,变量名紧跟后面的单词。例如
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"其中对于变量还有比较深入的用法,其中有兴趣可以参考大佬的文章对于变量总结的文章传送门
./sh最主要的功能就是帮我我们执行一些Linux命令。因此,这一部分也是最为关键的一部分。Linux命令众多,这里也不做过多的赘述,大家可以自行百度各个命令及其用法。这里简单叙述一下比较常用的命令,可以帮助你阅读一些简单的脚本文件:
cd :跳转当前工作路径 ls :显示当前目录下的文件 cp :复制文件(copy) mv :移动文件(move) rm :删除文件(remove) mkdir :创建文件夹 grep :管道符,查找文件里符合条件的字符串 touch :创建文件,若文件存在,则修改其时间属性 chmod :查看或修改文件权限 git :代码管理工具 ln :创建链接文件(同window的快捷方式) ps :显示所有进程状态 su :切换用户 tar :打包文件(打包而已,并没有进行压缩)其语法为
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其语法为
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其语法为
while [ 判断条件 ] # 循环执行的条件,请注意格式,中括号左右都要有空格 do # 循环体执行的开始 循环体代码块 # 循环执行的循环体 done # 循环体执行的结束对于有过编程基础的小伙伴一下子能够看懂,while...do...done的用法和其他编程语言中的while循环的用法是相同的。都是先进行一次判断,再执行循环体的代码块,其比较适用于循环次数不固定,循环次数时根据循环体的不同而改变。下面就通过简单的例子来展示其用法。
# 当输入n的时候退出程序,输入其他则接着循环 while [ "$input" != "n" -a "$input" != "N" ] # 中括号和判断符两侧都要有空格 do read -p "你确定要继续吗?(y/n)" input 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,就可以了。再次运行程序就正常了
对于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种条件判断叙述完了,对于有其他编程语言基础的,可以通过其他的语言中对应的语法来进行记忆。
对于条件判断,上面流程控制语句中大家也见过了,其大部分的使用位置便是在这些流程控制中。而在上文中我大都只是使用了比较简单的大于小于来进行判断,现在就正式进入条件判断,梳理那些比较复杂的条件判断,以及比较常用的用法。
这几个是条件判断中最常见的判断符了,上文中也有简单的使用,对于有编程的小伙伴应也非常熟了,这里就不过多介绍了。
在.sh脚本文件里面,更多的是对文件的操作。因此接下来主要针对文件进行判断
-e 文件路径字符串 # 判断文件是否存在 -d 文件路径 # 判断文件是否存在,且文件类型是目录(dir) -f 文件路径 # 判断文件是否存在,且文件类型是文件(file) -b 文件路径 # 判断文件是否存在,且文件类型是块设备文件(block) -c 文件路径 # 判断文件是否存在,且文件类型是字符设备文件(character) -S 文件路径 # 判断文件是否存在,且文件类型是socket文件(注意是大写的S) -p 文件路径 # 判断文件是否存在,且文件类型是FIFO文件 -L 文件路径 # 判断文件是否存在,且文件类型是连接文件(注意是大写的L)(link)其函数编写的语法为
# 函数的声明 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当程序只有一个if判断分支的时候,可以使用简便的方式进行书写,例如
[ "$#" -lt 1 ] && echo "执行文件时请添加参数" && exit 0 # 必须写为一行 echo "执行文件时带了参数,正确"当某个条件成立时,需要执行某些语句 。。。这种情况的时候,使用这个用法就比较简单了。注意:执行的语句为多条时,使用" && "来进行连接,且必须为一行。