目 录CONTENT

文章目录

Shell 编程语法

Sakura
2023-10-01 / 0 评论 / 0 点赞 / 13 阅读 / 14705 字 / 正在检测是否收录...

一: 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 互相切换

  1. 切换 sh 到 bash

# ln -sf /bin/bash /bin/sh
  1. 切换 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. 脚本的执行方式

  1. sh/bash + 脚本的路径 ( 相对绝对 )

好处: 不用赋予脚本执行的权限

sh HelloWorld.sh 
bash HelloWorld.sh 
  1. 输入脚本的相对位置或者绝对位置执行脚本

缺点: 必须要有可执行的权限

# 先赋予权限
chmod +x HelloWorld.sh
# 才能执行脚本
./HelloWorld.sh

注意:

第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行权限。

第二种执行 方法,本质是脚本需要自己执行,所以需要执行权限

另外这两种方式是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则子 shell 关闭,回到父 shell

  1. 在脚本的路径前加上 " ," 或者 source

source Test1.sh
. Test1.sh
  1. 第三种 , 可以使脚本内容在当前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. 变量的作用域

  1. 命令行中定义的普通变量在脚本中是不能访问的

# 命令行中定义的普通变量
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 也无法访问

  1. 临时的用户环境变量只作用到当前 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

五: 运算符

  1. $((运算式))$[运算式]

  2. 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

注意:

  1. [ 条件判断式 ],中括号和条件判断式之间必须有空格

  2. 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
  1. case 行尾必须为单词in,每一个模式匹配必须以右括号 )结束

  2. 双分号;;表示命令序列结束,相当于 break

  3. 最后的*)表示默认模式,相当于 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
  1. 必须在调用函数地方之前,先声明函数,因为 shell 脚本是逐行解释的

  2. 函数返回值,只能通过$?系统变量获得,

#!/bin/bash

function sum(){
	let s=$1+$2

	echo $s
	return $?
}

result=`sum 100 200`
# sum 10 400 
echo $result

return 返回的只是函数的执行状态

echo 真正的返回值是通过 echo 输出的

0

评论区