玩转Linux Shell编程(二)、Bash shell语法

159 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

前言

本系列目录

  1. Linux Shell和基本指令
  2. Bash shell语法
  3. 正则表达式
  4. 文本处理之grep
  5. 文本处理之sed
  6. 文本处理之awk
  7. Text GUI编程
  8. Tomcat启动脚本分析
  9. gradlew脚本分析

这一章来学习Bash shell的基础语法。

基础语法

变量

变量分为两种类型,局部变量和环境变量,局部变量仅可在生成的shell中使用,环境变量可用于shell的子进程(使用env或者printenv可以显示所有环境变量。)

而我们常熟悉的Int、Long等在这shell中没有这一说,默认情况下都是字符串型,bash没有类型系统。但可以使用declare命令获得类似类型的做法。

变量名必须以字母或者_开头,之后可以使用字母或者数字,在给变量赋值时候,=前后不允许有空格,如1name是不合法的一个变量名,name_name则可以使用,name = "张三"则也不合法,原因是=前后有空格,当使用变量时,需要在变量前面加$进行引用。

~$ name="张三"
~$ echo $name
张三

declare

变量除了上面直接使用变量名=变量值这种形式声明,还可以使用declare声明,declare声明方式如下。

declare [+/-][rxi][变量名称=设置值]
  • +/- "-"可用来指定变量的属性,"+"则是取消变量所设的属性。
  • -f 显示函数名。
  • -r 将变量设置为只读。
  • -x 向子shell传递变量名。
  • -i 生成整数类型变量。
  • -a 声明变量为普通数组。

如我们要声明一个整数类型,可以这样做。

#!/bin/bash
num=0
declare -i age=10
age=age+1
echo $age

age="a1"
echo $age

声明整形时需要使用-i标志。如果我们尝试为他赋值一个字符串类型的值时,通常感觉它会返回一个错误,说类型不正确,但在bash shell中,错误的变量赋值将被视为零,并不会抛出异常,所以最终结果一个是11一个是0。

还可以指定进制位数,语法是进制数#进制值

declare -i value=2#110101
echo $value

在如-r声明一个只读变量,只读变量还可以使用readonly,如下。

#!/bin/bash

declare -r PI_1=3.14
readonly PI_2=3.14

PI_1=3
PI_2=3

当我们尝试修改一个只读变量时,将会抛出错误。

并且在当前shell中生成的变量只能在当前shell中运行,如果需要转为环境变量供子进程使用,可以使用export导出。 如下

$ NAME="zhangsan"
$ bash
$ echo $NAME

$ exit
exit
$ echo $NAME
zhangsan
$ export NAME
$ bash
$ echo $NAME
zhangsan
$ exit
exit

数组

使用declare -a可以声明一个数组。

#!/bin/bash

declare -a nums=(1 2 3 4)

echo ${nums[@]} # 打印全部内容 
echo ${nums[1]} # 索引为1的值,为2

还可以使用-A声明一个类似Map的功能,这种被称为关联数组。

#!/bin/bash

declare -A name

name["zhangsan"]="1232"
name["lisi"]="a"
echo ${name["zhangsan"]}

