Leafee98's Blog

shell学习笔记

· 2085 words · 5 minutes to read
Categories: tech
Tags: linux

关于符号 🔗

  • $ 美元符号, 一般用于取变量的值, 不过总觉得和命令代换有很多相似之处, 具体可见样例, 另外美元符号也会和其他符号产生有意思的组合.

    $ var="echo hello"
    $ $var
    hello
    
  • [] 相当于test命令, 用于测试条件是否成立, 若成立则退出值为0(真), 由于if, for, case等结构语句通过上一条命令的退出值来决定运行流程, 因此方括号多用于这些结构语句中.

  • $() , ` ` 两种用法相同, 均是命令代换, 即取所包含的命令的输出作为文本值参与运行, 文本值甚至可以是命令, 如直接运行`echo pwd`则相当于直接运行pwd.

  • $[] 对于此命令需要提前了解双反引号(上一条), 以及expr命令, expr官方描述为Print the value of EXPRESSION to standard output, 所以可以把expr命令做命令代换, 于是$[] `expr expression`效果相同, 样例如下.

    $ var1=`expr 1 + 1`
    $ var2=$[1 + 1]
    $ echo var1=$var1 var2=$var2
    var1=2 var2=2
    

    不过有一点奇怪的是, 以下命令的运行结果并不能如我所愿输出hello, 不过至少反引号和美元符号加方括号的行为是一样的, 仍然支持我的猜想.

    $ alias 2="echo hello"
    $ alias
    alias 2='echo hello'
    $ `expr 1 + 1`
    bash: 2: command not found
    $ $[1 + 1]
    bash: 2: command not found
    
  • ; 可用于分隔命令, 书本上翻译为命令列表, 一般来说, 命令与命令之间通过换行符来进行分隔, 但是有时为了追求代码的紧凑会希望将几条命令放在同一行, 于是这便是分号的用途.

    if [ expression ] ; then
        command
    fi
    
  • () 用于表示数组, 数组样例见下一个符号的样例, 也用于进程列表, 进程列表中的命令会被开一个子shell运行, 进程列表样例如下.

    $ echo $BASH_SUBSHELL           # 当前子shell水平为0
    0
    $ (echo $BASH_SUBSHELL)	        # 当前子shell水平为0
    1
    

    那么之前的命令代换中的括号是否也会被开一个子shell呢,

    $ echo $BASH_SUBSHELL           # 当前子shell水平为0
    0
    $ echo $(echo $BASH_SUBSHELL)   # 命令代换中子shell水平为1
    1
    

    猜想得证.

  • ${} 此符号用于取数组的值, 需要注意的一点是, 当数组中某一个位置的值被unset之后, 该位置后面的值并不会自动向前移动一个序号, 可见样例.

    $ arr=( 1 2 3 )
    $ echo ${arr[2]}
    3
    $ unset arr[1]
    $ echo ${arr[2]}
    3
    $ echo ${arr[1]}
                                    #空行, 无输出
    $ 
    
  • [[ ]] (( )) 双方括号是拓展字符运算, 双圆括号是拓展数学运算, 其中拓展数学运算可支持移位操作,自增操作等高级操作, 拓展字符运算可支持通配符匹配.

  • ;; 双分号用于case的匹配中, 与单分号不同的是, 单分号只能结束当前命令, 后续仍被认为是这一块的可执行语句, 不能接下一个匹配条件, 只有以双分号结尾, 才能接下一个匹配

    $ cat caseExample.sh
    var=2
    case $var in
    1 | 2 )
        echo 1 ;
        echo 2 ;;
    3 | 4 )
        echo 3 ;
        echo 4 ;;
    esac
    $ ./caseExample.sh
    1
    2
    $ 
    

关于结构化语句 🔗

  • if 判断语句

    • 判断的方法只是简单地根据上一命令的返回值来判断执行结构, 关于这一点, 其实最常用的方括号只是test命令, 这一点在上面符号的部分有提及. 通常用法是

      if command1 ; then
          command2
      fi
      

      以上用法中, command1是任意一个可执行的shell命令, command2是条件为真则执行的语句体, 最后if语句使用fi进行结尾. 需要注意的是, command1的返回值若是0if判断为真, 非0则判断为假, 这一点与许多类C语言恰恰相反. 另外, command1是按照通常的命令执行方式执行的, 所以如果command1命令有输出, 则会直接输出在控制台中.

    • if语句也可以在条件为假的时候执行语句, 方法是在结尾fi之前加入一个else; 当然if也可以判断多个条件进行筛选, 方法是使用elfi, 样例如下

      if command1 ; then
          command2
      elif command3 ; then
          command4
      else
          command5
      fi
      

      为了代码紧凑, 有时候会在判断命令之后使用分号进行分隔, 并把then放在同一行的之后

    • if语句中也可以使用逻辑运算符, 和其他许多类C语言类似, 使用&&, ||, !, 分别作为与,或,非. 这些逻辑运算符可以用在test命令和普通命令之中. 需要注意的是, 在test手册中并没有&&,||的表述, 不过这些仍然被支持, 因为在shell中, &&的行为是若该符号之前的命令运行的返回值为, 则执行下一条命令, ||的行为是若该符号之前的命令运行返回值为, 则执行下一条命令, 这个被称为"短路", 在C语言中同样适用. ​ 所以如此来看的话, 使用command1 && command2的作为分析, 如果command1返回值为真, 则执行command2, 此时整个逻辑表达式的值就由command2的返回值决定, 若command2的返回值也为真, 则最后$?的值就是真, 反之则假. 若command1返回值为假, 则短路, 最终整个逻辑运算的结果就是command1的返回值–假. 原因是逻辑运算符的作用仅仅是决定是否进行短路, 而且条件判断依据仅仅是之前运行的最后一条命令的返回值, 于是整个逻辑运算十分顺利. 或运算同理. ​ 至于非运算, 叹号!其实也是一条命令, 这一点可以在终端不输入任何字符的情况下敲入双tab, 使其打印所有的可执行命令, 其中第一条就是!. 样例如下

      $ ! echo 'hello' && echo 'true' ; echo $?
      hello
      1
      $ echo 'hello' && echo 'true' ; echo $?
      hello
      true
      0
      $ ! echo 'hello' || echo 'true' ; echo $?
      'hello'
      'true'
      0
      

特殊变量 🔗

变量 含义
$$ 当前shell进程的PID
$? 上一命令运行的返回值
$# 命令行脚本传递参数的个数
$@ 作为数组(雾)获取全部命令行参数
$* 作为一整条字符串获取全部命令行参数
$0 当前shell脚本的运行名(脚本的绝对路径或者相对路径)
$n shell脚本的第n个参数

Categories