Shell基础学习笔记

Shell是一个命令的解释器,它可以接收应用程序或用户命令,然后调用系统内核。shell也是一个功能强大的脚本语言,易编写,易调试、灵活性强。

Linux提供的Shell解析器如下,写在/etc/shells

1
2
3
4
5
(base) [root@EverNorif ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
  • bash和sh即为一个链接的关系,sh -> bash
  • CentOS默认的解析器是bash

基础语法

入门知识

  • 脚本以#!/bin/bash开头,用以指定解析器

  • 脚本的常用执行方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 方法1
    # 使用bash或者sh的相对路径或者绝对路径,不需要赋予脚本+x的权限
    bash ./helloworld.sh

    # 方法2
    # 直接输入脚本的绝对路径或者相对路径,需要赋予脚本+x的权限
    chmod +x helloworld.sh
    # 如果直接在脚本文件所在目录里调用,则相对路径格式是./
    ./helloworld.sh

    # 方法3
    # 在脚本的路径前加上. 或者 source
    • 方法1和方法2,是在当前Shell中打开一个子Shell来执行脚本内容,当脚本内容结束,则子Shell关闭,回到父Shell中
    • 方法3,可以使得脚本内容在当前Shell里执行,无需打开子Shell
    • 是否打开子Shell的区别就在于环境变量的继承关系,例如子Shell中设置的当前变量,父Shell是不可见的

变量

变量包括系统预定义变量、自定义变量和特殊变量;根据变量的作用范围,可以分为全局的变量和局部的变量。全局指的是对所有的子Shell有效,局部指的是只对自己的Shell有效,对子Shell无效

系统预定义变量

常用的系统变量有:$HOME $PWD $SHELL $USER,可以使用echo查看对应的系统变量的值。set命令则可以显示当前Shell中的所有变量;env命令则可以显示当前系统的所有全局变量

自定义变量

基本语法如下:

1
2
3
4
5
6
7
8
9
10
11
# 定义变量,注意等号的前后不能有空格
A=5

# 查看变量的值
echo $A

# 撤销变量
unset A

# 声明只读变量,只读变量不能修改,也不能撤销unset
readonly B=2
  • 变量定义规则基本同其他的编程语言
  • 等号两侧不能有空格
  • 在bash中,变量的默认类型都是字符串类型,无法直接进行数值运算
    • 如果想要进行运算,需要使用到后续的运算符
  • 变量的值如果有空格,需要使用引号扩起来
  • 默认是局部变量,可以使用export 变量名将其提升为全局变量
  • 对于全局变量的更改,只在当前的Shell中有效,无法影响到父Shell的变量值

echo中引号的区别:

单引号:直接原封不动地输出引号中的内容,比较傻

双引号:会解析其中的使用了$解析的变量,比较聪明

特殊变量

主要用于脚本的输入参数的获取,处理等

  • $n:表示脚本的第几个参数
    • 其中n表示数字,如果数字超过9,则需要使用大括号进行包裹,如${10}
    • $0,表示该脚本的名称,根据调用方式的不同而不同
  • $#:获取所有的输入参数个数,常用于循环中的判断,判断参数的个数是否正确
  • $*:表示命令行中所有参数,将所有参数看作一个整体
  • $@:表示命令行中的所有参数,但是不是一个整体,可以看作是一个参数集合
  • $?:相当于返回值,为最后一次执行命令的返回状态
    • 如果值为0,证明上一个命令正确执行;非0则表示不正确

运算符

前面提到了变量默认都是字符串类型,计算需要使用运算符。

运算符的基本语法为$((运算式))或者$[运算式]

如果需要将一个命令的返回结果当作值进行赋值,则使用反引号或者$()

1
2
a=`ls`
b=$(ls -a)

条件判断

基本语法

  • test 条件表达式
  • [ 条件表达式 ],注意这里条件表达式的前后需要有空格
1
2
[ $B = 2]
echo $?

执行完命令之后,不会自动打印结果,需要使用$?来获取判断的返回值,如果返回值是0,则条件表达式的值为true,否则为false。并且注意这里的运算符两边也要有空格。以及条件非空即为true,即[ $B ]为true,[ ]为false

常用判断条件

  • 字符串之间的比较
1
2
3
4
= 
!=
# 字符串的拼接直接写在一起即可
"$1"_log_$(date +%s)
  • 整数之间的比较
1
2
3
4
5
6
-eq 等于 
-ne 不等于
-lt 小于
-le 小于等于
-gt 大于
-ge 大于等于
  • 文件权限判断
1
2
3
4
5
-r 有读的权限
-w 有写权限
-x 有执行权限

[ -r helloworld.sh ]
  • 文件类型判断
1
2
3
4
5
-e 文件存在
-f 文件存在并且是一个常规的文件
-d 文件存在并且是一个目录

[ -e /root/file]

多条件判断:与&&和或||

1
2
3
4
# 同样具有短路操作的特性,可以用来实现类似?:三元操作符的效果
# &&表示前一条命令执行成功后才执行下一条命令
# ||表示前一条命令执行失败后才执行下一条命令
[ 条件 ] && echo yes || echo no

如果在一个中括号中进行多条件判断,可以使用-a或者-o(and or)

1
2
[ $b -gt 18 -a $b -lt 35]
[ $b -gt 18 -o $b -lt 35]

流程控制

if判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 单分支
if [ 条件表达式 ];then
程序
fi

# 或者
if [ 条件表达式 ]
then
程序
fi

# 多分支
if [ 条件表达式 ]
then
程序
elif [ 条件表达式 ]
then
程序
else
程序
fi
  • 分号;表示一行中可以输入多个命令,使用分号隔开
  • if后面要有空格
  • 条件表达式和中括号之间需要有空格

case语句

1
2
3
4
5
6
7
8
9
10
11
case $变量名 in
"值1")
程序1
;;
"值2")
程序2
;;
*)
默认走的程序
;;
esac
  • case行尾必须为单词in,每个模式匹配必须以)结束
  • 双分号;;表示命令序列结束,相当于java中的break
  • 最后的*)表示默认模式,相当于java中的default

