认识Shell脚本语言

143 阅读4分钟

一.Shell基本概念

(一)Shell是什么?

在我们学习Linux时,通过不同的终端连接自己的Linux系统,此时连接用户和内核的桥梁就是Shell,可以说它是一个帮助我们安全、高效地使用内核。专业点说,Shell是一个命令解释器,它可以通过接受用户输入的Shell命令才控制程序或控制OS。

通俗的说,Shell就是一个外壳,它包裹着Linux内核,通过Shell,我们就可以访问OS。

(二)什么是脚本语言(Scripts)

计算机的编程语言主要分为三类:

  • 1.机器语言:二进制代码
  • 2.汇编语言:面向机器的程序设计语言
  • 3.高级语言:一般面向用户,易学,易维护,可移植性高。

在此着重解释高级语言,按照转换方式可以分为以下两类:

  • 1.编译型语言:如C、C++,想要运行需要经过预处理-编译-链接的过程。
  • 2.解释型语言:不需要编译,每个语句都是在执行的时候才翻译。

而脚本语言也属于解释型语言,再次基础上,它们一般需要对应的脚本引擎或者解释器才能运行,这也是为什么Shell被叫做命令解释器的原因。

同时它们简单、易学、易用,在日常工作时,能够带来很大的便利,但相对于编译型语言,它们执行的速度还是很慢,毕竟每执行一句都要翻译一次(日常使用中脚本语言已经够快了)。

常见的脚本语言有:Lua、Shell、Sql、PHP、Python等等

(三)Shell脚本语言和Linux命令行的异同

我们平时用的大部分指令其实都是Shell提供的,如ls,cd等等,当然也有很多是Linux内核提供的,想要查看自己Shell下的内置指令有哪些,可以使用help指令。下图就是笔者云服务器上Linux内置的Shell指令。

image-20240106203419849

(四)Shell和Shell脚本语言

Shell是解释器,解释的Shell脚本语言,Shell脚本语言不是Shell本身。

常见的Shell解释器有bash、sh、ash、csh等等,习惯上我们都把它们称作Shell。

  • bash:Linux系统默认使用的Shell,可以使用help指令查找内置的指令集,可以快速查找和修改指令,同时支持快速查命令或者文件等等。
  • sh:一般见于各种UNIX系统。

(五)什么场景下使用Shell

Shell现在基本上是各UNIX、Linux系统之间通用的功能,而今经过POSIX的标准化,因此Shell脚本基本只需要写一次,就能应用到很多地方,这也是它的特点:可以表达复杂操作、移植性不错、开发方便。

当然,脚本语言和编译语言的效率差距还是非常大的,以下场景一般不适用Shell:

  • 1.资源密集型任务,如排序。
  • 2.大量的高精度计算
  • 3.跨平台,需要移植
  • 4.复杂的应用,需要使用结构化编程
  • 5.对于影响全局性的关键任务
  • 6.闭源或者安全性要求高
  • 7.需要硬件、GUI、数据结构、I/O接口、socket接口中的一个
  • 8.项目依赖性较强

二.Shell脚本初认识

(一 )建立和运行Shell脚本

首先让我们先创建一个脚本文件,运行起来。

#! /bin/bash
#hello world example
echo hello

# Linux中用#!及该字符串后面的信息来确定文件类型,后面的路径让它确定这个一个解释脚本
# 第三行打印一个字符

image-20240106171651248

chomd a+x hello.sh
#为了确定脚本能正确运行,请设置权限,另外,为了安全,在学习脚本时建议不要使用root账户,因为脚本语言不编译

打印一个字符串怎么打印呢?在Shell中,空格作为分隔参数的指标,所以当我们想要打印字符串的时候可以用双引号或者单引号标注。

#! /bin/bash
#hello world example
#echo hello
echo "hello Shell"
echo 'hello Shell!'

image-20240106172029477

那单引号和双引号有区别吗?有的,先按下不表

(二)定义Shell变量

1.基本规则

接下来我们学习下Shell中定义变量的规则:

  • 只能包含字母、数字和下划线
  • 不能以数字开头,但是可以包含
  • 避免使用Shell关键字,如if、if、then、else、esle等等
  • 习惯上用大写字母表示常量
  • 避免使用空格,空格一般用作参数分割
  • 使用"$"符号引用变量
