前言
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是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
8
export
命令在 Unix 和 Linux 系统中用于将变量导出到环境变量中,使得这些变量在当前 Shell 会话及其子进程中可见。环境变量在多个方面都有重要作用,例如配置系统行为、设置路径、传递参数等,不过直接这样只是在当前命令窗口改变,不会影响别的窗口
永久添加
为了使变量在每次登录时都有效,可以将export
命令添加到 Shell 配置文件中。常见的配置文件包括:
Bash: ~/.bashrc
或~/.bash_profile
Zsh: ~/.zshrc
Fish: ~/.config/fish/config.fish
示例:在 ~/.bashrc
中添加 export
命令
首先还原一下
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
9
ll /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
4
shell3,参数传递+算术运算
参数传递
脚本程序传递参数如何实现 创建一个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
8
3.使用 let
命令
let
可以用来执行整数算术表达式,并将结果存储到变量中。
ll /bin/sh
9
4. 使用 bc
命令
bc
是一个强大的计算器,支持浮点运算和复杂的数学函数。
typesource
0
其中,-l
选项加载了标准数学库,允许进行更高级的数学运算。
5. 使用 awk
awk
不仅是一个文本处理工具,也提供了丰富的数学运算能力。
typesource
1
6. 使用 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
4
test和[]
[ ]
和test
命令在 Bash 脚本中实际上是等价的。[ ]
是test
命令的一种更直观的写法。你可以用test
来替换[ ]
,但需要注意的是,使用test
时不需要方括号,参数直接传递给test
命令。 简单的用法复杂一些的
echo aaa
ls
5
shell5,字符串运算符+逻辑运算符
字符串运算符
[[ ... ]]
和[...]
首先我们在终端利用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
5
shell6,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
5
EXP1
是初始化表达式。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'
7
seq 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'
9
echo 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
2
sh解释器不支持:(())
和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
5
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)
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
8
case 变量 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
6
2.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
8
2. 使用点号(.
)或者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
6
2.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是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
02
4. 设置环境变量
如果你想让被调用的脚本能够访问到某些环境变量,可以在调用之前设置它们。
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是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
09
cat
命令会读取从EOF
开始到下一个EOF
之间的所有文本,并将其作为输入。
<<<
使用<<<
符号将一个字符串作为命令的标准输入。
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
10
wc -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是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
16
bash解释器
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
17
扩展
获取ipv4的地址
chmod 777 1.sh
# 777是最高权限,我这里图方便直接赋予的,实际是不推荐的,有安全风险
18
crontab
使用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
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...