持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
前言
本系列目录
- Linux Shell和基本指令
- Bash shell语法
- 正则表达式
- 文本处理之grep
- 文本处理之sed
- 文本处理之awk
- Text GUI编程
- Tomcat启动脚本分析
- 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 | 它代表脚本名称。 |
| 9 | 9 表示与脚本一起传递的第 1 到第 9 个参数。 |
| {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"