#! /bin/bash
#hello world example
#echo hello
# echo "hello Shell"
# echo 'hello Shell!'
#echo "hello shell"
a="hello shell"
b = hello
echo $a $b
# 使用$引用变量

image-20240106174013099

第8行由于使用空格,导致Shell无法正确识别,同时因为无需编译,下一行依旧执行。

2.只读变量

可以使用readonly指令把变量定义成只读。

name="Shell"
readonly name
name="hello"
echo $name

image-20240106180530881

将变量变成只读,再修改,结果报错,修改失败。

3.删除变量

a=1
unset a
echo $a
#删除变量a,结果不打印

image-20240106180644587

4.定义字符串、单双引号的异同

之前说过,可以用单引号或双引号定义字符串,二者区别在于前者定义的字符串里的变量是无效的,只会原样输出,但是双引号会替换指定内容。

a=1
str1="hello $a"
str2='hello $a'
echo $str1 $str2

image-20240106181155436

对应的,双引号中可以有变量,会指定替换,同时可以出现转义字符。

5.拼接字符串

# 单双字符串都可以拼接字符串
#拼接字符串
#双引号拼接
name="str one"
foo1="hello, "$name" ! "
foo2="hello, ${name} ! "
echo $foo1 $foo2
#单引号拼接
foo3='hello, '$name' !'
foo4='hello, ${$name} !'
echo $foo3 $foo4

image-20240106181842288

如上,说明一下,花括号一般用来分割变量的范围。

6.定义数组

bash支持一维数组,不支持多维,但是没有限定数组大小。

# 定义数组
array=(1 2 3 4 5)
echo ${array[@]} ${array[0]}
#支持单独定义某个元素,用@可以获取所有元素,可以不使用连续的下标

image-20240106202725135

7.默认变量

在Shell中,一般都配置了默认的变量,接下来我们认识一下。

  • $0:执行的文件名
  • $1:脚本文件的第一个参数
  • $#:传递到脚本的参数个数
  • $*:以一个单字符串显示所有向脚本传递的参数
  • $?:显示最后命令的退出状态,0表示没有错误,其它值表示有错误例子a.sh

三.Shell的常见内置指令

想要完全了解当前主机下Shell内置指令有哪些,可以使用help指令,这里就只介绍几个常用的内嵌指令。

(一)echo

  • echo指令可以向终端打印文本信息

  • -e 允许我们解释反斜杠、换行符、制表符等等

  • -n 输出时不在末尾添加新行

  • 支持输入输出重定向

其他参数就不介绍了,有需要的可以help指令查询一下

echo "----------打印文本--------------------"
echo "----------解释特殊字符----------------"
echo -e "hello,
Linux,  "
echo "----------------换行-----------------"
echo -n "hello Linux!"

image-20240106204759127

演示下重定向

(二)read

read指令可以从键盘中读取变量内容

  • -a:把读取的数据赋值给数组array,从下标0开始
  • -p:显示提示信息,提示内容为prompt
  • -r: 原样读取(Raw mode),不把反斜杠字符解释为转义字符
  • -n:读取num个字符,而不是整行字符
  • -s:静默模式(Slient mode),不会在屏幕上显示输入的字符,输入密码或其它确认信息时常用
  • -t:设置超时时间
read 
echo $REPLY
#$REPLYread指令的默认变量名,没有提供变量名,那么读取的数据就会被放在REPLY中

image-20240106212107331

read -p "请输入姓名:" name
read -p "请输入性别:" sex

echo "姓名:${name}"
echo "性别: ${sex}"

read -t 10 -sp "请输入密码(10s):" pwd1
echo 
read -t 10 -sp "请再次输入密码(10s):" pwd2
printf "\n"

if [ $pwd1 == $pwd2 ]
then
 echo "密码一致,验证通过~"
else
 echo "密码错误,验证失败!"
fi

image-20240106221945649

(三)declare

众所周知,shell变量是弱类型变量,默认情况下都是字符串型。字符串不能直接进行数学运算,如果想要进行数学运算,便可使用declare声明变量类型。

#declare [+/-] [选项] 变量名
#+/- : 取消/设定变量类型的属性
  • -a:设定为数组array
  • -A:设置为key-value(键对值)关联数组
  • -f:定义为函数
  • -i:定义为整数
  • -r:定义为只读
