• 第二章 Bash脚本编程


    在前一章中,学习了Linux中的许多命令。现在,让我们在命令行工具中将您的技能提升到一个新的水平。在本章中,您将看到如何基于目前所学的知识使用Bash创建脚本命令。为什么使用Bash脚本?Bash的通用性使渗透测试人员能够灵活地执行功能强大的终端命令,而无需安装编译器或集成开发环境(IDE)。要开发Bash脚本,您只需要一个文本编辑器,就可以了。什么时候应该使用Bash脚本?这是在开始本章之前要解决的一个重要问题!Bash不适合开发复杂的工具。如果你想这样做,你应该使用Python (Python基础知识将在本书后面介绍)。Bash用于快速、小型的工具,您可以在希望节省时间时实现这些工具(例如,为了避免重复相同的命令,您只需在Bash脚本中编写它们)。本章不仅会教你Bash脚本语言,还会向你展示编程的思想。如果您是编程新手,这是了解编程语言如何工作的一个很好的起点(它们有很多相似之处)。

    一、使用BashUsing 打印到屏幕

    二、使用变量

    三、使用脚本参数

    四、处理用户输入

    五、创建函数

    六、使用条件if语句

    使用while和for循环


    基本Bash脚本

    基本的Bash脚本分为以下几类:

    用户

    输入

    脚本输出

    参数

    在Bash中打印到屏幕

    有两种常用的方法可以使用Bash脚本写入终端命令行输出。第一个简单的方法是使用echo命令。我们在前一章中看到(我们将文本值包含在单引号或双引号中):

    echo 'test'

    第二个方法是printf命令;这个命令比echo命令更灵活,因为它允许你格式化你想要打印的字符串:

    printf 'message to print'

    YPeegPe之前的公式过于简化;事实上,printf 还允许您格式化字符串(不仅仅是用于打印;它的作用还不止于此)。让我们看一个例子:如果我们想显示网络中活动主机的数量,我们可以使用以下模式:
    printf "%s %d\n" "Number of live hosts:"

     

    让我们把命令分开,这样你就可以理解发生了什么: 

    %s:表示我们要在这个位置插入一个字符串(文本)

    %d:表示我们要在这个位置添加一个小数(数字)

    \n:表示我们要在打印完成后跳转到新的行

    另外,请注意我们使用双引号而不是单引号。双引号使我们在处理字符串时比单引号更灵活。因此,大多数情况下,我们可以对printf使用双引号(很少需要使用单引号)。要使用printf命令格式化字符串,可以使用以下模式:

    %s:字符串(文本)

    %d:十进制(数字)

    %f:浮点数(包括带符号数字)

    %x:十六进制

    \n:换行

    \r:回车

    \t:水平制表符

    变量

    什么是变量?为什么每种编程语言都使用它?将变量视为存储区域,您可以在其中保存字符串和数字等内容。目标是在程序中一遍又一遍地重用它们,这个概念适用于任何编程语言(不仅仅是 Bash 脚本)。要声明变量,请为其指定名称和值(默认情况下该值是字符串)。变量的名称只能包含字母字符或下划线(其他编程语言使用不同的命名约定)。例如,如果您想将路由器的 IP 地址存储在变量中,首先您将创建一个文件 test.txt。 sh(Bash 脚本文件将以 .sh 结尾),在文件中,您将输入以下内容:

    让我们写入第一个脚本文件

    1. #!/bin/bash
    2. #Simple program with a variable
    3. ROUTERIP="10.0.0.1"
    4. printf "The router IP address: $ROUTERIP\n"

    运行之后发现权限不够 ,需要给文件添加权限

    chmod +x test.sh  #给文件添加执行权限

    重新执行后发现,脚本文件就可以被执行了 

    刚刚构建了第一个 Bash 脚本!假设我们希望该脚本自动运行,然后在系统中的任何位置指定其路径。因此,我们必须将其添加到 sPATH 变量中。在我们的示例中,我们将/opt 添加到 $PATH 变量,以便我们可以将自定义脚本保存在此目录中。首先,打开.使用任何文本编辑器编辑的 .bashrc 文件。
     export PATH=$PATH:/OPT/
     

    cp test.sh /opt/    #将文件拷贝到opt目录下

    cd /opt #进入opt目录

    ls -la | grep "test.sh"    #查看test.sh 文件 

    命令的变量

    有时,您可能想要执行命令并将其输出保存到变量中。大多数时候,其背后的目标是操纵命令输出的内容。这是一个简单的命令,它执行 ls 命令并使用 grep 命令过滤出包含单词 simple 的文件名。 (别担心,您将在本章接下来的部分中看到更复杂的场景。暂时练习并专注于基础知识。)
    1. #!/bin/bash
    2. LS_CMD=$(ls | grep 'simple')
    3. printf "$LS_CMD\n"

    脚本参数

    有时,您需要向 Bash 脚本提供参数。您必须用空格分隔每个参数,然后您可以在 Bash 脚本中操作这些参数。让我们创建一个简单的计算器(simpleadd .sh)来添加两个数字:
    1. #!/bin/bash
    2. #这是一个简单的计算机程序,需要你来输入两个数字
    3. #我们将第一个数字存储在NUM1中
    4. NUM1=$1
    5. #将第二个数字存储在NUM2中
    6. NUM2=$2
    7. #将结果保存在 total 变量中
    8. TOTAL=$(($NUM1 + $NUM2))
    9. echo '########################'
    10. printf "%s %d\n" "The total is =" $TOTAL
    11. echo '########################'

    您可以在前面的脚本中看到,我们使用$1语法访问第一个参数,使用$2访问第二个参数(您可以添加任意多的参数)。

    之前的脚本有一个限制;它只能添加两个数字。如果您想灵活地添加两到五个数字怎么办?在这种情况下,我们可以使用默认参数功能。换句话说,默认情况下,所有参数值都设置为零,一旦脚本提供实际值,我们就会将它们相加:
    1. #!/bin/bash
    2. #这是一个简单的计算机程序,需要你来输入两个数字
    3. #我们将第一个数字存储在NUM1中
    4. NUM1=$1
    5. #将第二个数字存储在NUM2中
    6. NUM2=$2
    7. #将第三个数字存储在NUM2中
    8. NUM3=$3
    9. #将第四个数字存储在NUM2中
    10. NUM4=$4
    11. #将第五个数字存储在NUM2中
    12. NUM5=$5
    13. #将结果保存在 total 变量中
    14. TOTAL=$(($NUM1 + $NUM2+$NUM3 + $NUM4+$NUM5))
    15. echo '########################'
    16. printf "%s %d\n" "The total is =" $TOTAL
    17. echo '########################'

    或者

    1. #!/bin/bash
    2. #添加5个数字并且相加
    3. #将第一个变量存储到NUM1中
    4. NUM1=${1:-0}
    5. #将第一个变量存储到NUM2中
    6. NUM2=${2:-0}
    7. #将第一个变量存储到NUM3中
    8. NUM3=${3:-0}
    9. #将第一个变量存储到NUM4中
    10. NUM4=${4:-0}
    11. #将第一个变量存储到NUM5中
    12. NUM5=${5:-0}
    13. #将相加的结果写在 total 变量中
    14. =$(($NUM1 + $NUM2 + $NUM3 + $NUM4 + $NUM5))
    15. echo '########################'
    16. printf "%s %d\n" "The total is =" $TOTAL
    17. echo '########################'
    为了理解它是如何工作的,让我们以 NUM1 变量为例(相同的概念适用于五个变量)。我们将告诉它从终端窗口读取第一个参数 {1,如果用户未提供该参数,则将其设置为零,如 :-o} 所示。使用默认变量,我们不限于添加五个数字;从现在开始,我们可以添加任意数量的数字,但最多为五个(在下面的示例中,我们将添加三个数字):
    如果您想知道脚本中提供的参数数量,则可以使用 $# 来获取总数。根据前面的示例,$# 将等于 3,因为我们传递了三个参数。
    printf "%s %d\n" "参数的总数为 =" $#

    用户输入

    与 shell 脚本提供的输入进行交互的另一种方法是使用 read 函数。同样,解释这一点的最佳方法是通过示例。我们将要求用户输入他们的名字和姓氏,然后我们将在屏幕上打印全名:

    要执行它,我们只需输入脚本名称(我们不需要像以前那样提供任何参数)。输入脚本名称后,系统将提示我们显示上一个脚本中定义的消息:

    1. #!/bin/bash
    2. read -p "请输入你的姓:" FIRSTNAME
    3. read -p "请输入你的名字:" LASTNAME
    4. printf "Your full name is: $FIRSTNAME $LASTNAME\n"
    Functions函数
    

    函数是一种将 Bash 脚本组织成逻辑部分的方法,而不是具有无组织的结构(程序员称之为意大利面条代码)。让我们对之前的计算器程序进行重新组织(重构),使其看起来更好。

    这个 Bash 脚本分为三个部分: 在第一部分中,我们创建所有全局变量。全局变量可以在您创建的任何函数内访问。例如,我们可以在 add 函数中使用示例中声明的所有 NUM 变量。接下来,我们通过将应用程序划分为逻辑部分来构建功能。 print_custom () 函数将只打印我们提供的任何文本。我们使用¥s1 来访问传递给该函数的参数值(即字符串 CALCULATOR)。最后,我们按顺序调用每个函数(每个函数按其名称)。打印标题,添加数字,最后打印结果。

    1. #!/bin/bash
    2. #一个简单的计算器
    3. #S将第一个参数存储在numl变量中
    4. NUM1=${1:-0}
    5. NUM2=${2:-0}
    6. NUM3=$(3:-0}
    7. NUM4=${4:-0}
    8. NUM5=${5:-0}
    9. function print_custom(){
    10. echo $1
    11. }
    12. function add(){
    13. #将加法结果存储在 TOTAL变量中
    14. TOTAL=$(($NUM1+$NUM2+$NUM3+$NUM4+$NUM5))
    15. }
    16. function print_total(){
    17. echo
    18. printf "%s %d\n" "结果为 ="$TOTAL
    19. echo_custom "计算器"
    20. add
    21. print_total

    条件和循环 

    现在您已经了解了Bash脚本编写的基础知识,我们可以介绍更高级的技术。当你用大多数编程语言(如PHP、Python、C、c++、c#等)开发程序时,包括Bash脚本,你会遇到条件(if语句)和循环。

     

     

    条件 

    if语句采用以下模式:

    1. if [[ 比较 ]]
    2. then
    3. True, 做某事
    4. else False, 做别的事

    如果你一直注意的话,你就会知道解释这种模式的最好方法就是通过例子。让我们开发一个程序,使用Nmap ping一个主机,我们将根据条件(主机是up或down)显示机器的状态:

    1. #!/bin/bash
    2. #使用Nmap ping主机
    3. ### 全局变量 ###
    4. #存储 ip地址
    5. IP_ADDRESS=$1
    6. function ping_host(){
    7. ping_cmd=$(nmap -sn $IP_ADDRESS | grep 'Host is up' | cut -d '(' -f 1)
    8. }
    9. function print_status(){
    10. if [[ -z $ping_cmd ]]
    11. then
    12. echo 'Host is down'
    13. else
    14. echo 'Host is up'
    15. fi
    16. }
    17. ping_host
    18. print_status

    如果主机关闭,nmap命令返回一个空字符串文本,如果主机正在响应,则返回值“主机已启动”。(尝试在终端窗口中执行完整的nmap命令,以直观地看到差异。如果是,将$IP_ADDRESs替换为真实的IP地址。)在if条件下,-z选项将检查字符串是否为空;如果是,则打印“主机已关闭”,否则打印“主机已启动”。 

    其他条件语句可以比较数字、字符串或文件

    循环

    可以用两种不同的方式编写循环:使用while循环或使用for循环。大多数编程语言都使用相同的循环模式。因此,如果您了解Bash中的循环是如何工作的,那么同样的概念也适用于Python。让我们从一个接受以下结构的while循环开始:

    1. while [[条件]]
    2. do
    3. do 某物
    4. done

    解释循环的最好方法是通过从1到10的计数器。我们将开发一个显示进度条的程序:

    1. #!/bin/bash
    2. #while循环
    3. #Counter
    4. COUNTER=1
    5. #Bar
    6. BAR='##########'
    7. while [[ $COUNTER -lt 11 ]]
    8. do
    9. #从0开始输出
    10. echo -ne "\r${BAR:0:COUNTER}"
    11. #休眠 1秒钟
    12. sleep 1
    13. #递增计数器
    14. COUNTER=$(( $COUNTER +1 ))
    15. done

    请注意,while 循环中的条件 ([[ $COUNTER -lt 11]]) 遵循与 if 条件相同的规则。由于我们希望分数停在 10,因此我们将使用以下数学公式:counter <11。运行时,它将显示进度。为了使这个程序更有趣,让它在进入下一个数字之前休眠一秒钟。另一方面,for循环将采用以下模式:、

    1. #!/bin/bash
    2. #For循环进度条
    3. #Bar
    4. BAR='##########'
    5. for COUNTER in {1..10}
    6. do
    7. #从0索引开始打印进度条
    8. echo -ne "\r${BAR:0:$COUNTER}"
    9. #睡眠1秒钟
    10. sleep 1
    11. done

    文件迭代

    下面是使用for循环在Bash中读取文本文件的操作

    1. for line in $(文件名)
    2. do
    3. do something
    4. done

    在下面的示例中,我们将在名为ips.txt的文件中保存IP地址列表。然后,我们将重用Nmap ping程序(我们之前创建的)来检查每个IP地址是up还是down。最重要的是,我们将检查每个IP地址的DNS名称:

    1. #!/bin/bash
    2. #Ping & get DNS name from a list of IPs saved in a file
    3. #Prompt the user to enter a file name and its path.
    4. read -p "Enter the IP addresses file name / path:" FILE_PATH_NAME
    5. (continued)
    6. function check_host(){
    7. #if not the IP address value is empty
    8. if [[ -n $IP_ADDRESS ]]
    9. then
    10. ping_cmd=$(nmap -sn $IP_ADDRESS| grep 'Host is up' | cut
    11. -d '(' -f 1)
    12. echo '------------------------------------------------'
    13. if [[ -z $ping_cmd ]]
    14. then
    15. printf "$IP_ADDRESS is down\n"
    16. else
    17. printf "$IP_ADDRESS is up\n"
    18. dns_name
    19. fi
    20. fi
    21. }
    22. function dns_name(){
    23. dns_name=$(host $IP_ADDRESS)
    24. printf "$dns_name\n"
    25. }
    26. #Iterate through the IP addresses inside the file
    27. for ip in $(cat $FILE_PATH_NAME)
    28. do
    29. IP_ADDRESS=$ip
    30. check_host
    31. done

    如果您仔细阅读了本章,您应该能够理解前面代码中的所有内容。这个程序的唯一不同之处在于,我使用了制表符间距来使脚本看起来更好。前面的例子涵盖了到目前为止我们所做的大部分工作,包括以下内容:

    用户输入、声明变量、使用函数、使用if条件、循环迭代、sprint到屏幕

    总结

    我希望您已经练习了本章中的所有练习,特别是如果您是编程新手的话。上面提到的许多概念将适用于许多编程语言,因此将练习视为学习基础知识的机会。我个人在小而快速的场景中使用Bash脚本。如果您想构建更复杂的应用程序,那么您可以尝试用Python代替。别担心!在本书的最后,您将了解Python,以便您可以处理渗透测试员职业生涯中的任何情况。最后,本章讨论了很多关于Bash脚本的信息。然而,还有比本章更多的信息。在实践中,我使用internet搜索引擎快速查找Bash脚本参考。事实上,你不需要记住本章所学的所有内容。请记住,这本书是一个参考,你可以依靠它来记住在每种情况下使用的语法。

  • 相关阅读:
    LeetCode--148. 排序链表(C++描述)
    (pytorch进阶之路)扩散概率模型
    C语言为什么不支持函数重载?C和C++程序怎样互调?
    C++模拟OpenGL库——图片处理及纹理系统(四):UV纹理坐标
    EchoServer回显服务器简单测试
    前端HTML5 +CSS3 5.CSS布局 6 装饰 && 7 选择器拓展
    笔记本电脑怎样重新安装系统
    LyScript 实现绕过反调试保护
    linux下的进程间通信
    Java多线程-synchronized同步方法及同步块简述
  • 原文地址:https://blog.csdn.net/weixin_47431459/article/details/133913201