Functions
我们的脚本越大,它们就越混乱。如果我们在脚本中多次使用相同的例程,脚本的大小会相应增加。在这种情况下,functions
可以多次提高脚本的大小和清晰度的解决方案。我们将几个命令组合在一个大括号 ( {
... }
) 之间的块中,并使用我们定义的函数名称调用它们functions
。一旦定义了函数,就可以在脚本期间再次调用和使用它。
Functions
是脚本和程序的重要组成部分,因为它们用于为脚本或程序的不同值和阶段执行重复命令。因此,我们不必重复整个代码段,而是可以创建一个单独的函数来执行特定的命令。此类函数的定义使代码更易于阅读,并有助于使代码尽可能短。重要的是要注意,函数必须始终before
在第一次调用时在逻辑上定义,因为脚本也是从上到下处理的。因此,函数的定义始终at the beginning
是脚本的。定义函数有两种方法:
方法 1 - 函数
function name {
<commands>
}
方法 2 - 函数
name() {
<commands>
}
我们可以选择方法来定义最适合我们的函数。在我们的CIDR.sh
脚本中,我们使用了第一种方法,因为使用关键字“function
”更容易阅读。
CIDR.sh
<SNIP>
# Identify Network range for the specified IP address(es)
function network_range {
for ip in $ipaddr
do
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
cidr_ips=$(prips $cidr)
echo -e "\nNetRange for $ip:"
echo -e "$netrange"
done
}
<SNIP>
函数只能通过调用函数的指定名称来调用,正如我们在 case 语句中看到的那样。
函数执行 - CIDR.sh
<SNIP>
case $opt in
"1") network_range ;;
"2") ping_host ;;
"3") network_range && ping_host ;;
"*") exit 0 ;;
esac
参数传递
此类函数的设计应使它们可以与值的固定结构一起使用,或者至少只能与固定格式一起使用。就像我们已经在CIDR.sh
脚本中看到的那样,我们为函数“ network_range
”使用了 IP 地址的格式。参数是可选的,因此我们可以在没有参数的情况下调用该函数。原则上,传递的参数与传递给 shell 脚本的参数相同。这些是$1
- $9
( ${n}
),或者$variable
正如我们已经看到的那样。每个函数都有自己的一组参数。因此它们不会与其他函数或 shell 脚本的参数发生冲突。
bash 脚本和其他编程语言之间的一个重要区别是,除非“ localglobally
”另有声明,否则始终处理所有定义的变量。这意味着我们第一次在函数中定义变量时,我们将在我们的主脚本(函数外部)中调用它。将参数传递给函数的方式与我们将参数传递给脚本的方式相同,如下所示:
PrintPars.sh
#!/bin/bash
function print_pars {
echo $1 $2 $3
}
one="First parameter"
two="Second parameter"
three="Third parameter"
print_pars "$one" "$two" "$three"
返回值
当我们启动一个新进程时,每个child process
(例如,已执行脚本中的 a)在其终止时function
将 a 返回return code
到parent process
(bash shell
通过它我们执行脚本),通知它执行的状态。此信息用于确定进程是否成功运行或是否发生特定错误。基于此信息,parent process
可以决定进一步的程序流程。
Return Code | 描述
| --- | --- |
1
| 一般错误
2
| 滥用 shell 内置函数
126
| 调用的命令无法执行
127
| 找不到相关命令
128
| 退出参数无效
128+n
| 致命错误信号“ n
”
130
| 由 Control-C 终止的脚本
255\*
| 退出状态超出范围
要取回函数的值,我们可以使用几种方法,例如return
、echo
或 a variable
。在下一个示例中,我们将看到如何使用“ $?
”来读取“” return code
,如何将参数传递给函数以及如何将结果分配给变量。
Return.sh
#!/bin/bash
function given_args {
if [ $# -lt 1 ]
then
echo -e "Number of arguments: $#"
return 1
else
echo -e "Number of arguments: $#"
return 0
fi
}
# No arguments given
given_args
echo -e "Function status code: $?\n"
# One argument given
given_args "argument"
echo -e "Function status code: $?\n"
# Pass the results of the funtion into a variable
content=$(given_args "argument")
echo -e "Content of the variable: \n\t$content"
Return.sh - 执行
$ ./Return.sh
Number of arguments: 0
Function status code: 1
Number of arguments: 1
Function status code: 0
Content of the variable:
Number of arguments: 1
调试
Bash 为我们提供了一个很好的机会来查找、跟踪和修复代码中的错误。该术语debugging
可以有许多不同的含义。尽管如此,Bash 调试是从我们的代码中删除错误(bug)的过程。调试可以通过许多不同的方式进行。例如,我们可以使用我们的代码进行调试以检查拼写错误,或者我们可以将其用于代码分析以跟踪它们并确定发生特定错误的原因。
此过程还用于查找程序中的漏洞。例如,我们可以尝试使用不同的输入类型导致错误,并通过汇编程序跟踪它们在 CPU 中的处理,这可能提供一种方法来操纵这些错误的处理,以插入我们自己的代码并强制系统执行它。该主题将在其他模块中详细介绍和讨论。Bash 允许我们使用 " -x
" ( xtrace
) 和 " -v
" 选项来调试我们的代码。现在让我们看一个CIDR.sh
脚本示例。
CIDR.sh - 调试
$ bash -x CIDR.sh
+ '[' 0 -eq 0 ']'
+ echo -e 'You need to specify the target domain.\n'
You need to specify the target domain.
+ echo -e Usage:
Usage:
+ echo -e '\tCIDR.sh <domain>'
CIDR.sh <domain>
+ exit 1
在这里,Bash 准确地向我们展示了使用哪些值执行了哪个函数或命令。这由行首的加号 ( +
) 表示。如果我们想查看特定函数的所有代码,我们可以设置 " -v
" 选项以更详细地显示输出。
CIDR.sh - 详细调试
darkinga@htb[/htb]$ bash -x -v CIDR.sh
#!/bin/bash
# Check for given argument
if [ $# -eq 0 ]
then
echo -e "You need to specify the target domain.\n"
echo -e "Usage:"
echo -e "\t$0 <domain>"
exit 1
else
domain=$1
fi
+ '[' 0 -eq 0 ']'
+ echo -e 'You need to specify the target domain.\n'
You need to specify the target domain.
+ echo -e Usage:
Usage:
+ echo -e '\tCIDR.sh <domain>'
CIDR.sh <domain>
+ exit 1
与正常调试相比,我们看到到目前为止已处理的整个代码部分,然后是已采取的各个步骤。
在本模块中,我们将介绍:
- Bash 脚本简介
- 条件执行
- 使用参数、变量和数组
- 比较运算符、算术和输入/输出控制
- 循环、分支和函数
- 调试 Bash 脚本的技术
本模块分为多个部分,并附有动手练习,以练习我们涵盖的每一种战术和技术。该模块以实用的动手技能评估结束,以衡量您对各个主题领域的理解。
在您完成该模块时,您将看到针对所介绍的各种主题的示例命令和命令输出。值得尽可能多地复制这些示例,以进一步强化每个部分中介绍的概念。您可以在交互式部分提供的 Pwnbox 或您自己的虚拟机中执行此操作。
您可以随时启动和停止模块并从上次中断的地方继续。没有时间限制或“评分”,但您必须完成所有练习和技能评估才能获得最大数量的立方体,并在您选择的任何路径中将此模块标记为完成。
该模块被归类为“简单”,但假设您具有 Linux 命令行的工作知识并了解信息安全基础知识。
牢牢掌握以下模块可被视为成功完成本模块的先决条件:
- Linux 基础
- 网络请求