#一般格式
a=1+1
declare -i b=1+1
echo $a
echo $b

image-20240106223232706

#普通数组
declare -a arr1=("李明" 33 "hobbit1")
echo ${arr1[*]}
echo ${arr1[1]}

declare -a arr2=([0]="王强" 55 "hobbit2")
echo ${arr2[@]}

#关联数组
declare -A arr3=(["name"]="王武" [age]=34 [hobbit]="hobbit3")
echo ${arr3[@]}
echo ${arr3["age"]}

image-20240106223321527

(四)test

1.数值测试

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。以下几个符号也是Shell中支持的关系运算符。

  • -eq:等于则为真
  • -ne:不等于则为真
  • -gt:大于则为真
  • -ge: 大于等于则为真
  • -lt:小于则为真
  • -le:小于等于则为真
  • eq表示等于--equal ,ne表示不等于--no equal,gt表示大于--greater than,lt表示小于--less than
num1=100
num2=200
if test $num1 -eq $num2
then
    echo "两个数相等!"
else
    echo "两个数不相等!"
fi

image-20240106224206269

2.字符串测试

  • =:等于则为真
  • !=:不相等则为真
  • -z:字符串的长度为零则为真
  • -n:字符串的长度不为零则为真
num1="helloaa"
num2="hello"
if test $num1 = $num2
then
    echo '两个字符串相等!'
else
    echo '两个字符串不相等!'
fi

image-20240106224733232

3.文件测试

  • -e [文件名]:如果文件存在则为真
  • -r [文件名]:如果文件存在且可读则为真
  • -w [文件名]:如果文件存在且可写则为真
  • -x [文件名]:如果文件存在且可执行则为真
  • -s [文件名]:如果文件存在且至少有一个字符则为真
  • -d [文件名]:如果文件存在且为目录则为真
  • -f [文件名]: 如果文件存在且为普通文件则为真
  • -c [文件名]: 如果文件存在且为字符型特殊文件则为真
  • -b [文件名]:如果文件存在且为块特殊文件则为真
cd /home/study
if [ -e log.txt ] 
then
    echo '文件已存在!'
else
    echo '文件不存在!'
fi

image-20240108102802671

同时,Shell还支持将与(-a)、或(-o)、非(!)连接起来,这里就不再详细介绍了。

四.Shell基本运算符

(一)expr

Shell和其他语言一样,支持多种运算符,包括算数运算符、关系运算符、布尔运算符、字符串运算符和文件测试语言符。

但是原生bash并不支持简单的数学计算,可以通过其他命令来实现,比如expr。

#expr
val=`expr 2 + 2`
echo "和为:$val"

#1.注意格式,使用的是反引号,不是单引号
#2.表达式和运算符之间要有空格,不能写成“2+2”

image-20240108103806901

(二)算术运算符

  • [+]:加法
  • [-]:减法
  • [\*]:乘法
  • [/]:除法
  • [%]:取余
  • [=]:赋值
  • [==]:相等,用来比较字符串,相同返回true
  • [!=]:不相等,用来比较字符串,不相同返回true
a=20
b=10
echo `expr $a + $b`
echo `expr $a - $b`
echo `expr $a \* $b`
echo `expr $a / $b`
echo `expr $a % $b`
echo  `expr $a == $b`
echo  `expr $a != $b`
x="xx"
y="xy"
echo  `expr $x == $y`
echo  `expr $x != $y`

#尽管如此,“==”和“!=”也能比较数字

image-20240108121146309

(三)关系运算符

Shell中的关系运算符只支持数字,不支持字符串,除非字符串的值为数字。

  • -eq:等于则为真
  • -ne:不等于则为真
  • -gt:大于则为真
  • -ge: 大于等于则为真
  • -lt:小于则为真
  • -le:小于等于则为真
  • eq表示等于--equal ,ne表示不等于--no equal,gt表示大于--greater than,lt表示小于--less than。
a=10
b=20

if [ $a -eq $b ]
then
    echo "$a -eq $b : 为真,a 等于 b"
else
    echo "$a -eq $b: 为假,a 不等于 b"
fi

if [ $a -ne $b ]
then
    echo "$a -ne $b: 为真,a 不等于 b"
