【Linux】3. Shell语言
本文主要介绍了Shell程序和Shell语言,Shell语言常被用来访问Linux操作系统,因此我们需要简单了解一些关于Shell语言的知识。我们需要重点了解Shell的概念及变量,它对我们学习Linux有重要的作用,其中的环境变量更是会影响程序的行为。对于其他运算符、流程控制语句、函数等了解即可(除了专业写Shell脚本的),这些语法与C语言有相似之处,可以在使用时再去查询。
Shell语言
- 一、Shell的介绍
- 1. Shell简介
- 2. Shell脚本的运行
- 二、注释与输出
- 1. 注释
- 2. 输出
- 三、变量
- 1. 变量的定义
- 1.1 变量的定义与使用
- 1.2 变量类型
- 2. 特殊变量
- 3. 环境变量
- 3.1 常见环境变量
- 3.2 环境变量的操作命令
- 3.3 代码中操作环境变量
- 四、运算符
- 1. 算数运算
- 2. 逻辑运算
- 3. 括号
- 4. 通配符
- 5. 字符串与文件运算
- 五、流程控制语句
- 1. 分支语句
- 2. 循环语句
- 六、函数
一、Shell的介绍
1. Shell简介
Shell程序、Shell语言和Shell脚本在现实中常会被简称为Shell,这导致很多人对Shell的概念比较混乱,其实它们本质是完全不同的东西。
(1)Shell程序
Shell程序是一种用C语言编译的程序,是Shell语言的解释器,专门将Shell语言翻译成CPU可以执行的二进制指令。该程序提供了一个界面,可以让用户输入Shell语言命令并解释成Linux操作系统可执行的指令,是用户与Linux操作系统内核沟通的桥梁。
Linux中的Shell程序种类众多,常见的有:
- Bourne Shell:
/usr/bin/sh
或/bin/sh
。- Bourne Again Shell:
/bin/bash
,该程序是大多数Linux系统的默认Shell程序。- C Shell:
/usr/bin/csh
。- K Shell:
/usr/bin/ksh
。- Shell for Root:
/sbin/sh
。注意:
- Bourne Shell是shell程序的英文叫法,
sh
才是程序本身的文件名。- 由于这些Shell程序都在系统目录里,可以直接使用程序名执行程序。
/bin/sh # 直接打开sh程序
ksh # 运行ksh程序
(2)Shell语言
Shell语言是用户访问Linux操作系统的计算机编程语言,用户在Shell程序中输入Shell语言操作Linux系统。Shell语言是一种解释型语言,即不需要编译成可执行文件,而是在运行时被解释器一条一条翻译成CPU认识的二进制指令并执行。
我们常用的Linux命令就使用的Shell语言,此外Shell语言也具有变量、流程控制语句等语法。
(3)Shell脚本
Shell脚本是一种用Shell语言编写的文本文件,可以直接在Shell中执行。由于Shell脚本文件可以直接被Linux环境中的Shell程序执行,就像命令一样,因此也会有人将其称为可执行程序。
Shell脚本文件以.sh
为后缀。
2. Shell脚本的运行
(1)运行Shell脚本
- 作为可执行程序直接运行(需要指定文件路径,除非在系统目录下或被添加到环境变量中)。
- 作为解释器的参数运行,即指定Shell程序运行脚本,此时脚本开头的
#!
语句会失效。
./test.sh # 执行./test.sh脚本
/bin/bash ./test.sh # 用bin/bash执行./test.sh脚本
(2)指定Shell程序
Shell脚本文件开头通常会有一句#! /bin/bash
,#!
用以指定执行该脚本的Shell程序,若是没有则使用默认Shell程序执行。
#! /bin/bash # 使用/bin/bash程序执行该脚本
(3)包含Shell文件
Shell脚本文件也能像C/C++的头文件一样,被多个源文件引用。在Shell文件中用. 文件路径
或source
命令的方式就能引用其他Shell文件。
. ./test.sh # 引用./test.sh
source ./a.sh # 引用./a.sh
- 注意
.
的后面有空格,不是紧挨文件路径的。 - 被引用的shell文件可以不用可执行
x
权限。
二、注释与输出
1. 注释
(1)单行注释
# 这是一个单行注释
(2)多行注释
:<<EOF 注释内容 EOF
,其中EOF
可以是其他符号,包括!
、'
等。:'注释内容'
。
:<<EOF
这是一个多行注释
这是一个多行注释
EOF:<<!
这是一个多行注释
这是一个多行注释
!:'
这是一个多行注释
这是一个多行注释
'
2. 输出
在Shell语言中,常使用3中方式将数据输出到控制台、文件或其他命令中。
(1)输出数据到控制台的命令
echo
:输出一串字符串(自带换行)。
echo 'abcdef' # 输出'abcdef'
abcdef
var="23456"
echo $var # 输出变量var的值
23456
-e
:启用转义字符。-n
:不换行输出。
printf
:用法与C语言的printf
相似,但是参数都以字符串的形式传输进去,变量需要用$
获取值,用空格隔开。
printf "Hello World"
printf "var=%s n=%d\n" "$var" "$n" # 与C语言的printf("var=%s n=%d\n", var, n);十分相似
printf
与echo
的区别:
printf
能像C语言一样,控制输出字符串格式。echo
自带换行,而printf
不自带,需要手动打。
(2)重定向
重定向:将原本输入到A文件的内容,输入到B文件中,这就是将A文件重定向到B文件。
重定向的应用场景举例:
——
printf
应该将内容打印到标准输出流,我们不想更改代码,但是想让内容打印到txt
文件中,就将该程序中printf
打印的内容输出到txt
文件中了。——如
echo "1234" > file
则是将echo
原本打印到标准输出流的"1234"
写入到file文件里。
- 输入、输出、追加重定向
常用的重定向类型有3种:
- 输出重定向
>
:将标准输出流重定向到指定文件。输出重定向会覆盖指定文件原有的内容。 - 输入重定向
<
:将标准输入流重定向到指定文件,即输入到标准输入流的内容都会写入指定文件中。 - 追加重定向
>>
:将标准输出流的内容以追加的形式重定向到指定文件,不会覆盖指定文件的内容。
cat hello.cpp > file.txt # 使用cat打印hello.cpp的内容,并重定向到file.txt中
wc -l < file.txt # 将wc -l中输入到标准输入流的内容重定向到file.txt中
cat hello.cpp >> file.txt # 以追加的形式重定向到file.txt中
重定向的本质是更改文件描述符表中的文件地址,如将标准输出流的地址改为file.txt的地址,那么该进程写入标准输出流的内存都会进入file.txt文件里。具体原理在《IO操作》种有说明
- 特殊的重定向
<1> 将屏幕输入的内容提供给进程。
wc -l << EOF # 将EOF包裹的内容作为输入提供给wc命令,EOF可以是其他字符"Hello World"Good Good StudyDay Day Up
EOF # 这里必须要顶格,不能空格。
<2> /dev/null
文件:写入它的内容都会被丢弃,在这个文件里是读取不到内容的。
- 它可以相当于一个垃圾桶,重定向到该文件可以自动丢弃写入该文件的任何内容。
- 将某个IO流重定向到这个文件,可以起到屏蔽这个IO流的效果,即往这里输入内存没有效果。
command > /dev/null # 屏蔽command命令的标准输出流
(3)管道
在Linux中,可以使用管道将进程的输出信息发送给其他进程,使用符号进程A | 进程B
(A发送给B)。
cat txt | grep "abcde" # 将cat txt打印的结果发送给grep进程,让grep进程在打印结果中筛选abcde所在的行
管道的原理在《进程间通信》中说明。
三、变量
Shell语言的变量有3种:
- 普通变量:也叫局部变量,该变量只能在当前Shell程序中使用,其他Shell程序中无法使用该变量。
- 环境变量:这些是操作系统或用户设置的特殊变量,用来配置Shell程序的运行环境,会影响Shell命令的行为。
- 特殊变量:如
$0
、$1
、$#
等特殊符号组成的变量,这些变量通常用来获取该脚本执行时的参数及其他信息等。
1. 变量的定义
1.1 变量的定义与使用
(1)定义变量
var="value"
PI=3.14
readonly PI # PI被设置为只读变量
- 变量名和等号直接不能有空格。
- 变量名只能包含字母、数字和下划线,不能以数字开头,避免Shell的关键字。
- 通常使用大写字母表示常量。等号两侧避免使用空格。
- 使用
readonly
关键字定义只读变量,只读变量的值不能改变。
(2)删除变量
unset var # 删除var变量
(3)获取变量值
使用$
符号获取变量值。
echo $var # 打印var的值
b=${var} # 将var的值赋值给b变量
“${var}abc" # 其中获取var变量的值,若是没有{}就会被识别成获取varabc的值
- 获取变量值时,建议用
{}
将变量名包裹起来,方式变量后紧贴着字符时,被识别成其他变量名。
1.2 变量类型
与其他语言一样,Shell语言同样有多种变量类型,但是实际上只有字符串和整型被广泛使用:
(1)字符串
在Shell中变量通常被视为字符串类型,如var=value
中的value
,也可以使用单引号和双引号来定义字符串。
- 单引号:单引号里面的任意字符都会被原样输出,里面的变量是无效的,里面不能出现单引号(使用转义字符后也不行),但可以字符串拼接。
- 双引号:字符串里可以出现变量和转义字符。
'${var}abc' # var变量不会被识别,${var}会被当成字符串的一部分
'abc\'edf' # 会出现错误,\'不会被识别成转义字符'
"${var}abc\'def" # var会被当做变量读取值,\'也会被当成转义字符'
字符串操作
- 拼接字符串:直接将让多个字符串紧跟着写即可拼接,单引号和双引号字符串均可拼接。
- 获取字符串长度:
${#var}
便能获取变量var
的字符串长度(var
被视为字符数组,${#var}
获取数组var
的长度)。 - 截取子串:
${var:i:n}
在var变量中从下标为i
的字符开始,截取n
个字符。
var="edf""abc"${var}"ghi" # 拼接为abcdefghi
${#var} # 获取变量var的字符串长度
${var:0:2} # 从下标0开始截2个字符,即ed
(2)整型
整型变量就是整数,为了防止将整数当成字符串,可以使用declare -i [var]
来显示定义整型变量。
declare -i var=20 # 定义整型变量var
(3)数组
bash
只支持一维数组,不支持多维数组,但没有限制一维数组的大小。
数组的定义有以下3种方式
array1=(value1 value2 value3)array2=(
value1
value2
value3
)array3[0]=value1
array3[1]=value2
array3[9]=value3 # 可以不连续,且大小没有限制
获取数组的值:使用下标获取,若下标为@
或*
则代表获取全部元素。
${array[0]} # 获取下标为0的元素
${array[@]} # 获取所有元素
(4)关联数组
关联数组:下标为字符串类型,bash
支持关联数组,使用declare -A
来定义。
# 定义并赋值
declare -A array1=(["key1"]="value1" ["key2"]="value2" ["key3"]="value3")declare -A array2 # 定义关联数组
array2["key1"]="value1" # 赋值
array2["key2"]="value2"
array2["key3"]="value3"
获取关联数组的值与普通数组差不多,下标用字符串。
${array["key1"]}
${array["key2"]}
${array["key3"]}
2. 特殊变量
特殊变量用于获取当前Shell脚本文件的参数等信息,执行Shell脚本文件时可以传递参数,这些参数靠特殊变量获取,Shell脚本的其他信息,如进程pid
等。
$0
:表示执行的文件名,包含文件路径。$1
、$2
、…、$n
:表示第1个、第2个、……、第n个参数。当n不止一位数时要加上{}
,如${10}
。$#
:参数的个数。$*
:显示所有参数为1个字符串,格式为"$1 $2 $3 ..."
。(以$*
作为参数传递的是一个字符串参数)$@
:显示所有参数,每个参数为1个字符串,"$1"
"$2"
"$3"
…。(以$@
作为参数传递的是多个字符串,即多个参数)$$
:脚本当前运行的进程的pid
。$!
:后台运行的最后一个进程的pid
。$?
:获取最后一个命令退出的状态(通常在命令结束后紧接着获取该命令的退出码或返回值),0表示没问题,其他表示有错误。
./test.sh t1 t2 -t3 t4 t5 # 执行./test.sh脚本,参数为t1 t2 -t3 t4 t5# ./test.sh中获取当前的参数等信息
$0 # 显示test.sh的路径和文件名
$1 # 第一个参数,即t1
$# # 参数个数,即5
$* # 全部参数为一个字符串,即"t1 t2 -t3 t4 t5"
$@ # 全部参数为多个字符串,即"t1" "t2" "-t3" "t4" "t5"
3. 环境变量
3.1 常见环境变量
(1)环境变量的概念
环境变量是操作系统的变量,它通常用于配置程序的运行环境,即程序可以获取环境变量的值,并根据环境变量的值决定自己的功能。如通过环境变量SHELL
知道当前使用的哪个Shell程序运行。用户也可以自己设置环境变量。
局部变量仅在程序内部使用,环境变量作为系统的变量,可以被多个程序共同访问。因此一些环境变量的改变会导致大部分软件的配置发生变化。
(2)常见的环境变量
HOSTNAME
:主机名称。SHELL
:当前使用的Shell程序。SSH_CLIENT
:主机的IP地址、端口号等。USER
:当前登录的用户。LOGNAME
:当前用户名。HISTSIZE
:历史命令的数量。history
:历史命令。PWD
:当前路径。HOME
:当前用户的家目录。PATH
(重要):可执行程序的路径,使用每条路径使用:
隔开。- 执行程序时需要指定路径,否则系统找不到程序的位置。
- 没有指定路径的程序在运行时,系统会在环境变量
PATH
中找该程序的地址,若是找不到则报错。
# PATH中有多个目录地址,这些目录里的可执行程序不需要指定路径就可以执行。
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/wss/.local/bin:/home/wss/bin
(3)环境变量的继承
子进程会继承父进程的变量,即使父进程没有导出为环境变量,子进程也能使用。
3.2 环境变量的操作命令
(1)查看环境变量:
env
:打印所有环境变量。
env
HOSTNAME=VM-12-10-centos
SHELL=/bin/bash
USER=wss
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/wss/.local/bin:/home/wss/bin
PWD=/home/wss
LANG=en_US.utf8
HOME=/home/wss
_=/usr/bin/env
# 此处省略n个字
echo
:打印环境变量的值
echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/wss/.local/bin:/home/wss/bin
(2)环境变量的定义
环境变量的定义与普通变量一样,但是普通变量只能在当前程序中使用,因此要让变量全局生效,则需要用export
导出变量。
- 环境变量命名:环境变量通常使用大写字母,单词间用
_
隔开。 - 环境变量的值通常为字符串类型。
export
:导出为环境变量,即让普通变量全局生效。
MY_ENV="Hello World" # 定义普通变量
export MY_ENV # 导出为环境变量export MY_ENV="Hello World" # 一步到位
(3)删除环境变量
unset
:删除变量,与删除普通变量一样都是用unset
。
unset MY_ENV
(4)修改环境变量
与修改普通变量一样,赋值即可。
- 直接赋值会导致新值覆盖旧值,若是使用追加的方式,则可以先
$
获取环境变量值,再拼接字符串,最后赋值。
MY_ENV="Hi" # 将环境变量赋值为Hi
MY_ENV="${MY_ENV}Hi" # 追加Hi
(5)永久生效的环境变量:
使用export
定义的环境变量只能在本次登录中使用,下次登录时该环境变量就不存在了。永久生效的环境变量是通过文件中的命令导入环境变量的,系统每次加载或用户登录时会自动执行这些文件里的命令。
/etc/profile
或/etc/bashrc
:系统加载时自动执行该文件,里面的命令在开机时执行,所以对所有用户都生效。~/.bash_profile
或~/.bashrc
:用户登录bash时才会执行该文件,只对单一的用户生效。
往这些文件写入导入环境变量的命令即可,第一次刚更改完文件需要用source
命令执行一下才能即刻生效。
# 修改~/.bash_profile文件,在里面加入export MY_ENV="Hello World"命令
sudo vim ~/.bash_profilesource ~/.bash_profile # 执行该文件里的命令,即将新内容执行,使其立刻生效。
3.3 代码中操作环境变量
(1)环境表
代码中通常会用以下方式组织环境变量:
- 每个程序都会收到一张环境表,环境表是一个字符指针数组,被二级指针
char** environ
指向。 - 环境表中的每个指针都指向一个字符串,字符串中就是环境变量及内容。
(2)main函数参数获取环境变量
C/C++语言中,main
函数的第三个参数char* env[]
就是环境变量。
env
是一个指针数组(有些代码也会写成二级指针的形式),数组中都是环境变量值的首地址,数组以空指针结尾。
int main(int argc, char* argv[], char* env[]) {// 遍历环境变量int i = 0;for (i = 0; env[i]; i++) {printf("%s\n", env[i]);}return 0;
}
(3)全局变量environ获取环境变量
environ
是系统提供的一个char**
类型的全局变量,使用前要用extern
声明一下。
int main() {extern char** environ; // 声明envrion变量// 遍历环境变量int i = 0;for (i = 0; environ[i]; i++) {printf("%s\n", environ[i]);}return 0;
}
(4)使用库函数获取getenv环境变量
stdlib.h
里的库函数getenv
获取环境变量。
#include <stdlib.h>char* getenv(const char* name); // 获取环境变量名name的值
- 返回值:环境变量的内容。
- 参数:
name
:环境变量的名字。
(5)导入环境变量的函数
putenv
:导入环境变量。如果导入的是已存在的环境变量,则会修改原来的环境变量的内容。
int putenv(char* string);
- 返回值:成功返回0,失败返回-1。
- 参数:
string
:需要改变或导入的环境变量,格式为变量名=内容
。
#include <stdio.h>
#include <stdlib.h>int main()
{// 导入自定义环境变量ABCint ret = putenv("ABC=123456789");if (ret != 0){perro("导入失败\n");exit(1);}// 获取环境变量ABC的值并打印char* abc = getenv("ABC");printf("ABC = %s\n", abc);return 0;
}
四、运算符
1. 算数运算
(1)算数运算符
bash
不支持原生的算术运算符,即+ - * / %
等,但是可以通过其他命令实现,如awk
或expr
,其中expr
最常见。
expr
:表达式计算工具,使用它能完成表达式的计算。
- 表达式计算需要被反引号`包裹起来,不是单引号’。被反引号包裹起来的语句会被当成命令执行。
- 表达式要与运算符直接有空格,连起来是不对的。(
expr 2 + 3
中expr
是命令,2
+
3
分别是3个参数,不能连一起)
var1=`expr 2 + 3` # 加
var2=`expr 2 - 3` # 减
var3=`expr 2 * 3` # 乘
var4=`expr 2 / 3` # 除
var5=`expr 2 % 3` # 取余
(2)自增与自减运算符
expr
:将变量加一,然后赋值给该变量。let
:该命令允许对整数进行算数操作,并且支持++
和--
作为参数$((表达式))
((表达式))
a=`expr a + 1`
let b++
c=$((c - 1))
((d--))
2. 逻辑运算
(1)关系运算符
除了==
和!=
,bash
同样不支持原生的关系运算符,如> < >= <=
,依然是通过其他命令实现——test
。
test
:关系运算的工具,使用它能完成关系表达式的运算。
test
的结果是true
或false
,它可以被变量赋值,但不会被echo
或printf
打印出来。test
命令的语法可以写成[ 表达式 ]
,[]
必须与表达式之间有空格。- 关系运算符作为参数出现在表达式里,以下运算符只能运算数字:
-eq
(equal):等于==
-ne
(not equal):不等于!=
-gt
(greater):大于>
-lt
(less):小于<
-ge
(greater or equal):大于等于>=
-le
(less or equal):小于等于<=
test $a == $b # a == b
[ $a != $b ] # a != b[ $a -eq $b] # a == b
b=test 1 -ne 2 # 1 != 2,并将结果赋值给变量b
c=[ $c -lt $d] # c < d,并将结果赋值给变量c
(2)逻辑运算符
bash
依然不支持原生的逻辑运算符,如&& || !
,这些依然是通过test
实现,但test
的参数中支持这些符号,但它同样提供了选项参数的格式:
- 与运算:
&&
或-a
(and)。 - 或运算:
||
或-o
(or)。 - 非运算:
!
。
test $a == $b && $c != $d # a == b && c != d
[ $a -gt 30 -o $b -lt 60 ] # a > 30 || b < 60
[ ! $a == $b ] # 对a == b的取反
3. 括号
(1)小括号
单小括号()
-
(命令组)
:小括号里面是命令组,括号中的命令会开一个新的Shell子进程按顺序执行,命令之间用分号隔开,命令和括号之间不需要空格。 -
$(命令组)
:新开一个子进程执行小括号里面的命令,并用标准输出流的内容替换它所在的位置。 -
常用于初始化数组:
arr=(a b c d)
。
注意:由于
()
里的命令会新开一个子进程执行,所以里面定义的变量无法在外面使用。
(cd ./dir; ls -l; touch file) # 新开一个子进程顺序执行cd、ls、touch命令
array=(1 2 3 4 5) # 初始化数组
双小括号(())
(部分Shell程序不支持)
((表达式))
:用于给整数扩展运算方式,双小括号里支持C语言的整数运算(甚至包括三目运算符),多个表达式用逗号隔开。- 双括号里面的变量可以不用
$
前缀获取值。 - 用于逻辑运算时(
if
、for
等的条件)运算结果为0返回true,否则返回false。若是运算结果为0,则该语句的进程退出码为1,否则为0(即运算结果非0时代表没有出错)
- 双括号里面的变量可以不用
((a++)) # 执行a++
for ((i=1;i<5;i++)) # for循环
(2)中括号
单中括号[]
[ 表达式 ]
:[
是test
命令的别名,中括号里的表达式是test
的参数,需要用空格隔开。arr[n]
:中括号也用来随机访问数组。
[ 20 -gt 30 ] # 20 > 30
array[20]
双中括号[[]]
[[ 表达式 ]]
:常用于逻辑判断,支持&&
、||
等符号,且支持正则表达式和通配符。
[[ hello == hell? ]] # ?代表任意字符,是通配符
[[ $a == 2 && $b == 3 ]]
(3)大括号
<1> 通配符
- 大括号中可以用逗号扩展文件名,如
touch {a,b}.txt
的结果就是a.txt
、b.txt
。(中间不能使用空格) - 大括号中以
..
分割文件代表顺序扩展,如touch {a..d}.txt
的结果是a.txt
、b.txt
、c.txt
、d.txt
(a..d
即a到d)。
ls {ex1,ex2}.sh
ex1.sh ex2.shls {ex{1..3},ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.shls {ex[1-3],ex4}.sh
ex1.sh ex2.sh ex3.sh ex4.sh
<2> 代码块
- 代码块也叫内部组,创建了一个匿名函数,不会新开子进程,里面创建的变量可以被外面使用。
- 代码块里面的命令必须用分号结尾(最后一个也要分号),第一个命令必须与
{
中间隔一个空格。
{ cd ./dir; ll; mkdir empty;} # 执行3格命令
大括号还有其他作用,如字符串提取等,不常用的就不赘述了。
(4)$
后的括号
${var}
:获取变量值,不连着其他字符的情况下可以省略。$(cmd)
:新开一个子进程执行小括号里面的命令,并用标准输出流的内容替换它所在的位置。$((expression))
:效果与`expression`
一样,将里面的表达式当做命令执行,支持C语言表达式。
${var}
var=$(ls -l) # 新开一个子进程执行ls、并将标准输出流的内容赋值给var
$((a>b))
4. 通配符
通配符通常用来匹配文件名。
通配符 | 含义 |
---|---|
* | 匹配任意字符串。如cat *.h 打印所有以.h 结尾的文件。 |
? | 匹配任意字符(只能一个字符),如cat abc? 打印文件名为abc+任意字符 的文件。 |
[abcd] | 匹配中括号中的任意字符,如cat [abcd].h ,只能打印a.h 、b.h 、c.h 或d.h 中的一个。 |
[a-z] | 匹配中括号中的a到z之间的所有字母,也能用[A-Z] 匹配大写或[0-9] 匹配数字。 |
{a,b} | 将大括号中的字符展开,如{a,b}.h 就是a.h 和b.h 两个文件。 |
{a...z} | 展开大括号中a到z之间的所有字母。(中间不能有空格) |
[!abcd] 或[^abcd] | 匹配中括号中没有的字符,如左边的式子代码没有abcd中的任意一个。 |
5. 字符串与文件运算
(1)字符串运算
test
也可以对字符串进行运算:
==
:检测2个字符串相等。(不能用-eq
)!=
:检测2个字符串不相等。(不能用-ne
)-z
:检测字符串长度为0。-n
:检测字符串长度不为0。$
:在test
内给字符串取值,字符串不为空时就会返回true,否则返回false。
[ $a == $b ] # 判断a == b
[ -z $a ] # 判断a长度为0
[ $a ] # 若a非空,则返回ture,否则false
(2)文件检测运算
test
可以对文件进行一些判断。
<1> 检测文件是否存在或为空
-e
:检测文件是否存在。-s
:检测文件是否为空。
test -e $file # file是否存在
[ -s $file ] # file是否为空
<2> 检测文件类型
-f
:检测文件是否为普通文件。-d
:检测文件是否为目录。-L
(大写):检测文件是否为符号链接(软链接)。-c
:检测文件是否为字符设备文件。-b
:检测文件是否为块设备文件。-p
:检测文件是否为有名管道文件。-S
(大写):检测文件是否为socket文件。
if [ -f $file ]
thenecho "普通文件"
elif [ -d $file ]
thenecho "目录文件"
elif [ -L $file ]
thenecho "软链接"
elif [ -c $file ]
thenecho "字符设备文件"
elif [ -p $file ]
thenecho "管道文件"
elif [ -S $file ]
thenecho "socket文件"
elseecho "块设备文件"
fi
<3> 检测文件权限
-r
:检测文件是否为可读。-w
::检测文件是否为可写。-x
:检测文件是否为可执行。-k
:检测文件是否设置了粘滞位。
if [ -r $file ]
thenecho "文件可读"
elseecho "文件不可读"
fiif [ -w $file ]
thenecho "文件可写"
elseecho "文件不可写"
fiif [ -x $file ]
thenecho "文件可执行"
elseecho "文件不可执行"
fi
五、流程控制语句
1. 分支语句
(1)if语句
- 以
if
起始,中间用elif
添加判断条件,else
后是条件以外的语句,fi
结束。 - 除了
else
,条件判断后都要加上then
才到执行语句。 - 写成一行时,要在条件判断和执行语句后加上
;
,通常用来在Shell终端中写命令行。 - 条件判断语句有多种编写方式:
test
命令或[ 表达式 ]
,里面的大于小于只能用-gt
、-lt
等。((表达式))
,大于小于可以直接用>
、<
等C语言表达式符号。
# 写成多行
if [ $a -gt 10 ] # 使用[]
thenecho "a > 10" echo "a大于10"
elif (($a > 5)) # 使用(())
thenecho "5 < a <= 10"echo "a大于5且小于等于10"
elseecho "a <=5"echo "a小于等于5"
fi# 写成一行
if test $b -gt 3; then echo "b > 3"; fi
(2)case语句
- case相当于C语言里的switch语句,以
case
开头,esac
结尾(case倒过来写)。 - 每个分支以
值)
开始,以2个分号;;
结束,值可以是常量或变量,匹配到哪个值就执行哪个分支。 - 若是都没有匹配到,则会执行
*)
分支。
case $var in1) echo 'var=1' # var==1时执行该语句;;2) echo 'var=2';;3) echo 'var=3';;4) echo 'var=4';;*) echo 'var!=1,2,3,4' # var都没有匹配到时执行该语句;;
esac
2. 循环语句
(1)for循环
for
后面的变量用于每次循环取值,in
后面是每次取值的元素。循环体用do
和done
包裹起来。- 若是
in
后面是数组变量,则需要用${arr[@]}
将数组展开。
# 循环多个元素
for var in 1 2 3 4 5
doecho "本次循环的var值是${var}"
done# 循环数组
for var in ${array[@]}
doecho "本次var的值是$var"
done
(2)while循环
除了条件判断格式、循环体被包裹、shell特色的自增自减语句,基本上与C语言的while
一样了。
while (( $a<=5 ))
doecho "a=$a"let a++;
done
死循环
# 用:作为条件语句
while :
docommand
done# 用true作为条件
while true
docommand
done# for循环里俩冒号
for (( ; ; ))
(3)until循环
与while
相反,当条件为false才执行循环体,条件为true时停止执行。
until [ ! $a -lt 10 ] # 结果为false时才执行
doecho $aa=`expr $a + 1`
done
(4)跳出循环
break
:中止所在的循环语句。continue
:中止该循环语句的本次循环。
for var in 1 2 3 4 5
doif [ $var == 3 ] thencontinue # 结束var==3时的循环,其他1245不影响fiecho "本次循环的var值是${var}"
done
六、函数
-
函数定义:函数在使用前必须定义,因此函数通常要写在前面。
-
函数参数:
- 函数在定义时不用写参数类型和参数名。
- 获取参数时使用特殊变量,如
$1
等。
-
函数调用:与执行命令一样,
函数名 参数列表
。 -
返回值:
- 返回值只能返回0到255之间的整数。
- 函数调用后,只能紧接着使用特殊变量
$?
获取返回值。
func(){ # func函数,不用在小括号中写参数名。var=$1 # 获取第一个参数... ...return $n # 只能返回0到255之间的整数。
}func 1 2 3 4 # 调用func函数并传参1 2 3 4
echo $? # 获取func的返回值。