一: Debain 的 Shell 解释器
在 Debian 系统和基于 Debian 的发行版中,/bin/sh
是指向 /bin/dash
的
root@VM-8-13-debian:/usr/bin# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 5 2023 /bin/sh -> dash
而其他的发行版中,/bin/sh
是指向 /bin/bash
的
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Oct 15 2017 /bin/sh -> bash
二: bash 和 dash 互相切换
切换 sh 到 bash
# ln -sf /bin/bash /bin/sh
切换 sh 到 dash
# ln -sf /bin/dash /bin/sh
如果制作 docker 镜像,想进入基于此镜像的容器后默认使用 bash,可以在对应的 Dockerfile 中添加这行命令
RUN ln -sf /bin/bash /bin/sh
三: Shell 脚本入门
1. 脚本格式
脚本以#!/bin/bash
开头( 指定解析器 )
vim HelloWorld.sh
#!/bin/bash
echo "Hello,Sakrua!"
2. 脚本的执行方式
sh/bash + 脚本的路径 ( 相对绝对 )
好处: 不用赋予脚本执行的权限
sh HelloWorld.sh
bash HelloWorld.sh
输入脚本的相对位置或者绝对位置执行脚本
缺点: 必须要有可执行的权限
# 先赋予权限
chmod +x HelloWorld.sh
# 才能执行脚本
./HelloWorld.sh
注意:
第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行权限。
第二种执行 方法,本质是脚本需要自己执行,所以需要执行权限
另外这两种方式是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则子 shell 关闭,回到父 shell
在脚本的路径前加上 " ," 或者 source
source Test1.sh
. Test1.sh
第三种 , 可以使脚本内容在当前shell里执行 , 而无需打开字shell , 这就是为什么每次修改完 /etc/profile 后 , 需要 source的原因
3. 多命令处理
需求: 在当前目录下创建一个 Test.txt,在 Test.txt 文件中写入一段话
#!/bin/bash
cd ~/ShellTest
touch Test.txt
echo "我永远喜欢玩ff14 ! " >> Test.txt
四: Shell 中的变量
1. 系统变量
系统变量: 作用域是整个操作系统或者整个用户,
# 查看系统变量的值
echo $HOME
# 显示当前Shell中所有变量
set
在/etc/profile
文件中定义的变量都是系统环境变量,所有用户可访问
# 在/etc/profile中定义系统变量
export ff14=Sakura
# 重新加载目录
source /etc/profile
# echo $ff14
Sakura
在~/.bahrc
中定义的变量是用户变量,其他用户无法访问
在命令行中定义的系统变量只作用在这一个回话中,exit 退出了就没有了
在配置文件定义的系统变量是永久的
2. 自定义变量
普通变量:作用域是当前 shell (当前的解释器)
# 1.定义变量:变量=值 ( =前后不能有空格 )
a=8
# 变量的值如果有空格,需要用双引号或者单引号括起来
ff14="The best MMo"
# 2.撤销变量:unset 变量
unset $a
# 3.声明静态变量: readonly 变量 ( 不能unset )
redonly b=2
3. 变量的作用域
命令行中定义的普通变量在脚本中是不能访问的
# 命令行中定义的普通变量
root@VM-8-13-debian:~/ShellTest# A=5
root@VM-8-13-debian:~/ShellTest# echo $A
5
# bash中取出这个变量
root@VM-8-13-debian:~/ShellTest# vim Test1.sh
echo $A
root@VM-8-13-debian:~/ShellTest# sh Test1.sh
用户登录之后开启一个 bash 解释器,当启动一个脚本文件:重新启动 一个 bash 去执行脚本,二号 bash 是一号子bash
而因为普通变量的作用域只有当前解释器,所以就算是子 bash 也无法访问
临时的用户环境变量只作用到当前 bash及它的子bash
// 在一个会话中定义一个用户系统
export TEST_A=1111
// 在另一个会话中无法访问这个用户系统变量
echo $TEST_A
因为另一个会话中开启了一个新的 bash , 和本次会话中的父 bash 是同级的,所以就算登录的用户是同一个也无法访问到
4. 特殊变量
$n
功能描述:n 为数字,$0 代表该脚本名称,$1-$9 代表第一到第九个参数,十以上的参数,十以 上的参数需要用大括号包含,如${10} )
输出该脚本文件名称,输入参数1和输入参数2的值
#!/bin/bash
echo "$0 $1 $2"
# sh Test11.sh Sakrua ff14
Test11.sh Sakrua ff14
$#
功能描述:获取所有输入参数个数,常用于循环
获取输入参数的个数
#!/bin/bash
echo "$0 $1 $2"
echo "$#"
# sh Test11.sh Sakruas ff14
Test11.sh Sakruas ff14
2
$* 和 $@
$*
功能描述:这个变量代表命令行中所有的参数,$*
把所有的参数看成一个整体
$@
功能描述:这个变量也代表命令行中所有的参数,不过 $@
把每个参数区分对待
#!/bin/bash
echo "$*"
echo "$@"
# sh Test11.sh Sakura ff14
Sakura ff14
Sakura ff14
在进行循环的时候这两个才有区别
$?
功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执 行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确 了。
判断
Test.sh
脚本是否正确执行
# sh Test11.sh Sakura ff14
Sakura ff14
Sakura ff14
# echo $?
0
五: 运算符
$((运算式))
或$[运算式]
expr + , - , \*, /, %
加,减,乘 ( 需要转义 ) ,除,取余
expr 运算符间要有空格
# 计算3+2
expr 2 + 3
# 计算3-1
expr 3 - 2
# 计算 (2+3)*4
expr `expr 2 + 3` \* 4
result=$[(2+3)*4]
echo $result
如果希望将 expr 的结果赋给某个变量 , 使用
``
六: 条件判断
1. 整数比较
-lt
小于(less than)
-le
小于等于(less equal)
-eq
等于(equal)
-gt
大于(greater than)
-ge
大于等于(greater equal)
-ne
不等于(Not equal)
# [ 22 -eq 22 ]
# echo $?
0
2. 文件权限
-r
有读的权限(read)
-w
有写的权限(write)
-x
有执行的权限(execute)
# [ -r HelloWorld.sh ]
# echo $?
0
3. 文件类型
-f
文件存在并且是一个常规的文件(file)
-e
文件存在(existence)
-d
文件存在并是一个目录(directory)
# [ -d HelloWorld.sh ]
t# echo $?
1
注意:任何命令的执行状态为0则为True,否则为False
七: 流程控制
1. if 判断
# 单分支
if [ 条件判断式 ]
then
程序
fi
# 多分支
if [ 条件判断式 ]
then
程序
elif [ 条件判断式 ]
then
程序
else
程序
fi
注意:
[ 条件判断式 ],中括号和条件判断式之间必须有空格
if 后要有空格
输入一个数字,如果是1,输出无主之地 ,如果是2,ff14,如果是其他,输出学习
#!/bin/bash
if [ $1 -eq "1" ]
then
echo "玩无主之地2"
elif [ $1 -eq "2" ]
then
echo "玩ff14"
else
echo "学习"
fi
2. case 语句
case $变量名 in
"值1")
如果变量的值等于值1,则执行程序1
;;
"值2")
如果变量的值等于值2,则执行程序2
;;
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
case 行尾必须为单词
in
,每一个模式匹配必须以右括号)
结束双分号
;;
表示命令序列结束,相当于 break最后的
*)
表示默认模式,相当于 default
case $1 in
"1")
echo "玩无主之地3"
;;
"2")
echo "玩ff14"
;;
*)
echo "学习"
;;
esac
3. for 循环
for (( 初始值;循环控制条件;变量变化 ))
do
程序
done
从1加到100
#!/bin/bash
result=0
for (( i=0;i<=100;i++ ))
do
# let result=result+i
result=$[$result+$i]
done
echo $result
root@VM-8-13-debian:~/ShellTest# sh Test11.sh
5050
4. for in 循环
for 变量 in 值1 值2 值3…
do
程序
done
打印所有输入参数
#!/bin/bash
for i in "$*"
do
echo "参数: $i"
done
for i in "$@"
do
echo "参数: $i"
done
可以看出加了引号之后$*
和$@
的区别
5. while 循环
while [ 条件判断式 ]
do
程序
done
#!/bin/bash
i=0
result=0
while [ $i -le 100 ]
do
let result=result+i
let i++
# result=$[$result+$i]
# i=$[$i+1]
done
echo $result
八: read 读取控制台输入
read (选项) (参数)
# 选项
# -p: 指定读取值时的提示符
# -t: 指定取值时等待的时间(秒)
# 参数
# 变量: 指定读取值的变量名
提示5秒内,输入姓名
#!/bin/bash
read -t 5 -p "请在5秒内输入的你的名字:" name
echo "Hello,$name"
九: 函数
1. 系统函数
basename
basename 命令会删掉所有的前缀包括最后一个 /
字符,然后将字符串显示出来
basename [string / pathname] [suffix]
# string 为路径
# suffix 为后缀,basename会去掉指定后缀
# 截取 /home/data/Sakura.txt .txt 路径下的文件名,去掉后缀
basename /home/data/Sakura.txt .txt
Sakura
dirname
从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)
获取 Sakura.txt 的文件路径
dirname /home/data/Sakura.txt
/home/data
2. 自定义函数
# 定义函数
[ function ] funname[()]
{
Action;
[return int;]
}
# 调用函数
funname
必须在调用函数地方之前,先声明函数,因为 shell 脚本是逐行解释的
函数返回值,只能通过
$?
系统变量获得,
#!/bin/bash
function sum(){
let s=$1+$2
echo $s
return $?
}
result=`sum 100 200`
# sum 10 400
echo $result
return 返回的只是函数的执行状态
echo 真正的返回值是通过 echo 输出的
评论区