else
    echo "$a -ne $b : 为假,a 等于 b"
fi

if [ $a -gt $b ]
then
    echo "$a -gt $b: 为真,a 大于 b"
else
    echo "$a -gt $b: 为假,a 不大于 b"
fi

if [ $a -lt $b ]
then
    echo "$a -lt $b: 为真,a 小于 b"
else
    echo "$a -lt $b: 为假,a 不小于 b"
fi
if [ $a -ge $b ]
then
   echo "$a -ge $b: 为真,a 大于或等于 b"
else
    echo "$a -ge $b: 为假,a 小于 b"
fi

if [ $a -le $b ]
then
    echo "$a -le $b: 为真,a 小于或等于 b"
else
    echo "$a -le $b: 为假,a 大于 b"
fi

image-20240108111330476

(四)布尔运算符

  • !:非运算或运算,有一个表达式为 true 则返回 true。,表达式为 true 则返回 false,否则返回 true。
  • -o:非运算,表达式为 true 则返回 false,否则返回 true。
  • -a:与运算,两个表达式都为 true 才返回 true。
x=10
y=20

if [ $x != $y ]
then
    echo "$x != $y : 为真,x不等于y"
else
    echo "$x != $y : 为假,x等于y"
fi

if [ $x -gt 10 -o $y -gt 10 ]
then
    echo "为真, $x 大于 10 或 $y 大于 10"
else
    echo "为假, $x 和 $y 都不大于10"
fi

if [ $x -gt 5 -a $y -gt 5 ] 
then
    echo "为真, $x 和 $y 都大于5"
else
    echo "为假, $x 和 $y 都不大于5"
fi

image-20240108112728416

(五)逻辑运算符

  • &&:逻辑的and
  • ||:逻辑上的or
x=10
y=20

if [[ $a -gt 100 && $b -gt 100 ]]
then
    echo "true"
else
    echo "false"
fi

if [[ $a -lt 10 || $b -lt 10 ]]
then
    echo "true"
else
    echo "false"
fi
#"[]"用于条件测试

image-20240108113303593

(六)字符串运算符

  • =:检测两个字符串是否相等,相等返回true
  • !=:检测两个字符串是否不相等,不相等返回 true。
  • -z: 检测字符串长度是否为0,为0返回 true。
  • -n:检测字符串长度是否不为 0,不为 0 返回 true。
  • $:检测字符串是否不为空,不为空返回 true。
a="abc"
b="efg"

if [ $a = $b ]
then
    echo "$a = $b, 为真, a 等于 b"
else
    echo "$a != $b, 为假,  a 不等于 b"
fi

if [ $a != $b ]
then
    echo "$a != $b, 为真,  a 不等于 b"
else
    echo "$a != $b, 为假,  a 不等于 b"
fi

if [ -z $a ]
then
    echo "为真,长度为0"
fi

if [ -n $a ]
then
    echo "为真,长度为 ${#a}"
fi

if [ $a ]
then
    echo "为真,字符串不为空"
fi

image-20240108123456575

(七)文件测试运算符

以下是文件测试运算符的参数,和其他运算符使用方法一致,文件测试多种多样,这里就不再示例了。

  • -b :检测文件是否是块设备文件,如果是,则返回 true。
  • -c :检测文件是否是字符设备文件,如果是,则返回 true。
  • -d:检测文件是否是目录,如果是,则返回 true。
  • -f :检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
  • -g :检测文件是否设置了 SGID 位,如果是,则返回 true。
  • -k :检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。
  • -p: 检测文件是否是有名管道,如果是,则返回 true
  • -u:检测文件是否设置了 SUID 位,如果是,则返回 true。
  • -r:检测文件是否可读,如果是,则返回 true。
  • -w: 检测文件是否可写,如果是,则返回 true。
  • -x:检测文件是否可执行,如果是,则返回 true。
  • -s: 检测文件是否为空(文件大小是否大于0),不为空返回 true。
  • -e:检测文件(包括目录)是否存在,如果是,则返回 true。

五.Shell流程控制

(一)if语句

1.单if

if [ condition ]
then
	command1
	command2	
	command3	
	......
	commandn
fi
#末尾的fi就是if的倒写,表示语句块的结束
#注意:Shell中的if语句需要使用方括号,而且两边一定要有空格分割