for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 语法1
for (( 初始值;循环控制条件;变量变化))
do
程序
done

# 举例1
#!/bin/bash
sum=0
for((i=0;i<=100;i++))
do
sum=$[$sum+$i]
done
echo $sum
1
2
3
4
5
# 语法2
for 变量 in 值1 值2 值3...
do
程序
done
  • 前面说到的$*$@,如果使用双引号包含的时候,会进行不同的变化,前者作为一个整体,后者展开成为一个序列
  • 如果没有双引号包裹的话,则没有区别
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
echo '=============$*============='
for i in "$*"
#$*中的所有参数看成是一个整体,所以这个 for 循环只会循环一次
do
echo "haha $i"
done
echo '=============$@============='
for j in "$@"
#$@中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
do
echo "haha $j"
done

while循环

1
2
3
4
while [ 条件判断式 ]
do
程序
done

read读取控制台输入

基本语法:read [选项] [参数]

  • 选项:
    • -p:指定读取值时的提示符
    • -t:指定读取值时等待的时间(单位为秒),如果不加-t则一直等待
  • 参数:
    • 变量:指定存放值的变量名
1
2
read -t 7 -p "Enter your name in 7 seconds :" name
echo $name

函数

函数又可以分成系统函数和自定义函数。

这里举几个系统函数的例子

1
basename [string/pathname] [suffix]
  • basename命令会删掉所有的前缀,包括最后一个/字符,然后将字符串显示出来
  • 可以理解为取出路径中的文件名称
  • suffix为后缀,如果被指定了,会将其中的suffix删除
1
dirname 文件绝对路径
  • 从给定给定包含绝对路径的文件名中去除文件名,返回目录的部分

我们也可以自定义函数实现,基本语法如下

1
2
3
4
5
[function] funname[()]
{
函数体;
[retur int;]
}
  • 上面的方括号表示可以省略
  • 函数的形参不需要定义,可以使用$n获取,与脚本类似;也可以有函数返回值,使用$?获取
  • 因为shell脚本是逐行运行的,所以在调用函数之前需要声明
  • 如果不写return,会返回最后一条命令的运行结果。并且return后跟数值n,范围为0-255
  • 一种方法可以绕过返回值的限制,在函数中进行echo,然后用命令替换$()进行捕获
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
function sum()
{
s=0
s=$[$1+$2]
echo "$s"
}

res=$(sum 255 255)
echo $res

应用举例

helloworld.sh

1
2
#!/bin/bash
echo "helloworld!"

acc.sh

1
2
3
4
5
6
7
#!/bin/bash
sum=0
for((i=0;i<=100;i++))
do
sum=$[$sum+$i]
done
echo $sum

printHa.sh

1
2
3
4
5
#!/bin/bash
for i in ha haha hahaha
do
echo "I say $i"
done

Shell基础学习笔记
http://example.com/2022/06/29/Shell基础学习笔记/
作者
EverNorif
发布于
2022年6月29日
许可协议