Shell编程基础整理 -- 变量

前言后语

  • 所需基础:
+ 基础的终端文件操作指令
  • 学习目标:
+ 使用终端编写shell脚本,了解PATH
  • 预期结果:
  能够通过脚本来执行命令行指令(例如启动postgresql) 
  脚本可以作为shell指令执行
  使用其他语言可以调用该脚本并且给出可读/可用的返回值
  待定。。。




铺垫知识

待更新

环境准备

我使用的是Linux Mint 21.3 Cinnamon,内核版本5.15.0
打开终端即可

或者写一个.sh文件 在文件第一行指定解释器(例如bash解释器对应的就是 #!/bin/sh
下面写脚本内容
然后给这个文件执行权限
执行即可

基础知识

一切的基础:变量

基本内容:变量定义与调用
参考网站
https://www.runoob.com/linux/linux-shell-variable.html
https://zhuanlan.zhihu.com/p/418357041

pre.

  • 可用变量类型:整数、字符串、数组
    然而,实质上在bash shell中,我们的值全部以字符串的形式存储,只是不同“类型”可使用的操作不同。
  • shell支持在命令行界面进行编程,在终端创建的变量在当前终端始终可用
    (也就是说对于某些情况 你不一定需要反复定义某个变量 只要直接调用之前创建好的即可)

使用方法

  1. 变量定义:

    • 字符串串:var_name=“strings” / var_name=‘strings’ / var_name=strings

      * 字符串内的特殊符号要使用转义符号\   e.g:\"
      * 双引号定义的字符串在内部允许变量替换
      * 无引号定义方式与双引号一样自由和奔放,但是出现空格会导致后面的字符被解释器认作参数
      * 单引号定义的字符串为常量字符串,若字符串内有变量符号,会作为正常字符输出
      * 单引号定义的字符串内部若需出现单引号 需要成对出现 此时表示拼接子串内容
      
      示例

      a="lalala" && b="${a}hahaha" && echo $b的输出会是lalalahahaha
      a="lalala" && b=${a}hahaha && echo $b的输出会是lalalahahaha
      a="lalala" && b=${a} hahaha && echo $b的输出会报错,因为hahaha被认为是命令了
      b='lalala '123456' hahaha' && echo $b的输出会是lalala 123456 hahaha
      a="lalala" && b='${a}hahaha' && echo $b的输出会是${a}hahaha
      b='lalala '123456' hahaha' && echo $b的输出会是lalala 123456 hahaha



    • 带值变量:var_name=value / declare -i var_name=var_name

      * 前者不限定变量类型,只是赋值为整数
      * 后者显式声明该变量为整型,解释器会尝试将赋值转换为整数
      
    • 常量约俗: CONST_NAME = something

  2. 调用通则:

    • 基本方法: $name
      该方法调用了一个变量,其中name为变量名
    • 标识边界:${name}
      该方法调用了一个变量,并且显式的标注了变量名为name
    • 使用示例:"unrelevent strings${name}unrelevent strings "
      该方法调用了一个name变量,其中显式的标注了变量名为name,前后的字符串不会被认作变量名
    • 只读标注: readonly existed_var
      之后更改该变量会报错readonly
    • 删除变量: unset existed_var
      之后调用该变量不会产生结果
  3. 注意事项:

    • 变量定义 等号两侧不能含有空格 × 例如( var_name = something )×
    • 变量名不能使用空格和特殊符号(不包括下划线 _ )
    • 不要使用保留关键字(例如 if)
进阶内容:变量的操作和组合
  • 数组

    • 定义方式
      与大多数编程语言类似,shell也支持数组,但他仅支持一维数组
      定义方式:
      array_name=(value0 value1 value2 value3)
      
      数组各个元素间使用空格或者换行符分隔:
      array_name=(
      value0
      value1
      value2
      value3
      )
      

    • 调用方式:
      与大多数语言类似,使用方括号即可(因为早期计算机编程语言中多使用方括号来表示“地址偏移” 尽管这里只是形式上追随一致)
      ${name[index]}

    • 获取长度:
      • 获取数组元素个数:
        ${#name[@]} 或者 ${#name[*]}
      • 获取数组当前元素的长度:
        ${#name[index]} (就像正常调用一个变量一样)


  • 字符串

    • 长度获取:在所取变量的变量名前加入#(需要使用显式边界标识)
    	例如:	先执行  strings="strings"  然后执行  echo ${#strings}   ,输出为7
    
    为什么需要显式指定边界?

    如果没有使用显式边界标识:

    	先执行  strings="strings"  然后执行  echo $#strings   ,输出为0strings
    

    问题分析:

    	先执行  strings="strings"  然后执行  echo $strings  ,输出为strings
    	执行  echo $#s       ,输出为0s
    	执行  echo $s        ,输出为s
    	执行  echo $#        ,输出为0
    

    说明这个问题是由于不显式指定边界,#指令读取到的变量为一个空字符串,然后其后的内容被作为额外的字符串送到了输出



    • 获取字串:变量名后加入:a:b(需要使用显式边界标识)
      其中 a 为字串起始位置(第a个字符后开始读取),而 b 为字串截断大小(一共取b个字符)
      其中 a 必须存在,b可以省略,表示从第a个字符后一直到末尾
    	例如:	先执行strings="strings"  然后执行  echo ${strings:3}  ,输出为ings
    		先执行strings="strings"  然后执行  echo ${strings:3:1}  ,输出为i
    

    与获取大小不同,如果不指定边界范围,解释器会直接将整行认作字符串送到输出


    • 查找位置:使用expr命令在指定字符串中查找对应字符第一次出现的位置
      注意使用expr的时候字符串最好带有双引号(原因见后文)
    `expr index "STRINGS" CHAR(s)`
    

    注意使用的是反引号
    在指定字符串中查找给定字符第一次出现的位置,如果有多个字符多个位置,则以第一次出现的那个字符的那个位置为准

    	例如:	先执行strings="strings" 然后执行  
    		 echo `expr index "$strings" r` , 输出3
    		 echo `expr index "$strings" gr`, 输出3
    		 echo `expr index "$strings" gn`, 输出5
    		 echo `expr index "$strings" gs`, 输出1
    

    实际上,expr是shell引进的外部的数学计算工具,我们之后还会接触到他


    为什么传入参数的字符串上需要使用双引号? 在给定的范例中可能看不出来,让我们定义一个新的字符串:
    Who_I_Am="I am Jerry"
    

    这是一个带有空格的字符串,我们搜索字母m

    echo ${Who_I_Am}		(输出I am Jerry)
    echo ${#Who_I_Am}		(输出10)
    echo `expr index ${Who_I_Am} m`		(报错!)
    

    expr: 语法错误:未预期的参数 "Jerry"
    我们的解释器抛出了一个错误,从报错上分析,他将参数Jerry输入进了expr,
    我们的确需要他读取“Jerry”,但是注意我们给定的字符串,应该为"I am Jerry",而非“Jerry”
    很明显expr读取到字符串后,字符串被从中截断,导致“Jerry”被单独列出来,产生了多余的参数输入

    在和字符串相关的操作中,很经典的一类问题是,字符串内的“关键词”和“特殊符号”被读取为操作,导致输出与预期结果不符。

    尝试输入以下指令:

    echo `expr index I am Jerry m`,发现报错信息一致
    

    也就是说,解释器先进行了变量替换,导致echo后面执行的实际命令为expr index I am Jerry m,我们的"I am Jerry"由于含有空格,在执行expr操作的时候被拆成了字符串I 字符序列am 和多余的参数Jerry和m!
    由于expr只接受两个输入参数,尝试输入Jerry的时候解释器发现问题,则报错

    解决办法很简单,我们告诉expr,我们那个变量是一整条字符串,那么就不会产生这个“错位问题”
    即在expr的字符串参数处外括双引号(还记得我们说过 双引号字符串允许变量替换这件事吗)
    也就是:

    echo `expr index "${Who_I_Am}" m`
    

    成功执行,结果为4

    这类问题其实是非常经典的“输入类型判断错误”问题
    有一些相关的非常有意思的应用,比如“SQL注入”,感兴趣可以了解一下:

    数据库的SQL语言中,假设我们有一个登陆界面,需要验证用户输入的用户名和密码的匹配问题:
    SELECT * FROM users WHERE username = '[用户输入内容]' AND password = '[用户输入内容]
    如果有人在username处输入: admin'--
    SELECT * FROM users WHERE username = 'admin' --' AND password = '[用户输入]'
    --是sql中的注释符号,这会导致实际传入的语句变为:
    SELECT * FROM users WHERE username = 'admin'
    于是我们的用户就跳过密码检查,成为了系统的管理员
    
    由于没有进行严格的类型检查,输入的字符串被错误的当成了操作命令,给了攻击者可乘之机
    
终极内容:全局变量与特殊变量


posted @ 2024-09-20 19:21  Brakeintime  阅读(63)  评论(0)    收藏  举报