2.单if esle

if [ condition ]
then
	command1
	command2	
	command3	
	......
	commandn
else
	command
fi
#注意:shell中else部分不能为空,没有就不要写

3.多if else

if [ condition1 ]
then
    command1
elif [ condition2 ] 
then 
    command2
else
    commandN
fi
#示例
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

image-20240108125156195

4.if与test指令结合

if test condition
then
	command
fi
#iftest结合的时候就不用方括号了
a=10
b=20
if test $[a] -lt $[b]
then
    echo "$a 小于 $b"
fi

image-20240108125412261

(二)for循环

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done
#in列表里存储了for循环的变量值,可以包含替换、字符串和文件名
#同样,done表示循环结束
for val in 1 2 3 4 5
do
    echo "The val is : $val"
done

image-20240108160639073

#顺序输出字符
for str in This is a string
do
    echo $str
done

image-20240108160802009

(三)while循环

while condition
do
	command
done
#示例, 循环输出1到5
val=1
while(( $val<=5 ))
do
    echo $val
    let "val++"
done

#以上是哦那个了Bash let指令,用于执行多个表达式,不需要加上$表示变量

image-20240108161122686

(四)until循环

#until循环会一直执行命令直到条件为true,和while相反
until condition
do
	command
done
val=0
until [  $val -gt 10 ]
do
    echo $val
    val=`expr $val + 1`
done

(五)case...esac

这个其实就对应其他语言的switch语句,是多分支的选择结构。

case 值 in
1)
	command1
	......
	;;
2)
	command1
	......
	;;
*)
	command1
	......
	;;
esac
#最后如果没有匹配的,可以用*捕获该值
echo "请输入1到3的数字"
echo '你输入的数字为:'
read num
case $num in
1)  echo "你选择了1" ;;
2)  echo "你选择了2" ;;
3)  echo "你选择了3" ;;
*)  echo "你没有选择范围内的数字" ;;
esac

image-20240108164438058

最后,对于循环语句,Shell同样支持使用“break”跳出所有循环,“continue”跳出所有循环,和其他语言稍微不同,但用法类似,不再过多介绍。

六.Shell函数

(一)基本格式

同样的,Shell也支持我们定义函数供我们调用,格式如下:

[ function ] 函数名 [()]
{
	command;
	返回值
}
#可以带function fun()定义,也可以直接定义,不带参数
demoFun()
{
    echo "hello Shell, this is the first function"
    return 0
}

echo "------开始执行------"
demoFun
echo "------执行结束------"

image-20240108170322765

AddFun()
{
    echo "计算两数之和"
    echo "输入第一个数字-> "
    read num1
    echo "输入第二个数字->"
    read num2
    return $(($num1+$num2))
}

AddFun
echo "两数之和为-> $?"

(二)返回值

Shell每个命令都有退出码,退出码从0-255,0为正确退出,1-255为错误退出码。在定义的函数中,一般返回最后一个命令的退出码,如果超出255,则会取模。

一般最后可以使用return返回返回值,但这样就很不灵活,因为我们只能返回整数。但是我们可以使用echo,echo可以把输出到标准输出返回,这样就可以返回任何类型的数据了。

(三)函数传参

Shell的参数不能够直接设置,但是在调用时可以传递参数,你传入什么参数他就接受什么参数。

fun()
{
    echo "参数1-> $1"
    echo "参数9-> $9"
    echo "参数10-> $10"
    echo "参数10-> ${10}"
    echo "参数11-> ${11}"
    echo "输出所有参数 $*"
}
fun 0 1 2 3 4 5 6 7 8 9 10

#从第10个参数开始,要用${n}

image-20240108172736498

七.Shell特殊符号讲解

细心的朋友可能会发生,我们在讲解上述语法的时候,有的时候用方括号,有的时候用大括号,有的时候用两个小括号,其中有什么区别?接下来讲解下Shell中国特殊符号的用法

(一)单引号、双引号、反引号

在第二章节里我们简单讲解了单引号、双引号的区别,其实很简单,单引号所见即所得,原样输出,双引号会解析特殊变量,如“$”, 反斜杠等。

在讲解expr指令时,介绍了反引号,他在esc键的下方,用于命令替换,即先执行反引号里的命令,再将结果加入原命令。

