Bash语法

Shell简介

shell是一种脚本语言,通过对应的脚本解释器解释执行,一般作为内置于操作系统的应用程序向用户提供访问操作系统内核的服务。不过shell也分化出了很多种类,常见的有shell(/bin/sh)、bash(/bin/bash)、csh(/usr/bin/csh)、ksh(/usr/bin/ksh)、powershell(windows的shell)等,大部分shell语法互相兼容,其中bash是linux默认的shell,后面以bash作为典型shell进行介绍

bash语法

语句
  • shell所有语句不需要使用分号 ; 进行终结
  • 执行shell脚本文件等价于在bash窗口即时逐行执行语句,所以某一行的语法错误或执行错误不会影响后续语句的执行
变量
  • 声明(定义)&赋值变量
    和大部分脚本语言类似,shell无需声明变量,或者说直接给某个变量名赋值则相当于声明了该变量,如:
variable="abc" #✅
variable = "abc" #❌

🌟和大部分语言不同的是,shell赋值语句的等号 = 两边不能有 空格, 后面会看到空格的存在与否会影响shell语法解析,就像python使用缩进来标识语句块一样令人讨厌。。

  • 使用变量
    当使用一个变量时(即变量作为右值),需要在变量名前面加上 $,在变量名两边加上大括号 {}, 大括号一般情况下可缺省,主要为了帮助解释器识别变量的边界,在部分必要情况下不可缺省
echo $variable #可缺省
echo ${variable}
echo "I am good at ${skill}Script" #如果使用的是skill变量则大括号不可缺省,缺省时解释器会使用skillScript变量

所以推荐使用变量时均不缺省大括号

  • 只读变量
    使用 readonly 命令可以将变量定义为只读变量,被定义为只读变量后变量无法再被赋值
url="www.google.com"
readonly url
url="www.baidu.com" #赋值失败
  • 删除变量
    使用 unset 命令可以删除变量,使该变量重新回到赋值之前的状态(即未定义状态),输出为定义变量时shell解释器的行为是输出一个空行,同时只读变量无法被删除
variable=1
unset variable
echo $variable #输出空
  • 变量类型
    在运行shell时,会同时存在如下三种变量
    • 局部变量
      局部变量在脚本或命令中定义,仅在当前shell实例中生效,其它shell实例无法访问
    • 环境变量
      定义在os中,所有的程序(包括shell)都可以访问环境变量,有的程序需要环境变量作为参数来运行,shell脚本中也可以定义环境变量
    • shell变量
      由shell程序(解释器)设置的特殊变量,一部分是环境变量,一部分是局部变量
数据类型

shell支持的数据结构不多,所以shell脚本与其他脚本语言的优势在于环境兼容性强,劣势在于不适合复杂度较高,要进行数据结构比较复杂的操作时最好使用其它复杂脚本语言(如python),然后使用shell进行调用即可

  • 字符串
    字符串是shell脚本中最常用的数据类型,字符串可以使用 单引号双引号不使用引号,需要注意单引号中的任何字符都会原样输出,而双引号支持转义字符和变量,另外连续的字符串会直接拼接而无需操作符
name="bob"
echo "hello,$name!" #hello,bob!
echo "hello,"$name"!" #hello,bob!
echo 'hello,$name!' #hello,$name!
echo 'hello,'$name'!' #hello,bob!
  • 数字
    直接将数字给变量赋值即可,但是shell默认只支持整数运算而不支持浮点数运算,需要算术运算时使用 expr。因为本质上解释器 大部分时候 将数字当作字符串处理的,毕竟字符串可以缺省引号,expr只是将字符串转化为数字进行算术运算并将结果重新以字符串返回
