shell编程总结

news/2025/2/21 11:34:59/

前言

shell编程学习总结,1万3千多字带你学习shell编程

往期推荐

14w+poc,nuclei全家桶:nuclei模版管理工具+Nuclei

哥斯拉二开,免杀绕过+规避流量检测设备

fscan全家桶:FscanPlus,fs,fscan适用低版本系统,FscanParser

自动爬取url地址,检测sql注入漏洞,sqlmc安装+使用

一键转换订阅为代理池工具+白嫖思路

TestNet,安装+使用,可以代替灯塔

shell1,脚本的创建和执行+变量的使用

脚本的创建和执行

以kail为例,进入终端,创建一个sh脚本

vim 1.sh
# 按i进入插入模式,输入
echo hello world
# 按esc退出插入模式,然后:wq 保存文件并且退出

直接执行1.sh发现权限不够,需要赋予权限

chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险

image.png

除了赋予权限,我们还可以让脚本解释器帮我们执行脚本,由于解释器本身就赋予权限,所有不需要在为脚本赋予权限,我们在创建一个2.sh,内容为:echo 111
#!/bin/sh#!/bin/bash#!/bin/dash三个都是脚本解释器
image.png
可以发现2.sh是没有x:执行权限的,不过我们可以通过解释器进行执行,这里用的都是绝对路径,也可以直接sh,bash,dash这样执行
image.png

#!/bin/sh#!/bin/bash#!/bin/dash三者的区别

在kali中,使用

ll /bin/sh

可以发现,sh指向的是dash,当然/bin/sh 指向 /bin/dash,但这并不意味着所有的脚本都会被Dash解释器执行,别的可能就不是了,不过大部分linux系统都是这样的
image.png

使用stat命令可以更加细致的对比三者的关系,通过对比发现,bash解释器的大小差不多是dash的十倍,事实上

  • 鉴于 bash 过于复杂,有人把 bash 从 NetBSD 移植到 Linux 并更名为 dash(Debian Almquist Shell),并以获得更快的脚本执行速度。Debian Almquist shell,缩写为dash,一种 Unix shell。它比 Bash 小,只需要较少的磁盘空间,但是它的对话性功能也较少。它由 NetBSD版本的Almquist shell (ash)发展而来,于1997年,由赫伯特·许(Herbert Xu)移植到Linux上,于2002年改名为 dash。
    所以,dash其实是bash的简化版

image.png

总结

一般情况下sh其实调用的就是dash,而dash其实是bash的简化版
除了上面的三个脚本解释器,还有一个source,他是内置的命令没有固定的路径,而是由Shell直接解析和执行。可以使用 type 命令来检查命令的类型:

type source

可以发现,他没有固定的路径,是内置命令
image.png

source和其他三个脚本解释器的区别

  • 首先他是内置的命令,是由shell直接解析和执行的
    这里主要对比一下和sh的区别
    source
  • 当前Shell执行:命令在当前Shell会话中执行,不会启动新的子Shell。
  • 变量作用域:文件中定义的变量、函数和别名会直接在当前Shell会话中生效。
  • 环境变化:文件中对环境变量的修改会立即反映在当前Shell会话中。
  • 权限要求:文件不需要具有可执行权限,只需要有读权限即可。
    sh
  • 子Shell执行:命令在一个新的子Shell中执行,与当前Shell会话隔离。
  • 变量作用域:文件中定义的变量、函数和别名仅在子Shell中生效,不会影响当前Shell会话。
  • 环境变化:文件中对环境变量的修改仅在子Shell中生效,不会影响当前Shell会话。
  • 权限要求:文件需要具有读权限,但不需要可执行权限。
    对我们来说最大的影响有两个
  • source执行的脚本变量会影响到当前会话,sh不会
  • source执行之后输出结果有颜色变化
    创建一个3.sh,内容为name=1,通过我的演示可以很直观的感受到source影响了当前会话
    image.png
    创建4.sh,内容为
echo aaa
ls

可以发现有颜色变化
image.png

变量的使用

变量声明和定义

举例定义一个name变量,name=“xiaoyu”
再利用echo $name打印出来,这就是简单的变量声明
image.png
再定义一个age变量

age=20
echo $age

可以写复杂点,比如说

echo my name is $name,and my age is $age years old

然后就直接打印出了姓名和年纪
image.png

单引号和双引号的区别