(二)小括号

1.单小括号--()

  • 相当于一个命令组,单小括号的内容会先用一个子Shell顺序执行,括号中多个命令用分号隔开,最后一个不需要分号,不必有空格
  • 相当于命令替换,例如$(cmd),会将括号内的内容先执行一遍,再加入原命令中重新执行
  • 定义数组

2.双小括号--(())

  • 用于整数计算:不支持浮点数,((exp))可以拓展计算一个表达式的值,同时也有自己的返回值。
  • 运行C语言代码:双小括号的运算符只要满足C语言运算规则,都可以用运算,结果转化为10进制
  • 重新定义变量值
  • 算术运算符比较,常见于语句判断中

image-20240108175106987

(三)中括号

1.单中括号--[]

  • 条件判断的表示:在Bash的内部指令中,[]和test指令作用等同,不用绝对路径指明时,一般都是使用bash自带的命令。在常用的if/else语句中,左中括号用来调用test命令标识,右中括号用来结束条件判断,这也是为什么if和test混用的时候不再需要中括号的原因。
  • 比较运算符:在讲解运算符时已经说过,单中括号可以用与运算符比较
  • 字符范围:用来作为正则表达式的一部分,描述一个匹配的字符范围(后续讲解)
  • 索引编号:单中括号可以用来引用数组中的元素。
#testif混用,不用单中括号
a=10
b=20
if test $[a] -lt $[b]
then
    echo "$a 小于 $b"
fi

# 判断字符串 str 是否为空
str=""
if [  -z $str ]; then
echo "str is empty"
fi

#引用数组元素
arr=(10 20 30 40)
echo $[arr[0]]

image-20240108183238951

2.双中括号--[[]]

  • 保护字符:在双中括号的内容,不会发生文件名的拓展或者单词分割。因此使用双中括号进行判断时,不需要再用引号。

  • 支持字符串的模式匹配。

  • 支持更复杂的逻辑判断:在if语句中,单中括号有的时候很容易出现逻辑错误,双中括号能尽量避免。

  • 支持正则表达式的匹配。

# 判断变量 a 是否为空
a=""
if [[ -z $a ]]; then
echo "a is empty"
fi

# 判断变量 b 是否为非空
b="hello"
if [[ -n $b ]]; then
echo "b is not empty"
fi

# 判断变量 c 是否以 h 开头,以 o 结尾
c="hello"
if [[ $c == h*o ]]; then
echo "c matches the pattern h*o"
fi