a=10
b=-5
echo `expr $a + $b` #5
  • 数组
    shell支持一维数组(不支持多维数组),通过下标索引,且下标由0开始,可以使用不连续的下标,下标的范围也没有限制,和lua比较像
    • 定义(初始化)数组
      shell中使用小括号来表示数组,数组元素之间用 空格 分开,两边用 小括号() 包围
    array=(value0 value1 value2)
    
    注意:和其它动态语言类似,数组不初始化也可以直接进行赋值使用
    • 使用数组
      使用 数组名[下标] 即可访问数组某个元素(作为变量)
    echo ${array} #不指定下标时直接读取第0个元素的值
    echo ${array[0]} #读取数组的第0个元素的值(这种情况大括号无法省略,否则或读取$array变量)
    echo ${array[@]} #使用@符号获取数组中所有元素
    array[0]=5 #修改第0个元素的值
    
    • 数组长度
      shell使用 # 获取数组或字符串变量的长度,但是不能获取常量的长度
    str="string"
    array=(1 "abcdefg" -4)
    echo ${#str} #6
    echo ${#"string"} #报错 “string”是字面值&常量
    echo ${#array[@]} #3 返回数组长度3
    echo ${#array[1]} #7
    echo ${#array[2]} #2(-4仍然被当作"-4"字符串处理)
    
运算符

shell语法中,多元运算符的两边必须有 空格

  • 算术运算符
    原生bash不支持简单的数学运算,但是可以通过其它命令如expr(一种表达式计算工具)完成表达式的求值操作,例如value=`expr 2 + 2`语句即可将2+2的结果计算出来并赋值给val变量
    • + 加法运算
    • - 剑法运算
    • * 乘法运算
      注意乘号(*)前边必须加反斜杠(\)转义才能实现乘法运算
    • / 除法运算
    • % 取余运算
  • 关系运算符
    关系运算符仅适用于数字,其中=和!=不仅适用于数字也适用于字符串的比较
    • -eq(=) equal 判断是否相等
    • -ne(!=) not equal 判断是否不等
    • -gt(>) greater than 判断是否大于
    • -ge(>=) greater equal 判断是否大于等于
    • -lt(<) less than 判断是否小于
    • -le(<=) less equal 判断是否小于等于
  • 逻辑运算符
    逻辑运算符仅适用于运算符两边均是布尔值的运算
    • ! 非运算
    • -o(||) 或运算 or
    • -a(&&) 与运算 and
  • 字符串运算符
    字符串运算符任何时候均适用(因为bash支持将数字当作字符串处理)
    • = 判断是否相等
    • != 判断是否不等
    • -z zero 判断字符串长度是否为0
    • -n not zero 判断字符串长度是否不为0
    • $ 判断字符串是否不为空,即该变量是否存在,存在时返回true
  • 文件检测运算符
    文件检测运算符适用于检测Unix文件的各种属性,作为路径字符串的单元操作符
    • -b block 判断路径是否是块设备文件
    • -c char 判断路径是否是字符设备文件
    • -d directory 判断路径是否是目录路径(即文件夹)
    • -e exist 判断路径是否存在(即该路径对应一个文件或者目录)
    • -f file 判断路径是否是文件路径(既不是目录也不是设备文件)
    • -r read 判断文件是否可读
    • -w write 判断文件是否可写
    • -x exec 判断文件是否可执行
    • -s 判断文件是否不为空(文件大小是否大于0)
流程控制语句
  • 条件判断
    完整的条件判断语句快如下所示
if condition1
then
  command1
  command2
  commandN
elif condition2;then #用;可以代替换行
  command3
else
  command4
fi
  • 注意如果某个elif分支或else分支没有任何指令执行,则不能写这个分支,否则解释器会报错
  • condition语句一般使用 中括号[] 包含返回布尔值的运算符的运算语句,如 [ a == b ],且变量名与中括号之间必须有 空格
  • 循环
    shell支持多种循环语句
    • for循环
      可以使用for循环语句遍历变量列表、数组或二者的组合
    for var in item1 item2 ${array[@]} item3 itemN
    do
      command1
      command2
    done
    
    • while/until循环
      while/until循环用于不断执行一系列命令,也可以用于输入文件中读取数据,区别在于until循环的条件语句condition是终止条件且循环体至少执行一次
    while/until condition
    do
      command1
      command2
    done
    
    • break和continue
      和其它语言一样,break命令跳出循环体执行后面的语句,continue跳出本次循环而不影响后面循环的执行
  • 选择语句
    shell支持case xxx in形式的选择语句,类似于其它语言的switch case语句。shell的选择语句使用 ;; 表示break,每个case选项使用 xxx) 表示,*)表示default,使用esac结束该选择语句
case $value in
  "value1")
    echo "case1"
    ;;
  "value2")
    echo "case2"
    ;;
  *)
    echo "case default"
    ;;
