Shell语法
一、shell的概论
1.1 概念
shell是用C语言编写的程序,是用户通过命令行和操作系统沟通的语言,shell既是一中命令语言,也是一种程序设计语言.简述来说,shell脚本可以直接在命令行执行,即shell在终端的命令行中逐行执行.shell脚本也可以将一套的逻辑放到文件中,直接执行该文件,方便复用.
1.2 类型
在Linux中常见的shell脚本有很多种,常见的有
- Bourme Shell(
user/bin/sh
或者/bin/sh
)- Bourme Again Shell(
/bin/bash
)- C Shell(
/bin/bash/csh
)- K Shell(
/bin/bash/ksh
)- …
shell编程只要有写代码的文本编辑器和一个能解释执行的脚本解释器就可以了,shell脚本在Linux可以直接执行,在windows终端中需要git去Git Bash Here才能执行.Windows CMD常用命令大全(值得收藏) - 知乎 (zhihu.com)
Linux系统一般默认使用bash,所以以下的都是bash中的语法.
shell文件开头的开头都要加上!# /bin/bash
,表明bash为脚本的解释器(即编译器)
1.3 脚本示例:
新建一个test.sh文件,内容如下
#! /bin/bash echo "Hello world!"
1.4 运行方式
作为可执行文件
acs@1463acb54038:~$ ls homework acs@1463acb54038:~$ vim test.sh acs@1463acb54038:~$ ls homework test.sh acs@1463acb54038:~$ chmod +x test.sh acs@1463acb54038:~$ ls homework test.sh acs@1463acb54038:~$ bash test.sh Hello World!
二、变量
2.1 Shell变量的种类
运行shell脚本时,会同时存在三种变量:
- 局部变量:局部变量在脚本或命令中定义,仅在当前的shell实例中有效,其他shell启动的程序不能访问局部变量
- 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常的运行.
shell变量:shell变量是由shell程序设置的特殊变量,shell变量有一部分是环境变量,有一部分是局部变量.
将局部变量变为环境变量:acs@1463acb54038:~$ name=lwy acs@1463acb54038:~$ export name acs@1463acb54038:~$ declare -x name acs@1463acb54038:~$ bash acs@1463acb54038:~$ echo ${name} lwy
2.2 变量的使用
# 变量的定义
name='lwy' # 单引号定义字符串
name="lwy" # 双引号定义字符串
name=lwy # 不加引号也表示字符串
# 使用变量 在定义过之前加上$,最好是加上{},用来识别变量的边界
echo ${name} # 输出lwy
echo $name # 输出lwy
echo ${name}hhh # 输出lwyhhh
# 只读变量和删除变量
readonly boy=lwy # 定义只读变量
boy=zyc # 会报错,只读变量不能修改
unset name # 删除name变量
echo name # 会输出为空
2.3 字符串
# 字符串有三种方式 单引号 双引号 不加引号
name='lwy'
name="lwy"
name=lwy
# 字符串拼接
echo "hello, "${name}"!"
echo 'hello,'${name}'!'
echo 'hello,${name}!'
# 分别输出 hello,lwy/hello,lwy/hello,${name}!
# 获取字符串长度 #获取长度
str="qwer"
echo ${#str}
# 提取子字符串
echo ${str:0:2}
# 输出qw
三、传递参数
3.1 文件参数变量
当我们在执行Shell脚本时,向脚本传递参数,脚本内获得参数的格式为:$n,n表示第n个参数,$0表示执行的文件名(含文件路径)
#! /bin/bash # 该文件的文件名为test.sh str="传递参数" echo "这个文件名为"${0} echo "who's boy?"${1} echo "who's girl?"${2}
可以将该脚本设置执行权限,并执行脚本
chmod +x test.sh ./test lwy zyc :<<abc 这是文件的注释,输出的结果如下 这个文件名为./test.sh who's boy?lwy who's girl?zyc abc
3.2 其他参数相关变量
参数 说明 $# 获取参数的个数 $@ 输出" ${1}, ${2}, ${3} "的值(数组) $* 输出" ${1}, ${2}, ${3} "的值(字符串) $? 上一条命令的退出状态,即exitcode,0表示正常退出,其他表示错误 $(command) 返回 common
这条命令的stdout(可嵌套)`command`
返回这个 command
这条命令的stdout(可嵌套)$$ 当前脚本的进程id
四、数组
4.1 定义和读取数组
Shell数组中可以放多个不同类型的值,Bash Shell只支持一维数组,初始化时不需要定义大小,数组元素下标> 由0开始,Shell数组由括号来表示,元素由空格分隔开,语法格式如下
array_name=(value1 value2 value3 … valuen)ararry = (a 123 "lwy" 'c') #可以用下标来定义数组 array[0]=a array[1]=123 # 可以直接下标读取 echo ${array[0]} # 输出a echo ${array[1]} # 输出123
4.2关联数组
Bash支持关联数组,可以使用任意的字符串、或整数作为下标来访问数组元素
关联数组使用declare
命令来声明declare -A arrayname declare -A site = (["name"]="lwy" ["age"]=18 ["sex"]=man) echo ${site["name"]}
使用@和*可以数组的所有元素
echo "数组的元素为"${site[@]} echo "数组的元素为"${site[*]} # 数组前加!表示获取数组所有的键值 echo "数组的元素为"${!site[*]} # 获取数组的长度 echo "数组的长度为"${#site[*]} echo "数组的长度为"${#site[@]}
Bash支持关联数组(也就是哈希表),可以使用任意的字符串、或者整数作为下标来访问数组元素
关联数组的命令用declare
命令来声明
declare -A arraynamedeclare -A student student["boy"]=lwy student["girl"]=zyc student["one"]=hhhh echo ${!student[*]} echo${student[*]}
五、expr命令
expr
用于求表达式的值,格式为
expr 表达式
表达式说明:
- 用空格隔开每一项
- 用反斜杠转义
\
- 对包含空格和其他特殊字符的字符串要用引用括起来
- expr 会在
stdout
输出结果.如果为逻辑关系表达式,则结果为真时,stdout
输出1,否则输出0
- expr也会有
exit code:
,如果为逻辑关系表达式,则结果为真时,exit code
为0,否则为1
5.1 基本的运算符
假设变量a为10,变量b为20
- 运算符号:
+ - * / % ()
运算符 说明 举例 + 加法 `expr $a + $b`结果为30 - 减法 `expr $b - $a`结果为10 * 乘法 `expr $a \ * $b`结果为200 / 除法 `expr $b / $a`结果为2 % 取余 `expr $b % $a`结果为0 () 改变优先级 `expr \ ($b + $a\ ) \ * a `结果为300
5.2 逻辑关系表达式
- 逻辑符号:
| & < <= = == ! >= >
- 假设变量a为10,变量b为20
运算符 说明 举例 | 或 `expr $a \ | $b`结果为10 & 与 `expr $b \ & $a`结果为20 > 大于 `expr $a \ > $b`结果为0 == 等于 `expr $b \ = \ = $a`结果为0 ! 非 `expr !a`结果为0
六、read命令
read命令用于从标准输入中读取单行数据。当读到文件结束符时,exit code为1,否则为0。
参数说明:
- -p :后面可以接提示信息
- -t:后面可以跟秒数,定义输入字符等待时间
read name lwy echo ${name} # 输出为lwy read -p "what's your name?" -t 10 name lwy what's your name? lwy echo ${name} lwy
七、echo命令
7.1显示普通字符
echo string
#例子
echo "It is a test" # 字符串
echo It is a test # 可以省略""
echo "\"It is a test\"" # 转义字符 输出“It is a test”
7.2显示开启转义
echo -e "Hello\n"
echo "lwy"
:<<abc
输出
hello
lwy
abc
echo -e "hello \c"
echo lwy
#输出 hello lwy
7.3 显示结果定向至文件
echo "Hello World" > out.txt # 将内容以覆盖的方式out.txt中
cat out.txt # 显示 Hello World
7.4显示命令执行的结果
echo `date`
# 输出的结果为:Thu Jun 8 22:33:17 CST 2023
八、printf命令
printf
命令用于格式化输出,类似于C/C++中的printf函数.
默认不会再字符串的末尾添加换行符
echo "Hello, lwy"
# 输出 Hello lwy
printf "Hello, lwy\n"
# 与上面的运行结果一致
命令格式:
printf format-string [arguements]
- format-string :为格式控制字符串
- arguments: 参数列表
#! /bin/bash
printf "%s %s %s\n" lwy love zyc
printf "%10d\n" 1234
printf "%10.2f\n" 123.245616
printf "who're you? %s\n" lwy
九、Test命令与判断符号[]
test 与 expr 的不同 :
- test表示的是
exitcode
值,exitcode
为0是表示真,1则表示假- expr表示的是
stdout
值,stdout
为1表示真,0表示假- test和expr作用:test更多用来检测,expr更多的是求表达值
9.1 逻辑运算符 && 和 ||
- && 表示与,||表示或
expr1 && expr2
:当expr1
为假时,直接忽略expr2
expr1 || expr2
:当expr1
为真时,直接忽略expr2
9.2 test命令
可以在命令行中输入
help test
,可以查看test
命令的用法
test
命令用于判断文件类型,以及对变量做比较
9.3 变量比较
9.3.1 整数比较
命令格式:
test &a -gt $b # a是否大于b
例如:
test 1 -lt 2
echo $?
test 1 -lt 2 && echo "true" || echo "false"
# exitcode的值为0,所以输出为0
# 1 < 2 输出为true
命令 | 表示意义 |
---|---|
| 等于 |
| 不等于 |
| 大于 |
| 大于等于 |
| 小于等于 |
| 小于 |
9.3.2 字符串比较
命令格式:
test -z string
命令 | 表示意义 |
---|---|
| 判断string是否为空,如果为空,则返回0 |
| 判断string是否为非空,如果为非空,则返回0 |
| 判断两个字符串是否相等 |
| 判断两个字符串是否不相等 |
| 字符串也可以做比较 |
9.4 文件格式的判断
9.4.1 文件类型的判断
命令格式:
test -e filename # 判断文件是否存在
命令 | 表示意义 |
---|---|
| 判断文件是否存在 |
| 判断是否为文件 |
| 判断是否为文件夹 |
9.4.2 文件权限的判断
命令格式:
test -x filename # 判断文件是否可执行
命令 | 表示意义 |
---|---|
| 判断文件是否为可执行文件 |
| 判断文件是否为可读文件 |
| 判断文件是否为可写文件 |
| 判断文件是否为空文件 |
9.4.3 多重条件的判断
命令格式:
test -r filename -a -x filename #判断是否同时成立
test -r filename -a -x filename && echo "true" || echo "false"
命令 | 表示意义 |
---|---|
| 判断两个条件是否同时成立 |
| 两条件是否至少一个成立 |
| 取反 test ! -x file 当文件不可执行是返回0(也就是true) |
十、判断语句
判断语句为if…then 或 case …esac类似于
C/C++
的if-else和switch
**condition:**表示的是exitcode
的值
10.1 if …then
- 单层if
命令格式:
if condition then 语句1 语句2 fi
- 单层if-else
命令格式:
if condition then 语句1 语句2 esle 语句3 语句4 fi
- 多层if-elif-elif-else
命令格式:
if condition then 语句1 语句2 elif condition 语句3 语句4 esle condition 语句5 语句6 fi
10.2 case …esac
命令格式:
case $变量名称 in 值1) 语句1 语句2 ;; 值2) 语句3 语句4 ;; *) #类似于C/C++的default 语句5 语句6 ;; esac
示例:
a = 4
case $a in
1)
echo "${a}" 等于1
;;
2)
echo "${a}" 等于2
;;
3)
echo "${a}" 等于3
;;
*)
echo 其他
;;
esac
# 输出其他
十一、循环语句
循环语句包括
- for …in … do … done
- for … in $(seq 1 10) do … done
{1 .. 10}
或者{a .. z}
- for ((…;…;…)) do … done
- while …do … done 循环
- until… do …done 循环
- break命令
- continue命令
11.1 for循环
- for …in … do … done
示例 :
for i in a abc 123 do echo $i done # 输出a abc 123 for i in `ls` do echo $i done # 输出当前文件夹的文件名
- for … in $(seq 1 10) do … done
示例 :
for i in $(seq 1 10) do printf "%d " $i done 输出 1 2 3 4 5 6 7 8 9 10 for i in {1..10} # {1..10}不能有空格 do echo $i done # 输出1 2 3 4 5 6 7 8 9 10
- for((…;…;…)) do … done # 类似于C/C++中的for循环, 不需要在意空格
示例 :
for ((i = 0; i < 10; i ++)) do echo $i done # 输出的结果是 0 1 2 3 4 5 6 7 8 9
11.2 while和until
while与until的作用是一样的,但是判断条件不同,while是判断为真则运行语句,until是判断为假则运行语句.
命令格式::# while的格式 while condition do 语句1 语句2 done until的格式 until condition do 语句1 语句2 done
示例:
while [ $i -ne 0]. do echo $i let "i -= 1" echo $i ((i --)) done
11.3 continue和break
countinue和break两个语句与
C/C++
基本上类似,不过要注意的点就是break不能跳出case语句.
示例::# continue: for ((i = 0; i < 10; i ++)) do if [ `expr $i % 2` -eq 0 ] then continue else echo $i fi done # 输出1 3 5 7 9 # break: for i in {1..10} do case $i in 5) break; ;; *) echo $i ;; esac done # 输出0 1 2 3 4 5
11.4 死循环处理的方式
ctrl + c
即可
或者可以直接关闭进程
- 使用
top
命令查看进程的PID- 使用
kill -9 PID
即关闭此进程
十二、函数
shell中的函数类似于
C/C++
中的函数,但return
的返回值与C/C++不同,返回的是exit code
,取值为0 - 255,0表示正常结束
如果想获取函数的输出结果,可以通过echo输出到stdout中,然后通过$(function_name)
来获取stdout的结果.
函数的return
值可以通过$?
来获取.
函数的命令格式:
[function] function_name() { # 前缀的function可以省略,参数可以直接与 $1 $2去获取 语句1 语句2 ... }
示例:
func(){ local name="lwy" # 局部变量 echo $name is a $1 return 0 #可以不写,默认返回值为0 } res=$? stdout=$(func boy) echo "res = $res" echo "stdout = $stdout" # 输出 res = 0 std = lwy is a boy
用函数实现斐波拉契数列 f(n) = f(n - 1) + f(n - 2)
#! /bin/bash func(){ # 实现斐波拉契数列 f(n) = f(n - 1) + f(n - 2) if [ $1 -eq 1 ]- then echo 0 return 0 fi if [ $1 -eq 2 ]- then echo 1 return 0 fi a=$(func $(expr $1 - 1)) b=$(func $(expr $1 - 2)) echo $(expr $a + $b) } echo $(func 10)
十二、exit命令
exit
命令是用来退出当前shell进程的,并返回一个退出状态,使用**$?**可以接受这个退出状态.
exit
和return
的区别:exit和return都返回的是exitcode
值,但exit是退出的是整个shell进程,而return只是退出整个函数.
示例:
while read name do echo $name if [ $name == "lwy"] then echo "exit" exit 2 fi done echo "没有杀死整个进程" # 不会输出 没有杀死整个进程.
十三、文件重定向
每个进程默认打开三个文件描述符
stdin
:标准输入,从命令行读取数据,文件描述符为0stdout
:标准输出,从命令行输出数据,文件描述符为1stderr
:标准错误输出,向命令行输出数据,文件描述符为2
命令表:
命令 | 说明 |
---|---|
| 将stdout 以覆盖的方式重定向到file 中 |
| 将stdin 以覆盖的方式重定向到file 中 |
| 将stdout 以追加的方式重定向到file 中 |
| 以文件描述符n并追加的方式重定向到file 中 |
| 以文件描述符n并覆盖的方式重定向到file 中 |
输入输出重定向
echo "lwy hhh zyc" > output.txt read str < output.txt echo $str # 输出lwy hhh zyc
同时重定向stdin和stdout
#! /bin/bash # 创建test.sh脚本 read a read b echo $(expr ${a} + ${b}) #再创建input.txt 3 4 ./test.sh < input.txt > output.txt cat output.txt # 查看内容为7
十四、引入外部脚本
类似于C/C++
的include操作,bash可以引用其他脚本的代码
命令格式
. filename # .和 filename之间有一个空格 # 或者 source filename
示例:
创建脚本test1.sh
#! /bin/bash boy=lwy girl=zyc
然后创建脚本
test2.sh
#! /bin/bash echo "${lwy} is a boy" echo "${zyc} is a girl" # 输出 lwy is a boy zyc is a girl
总结
以上就是shell所有的基本内容,其中还有许多没有涉及,如let的使用 top type等一些命令,大家去网上搜一搜就行啦,以上的内容大部分是根据www.acwing.com的yxc老师来写的,所以大家如果能看到最后的话也可以去多多关注yxc老师.