单引号 (')

  • 字面量引用:单引号内的所有字符都被视为普通字符,不进行变量替换或转义字符处理。
  • 特殊字符无效:单引号内的 $\"' 等特殊字符都会被视为普通字符,不会被解释。

双引号 (")

  • 部分解释:双引号内的大多数字符会被视为普通字符,但某些特殊字符(如 $`\)会被解释。
  • 变量替换:双引号内的变量会被替换为其值。
  • 命令替换:双引号内的命令替换(`command` 或 $(command))会被执行。
  • 转义字符:双引号内的某些转义字符(如 \n\t)会被解释。
    总之,单引号里面的字符视为普通字符,双引号不会

可以发现,使用单引号不会打印变量的值,原因是$符没有被解释

echo "$name $age"
echo '$name $age'

image.png

变量拼接

当我们想要将变量和字符连接起来

echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old

可以发现$ageyears被当作新的变量,并且没有被声明,所以打印了空值
image.png

为了解决这个问题,问题我们可以使用 双引号" 或者 花括号{}拼接起来

echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old

image.png

变量命名规则

上面讲的全部是临时的一个变量,变量是由数字,字符串下划线组成,但是不能以数字开头例如1aa这种是不行的,变量中间最好不要有空格比如说a a如果非要用这种可以加个下划线a_a="aaa"这种
image.png

查找和删除定义的变量

利用set命令查找

set
set | grep name # 可以使用grep进行筛选

image.png

使用unset删除变量

unset name
set | grep name

可以发现name变量没有了
image.png

shell2,临时变量和永久变量+字符串相关的操作

临时变量和永久变量

临时变量与永久变量是相对应得
常见永久变量

echo $HOME # (家目录root用户)echo $PATH # 环境变量

image.png
对于 P A T H 变量, w i n d o w s 也有类似的, P a t h ! [ i m a g e . p n g ] ( h t t p s : / / i − b l o g . c s d n i m g . c n / i m g c o n v e r t / e f 6 e 91 e e d c b 04472 c b 8 b 4 a 3 b 29516811. p n g ) 他们的效果都是类似的,为了简化用户输入命令的过程,不需要指定目录再进行命令使用了比如, PATH变量,windows也有类似的,Path ![image.png](https://i-blog.csdnimg.cn/img_convert/ef6e91eedcb04472cb8b4a3b29516811.png) 他们的效果都是类似的,为了简化用户输入命令的过程,不需要指定目录再进行命令使用了 比如, PATH变量,windows也有类似的,Path![image.png](https://iblog.csdnimg.cn/imgconvert/ef6e91eedcb04472cb8b4a3b29516811.png)他们的效果都是类似的,为了简化用户输入命令的过程,不需要指定目录再进行命令使用了比如,PATH下面有/bin目录,而我们的sh、bash、dash三个解释器就在这个目录下面,所有就不需要指定路径,就能直接使用,当然指定路径也可以
创建1.sh,里面写入:echo hello world

sh 1.sh
/bin/sh 1.sh

image.png

查看命令绝对路径,which

可以使用which命令查看

which sh
which -a ls # 显示所有命令的绝对路径

这里我们可以发现直接使用which ls,不行,原因是ls命令有两个绝对路径,只会输出优先级高的那个命令的全称,加上-a参数就行了
image.png

添加可直接使用的命令

添加到/bin/之类的已经被系统定义的路径中

比如,将1.sh添加到/bin/目录下面

mv 1.sh /bin/1.sh

image.png

直接将命令的绝对路径写入到$PATH环境变量中

我们先删除/bin/1.sh

rm /bin/1.sh

将目录写入,会导致目录其他命令也会直接被写入

# 首先先复制一下原来的$PATH内容以做备用
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
临时添加
export PATH=$PATH:"/home/kali/桌面/dir/test" # 有中文最好使用""括起来记得给脚本赋予权限,不然不能使用
chmod 777 2.sh

export 命令在 Unix 和 Linux 系统中用于将变量导出到环境变量中,使得这些变量在当前 Shell 会话及其子进程中可见。环境变量在多个方面都有重要作用,例如配置系统行为、设置路径、传递参数等,不过直接这样只是在当前命令窗口改变,不会影响别的窗口
image.png

永久添加

为了使变量在每次登录时都有效,可以将 export 命令添加到 Shell 配置文件中。常见的配置文件包括:

  • Bash~/.bashrc 或 ~/.bash_profile
  • Zsh~/.zshrc
  • Fish~/.config/fish/config.fish
示例:在 ~/.bashrc 中添加 export 命令

首先还原一下

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games" 
echo 'export PATH=$PATH:/home/kali/桌面/dir/test' >> ~/.bashrc 
source ~/.bashrc

如果直接使用,发现不行

找不到命令 “shopt”,您的意思是:“shout” 命令来自 Debian 软件包 libshout-tools
尝试 apt install <deb name>
找不到命令 “shopt”,您的意思是:“shout” 命令来自 Debian 软件包 libshout-tools
尝试 apt install <deb name>
找不到命令 “shopt”,您的意思是:“shout” 命令来自 Debian 软件包 libshout-tools
尝试 apt install <deb name>
找不到命令 “shopt”,您的意思是:“shout” 命令来自 Debian 软件包 libshout-tools
尝试 apt install <deb name>

image.png
原因是~/.bashrc是bash解释器的,需要使用bash解释器,而默认的是使用sh解释器。这里也可以看出,bash解释器是三个当中最全的解释器
image.png

字符串相关的操作

假设我们想知道一个字符串的长度,比如我们想解析一个字符串的长度我们如何进行实现
比如

name="cybc" age=20
然后我们通过
echo "my name is $name,and I am $age years old" 打印完整字符串

image.png

使用 ${#variable} 来获取字符串的长度

str="hello world"
echo ${#str}

image.png

使用 ${variable:num:num} 来获取字符串的切片

输出前6个字符,hello
echo ${str:0:6}

image.png

shell3,参数传递+算术运算

参数传递

脚本程序传递参数如何实现
创建一个a.sh,内容如下如下

echo 执行的文件名是:$0
echo 第一个参数是:$1
echo 传递的参数作为一个字符串显示:$*
echo 传递的参数独立作为每个字符串显示:$@
echo 传递到脚本的参数个数是:$#
echo 最后命令的退出状态:$?
echo 脚本运行的当前进程ID是:$$
  • $0:这个变量包含了当前执行脚本的名称。如果脚本是通过完整路径调用的,它将包含整个路径。
  • $1, $2, ...:分别表示传递给脚本的第一个、第二个等参数。可以一直递增到脚本接收到的最后一个参数。
  • $*:当未被双引号包围时,$*"$@"的行为相同,都是将所有位置参数视为一个字符串。但当它们被双引号包围时,"$*"会将所有参数视为单个以首个字符为分隔符(通常是空格)连接起来的字符串,而"$@"则会保持每个参数独立。
  • $@:与$*类似,但在被双引号包围时,它将每个参数都作为独立的字符串处理。
  • $#:表示传递给脚本或函数的参数数量。
  • $?:存储最近一次执行的前台管道的退出状态。通常,0 表示成功,非零值表示错误。
  • $$:表示当前shell进程的PID(进程ID)。

更多的Shell特殊变量
除了上述变量之外,还有其他一些有用的特殊变量:

  • $_:这是上一个命令执行的最后一个参数。这在交互式shell中特别有用。
  • $!:最近一个后台进程中运行的作业的PID。
  • $-:显示当前shell选项设置的状态。
  • $IFS(Internal Field Separator):定义了用于分割单词的字符,默认为空格、制表符和换行符。这对于控制如何解析输入非常关键。
  • $BASH_VERSION:如果你使用的是Bash shell,这个变量保存了当前Bash版本的信息。
  • $HOME:用户的家目录。
  • $PWD:当前工作目录。
  • $SECONDS:自脚本开始执行以来经过的秒数。
  • $RANDOM:生成一个随机整数。每次引用该变量时都会产生一个新的随机数。
  • $LINENO:当前正在执行的代码行号。
  • $BASH_SOURCE:对于函数或脚本,提供了一个数组,其中包含了调用栈中每个元素的文件名。
  • $FUNCNAME:如果在一个函数内,该变量包含了函数的名字。

image.png

$*"$@"

当未被双引号包围时,$*"$@"的行为相同,都是将所有位置参数视为一个字符串。但当它们被双引号包围时,"$*"会将所有参数视为单个以首个字符为分隔符(通常是空格)连接起来的字符串,而"$@"则会保持每个参数独立。
新建一个b.sh

#!/bin/bash
echo "Using \$*: $*"
echo "Using \"\$*\": \"$*\""
echo "Using \$@: $@"
echo "Using \"\$@\": \"$@\""

当你运行这个脚本并传入参数./script.sh "hello world" goodbye时,输出将会是这样的:

  • Using $*: hello world goodbye # 参数被当作单个字符串处理
  • Using "$*": "hello world goodbye" # 所有参数作为一个字符串,中间用空格分割
  • Using $@: hello world goodbye # 参数被视为独立的字符串
  • Using "$@": "hello world" "goodbye" # 每个参数都被独立地引用
    image.png

算术运算

常见的命令

1. 使用 expr 命令

expr 是一个非常基础但功能有限的工具,用于执行简单的算术运算。

result=$(expr 5 + 3)
echo $result  # 输出 8

注意:使用 expr 时,操作符和数字之间需要有空格。

2. 使用 $((...)) 语法

这是一种更现代且更简洁的方法,可以直接在变量赋值或命令替换中使用。

result=$((5 + 3))
echo $result  # 输出 8# 或者直接输出
echo $((5 * 3))  # 输出 15

3.使用 let 命令

let 可以用来执行整数算术表达式,并将结果存储到变量中。

let result=5+3
echo $result  # 输出 8

4. 使用 bc 命令

bc 是一个强大的计算器,支持浮点运算和复杂的数学函数。

result=$(echo "scale=2; 5 / 3" | bc)
echo $result  # 输出 1.67 (保留两位小数)# 执行更复杂的计算
result=$(echo "sqrt(9)" | bc -l)
echo $result  # 输出 3.00000000000000000000

其中,-l 选项加载了标准数学库,允许进行更高级的数学运算。

5. 使用 awk

awk 不仅是一个文本处理工具,也提供了丰富的数学运算能力。

result=$(awk 'BEGIN{print 5+3}')
echo $result  # 输出 8# 浮点运算
result=$(awk 'BEGIN{printf "%.2f\n", 5/3}')
echo $result  # 输出 1.67

6. 使用 declare -i 来定义整数变量

这可以让你对变量进行直接的算术运算而不需要额外的命令。

declare -i num=5
num+=3
echo $num  # 输出 8

以expr为例,演示加减乘除取模

注意,符前后都需要空格隔开

+
-
\*  转义一下,linux的*代表任意
/
%\( \) 括号需要转义

expr 6 \* 6(乘法是用\*,python.直接*就行了 要用\进行转义)

image.png
复杂一些的运算
注意括号需要转义,并且前后需要空格
image.png

shell4,shell脚本于用户交互+关系运算符

shell脚本于用户交互,read命令

read [选项] [变量名...]

常用参数

  • -p "提示信息": 在读取之前显示一条提示信息,记得手动打印一个换行符,因为该选项会阻止自动换行
  • -t 秒数: 设置等待用户输入的时间限制(秒)。如果超时,则返回一个非零退出状态。
  • -s: 安静模式,输入的内容不会回显到终端上,适用于密码输入等敏感信息。
  • -n 字符数: 限制输入的最大字符数。
  • -d 分隔符: 指定结束输入的分隔符,默认为换行符。

可以利用read命令进行shell脚本于用户的交互
例如

read nameecho $echo

image.png
但是当我们使用-p参数的时候,会报错
image.png
原因很简单,kali中默认使用sh脚本解释器运行脚本,sh本质是指向dash解释器,而dash解释器其实是bash解释器的简化版,大小只有bash的十分之一左右,所以有很多命令参数解释并不支持

需要使用bash脚本

bashread -p "请输入姓名:" name 

image.png
可以多个参数输入

read -p "请输入姓名和年龄:" name  ageecho $name $age

image.png
我们在看个例子,使用参数-t(指定时间)

read -t 10 -p "请输入您的姓名:" name# 如果我们在10秒内输入了就会执行成功,10秒内没有输入就会自动退出

image.png

可以使用-n参数,限制用户输入的字符个数,

read -n 3 -t 10 -p" 请输入您的姓名:" name

输入三个字符自动执行,如果输入了三个以下的字符需要自己敲回车执行
image.png

关系运算符

在脚本环境中如何简单的做条件判断
运算符

  • -eq:检查两个数是否相等。
  • -lt:检查左边的数是否小于右边的数。
  • -gt:检查左边的数是否大于右边的数。
  • -ne:检查两个数是否不相等。
使用
vim o.sh

首先定义两个变量,然后通过if条件判断来进行两个简单的条件判断再接入关系运算符
内容为

num1=11
num2=22if [ $num1 -eq $num2 ]; thenecho "数字相等"
else echo "数字不相等"
fi

image.png
更加复杂一些的

#!/bin/bash# 定义两个变量
num1=10
num2=20# 使用 if-then-else 语句进行条件判断
if [ $num1 -eq $num2 ]; thenecho "num1 等于 num2"
elif [ $num1 -lt $num2 ]; thenecho "num1 小于 num2"
elif [ $num1 -gt $num2 ]; thenecho "num1 大于 num2"
elseecho "num1 不等于 num2(虽然这个条件总是为真,但这里是为了展示 else 的用法)"
fi# 另一个例子,使用更复杂的条件
if [ $num1 -ne $num2 ] && [ $num1 -lt 15 ]; thenecho "num1 不等于 num2 并且 num1 小于 15"
elseecho "num1 不满足上述条件"
fi

image.png

注意事项
  • 在 [ ] 中使用空格是很重要的。例如,[ $num1 -eq $num2 ] 必须在每个元素之间加上空格。
  • 如果你想要对字符串进行比较,可以使用 == 或者 !=。但是请注意,这些操作符需要在 [[ ]] 中使用,而不是 [ ]。例如:
if [[ "hello" == "hello" ]]; thenecho "字符串相等"
fi
test和[]

[ ]test 命令在 Bash 脚本中实际上是等价的。[ ]test 命令的一种更直观的写法。你可以用 test 来替换 [ ],但需要注意的是,使用 test 时不需要方括号,参数直接传递给 test 命令。
简单的用法
image.png
复杂一些的

#!/bin/bash# 定义两个变量
num1=10
num2=20# 使用 test 命令进行条件判断
if test $num1 -eq $num2; thenecho "num1 等于 num2"
elif test $num1 -lt $num2; thenecho "num1 小于 num2"
elif test $num1 -gt $num2; thenecho "num1 大于 num2"
elseecho "num1 不等于 num2(虽然这个条件总是为真,但这里是为了展示 else 的用法)"
fi# 另一个例子,使用更复杂的条件
if test $num1 -ne $num2 && test $num1 -lt 15; thenecho "num1 不等于 num2 并且 num1 小于 15"
elseecho "num1 不满足上述条件"
fi

image.png

shell5,字符串运算符+逻辑运算符

字符串运算符

[[ ... ]][...]

首先我们在终端利用vim打开u.sh
内容为:

str1="hello"
str2="hello"if [[ "$str1" == "$str2" ]]; thenecho True
elseecho false
fi
  • 使用==来比较两个字符串是否相等。
  • 使用双方括号[[ ... ]]来进行字符串比较,它支持模式匹配和更复杂的表达式。
  • 变量应当用双引号包围,以确保即使变量值为空或包含空格时也能正确处理。
    image.png

提示[[: not found。这通常是因为shell环境不支持[[ ... ]]条件表达式,这可能是由于您使用的是一个较旧的或非常基础的shell版本,比如sh(Bourne shell),它不支持这种语法。
kail默认使用sh解释器,我们可以使用bash解释器,因为在kali中sh解释器的其实最终用的dash解释器,而dash解释器是bash解释器的简化版

image.png

当然了,我们如果要使用sh解释器,也可以使用旧的语法

str1="hello"
str2="hello"if [ "$str1" = "$str2" ]; thenecho True
elseecho false
fi

注意以下几点:

  • 使用单个等号=
  • 确保在[]两边都有空格。
  • 变量仍然需要用双引号包围以确保安全处理。
    image.png

上面的两个例子中,双引号都是可以省略的,加上可以确保即使变量值为空或包含空格时也能正确处理
image.png

大小写是否敏感

我们可以更改str1为Hello,来看看效果

str1="Hello"
str2="hello"if [ $str1 = $str2 ]; thenecho True
elseecho false
fi

image.png
我们使用!=在来看看他们是否不想等

str1="Hello"
str2="hello"if [ $str1 != $str2 ]; thenecho True
elseecho false
fi

image.png
可以发现,对大小写敏感

检查字符串的长度是否为0、不为0

str1="hello"
str2="helllo"# 检查str1的长度是否为0
if [ -z "$str1" ]; thenecho True  # 如果str1长度为0,则输出True
elseecho False  # 否则输出False
fi

image.png

  • 使用-z来检测字符串长度是否为零。
  • 变量名$str1应该被双引号包围以防止空值或包含空格的值导致的问题。
  • if[之间以及[和条件表达式之间需要有空格。
  • then关键字之前也需要有一个空格。

使用-n测试来检查字符串是否不为空。如果字符串不为空,则返回True;如果字符串为空,则返回False。我们将把str1改为str11并检查其长度。

# 定义变量
str11=""  # 这里设置为一个空字符串# 检查str11的长度是否不为0
if [ -n "$str11" ]; thenecho True  # 如果str11不为空,则输出True
elseecho False  # 如果str11为空,则输出False
fi

image.png

逻辑运算符之布尔运算符

# 定义变量
num1=9# 检查num1是否不等于9
if [ "$num1" != "9" ]; thenecho "num1不等于9"
elseecho "num1等于9"
fi

image.png

  • 使用单方括号[ ... ]来进行条件测试。
  • 使用!=来检查num1是否不等于9。
  • 变量num1被双引号包围以确保安全处理。
  • if语句的格式已经正确调整。
-a和-o 参数

-a来连接两个条件,相当于&&
-o来连接两个条件,相当于||

num1=9
num2=19# 检查num1是否不等于9并且num2是否小于20
if [ "$num1" != "9" -a "$num2" -lt 20 ]; thenecho True
elseecho False
fi
  • 使用单方括号[ ... ]来进行条件测试。
  • 使用!=来检查num1是否不等于9。
  • 使用-lt来检查num2是否小于20。
  • 使用逻辑与运算符-a来连接两个条件。
  • 变量num1num2被双引号包围以确保安全处理。
    image.png

更改为-o

num1=9
num2=19# 检查num1是否不等于9并且num2是否小于20
if [ "$num1" != "9" -o "$num2" -lt 20 ]; thenecho True
elseecho False
fi

image.png

我们也可以利用多个[...],这样调理更加清楚
比如刚才的例子,我们可以这样
注意:这里要使用管道符链接,比如&&、||,因为-a-o都是需要在[]才能被识别的

#!/bin/sh# 定义变量
num1=9
num2=19# 检查num1是否不等于9或者num2是否小于20
if [ "$num1" != "9" ] || [ "$num2" -lt 20 ]; thenecho True
elseecho False
fi

image.png

shell6,if条件判断语句+for循环结构

if条件判断语句

有一点编程基础的应该都知道if语句,这里就不解释了,看一下实例就都会了

参数使用

if语句中使用测试表达式时,需要特别注意格式。[ ]test命令的一种符号链接形式,用于进行条件测试。
参数:

  • 文件测试:-e存在,-d目录,-f普通文件等。
  • 字符串测试:-z空字符串,-n非空字符串,==相等,!=不等。
  • 数字比较:-eq等于,-ne不等于,-lt小于,-le小于等于,-gt大于,-ge大于等于。

注意:

  • [ ]内确保有适当的空格,如 [ -f file ]
  • 使用双引号包围变量,以防止变量为空或包含空格时出现错误。
  • 当条件复杂时考虑使用[[ ]],它提供了更多的特性,比如支持模式匹配和更宽松的空格规则。
  • 利用&&||组合命令,可以在一条命令行上完成复杂的逻辑判断。

实例

# 定义变量
a=10
b=20# 进行条件判断
if [ "$a" -eq "$b" ]; thenecho "a=b"
elif [ "$a" -gt "$b" ]; thenecho "a>b"
elseecho "没有符合上述条件"
fi

语句很好看懂,不过需要注意

  • 使用[ ]来创建测试表达式,这是test命令的一个符号链接。
  • -eq-gt等运算符两边都需要有空格。
  • 每个[后面应该有一个对应的],并且[]之间至少要有一个空白字符(通常是一个空格)与之分隔。
  • elifelse块同样需要以fi结束。
    image.png
    这里我们可以再增加一条elif语句,
elif [ $a -lt $b ]; thenecho "a<b"

注意:这里变量最好加上双引号,不然如果脚本复杂的话容易出事
image.png

for循环结构

在Shell脚本中,for循环是一种非常有用的控制结构,它允许你重复执行一系列命令直到满足特定条件。for循环可以处理数字、字符串列表、文件名以及命令的输出等。下面将详细介绍几种常见的for循环用法及其示例。

基本格式

列表迭代

这是最简单的for循环形式,它遍历一个预定义的值列表。

for variable in list
do# 执行的命令
done

例如,打印从1到5的数字:

for i in 1 2 3 4 5
doecho $i
done
使用范围

Bash支持使用花括号{}来指定一个数值范围。

for i in {start..end}
do# 执行的命令
done

例如,打印从1到10的数字:

for i in {1..10}
doecho "Number: $i"
done

您还可以通过增加第三个参数来指定步长:

for i in {1..10..2}  # 步长为2
doecho "Odd number: $i"
done
遍历文件

for循环也可以用于遍历目录中的文件。

for file in /path/to/directory/*
do# 对每个文件执行操作echo "Filename: $file"
done
处理命令输出

您可以使用$(command)来获取命令输出,并将其作为for循环的输入。

for line in $(cat filename.txt)
do# 处理每一行echo "Line: $line"
done

类C语言风格的for循环

Bash还支持一种类似于C语言的for循环语法,这在需要更复杂的初始化、条件测试和更新逻辑时非常有用。

for (( EXP1; EXP2; EXP3 ))
do# 执行的命令
done
  • EXP1 是初始化表达式。
  • EXP2 是条件表达式。
  • EXP3 是每次循环后执行的更新表达式。

例如,打印1到10的数字:

for (( i=1; i<=10; i++ ))
doecho "Count: $i"
done

image.png

特殊用途

无限循环

如果您想创建一个无限循环,可以省略for语句中的所有元素。

for (( ; ; ))
do# 无限执行的命令read -p "Press [Enter] key to continue or [Ctrl+C] to exit..." key
done
跳出循环

使用break语句可以在满足特定条件时提前退出循环。

for i in {1..10}
doif [ $i -eq 5 ]; thenbreakfiecho "Number: $i"
done
继续下一次迭代

使用continue语句可以让循环跳过当前迭代的剩余部分,直接进入下一次迭代。

for i in {1..10}
doif [ $((i % 2)) -eq 0 ]; thencontinuefiecho "Odd number: $i"
done

实际应用案例

批量添加用户

假设我们有一个包含用户名的文本文件,我们可以使用for循环来批量添加这些用户。

#!/bin/bash
while IFS= read -r user
douseradd $userecho "User added: $user"
done < users.txt
检查网络连通性

我们可以使用for循环结合ping命令来检查一组IP地址的连通性。

#!/bin/bash
for ip in 192.168.1.{1..254}
doping -c 1 $ip &>/dev/nullif [ $? -eq 0 ]; thenecho "$ip is up"elseecho "$ip is down"fi
done

通过以上介绍,您应该能够理解如何在Shell脚本中使用for循环来处理各种任务。for循环是编写自动化脚本时非常强大的工具,掌握其用法可以使您的脚本更加高效和灵活。

实例

for num in 1 2 3 4 5 6
doecho "num is $num"
done

image.png
我们可以使用{..}来简化数值列表的定义,不过需要使用bash脚本
image.png

for str in "Hello world"
doecho "str is $str"
done

image.png
这是因为 for 循环在这里只迭代了一次,且迭代的值就是整个字符串 "Hello world"

如果想要遍历 "Hello world" 中的每个单词(即 Helloworld),需要使用空格来分隔这些单词:

for str in Hello world
doecho "str is $str"
done

这样,for 循环会把 Helloworld
image.png

扩展:遍历字符串中的每个字符

如果你的目标是遍历 "Hello world" 中的每一个字符,那么你需要稍微修改一下脚本。一种方法是使用 while 循环结合 read 命令来逐个读取字符,或者使用 fold 命令来拆分字符串。以下是两种方法的示例:

方法一:使用 while 循环和 read 命令

需要使用bash脚本

string="Hello world"
while IFS= read -r -n1 char
doecho "char is $char"
done <<< "$string"

这里,IFS= 确保了内部字段分隔符为空,使得 read 不会跳过空白字符。-n1 参数告诉 read 每次只读取一个字符。<<< 是 here string 的语法,用于将字符串作为输入提供给循环。
image.png

方法二:使用 fold 命令

sh和bash脚本解释器都可以

string="Hello world"
for char in $(echo -n "$string" | fold -w1)
doecho "char is $char"
done

这里,fold -w1 将字符串按照每1个字符宽度进行分割,然后 for 循环遍历这些字符。
image.png

shell7,bash解释器的 for循环+while循环

for

前面已经讲过for循环了,不过是sh解释器的for循环,前面已经说过了:在kali中sh解释器的其实是指向dash解释器,而dash解释器是bash解释器的简化版,只有bash解释器的1/10左右的大小,所以bash解释器可以支持更多更复杂的语法
for循环有三种写法:反引号``$(...)((...))
第一种,``

for i in `seq 1 100`
doecho $i
done
  • seq 1 100: 这个命令生成一个数字序列,从1开始直到100(包括100)。seq是一个在Linux/Unix系统中用来产生一系列数字的工具。在这个例子中,它将生成一系列连续的整数:1, 2, 3, …, 98, 99, 100。
  • for i in ...: 这是for循环的开始,它会遍历由seq 1 100生成的所有数字。每次迭代时,变量i都会被设置为当前迭代中的数字。
  • dodone: 这两个关键字定义了循环体,即在每次迭代时要执行的代码块。在这个例子中,循环体只包含了一条语句——echo $i,这条语句用于输出当前迭代中的数字i
  • echo $i: 在每次循环中,echo命令会被调用,并且当前值$i会被打印到标准输出。
    image.png

第二种,$(...)

for i in $(seq 1 100)
doecho $i
done

image.png

第三种,((...))
或者使用C语言风格的for循环语法,这可能对熟悉C语言的程序员来说更加直观:
注意:这里的语法不像sh解释器那么严苛,符号前后不需要强制空格
这样标准的也行

for (( i=1; i<=100; i++ ))
doecho $i
done

image.png

for ((i=i;i<100;i++ ))
doecho $i
done

image.png

这里,(( i=1; i<=100; i++ ))直接指定了循环的初始化、条件测试和增量操作,使得整个循环结构看起来更加紧凑。

while

while循环有编程基础的应该都知道,就不细讲语法了,大家看一下实例就都知道怎么写了。还是和for循环一样,将一下sh和bash,注:sh可以的bash都可以

sh解释器

首先sh解释器只能使用[]或者test来规定循环的结束条件,其次对于,循环变量的变化sh解释器可以使用的语法有:$((...))$(expr $i + 1)
$((...))

i=1
while [ $i -lt 10  ]
doecho $ii=$((i+1))
done

image.png

$(expr $i + 1)

i=1
while [ $i -lt 10  ]
doecho $ii=$(expr $i + 1)
done

image.png

sh解释器不支持:(())和let
如果将i=$((i+1))替换成((i++))

i=1
while [ $i -lt 10  ]
doecho $i((i++))
done

不支持
程序报错,但是不会停止运行,会直接死循环
image.png

替换成let

i=1
while [ $i -lt 10  ]
doecho $ilet i=i+1
done

image.png
还是一样,程序报错,但是不会停止运行,会直接死循环

bash解释器

sh可以的bash一定可以
bash可以使用更加简单的语法:循环结束条件可以使用(()),循环变量的变化可以使用$((...))$(expr $i + 1)(())和let

i=1
while(($i<=10))
doecho $i((i++))
done

image.png
let的使用上面已经讲了,这里就不演示了

shell8

until循环

until循环与while循环的区别

  • while循环是在条件为真时执行循环体,而until循环则是在条件为假时执行循环体。
  • 通常情况下,while循环更常用,因为它直观地反映了“只要条件成立就继续做某事”的逻辑。
  • until循环更适合于那些默认行为是重复执行,直到发生某种变化的情况。
    所以until循环和while循环除了循环条件是相反的(while:为真时循环,until:为假时循环),几乎一样

还是和while一样,分为sh解释器和bash解释器:在kali中sh解释器的其实是指向dash解释器,而dash解释器是bash解释器的简化版,只有bash解释器的1/10左右的大小,所以bash解释器可以支持更多更复杂的语法
while讲的文章:shell编程7,bash解释器的 for循环+while循环
这里简单演示一下,详细的去看while的文章

sh解释器

首先sh解释器只能使用[]或者test来规定循环的结束条件,其次对于,循环变量的变化sh解释器可以使用的语法有:$((...))$(expr $i + 1)

i=1
until [ $i -gt 10 ]
doecho $ii=$((i+1))
done
  • 初始化:首先,变量i被初始化为1。
  • 条件判断until [ $i -gt 10 ]部分定义了循环的终止条件。这里的条件是当$i大于10时,循环停止。-gt是一个比较运算符,表示“大于”。
  • 循环体
    • echo $i命令输出当前i的值。
    • i=$((i+1))这条语句将i的值增加1。这里使用了算术扩展$((...))来进行数学运算。
      image.png

bash解释器

sh可以的bash一定可以
bash可以使用更加简单的语法:循环结束条件可以使用(()),循环变量的变化可以使用$((...))$(expr $i + 1)(())和let

i=1
until ((i>=10))
doecho $i;((i++))
done
  • 初始化:变量i被初始化为1。
  • 条件判断until ((i >= 10))部分定义了循环的终止条件。这里的条件是当i大于或等于10时,循环停止。((...))是用于执行算术运算的结构,它允许在表达式中直接使用比较操作符(如>=)。
  • 循环体
    • echo $i;命令输出当前i的值。
    • ((i++))这条语句将i的值增加1。这里使用了算术扩展((...))来进行数学运算,i++表示将i递增1。
      image.png

case语句

基本语法

有编程基础的应该都知道case多分支结构,我这里就不细讲了
case语句的基本格式如下:

case 变量 in模式1)# 当变量匹配模式1时执行的命令;;模式2|模式3)# 当变量匹配模式2或模式3时执行的命令;;*)# 当变量不匹配任何模式时执行的命令(默认情况);;
esac
  • case 变量 in:开始一个case语句,其中变量是要检查的值。
  • 模式):定义一个模式,如果变量与这个模式匹配,则执行紧接着的命令序列。
  • ;;:表示一个模式结束。
  • *):通配符,代表所有情况,通常作为默认分支使用。
  • esac:结束case语句。
    特殊模式
  • 精确匹配:如1)abc)等,仅当变量值完全等于指定字符串时匹配。
  • 通配符:如*word*,可以用来匹配包含特定子串的任意字符串。
  • 范围:如[0-9],可以匹配一个数字字符。
  • 多个模式:通过|分隔,例如a|b|c),可以匹配’a’、‘b’或’c’。

实例

read -p "请输入一个数字:" num
case $num in1)  # 如果输入的是1echo "您输入的数字是1";;2)  # 如果输入的是2echo "您输入的数字是2";;*)  # 如果输入的是其他任何值echo "您输入的是其他数字";;
esac
  • read -p "请您输入一个数值: " num:这条命令提示用户输入一个数值,并将输入存储到变量num中。
  • case $num in ... esac:这是case语句的基本结构。它会检查$num的值,并根据匹配的情况执行相应的代码块。
  • 1)2) 和 *):这些是模式匹配项。1)匹配输入为1的情况,2)匹配输入为2的情况,而*)则匹配所有其他情况(即除了1和2之外的所有输入)。
  • ;;:每个case分支以;;结束,表示该分支的结束。
    image.png

基本函数

在Shell脚本中,函数是一种组织代码的方式,可以将一段代码封装起来,以便于重复使用。通过定义和调用函数,可以使脚本更加模块化、易于维护,并且提高代码的复用性。下面是一些关于如何在Shell脚本中使用函数的基本指导和示例。

定义函数

在Bash中,可以通过以下方式定义一个函数:

function_name() {# 函数体
}

其中function_name是你给函数起的名字,应该具有描述性以方便理解其功能。

调用函数

一旦定义了函数,就可以通过简单地写函数名来调用它:

function_name

如果函数需要参数,可以在调用时传递这些参数:

function_name arg1 arg2

实例

基本函数

这是一个简单的函数,用于打印一条消息:

greet() {echo "Hello, $1"
}# 调用函数
greet "World"  # 输出: Hello, World

在这个例子中,$1是第一个参数的位置变量,代表传入的第一个参数。
image.png

带返回值的函数

函数可以返回一个状态码(0表示成功,非0表示错误),也可以通过全局变量或输出来返回结果:

add_numbers() {local sum=$(( $1 + $2 ))echo $sum  # 输出求和的结果
}result=$(add_numbers 5 3)  # 将函数的输出捕获到变量result中
echo "The result is: $result"  # 输出: The result is: 8

这里使用local关键字声明了一个局部变量sum,它只在函数内部可见。echo $sum用来输出计算结果,这个输出被外部命令$(...)捕获并赋值给result变量。
image.png

使用return语句

虽然return通常用于返回状态码,但也可以用来控制流程:

check_number() {if [ "$1" -gt 10 ]; thenreturn 0  # 成功elsereturn 1  # 失败fi
}if check_number 15; thenecho "Number is greater than 10."
elseecho "Number is not greater than 10."
fi

在这个例子中,check_number函数检查传入的数字是否大于10,并通过return语句返回相应的状态码。
image.png

注意事项

  • 作用域:在函数内部定义的变量默认是局部的,除非使用global关键字。
  • 参数处理:函数可以接受任意数量的参数,它们分别对应位置参数$1, $2, … 等。
  • 返回值:函数的返回值通常是通过return语句设置的状态码,范围从0到255。对于复杂的数据结构,通常使用输出或全局变量来返回数据。
  • 命名规则:函数名遵循与变量相同的命名规则,即不能以数字开头,且不能包含特殊字符。

shell9,不同脚本的互相调用+重定向的使用

  • 重定向操作和不同脚本的互相调用

不同脚本的互相调用

1. 直接调用

直接从一个脚本中使用bash(或sh等其他shell解释器)命令来执行另一个脚本文件

1.sh

#!/bin/bashlet num=5+3
echo $num

2.sh

#!/bin/shbash ./1.sh

看这个例子,可以发现,哪怕2.sh用的解释器是sh,也不会影响必须要使用解释器的1.sh,除非1.sh没有指定bash解释器,那么就会按照默认的解释器,kali中刚好是sh,所以报错

echo $SHEll # 查看默认解释器

image.png

image.png

image.png

2. 使用点号(.)或者source命令来包含脚本

可以使用.操作符(也称为source命令)。使得被包含的脚本在当前shell环境中执行,而不是创建一个新的子shell。
相当于c语言中的include
1.sh

#!/bin/bashlet num=5+3
echo $num

2.sh

#!/bin/sh. 1.sh

image.png

点号.,和source的区别
  • 虽然他们都可以进行包含,并且都可以进行命令执行,甚至使用which命令指向的内置shell都一样,但是还是有区别的
    image.png
  • 通上面图片和下面的图片,可以发现,使用source命令必须指定bash解释器,而.不需要,所有优先使用.,代码的鲁棒性更好
    image.png
    其他的两个就没有这么常用了,就自己看看就行了

3. 传递参数

当调用另一个脚本时,可以传递参数给它。这些参数可以通过位置参数$1, $2, … 来接收。

#!/bin/bash
# script1.shecho "This is script1"
./script2.sh arg1 arg2
echo "Back to script1"

script2.sh中,你可以这样获取参数:

#!/bin/bash
# script2.shecho "Received arguments: $1 and $2"

4. 设置环境变量

如果你想让被调用的脚本能够访问到某些环境变量,可以在调用之前设置它们。

#!/bin/bash
# script1.shexport MY_VAR="Hello, World!"
./script2.sh

然后,在script2.sh里就可以读取这个环境变量了:

#!/bin/bash
# script2.shecho "Value of MY_VAR: $MY_VAR"

重定向

  • 分为输出重定向和输入重定向

输出重定向

使用 >>>来实现

  • 使用符号 > 将标准输出重定向到一个文件。如果文件已经存在,则会被覆盖。
  • 使用符号 >> 将标准输出追加到一个文件的末尾,不会覆盖现有内容。
ls > 1.txtls >> 1.txt

image.png

输入重定向

使用<<<<<<实现

  • 使用 < 符号将文件内容作为命令的标准输入
  • 使用 << 符号可以从标准输入中读取多行文本,直到遇到指定的终止字符串
  • 使用 <<< 符号将一个字符串作为命令的标准输入。
    2.txt内容为
. # 表示当前目录
test# 空的也表示当前目录
<+xargs 命令进行转换的使用

使用 < 符号将文件内容作为命令的标准输入,使用命令行参数的命令不能直接这样输入,需要使用 xargs 命令进行转换
比如:这是因为 ls 命令不从标准输入读取文件名,而是从命令行参数中读取。
创建2.txt,写入

/home/kali/桌面/dir/test
.
  • . 表示当前目录
ls < 2.txt
xargs ls < 2.txt

可以发现,直接使用ls是不行的,运行 ls <2.txt 时,ls 命令并没有从 2.txt 文件中读取文件名,而是直接列出了当前目录下的文件和文件夹
image.png

<<

使用 << 符号可以从标准输入中读取多行文本,直到遇到指定的终止字符串。

cat << EOF
Hello, World!
This is a test.
EOF

cat 命令会读取从 EOF 开始到下一个 EOF 之间的所有文本,并将其作为输入。

<<<

使用 <<< 符号将一个字符串作为命令的标准输入。

wc -c <<< "Hello World"

wc -c 命令会计算字符串 "Hello World" 中的字符数

image.png

文件描述符+/dev/null

  • 标准输入(0): 默认情况下,标准输入是从键盘读取数据。
  • 标准输出(1): 默认情况下,标准输出是显示在终端上的文本。
  • 标准错误(2): 默认情况下,标准错误也是显示在终端上的文本,但通常用于输出错误信息。
ls > 1.txts > 1.txt 2>2.txts 1> 1.txt 2>2.txt

这里我们可以发现,使用s > 1.txt 2>2.txt命令使1.txt的内容为空了,其实这里相当于执行了

> 1.txt

所以内容被覆盖了,刚好覆盖的内容是空,所以1.txt为空
image.png
使用s 1> 1.txt 2>2.txt发现是一样的
image.png
/dev/null指向的是kali的回收站,所以如果我们直接指向回收站

ls > /dev/nullls 2> /dev/nullls 1> /dev/null

image.png

> /dev/null 2>&1详细解释

&是在这里是重定向符,可以重定向流,这里就是将2:报错流重定向到1:标准流,而标准流到了回收站,所以这里不显示任何输出和报错

  • 2>&1:
    • 2 表示标准错误流(文件描述符 2)。
    • &1 表示标准输出流(文件描述符 1),这里的 & 是必要的,因为它表示这是一个文件描述符,而不是一个文件名
    • 2>&1 的意思是将标准错误流(2)重定向到标准输出流(1)的位置。

执行顺序

  1. > /dev/null:

    • 首先,标准输出(1)被重定向到 /dev/null。这意味着任何原本会输出到标准输出的内容都会被丢弃。
  2. 2>&1:

    • 接着,标准错误(2)被重定向到标准输出(1)的位置。由于标准输出已经被重定向到 /dev/null,因此标准错误也会被重定向到 /dev/null

shell编程作业

⼀、⽤Shell写⼀个计算器

通过 read 命令获取用户输入的表达式,表达式的格式设定为 操作数1 运算符 操作数2 ,例如 5 + 3 ,然后利用设计的脚本输出运算结果。

要求:实现 加、减、乘、除运算

简单的可以使用bc命令

#!/bin/bashwhile true
doread -p "请输入一个表达式(格式是:操作数1 运算符 操作数2):" expressionresult=$(echo "$expression" | bc)echo $result
done

image.png

复杂的可以是case语句或者if-else语句

read -p "请输入一个表达式(格式是:操作数1 运算符 操作数2):" expressionIFS=' ' read -r num1 operator num2 <<< "$expression"
case $operator in+)result=$((num1 + num2));;-)result=$((num1 - num2));;\*)result=$((num1 * num2));;/)if (("$num2" == "0")); thenecho "错误: 除数不能为零"exit 1firesult=$((num1 / num2));;*)echo "不支持,请重新输入"exit 1;;
esacecho "结果为:$result"

image.png

⼆、⽤Shell定义⼀个求n的阶乘函数

定义一个计算n的阶乘的函数(含参函数、if判断、for循环)
写一个脚本去调用求阶乘的函数,并定义一个变量 n 可用read交互输入,最终输出 n的阶乘 结果

在kali中sh解释器的其实是指向dash解释器,而dash解释器是bash解释器的简化版,只有bash解释器的1/10左右的大小,所以bash解释器可以支持更多更复杂的语法

sh解释器

jiechen() {local n=$1local result=1if [ "$n" -lt 0 ]; thenecho "错误: 阶乘只适用于非负整数"return 1fifor i in $(seq 1 $n); doresult=$((result * i))doneecho $result
}read -p "请输入一个非负整数 n:" nnn=$(jiechen $n)
echo "n 的阶乘是: $nn"

image.png

bash解释器

jiechen() {local n=$1local result=1if (("$n" < 0)); thenecho "请输入一个>=0的数字"return 1fifor (( i=1; i<=n; i++ )); doresult=$((result * i))doneecho $result
}read -p "请输入一个非负整数 n:" nnn=$(jiechen $n)
echo "n 的阶乘是: $nn"

image.png

扩展

获取ipv4的地址

写⼀个Shell脚本去筛选出eth0⽹卡的ipv4地址,并赋值⼀个变量输出
(可以去了解grep、awk文本处理工具)

ip_address=$(ip a show eth0 | grep "inet" | awk  '$1=="inet" {print $2}' | cut -d '/' -f1
)if [ -z "$ip_address" ]; thenecho "没有找到eth0的ipv4的地址"
elseecho "eth0的ipv4的地址是: $ip_address"
fi

image.png

image.png

crontab

将上⾯的脚本编辑到计划任务中,并将echo输出内容重定向到⼀个固定⽂件中,计划时间随意⼀天⼀次也可以

使用crontab -e命令进行写入,选择nano
image.png

0 0 * * * /home/kali/桌面/dir/test/ipv4.sh >> ipv4.txt

image.png

无限重启Linux

做⼀个像Windows中的⼀样⽆限重启脚本(了解LINUX中的自启动)

#!/bin/bashwhile true; doecho "5秒后重启"sleep 5sudo reboot
done

使用nano进行写入

sudo nano /etc/systemd/system/cq.service

写入内容

[Unit]
Description=Infinite Reboot Service
After=network.target[Service]
ExecStart=/home/kali/桌面/dir/test/cq.sh
Restart=always
User=kali[Install]
WantedBy=multi-user.target

启用并启动服务

sudo systemctl daemon-reload
sudo systemctl enable cq.service
sudo systemctl start cq.service

image.png
失败了,应该是sudo reboot哪里没有权限
使用root

[Unit]
Description=Infinite Reboot Service
After=network.target[Service]
ExecStart=/home/kali/桌面/dir/test/cq.sh
Restart=always
User=root[Install]
WantedBy=multi-user.target

image.png
可以了,现在只需要写成一个脚本就行了


http://www.ppmy.cn/news/1573870.html

相关文章

用DeepSeek零基础预测《哪吒之魔童闹海》票房——从数据爬取到模型实战

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 **一、为什么要预测票房&#xff1f;****二、准备工作****三、实战步骤详解****Step 1&#xff1a;数据爬取与清洗&am…

斐波那契数列模型:在动态规划的丝绸之路上追寻斐波那契的足迹(下)

文章目录 引言一. 第n个泰波那契数1.1 题目链接&#xff1a;https://leetcode.cn/problems/n-th-tribonacci-number/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 二. 三步问题2.1 题目链接&#xff1a;https://leetcode.cn/problem…

显微镜下的人体结构

显微镜下的人体结构&#xff0c;看完以后&#xff0c;你还觉得人类是进化而来的吗&#xff1f;...... 第一张&#xff1a;电子显微镜所观察到的人类血管&#xff0c;可以非常清楚的看到里面的白细胞和红细胞 第二张&#xff1a;正在分泌耳垢&#xff08;耳屎&#xff09;的耳道…

pytorch训练五子棋ai

有3个文件 game.py 五子棋游戏 mod.py 神经网络模型 xl.py 训练的代码 aigame.py 玩家与对战的五子棋 game.py class Game:def __init__(self, h, w):# 行数self.h h# 列数self.w w# 棋盘self.L [[- for _ in range(w)] for _ in range(h)]# 当前玩家 - 表示空 X先…

【数据结构-并查集】力扣1202. 交换字符串中的元素

给你一个字符串 s&#xff0c;以及该字符串中的一些「索引对」数组 pairs&#xff0c;其中 pairs[i] [a, b] 表示字符串中的两个索引&#xff08;编号从 0 开始&#xff09;。 你可以 任意多次交换 在 pairs 中任意一对索引处的字符。 返回在经过若干次交换后&#xff0c;s …

1.3 嵌入式系统的固件

以STM32F103C8T6单片机举例&#xff0c;固件代码都是放在Flash闪存中&#xff0c;以Keil界面举例&#xff0c;该界面分为启动代码&#xff0c;库函数代码&#xff0c;还有用户代码&#xff0c;编译时&#xff0c;这些代码会被编译并链接成一个单一的固件映像&#xff0c;然后通…

C语言-进程

1、进程是什么&#xff1f; 一个具有一定独立功能的程序关于某个数据集合的一次运行活动&#xff08;程序执行的过程&#xff09;&#xff0c; 是系统进行资源分配和调度运行的基本单位。是动态的&#xff0c;随着程序的使用被创建&#xff0c;随着 程序的结束而消亡。 什么是程…

Stack和Queue—模拟实现,实战应用全解析!

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好&#xff0c;我们今天来学习java数据结构的Stack和Queue&#xff08;栈和队列&#xff09; 一&#xff1a;栈 1.1&#xff1a;栈的概念 …