esac
  • 关于(、((、[、[[的区别
  • 凡是计算结果是布尔值的运算符,必须只存在于条件判断语句之中而不能给变量赋值, 因为shell把一切变量赋值当作字符串赋值处理
函数

shell的函数用起来有点像宏, 而不是编程语言普遍意义上传给函数,因为函数没有对应的局部变量栈,内部定义的变量可作为shell的变量在函数外获取。传入函数的参数在函数内部通过$n来获取,所以函数体内部无法获取shell外部传来的参数(如果想将其传入函数内可以作为函数的参数传入),但是函数体内可以获取shell中定义的局部变量和环境变量。函数体内可以用return语句返回一个数值(0-255),然后在函数调用完成后通过$?获取

a=1
function funcName() {
  echo $1
  echo $a
  b=2
  return 3
}
funcName "param1" param2 # param1 1
echo $? #3
echo $b #2
  • 函数声明
    函数声明使用function func() {...},其中function和()二者可以缺省一个,()和{}之间的空格也可以缺省
  • 函数调用
    和调用指令一样通过funcName param1 param2...paramN进行调用,函数名后面是空格分开的参数列表
注释
  • 单行注释
    # 符号后面的即为单行注释,会被解释器忽略
a=1 #注释内容
  • 多行注释
    多行注释使用如下格式
:<<EOF
comment...
comment...
EOF

其中EOF可以替换成任意注释中不存在的字符串,类似http协议中的boundary

常用命令&约定
  • shell强大之处在于可以调用所有环境变量Path中的程序,每个程序在shell都作为一个命令
  • #!是shell文件开始部分一个约定的标记(和python声明文件编码类似),它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell, 如:#!/bin/bash即声明该shell脚本使用bash解释执行
  • 参数处理
    在执行shell脚本时,通常需要向脚本传递参数,在脚本内通过 $n 获取参数,n代表参数的位置从1开始,因为无法以纯数字作为变量名所以并不会冲突,但是超过10后必须使用 ${n} 获取避免歧义。下面还有一些特殊字符处理参数
    • $# 参数总个数
    • $* 以单字符串返回脚本传递的所有参数(一般用于log)
    • $@ 返回每个参数构成的数组(一般用于遍历)
    • $- 显示shell当前使用的选项,和set命令对应
    • $? 返回上一条命令(包括函数调用)的退出状态 0表示正常返回
  • 文件包含
    shell可以包含外部脚本,通过 .source 进行包含,是类似include的概念,当解释器识别到文件包含指令时,会将对应文件中的文本替换到包含指令中再进行执行。
. fileName1 #包含fileName1文件
source fileName2 #包含fileName2文件
  • 文件包含和直接执行其它shell的区别
    我们知道也可以用./script.shbash script.shsh script.sh等方式直接执行其它shell,但是二者有本质区别:文件包含形式执行的shell文件不需要有可执行权限,只需要有可读权限,因为shell将包含文件的文本内容视为shell本身的一部分。文件包含的shell脚本和原shell共享所有变量,是在同一个进程中执行的;而第二种方式直接执行的shell是作为当前父进程下的子进程执行的,只能通过传入参数的方式将变量传入子进程,且子进程中各项变量和操作在结束后不会传回父进程(只能设置一个返回值)。所以某种意义上来说执行其它shell更像是一次函数调用。
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值