前言
shell编程学习总结,1万3千多字带你学习shell编程
往期推荐
shell1,脚本的创建和执行+变量的使用
脚本的创建和执行
以kail为例,进入终端,创建一个sh脚本
vim 1.sh
# 按i进入插入模式,输入
echo hello world
# 按esc退出插入模式,然后:wq 保存文件并且退出
直接执行1.sh发现权限不够,需要赋予权限
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
除了赋予权限,我们还可以让脚本解释器帮我们执行脚本,由于解释器本身就赋予权限,所有不需要在为脚本赋予权限,我们在创建一个2.sh,内容为:echo 111#!/bin/sh、#!/bin/bash 、#!/bin/dash三个都是脚本解释器可以发现2.sh是没有x:执行权限的,不过我们可以通过解释器进行执行,这里用的都是绝对路径,也可以直接sh,bash,dash这样执行
#!/bin/sh、#!/bin/bash 、#!/bin/dash三者的区别
在kali中,使用
ll /bin/sh
可以发现,sh指向的是dash,当然/bin/sh 指向/bin/dash,但这并不意味着所有的脚本都会被Dash解释器执行,别的可能就不是了,不过大部分linux系统都是这样的
使用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的简化版
总结
一般情况下sh其实调用的就是dash,而dash其实是bash的简化版 除了上面的三个脚本解释器,还有一个source,他是内置的命令没有固定的路径,而是由Shell直接解析和执行。可以使用type 命令来检查命令的类型:
typesource
可以发现,他没有固定的路径,是内置命令
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影响了当前会话创建4.sh,内容为
echo aaa
ls
可以发现有颜色变化
变量的使用
变量声明和定义
举例定义一个name变量,name="xiaoyu" 再利用echo $name打印出来,这就是简单的变量声明再定义一个age变量
age=20
echo$age
可以写复杂点,比如说
echo my name is $name,and my age is $age years old
然后就直接打印出了姓名和年纪
单引号和双引号的区别
单引号 (')
字面量引用:单引号内的所有字符都被视为普通字符,不进行变量替换或转义字符处理。 特殊字符无效:单引号内的 $、、"、'等特殊字符都会被视为普通字符,不会被解释。
双引号 (")
部分解释:双引号内的大多数字符会被视为普通字符,但某些特殊字符(如 $、`、)会被解释。变量替换:双引号内的变量会被替换为其值。 命令替换:双引号内的命令替换( `command`或$(command))会被执行。转义字符:双引号内的某些转义字符(如 n、t)会被解释。总之,单引号里面的字符视为普通字符,双引号不会
可以发现,使用单引号不会打印变量的值,原因是$符没有被解释
echo"$name$age"
echo'$name $age'
变量拼接
当我们想要将变量和字符连接起来
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
可以发现$ageyears被当作新的变量,并且没有被声明,所以打印了空值
为了解决这个问题,问题我们可以使用 双引号" 或者 花括号{}拼接起来
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
变量命名规则
上面讲的全部是临时的一个变量,变量是由数字,字符串下划线组成,但是不能以数字开头例如1aa这种是不行的,变量中间最好不要有空格比如说a a如果非要用这种可以加个下划线a_a="aaa"这种
查找和删除定义的变量
利用set命令查找
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
0使用unset删除变量
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
1可以发现name变量没有了
shell2,临时变量和永久变量+字符串相关的操作
临时变量和永久变量
临时变量与永久变量是相对应得 常见永久变量
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
2对于PATH下面有/bin目录,而我们的sh、bash、dash三个解释器就在这个目录下面,所有就不需要指定路径,就能直接使用,当然指定路径也可以 创建1.sh,里面写入:echo hello world
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
3查看命令绝对路径,which
可以使用which命令查看
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
4这里我们可以发现直接使用which ls,不行,原因是ls命令有两个绝对路径,只会输出优先级高的那个命令的全称,加上-a参数就行了
添加可直接使用的命令
添加到/bin/之类的已经被系统定义的路径中
比如,将1.sh添加到/bin/目录下面
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
5直接将命令的绝对路径写入到$PATH环境变量中
我们先删除/bin/1.sh
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
6将目录写入,会导致目录其他命令也会直接被写入
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
7临时添加
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
8export 命令在 Unix 和 Linux 系统中用于将变量导出到环境变量中,使得这些变量在当前 Shell 会话及其子进程中可见。环境变量在多个方面都有重要作用,例如配置系统行为、设置路径、传递参数等,不过直接这样只是在当前命令窗口改变,不会影响别的窗口
永久添加
为了使变量在每次登录时都有效,可以将export 命令添加到 Shell 配置文件中。常见的配置文件包括:
Bash: ~/.bashrc或~/.bash_profileZsh: ~/.zshrcFish: ~/.config/fish/config.fish
示例:在 ~/.bashrc 中添加 export 命令
首先还原一下
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
9ll /bin/sh
0如果直接使用,发现不行
ll /bin/sh
1原因是~/.bashrc是bash解释器的,需要使用bash解释器,而默认的是使用sh解释器。这里也可以看出,bash解释器是三个当中最全的解释器
字符串相关的操作
假设我们想知道一个字符串的长度,比如我们想解析一个字符串的长度我们如何进行实现 比如
ll /bin/sh
2使用 ${#variable} 来获取字符串的长度
ll /bin/sh
3使用 ${variable:num:num} 来获取字符串的切片
ll /bin/sh
4shell3,参数传递+算术运算
参数传递
脚本程序传递参数如何实现 创建一个a.sh,内容如下如下
ll /bin/sh
5** $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**:如果在一个函数内,该变量包含了函数的名字。
$*与"$@"
当未被双引号包围时,$*与"$@"的行为相同,都是将所有位置参数视为一个字符串。但当它们被双引号包围时,"$*"会将所有参数视为单个以首个字符为分隔符(通常是空格)连接起来的字符串,而"$@"则会保持每个参数独立。 新建一个b.sh
ll /bin/sh
6当你运行这个脚本并传入参数./script.sh "hello world" goodbye时,输出将会是这样的:
Using $*: hello world goodbye# 参数被当作单个字符串处理Using "$*": "hello world goodbye"# 所有参数作为一个字符串,中间用空格分割Using $@: hello world goodbye# 参数被视为独立的字符串Using "$@": "hello world" "goodbye"# 每个参数都被独立地引用
算术运算
常见的命令
1. 使用 expr 命令
expr 是一个非常基础但功能有限的工具,用于执行简单的算术运算。
ll /bin/sh
7注意:使用expr 时,操作符和数字之间需要有空格。
2. 使用 $((...)) 语法
这是一种更现代且更简洁的方法,可以直接在变量赋值或命令替换中使用。
ll /bin/sh
83.使用 let 命令
let 可以用来执行整数算术表达式,并将结果存储到变量中。
ll /bin/sh
94. 使用 bc 命令
bc 是一个强大的计算器,支持浮点运算和复杂的数学函数。
typesource
0其中,-l 选项加载了标准数学库,允许进行更高级的数学运算。
5. 使用 awk
awk 不仅是一个文本处理工具,也提供了丰富的数学运算能力。
typesource
16. 使用 declare -i 来定义整数变量
这可以让你对变量进行直接的算术运算而不需要额外的命令。
typesource
2以expr为例,演示加减乘除取模
注意,符前后都需要空格隔开
typesource
3如
typesource
4复杂一些的运算注意括号需要转义,并且前后需要空格
shell4,shell脚本于用户交互+关系运算符
shell脚本于用户交互,read命令
typesource
5常用参数
-p "提示信息": 在读取之前显示一条提示信息,记得手动打印一个换行符,因为该选项会阻止自动换行-t 秒数: 设置等待用户输入的时间限制(秒)。如果超时,则返回一个非零退出状态。-s: 安静模式,输入的内容不会回显到终端上,适用于密码输入等敏感信息。-n 字符数: 限制输入的最大字符数。-d 分隔符: 指定结束输入的分隔符,默认为换行符。
可以利用read命令进行shell脚本于用户的交互 例如
typesource
6但是当我们使用-p参数的时候,会报错原因很简单,kali中默认使用sh脚本解释器运行脚本,sh本质是指向dash解释器,而dash解释器其实是bash解释器的简化版,大小只有bash的十分之一左右,所以有很多命令参数解释并不支持
需要使用bash脚本
typesource
7可以多个参数输入
typesource
8我们在看个例子,使用参数-t(指定时间)
typesource
9可以使用-n参数,限制用户输入的字符个数,
echo aaa
ls
0输入三个字符自动执行,如果输入了三个以下的字符需要自己敲回车执行
关系运算符
在脚本环境中如何简单的做条件判断运算符
** -eq**:检查两个数是否相等。** -lt**:检查左边的数是否小于右边的数。** -gt**:检查左边的数是否大于右边的数。** -ne**:检查两个数是否不相等。
使用
echo aaa
ls
1首先定义两个变量,然后通过if条件判断来进行两个简单的条件判断再接入关系运算符 内容为
echo aaa
ls
2更加复杂一些的
echo aaa
ls
3注意事项
在 [ ]中使用空格是很重要的。例如,[ $num1 -eq $num2 ]必须在每个元素之间加上空格。如果你想要对字符串进行比较,可以使用 ==或者!=。但是请注意,这些操作符需要在[[ ]]中使用,而不是[ ]。例如:
echo aaa
ls
4test和[]
[ ] 和test 命令在 Bash 脚本中实际上是等价的。[ ] 是test 命令的一种更直观的写法。你可以用test 来替换[ ],但需要注意的是,使用test 时不需要方括号,参数直接传递给test 命令。 简单的用法复杂一些的
echo aaa
ls
5shell5,字符串运算符+逻辑运算符
字符串运算符
[[ ... ]]和[...]
首先我们在终端利用vim打开u.sh 内容为:
echo aaa
ls
6使用 ==来比较两个字符串是否相等。使用双方括号 [[ ... ]]来进行字符串比较,它支持模式匹配和更复杂的表达式。变量应当用双引号包围,以确保即使变量值为空或包含空格时也能正确处理。
提示[[: not found。这通常是因为shell环境不支持[[ ... ]]条件表达式,这可能是由于您使用的是一个较旧的或非常基础的shell版本,比如sh(Bourne shell),它不支持这种语法。kail默认使用sh解释器,我们可以使用bash解释器,因为在kali中sh解释器的其实最终用的dash解释器,而dash解释器是bash解释器的简化版
当然了,我们如果要使用sh解释器,也可以使用旧的语法
echo aaa
ls
7注意以下几点:
使用单个等号 =确保在 [和]两边都有空格。变量仍然需要用双引号包围以确保安全处理。
上面的两个例子中,双引号都是可以省略的,加上可以确保即使变量值为空或包含空格时也能正确处理
大小写是否敏感
我们可以更改str1为Hello,来看看效果
echo aaa
ls
8我们使用!=在来看看他们是否不想等
echo aaa
ls
9可以发现,对大小写敏感
检查字符串的长度是否为0、不为0
age=20
echo$age
0使用 -z来检测字符串长度是否为零。变量名 $str1应该被双引号包围以防止空值或包含空格的值导致的问题。if和[之间以及[和条件表达式之间需要有空格。then关键字之前也需要有一个空格。
使用-n测试来检查字符串是否不为空。如果字符串不为空,则返回True;如果字符串为空,则返回False。我们将把str1改为str11并检查其长度。
age=20
echo$age
1逻辑运算符之布尔运算符
age=20
echo$age
2使用单方括号 [ ... ]来进行条件测试。使用 !=来检查num1是否不等于9。变量 num1被双引号包围以确保安全处理。if语句的格式已经正确调整。
-a和-o 参数
-a来连接两个条件,相当于&&-o来连接两个条件,相当于||
age=20
echo$age
3使用单方括号 [ ... ]来进行条件测试。使用 !=来检查num1是否不等于9。使用 -lt来检查num2是否小于20。使用逻辑与运算符 -a来连接两个条件。变量 num1和num2被双引号包围以确保安全处理。
更改为-o
age=20
echo$age
4我们也可以利用多个[...],这样调理更加清楚 比如刚才的例子,我们可以这样 **注意:这里要使用管道符链接,比如&&、||**,因为-a和-o都是需要在[]才能被识别的
age=20
echo$age
5shell6,if条件判断语句+for循环结构
if条件判断语句
有一点编程基础的应该都知道if语句,这里就不解释了,看一下实例就都会了
参数使用
在if语句中使用测试表达式时,需要特别注意格式。[ ]是test命令的一种符号链接形式,用于进行条件测试。参数:
文件测试: -e存在,-d目录,-f普通文件等。字符串测试: -z空字符串,-n非空字符串,==相等,!=不等。数字比较: -eq等于,-ne不等于,-lt小于,-le小于等于,-gt大于,-ge大于等于。
注意:
在 [ ]内确保有适当的空格,如[ -f file ]。使用双引号包围变量,以防止变量为空或包含空格时出现错误。 当条件复杂时考虑使用 [[ ]],它提供了更多的特性,比如支持模式匹配和更宽松的空格规则。利用 &&和||组合命令,可以在一条命令行上完成复杂的逻辑判断。
实例
age=20
echo$age
6语句很好看懂,不过需要注意
使用 [ ]来创建测试表达式,这是test命令的一个符号链接。在 -eq,-gt等运算符两边都需要有空格。每个 [后面应该有一个对应的],并且[和]之间至少要有一个空白字符(通常是一个空格)与之分隔。elif和else块同样需要以fi结束。这里我们可以再增加一条elif语句,
age=20
echo$age
7注意:这里变量最好加上双引号,不然如果脚本复杂的话容易出事
for循环结构
在Shell脚本中,for循环是一种非常有用的控制结构,它允许你重复执行一系列命令直到满足特定条件。for循环可以处理数字、字符串列表、文件名以及命令的输出等。下面将详细介绍几种常见的for循环用法及其示例。
基本格式
列表迭代
这是最简单的for循环形式,它遍历一个预定义的值列表。
age=20
echo$age
8例如,打印从1到5的数字:
age=20
echo$age
9使用范围
Bash支持使用花括号{}来指定一个数值范围。
echo my name is $name,and my age is $age years old
0例如,打印从1到10的数字:
echo my name is $name,and my age is $age years old
1您还可以通过增加第三个参数来指定步长:
echo my name is $name,and my age is $age years old
2遍历文件
for循环也可以用于遍历目录中的文件。
echo my name is $name,and my age is $age years old
3处理命令输出
您可以使用$(command)来获取命令输出,并将其作为for循环的输入。
echo my name is $name,and my age is $age years old
4类C语言风格的for循环
Bash还支持一种类似于C语言的for循环语法,这在需要更复杂的初始化、条件测试和更新逻辑时非常有用。
echo my name is $name,and my age is $age years old
5EXP1是初始化表达式。EXP2是条件表达式。EXP3是每次循环后执行的更新表达式。
例如,打印1到10的数字:
echo my name is $name,and my age is $age years old
6特殊用途
无限循环
如果您想创建一个无限循环,可以省略for语句中的所有元素。
echo my name is $name,and my age is $age years old
7跳出循环
使用break语句可以在满足特定条件时提前退出循环。
echo my name is $name,and my age is $age years old
8继续下一次迭代
使用continue语句可以让循环跳过当前迭代的剩余部分,直接进入下一次迭代。
echo my name is $name,and my age is $age years old
9实际应用案例
批量添加用户
假设我们有一个包含用户名的文本文件,我们可以使用for循环来批量添加这些用户。
echo"$name$age"
echo'$name $age'
0检查网络连通性
我们可以使用for循环结合ping命令来检查一组IP地址的连通性。
echo"$name$age"
echo'$name $age'
1通过以上介绍,您应该能够理解如何在Shell脚本中使用for循环来处理各种任务。for循环是编写自动化脚本时非常强大的工具,掌握其用法可以使您的脚本更加高效和灵活。
实例
echo"$name$age"
echo'$name $age'
2我们可以使用{..}来简化数值列表的定义,不过需要使用bash脚本
echo"$name$age"
echo'$name $age'
3这是因为for 循环在这里只迭代了一次,且迭代的值就是整个字符串"Hello world"。
如果想要遍历"Hello world" 中的每个单词(即Hello 和world),需要使用空格来分隔这些单词:
echo"$name$age"
echo'$name $age'
4这样,for 循环会把Hello 和world
扩展:遍历字符串中的每个字符
如果你的目标是遍历"Hello world" 中的每一个字符,那么你需要稍微修改一下脚本。一种方法是使用while 循环结合read 命令来逐个读取字符,或者使用fold 命令来拆分字符串。以下是两种方法的示例:
方法一:使用 while 循环和 read 命令
需要使用bash脚本
echo"$name$age"
echo'$name $age'
5这里,IFS= 确保了内部字段分隔符为空,使得read 不会跳过空白字符。-n1 参数告诉read 每次只读取一个字符。<<< 是 here string 的语法,用于将字符串作为输入提供给循环。
方法二:使用 fold 命令
sh和bash脚本解释器都可以
echo"$name$age"
echo'$name $age'
6这里,fold -w1 将字符串按照每1个字符宽度进行分割,然后for 循环遍历这些字符。
shell7,bash解释器的 for循环+while循环
for
前面已经讲过for循环了,不过是sh解释器的for循环,前面已经说过了:在kali中sh解释器的其实是指向dash解释器,而dash解释器是bash解释器的简化版,只有bash解释器的1/10左右的大小,所以bash解释器可以支持更多更复杂的语法 for循环有三种写法:反引号``,$(...),((...))第一种,``
echo"$name$age"
echo'$name $age'
7seq 1 100: 这个命令生成一个数字序列,从1开始直到100(包括100)。seq是一个在Linux/Unix系统中用来产生一系列数字的工具。在这个例子中,它将生成一系列连续的整数:1, 2, 3, ..., 98, 99, 100。for i in ...: 这是for循环的开始,它会遍历由seq 1 100生成的所有数字。每次迭代时,变量i都会被设置为当前迭代中的数字。do...done: 这两个关键字定义了循环体,即在每次迭代时要执行的代码块。在这个例子中,循环体只包含了一条语句——echo $i,这条语句用于输出当前迭代中的数字i。echo $i: 在每次循环中,echo命令会被调用,并且当前值$i会被打印到标准输出。
第二种,$(...)
echo"$name$age"
echo'$name $age'
8第三种,((...))或者使用C语言风格的for循环语法,这可能对熟悉C语言的程序员来说更加直观: 注意:这里的语法不像sh解释器那么严苛,符号前后不需要强制空格 这样标准的也行
echo"$name$age"
echo'$name $age'
9echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
0这里,(( i=1; i<=100; i++ ))直接指定了循环的初始化、条件测试和增量操作,使得整个循环结构看起来更加紧凑。
while
while循环有编程基础的应该都知道,就不细讲语法了,大家看一下实例就都知道怎么写了。还是和for循环一样,将一下sh和bash,注:sh可以的bash都可以
sh解释器
首先sh解释器只能使用[]或者test来规定循环的结束条件,其次对于,循环变量的变化sh解释器可以使用的语法有:$((...))和$(expr $i + 1)$((...))
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
1$(expr $i + 1)
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
2sh解释器不支持:(())和let如果将i=$((i+1))替换成((i++))
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
3不支持 程序报错,但是不会停止运行,会直接死循环
替换成let
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
4还是一样,程序报错,但是不会停止运行,会直接死循环
bash解释器
sh可以的bash一定可以 bash可以使用更加简单的语法:循环结束条件可以使用(()),循环变量的变化可以使用$((...))、$(expr $i + 1)、(())和let
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
5let的使用上面已经讲了,这里就不演示了
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)
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
6初始化:首先,变量 i被初始化为1。条件判断: until [ $i -gt 10 ]部分定义了循环的终止条件。这里的条件是当$i大于10时,循环停止。-gt是一个比较运算符,表示“大于”。循环体: echo $i命令输出当前i的值。i=$((i+1))这条语句将i的值增加1。这里使用了算术扩展$((...))来进行数学运算。
bash解释器
sh可以的bash一定可以bash可以使用更加简单的语法:循环结束条件可以使用(()),循环变量的变化可以使用$((...))、$(expr $i + 1)、(())和let
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
7初始化:变量 i被初始化为1。条件判断: until ((i >= 10))部分定义了循环的终止条件。这里的条件是当i大于或等于10时,循环停止。((...))是用于执行算术运算的结构,它允许在表达式中直接使用比较操作符(如>=)。循环体: echo $i;命令输出当前i的值。((i++))这条语句将i的值增加1。这里使用了算术扩展((...))来进行数学运算,i++表示将i递增1。
case语句
基本语法
有编程基础的应该都知道case多分支结构,我这里就不细讲了case语句的基本格式如下:
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
8case 变量 in:开始一个case语句,其中变量是要检查的值。模式):定义一个模式,如果变量与这个模式匹配,则执行紧接着的命令序列。;;:表示一个模式结束。*):通配符,代表所有情况,通常作为默认分支使用。esac:结束case语句。特殊模式精确匹配:如 1)、abc)等,仅当变量值完全等于指定字符串时匹配。通配符:如 *word*,可以用来匹配包含特定子串的任意字符串。范围:如 [0-9],可以匹配一个数字字符。多个模式:通过 |分隔,例如a|b|c),可以匹配'a'、'b'或'c'。
实例
echo my name is $name,and my age is $ageyears old
echo my name is $name,and my age is$age years old
9** read -p "请您输入一个数值: " num**:这条命令提示用户输入一个数值,并将输入存储到变量num中。** case $num in ... esac**:这是case语句的基本结构。它会检查$num的值,并根据匹配的情况执行相应的代码块。** 1)、2)和*)**:这些是模式匹配项。1)匹配输入为1的情况,2)匹配输入为2的情况,而*)则匹配所有其他情况(即除了1和2之外的所有输入)。** ;;**:每个case分支以;;结束,表示该分支的结束。
基本函数
在Shell脚本中,函数是一种组织代码的方式,可以将一段代码封装起来,以便于重复使用。通过定义和调用函数,可以使脚本更加模块化、易于维护,并且提高代码的复用性。下面是一些关于如何在Shell脚本中使用函数的基本指导和示例。
定义函数
在Bash中,可以通过以下方式定义一个函数:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
0其中function_name是你给函数起的名字,应该具有描述性以方便理解其功能。
调用函数
一旦定义了函数,就可以通过简单地写函数名来调用它:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
1如果函数需要参数,可以在调用时传递这些参数:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
2实例
基本函数
这是一个简单的函数,用于打印一条消息:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
3在这个例子中,$1是第一个参数的位置变量,代表传入的第一个参数。
带返回值的函数
函数可以返回一个状态码(0表示成功,非0表示错误),也可以通过全局变量或输出来返回结果:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
4这里使用local关键字声明了一个局部变量sum,它只在函数内部可见。echo $sum用来输出计算结果,这个输出被外部命令$(...)捕获并赋值给result变量。
使用return语句
虽然return通常用于返回状态码,但也可以用来控制流程:
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
5在这个例子中,check_number函数检查传入的数字是否大于10,并通过return语句返回相应的状态码。
注意事项
作用域:在函数内部定义的变量默认是局部的,除非使用 global关键字。参数处理:函数可以接受任意数量的参数,它们分别对应位置参数 $1,$2, ... 等。返回值:函数的返回值通常是通过 return语句设置的状态码,范围从0到255。对于复杂的数据结构,通常使用输出或全局变量来返回数据。命名规则:函数名遵循与变量相同的命名规则,即不能以数字开头,且不能包含特殊字符。
shell9,不同脚本的互相调用+重定向的使用
重定向操作和不同脚本的互相调用
不同脚本的互相调用
1. 直接调用
直接从一个脚本中使用bash(或sh等其他shell解释器)命令来执行另一个脚本文件
1.sh
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
62.sh
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
7看这个例子,可以发现,哪怕2.sh用的解释器是sh,也不会影响必须要使用解释器的1.sh,除非1.sh没有指定bash解释器,那么就会按照默认的解释器,kali中刚好是sh,所以报错
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
82. 使用点号(.)或者source命令来包含脚本
可以使用.操作符(也称为source命令)。使得被包含的脚本在当前shell环境中执行,而不是创建一个新的子shell。相当于c语言中的include1.sh
echo my name is $name,and my age is "$age"years old
echo my name is $name,and my age is {$age}years old
62.sh
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
00点号.,和source的区别
虽然他们都可以进行包含,并且都可以进行命令执行,甚至使用which命令指向的内置shell都一样,但是还是有区别的 通上面图片和下面的图片,可以发现,使用source命令必须指定bash解释器,而 .不需要,所有优先使用.,代码的鲁棒性更好其他的两个就没有这么常用了,就自己看看就行了
3. 传递参数
当调用另一个脚本时,可以传递参数给它。这些参数可以通过位置参数2, ... 来接收。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
01在script2.sh中,你可以这样获取参数:
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
024. 设置环境变量
如果你想让被调用的脚本能够访问到某些环境变量,可以在调用之前设置它们。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
03然后,在script2.sh里就可以读取这个环境变量了:
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
04重定向
分为输出重定向和输入重定向
输出重定向
使用>和>>来实现
使用符号 >将标准输出重定向到一个文件。如果文件已经存在,则会被覆盖。使用符号 >>将标准输出追加到一个文件的末尾,不会覆盖现有内容。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
05输入重定向
使用<、<<、<<<实现
使用 <符号将文件内容作为命令的标准输入使用 <<符号可以从标准输入中读取多行文本,直到遇到指定的终止字符串使用 <<<符号将一个字符串作为命令的标准输入。 2.txt内容为
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
06<+xargs 命令进行转换的使用
使用< 符号将文件内容作为命令的标准输入,使用命令行参数的命令不能直接这样输入,需要使用xargs 命令进行转换比如:这是因为ls 命令不从标准输入读取文件名,而是从命令行参数中读取。 创建2.txt,写入
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
07. 表示当前目录
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
08可以发现,直接使用ls是不行的,运行ls <2.txt 时,ls 命令并没有从2.txt 文件中读取文件名,而是直接列出了当前目录下的文件和文件夹
<<
使用<< 符号可以从标准输入中读取多行文本,直到遇到指定的终止字符串。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
09cat 命令会读取从EOF 开始到下一个EOF 之间的所有文本,并将其作为输入。
<<<
使用<<< 符号将一个字符串作为命令的标准输入。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
10wc -c 命令会计算字符串"Hello World" 中的字符数
文件描述符+/dev/null
标准输入(0): 默认情况下,标准输入是从键盘读取数据。 标准输出(1): 默认情况下,标准输出是显示在终端上的文本。 标准错误(2): 默认情况下,标准错误也是显示在终端上的文本,但通常用于输出错误信息。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
11这里我们可以发现,使用s > 1.txt 2>2.txt命令使1.txt的内容为空了,其实这里相当于执行了
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
12所以内容被覆盖了,刚好覆盖的内容是空,所以1.txt为空使用s 1> 1.txt 2>2.txt发现是一样的/dev/null指向的是kali的回收站,所以如果我们直接指向回收站
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
13> /dev/null 2>&1详细解释
&是在这里是重定向符,可以重定向流,这里就是将2:报错流重定向到1:标准流,而标准流到了回收站,所以这里不显示任何输出和报错
2>&1:2表示标准错误流(文件描述符 2)。&1表示标准输出流(文件描述符 1),这里的&是必要的,因为它表示这是一个文件描述符,而不是一个文件名2>&1的意思是将标准错误流(2)重定向到标准输出流(1)的位置。
执行顺序
> /dev/null:
首先,标准输出(1)被重定向到 /dev/null。这意味着任何原本会输出到标准输出的内容都会被丢弃。
2>&1:
接着,标准错误(2)被重定向到标准输出(1)的位置。由于标准输出已经被重定向到 /dev/null,因此标准错误也会被重定向到/dev/null。
shell编程作业
⼀、⽤Shell写⼀个计算器
简单的可以使用bc命令
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
14复杂的可以是case语句或者if-else语句
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
15⼆、⽤Shell定义⼀个求n的阶乘函数
在kali中sh解释器的其实是指向dash解释器,而dash解释器是bash解释器的简化版,只有bash解释器的1/10左右的大小,所以bash解释器可以支持更多更复杂的语法
sh解释器
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
16bash解释器
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
17扩展
获取ipv4的地址
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
18crontab
使用crontab -e命令进行写入,选择nano
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
19无限重启Linux
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
20使用nano进行写入
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
21写入内容
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
22启用并启动服务
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
23失败了,应该是sudo reboot哪里没有权限 使用root
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
24可以了,现在只需要写成一个脚本就行了
适合想走渗透和红队方向的师傅,含金量比国内的高了不少。如果和我一样学历不太好的师傅,凭借这个可以抹平211的学历差距,感兴趣的师傅可以扫描加我微信,保证全网最低价oscp+的培训,而且是7年红队经验,红队队长带领培训
可以关注一下关注公众号,里面有大量的工具和课程免费提供
可以加入一下我们的帮会,是真正的红队大佬创建的,里面会定时丢些网上没有的工具(比如安卓远控7.4,不过现在已经删除了,有时限,加入的记得看好时间),除了这个:还有大量的poc、渗透工具、渗透课程、实战案例等等。现在只要99就可以终身,后面人多了就会涨价了
大量的课程和工具
一些工具,二开的网恋避险工具(不清楚的可以去搜一下,黑客网络避险工具)
还有大量内部整理POC合集
我们红队全栈公益课链接:https://space.bilibili.com/350329294
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




还没有评论,来说两句吧...