# 判断变量 d 是否是一个 IP 地址
d="192.168.1.1"
if [[ $d =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
echo "d is a valid IP address"
fi

image-20240108182454905

其实单中括号和双中括号的差别就在于前者容易出现逻辑错误,且不支持通配符和正则表达式的匹配,也不支持&&、||,只能使用-a和-o来表示。

(四)大括号(花括号)

  • 对大括号内的内容进行拓展,可以用点号分割,也可以用逗号分割。
  • 创建代码块:与单中括号不同,大括号不会新建子Bash运行,而是在当前Bash下运行,使用注意第一个命令和左括号一定要有一个空格,最后用分号结尾。
  • 划分变量:有的时候我们想要连续输出多个变量,且无法使用空格分割,就可以用大括号分割变量。
  • 结构替换:大括号支持结构替换,具体有+、-、=、?几个符号具体如下方代码示例。
  • 模式替换:大括号支持模式替换,具体有#、##、%、%%几个结构,具体如下方代码示例。
echo  "------创建多个文件文件------"
touch {a,b,c,d}.txt
touch {1..5}.c
ls

echo "------创建代码块------"
#下方代码执行五件事:1.打印hello Shell 2.打印时间 3.展示目录 4.创建文件 5.输出重定向
{ 
    echo "hello Shell"
    date
    ls
    touch log.txt
} > log.txt

echo "------分割变量------"
val=Shell
echo "hello$val"
echo "$valhello"
echo "${val}hello"


image-20240108191121755

#结构替换
#${val:-string}:当val为空,${val:-string}输出a,val不为空时,则输出val
val1=""
echo ${val1:-a1} "val1此时值是:$val1"
val2="hello ---"
echo ${val2:-a1} "val2此时值是:$val2"
echo -e "\n"
echo -e "\n"
echo -e "\n"



#${val:+string}:与-string相反,为空${val:+string}输出val, 不为空输出a2
val3=""
echo ${val3:+a2} "val3此时值是:$val3"
val4="hello +++"
echo ${val4:+a2} "val4此时值是:$val4"
echo -e "\n"
echo -e "\n"
echo -e "\n"

#${val:=string}:val为空时,${val:=string}输出为a,同时val的值也会被赋值为a,不为空则不作为
val5=""
echo ${val5:=a3} "val5此时值是:$val5"
val6="hello ==="
echo ${val6:=a3} "val6此时值是:$val6"
echo -e "\n"
echo -e "\n"
echo -e "\n"

#${val:?string}:val不为空则${val:=string}输出为val,val为空,则会将string输出到标准错误,并且退出脚本
val7="hello ???"
echo ${val7:?a4} "val7此时值是:$val7"
val8=""
echo ${val8:?a4} "val8此时值是:$val8"
echo "end-----"

image-20240108221812106

image-20240108221842987

之所以列的这么清楚,是希望大家能看清楚这几个结构替换的关系,-和+的操作上是对应的,不会修改val的值,但是=和?会修改,且它们在操作上目的不同,=只检查val是否为空,?不仅仅检测是否为空,而且会返回错误码。

当然stirng的位置不仅仅可以是string,同样可以替换其他变量或者命令。

#模式替换
#${val%pattern}:检测字符串中是否满足同一的模式结尾,如果是则删除右边最短的匹配模式
val1=helloshellhelloshellhelloshell
echo ${val1%s*}
echo -e "\n"

#${val%%pattern}:检测字符串中是否满足同一的模式结尾,是就删除右边最长的匹配模式
val2=helloshellhelloshellhelloshell
echo ${val2%%s*}
echo -e "\n"

#${val#pattern}:检测字符串中是否满足同一的模式结尾,是就删除左边边最短的匹配模式
val3=helloshellhelloshellhelloshell
echo ${val3#*s}
echo -e "\n"

#${val##pattern}:检测字符串中是否满足同一的模式结尾,是就删除左边边最长的匹配模式
val4=helloshellhelloshellhelloshell
echo ${val4##*s}
echo -e "\n"

image-20240108223339850

8.重新认识Shell基本变量

讲到这里,其实对Shell大部分语法都了解了,在第二章节里我们简单介绍了Shell的内置变量,如今,我们再重新介绍它们,让大家有个不同的认识。

(一)基本变量

  • $0:执行的文件名
  • $1:脚本文件的第一个参数
  • $#:传递到脚本的参数个数
  • $*:以一个单字符串显示所有向脚本传递的参数
  • $?:显示最后命令的退出状态,0表示没有错误,其它值表示有错误例子a.sh
  • @:与@:与*类似,但是使用时要加引号,如"@",可以以@",可以以1,$2...的形式输出参数。
  • $-:显示Shell使用的当前选项,和set类似

我相信,大家此时再看见这些符号就会熟悉很多了。

(二)常见通配符

  • *:匹配0或者多个字符,如a*,那么aa,ab,acaaaa,等等都是符合的
  • ?:匹配任意一个字符,如a?b,那么abb,anb等等都是符合的
  • [list]:匹配列表内的一个字符,如a[xyz]b,那么只有axb,ayb,azb.
  • [!list]:匹配一个不满足列表条件的字符,如a[!0-9]b,那么中间字符就不能是阿拉伯数字。
  • {string1,string2,string3...}:匹配大括号内的一个字符串

尽管通配符看起来和正则表达式差不多,但是还是不同的,把通配符当作正常符号看就行了。

(三)常见元字符(特殊字符)

  • IFS:由或或三者之一组成
  • CR:
  • =:变量赋值
  • $:引用变量或者运算替换
  • >:重定向至标准输出
  • <:重定向至标准输入

剩下的一部分元字符已经再之前的章节里介绍了,就不再赘述。

(四)转义符

有的时候我们想让特殊符号单纯作为一个字符,就需要用到转义字符。

  • '':单引号,其中的Shell元字符通配符都会被关掉,但是不允许在单引号内再出现单引号。
  • "":双引号,其中只允许出现特定的Shell元字符--$和`