如要获取数组大小,可以使用${#name[*]}.

清除变量

变量若不是只读的,可以使用unset命令清除,也就是说,从shell内存中删除变量,如下,declare有个p选项可以检测变量是否存在,在通过unset清除后,他会报错,表示未找到该变量。

#!/bin/bash

name="张三"
echo $name

declare -p name

unset name
echo $name
declare -p name

内置变量

shell还有众多的内置变量,如HOME是当前用户的目录,详细可在这里浏览。 ss64.com/bash/syntax…

内置特殊变量

shell 带有一些内置的特殊变量。这些变量存储了一些重要的数据,当编写 shell 脚本时可以派上用场。以下是这些特殊变量的列表以及它们存储的结果:

名称意义
$$它代表用户当前shell的进程ID。示例:1692
$?它返回最后执行的命令的退出状态。示例:127–> 如果未找到命令,这是退出状态。
$0它代表脚本名称。
11-911 到 9 表示与脚本一起传递的第 1 到第 9 个参数。
10{10}-{n}它表示与脚本一起传递的第 n 个参数的第 10 个参数。
$#它表示任何值的长度或传递给给定脚本的参数数量。
$@$*它表示传递给脚本的所有参数的列表

函数

shell中有两种格式声明一个函数,如下。

#!/bin/bash

function function_1(){
    echo "function_1"
}
function_2(){
    echo "function_2"
}
function_1
function_2

如果写了function,那么函数名后面的()可以省略,但函数名和后面的{必须有一个空格。

调用时候不能加(),只写函数名即可,如果要传递参数,直接写在后面用空格分割,如下。

function function_1 {
    echo "共"$#"个参数"
    echo $@
    echo $1
}

function_1 "张三" "李四"

参数个数通过$#获取,通过$@获取全部参数,$n获取指定位置的参数,如$1是第一个参数,以此类推。

返回值

bash shell函数与大多数编程语言中的函数不同,它的返回值表示函数调用状态,零表示成功,非零表示失败。如果你需要返回值,可以使用全局变量,或使用命令替换,另外函数返回结果可以通过$?获取,他表示获取上一个函数通过return返回的结果,利用这个特性也可以当作一个处理结果,但只能是整数。

如下是通过$?这种方式获取

#!/bin/bash
function myfunc() {
    return 10
}

myfunc
echo "返回值 $?"

下面是用过全局变量获取

#!/bin/bash
function myfunc()
{
    retval='张三'
}

retval='李四'
echo $retval
myfunc
echo $retval

还有下面这种方式,这种方式叫做命令替换,$()中是要执行的命令,他会以标准输出返回结果。

#!/bin/bash
function myfunc () 
{ 
    local   myresult='张三' 
    echo  "$myresult" 
} 

result=$(myfunc)    # 或 result=`myfunc` 
echo  $result

if语句

if语句是最简单的条件语句,如下,但是你会发现比较运算符是一些-eq-gt之类的。

#!/bin/bash
a=10
b=20

if [ $a -eq $b ]
then
   echo "a = b"
elif [ $a -gt $b ]
then
   echo "a > b"
elif [ $a -lt $b ]
then
   echo "a < b"
fi

他们被称为test运算符,种类有很多,如下

-e 该“文件名”是否存在

-f 是否为文件

-d 是否为目录

-b 该“文件名”是否一个块设备

-c 该“文件名”是否一个字符设备

-S该“文件名”是否一个套接字文件

-p 该“文件名”是否一个FIFO(管道)文件

-L 该“文件名”是否一个连接文件

-r 检测该文件名是否具有“可读”属性

-w 检测该文件名是否具有“可写”属性

-x 检测该文件名是否具有“可执行”属性

-u 检测该文件名是否具有“SUID”属性

-g 检测该文件名是否具有“SGID”属性

-k 检测该文件名是否具有“Sticky bit”属性

-s 检测该文件名是否为“非空白文件”

-nt (newer than)判断file1 是否比file2 新

-ot (older than)判断file1 是否比file2 旧

-ef 判断file1与file2是否为统一文件,可用于判断硬连接

-eq 两数值相等(equal)

-ne 两数值不等(not equal)

-gt n1大于n2(greater than)

-lt n1小于n2(less than)

-ge n1大于等于n2(greater than or equal)

-le n1小于等于n2(less than or equal)

test -z string 判断字符串是否为0,若string空字符串,则为ture

test -n string 判断字符串是否非未0,若string空字符串,则为false

test str1=str2 判断str1是否等于str2,若相等,则回传true

test str1!=str2 判断str1是否等于str2,若不相等则回传true

-a (and)两个条件同事成立。

-o (or)两个条件任何一个成立。

! 条件反求,如test! -x file,当file不具有x时,回传true。

! 条件反求,如test!-x file,当file不具有x时,回传true

当然也可以使用<>之类的,但他必须包含在(())中,比如下面。

#!/bin/bash
a=20
b=10

if ((a >b))
then
   echo "a > b"

fi

循环语句

for

for循环有多种多样,下面做几个示例

指定循环的常量

for i in 2 4 6 8 10 张三
do
    echo $i
done

还有下面是我们最常用的一种形式。

for((i=0;i<=10;i++))
do
    echo $i
done

指定步长

for i in {1..15..3}
do
    echo $i
done

遍历数组

names=('张三' '李四')
for i in "${names[@]}"
do
    echo $i
done

遍历文件

for i in ./*.txt
do
    echo $i
done

while

while的语法如下

while 条件
do
   ...
done

如根据整数条件。

a=0

while [ $a -lt 10 ]
do
   echo $a
   a=$(($a+1))
done

条件也可以使用(())这种方式

while [ $num -le 10 ] 
while (( num <= 10 ))

按行读取文件。

file=./log.txt
while ifs= read -r line
do
        # echo line is stored in $line
	echo $line
done < "$file"

当然也可以通过continue跳出本次循环以及break终止循环。

i=0

while [ $i -lt 5 ]
do
  ((i++))
  if [[ "$i" == '2' ]]; then
    continue
  fi
  echo " $i"
done

用户输入

read命令用来从终端或者文件中读取字符串,他会按行读取,在使用read后,程序将保持停止状态,直到按下Enter键,他还有一些选项,如-r选项允许输入反斜杠(),没有这个选项,输入的\将被忽略

如下

read -r  value
echo $value

-p选项用来显示prompt,返回的数据被保存在REPLY中.

read -p  "输入名字"
echo "名字是:$REPLY"

exec

exec命令用于从 shell 本身执行命令,不会创建新进程,它只是将shell替换为要执行的命令,如果exec命令成功,则不会返回调用进程,也就是在exec后面的命令得不到执行。

如下,echo "done"将得不到执行。

exec ls>ls.txt
echo "done"