Shell菜鸟笔记(1)--Shell中的一些常用特殊字符

1、关于$

$是个在shell中有多种使用方式的东东。不经常使用时,也是很容易忘记的一个特殊标识符,记录一下比较常用的几种用法:

 

  • $# : 获得脚本传入参数的个数。
  • $? :  获得上面函数或脚本执行之后的返回值(或者叫退出码)。(默认成功执行之后返回0)
  • $$ :  获得当前所在脚本的进程ID号。(通常会被作为生成唯一标识的一种手段)
  • $! : 获得最后一个后台进程PID。(通常这个可以用来结合后台任务& 来完成一定时间内运行任务的需求:cmd & (sleep 10; kill -9 $! 2>/dev/null))
  • $() : 相当于``,也用作命令替换,即先执行括号中的命令,然后返回命令执行的结果。
  • $*/$@ : 获取传入脚本的全部参数。但两者在加上引号之后的行为有些差别,后者会把传入参数以空格为分割拆分成多个,前者则还是作为一个整体看待。另外,这个差异也跟脚本的传入参数是否使用了引号有关,具体看一下例子。
    • #a.sh为测试脚本,内容如下
      #!/bin/bash
      echo "The passed in parameters are : $@"
      echo 'Test $@ : '
      for arg in $@
      do
        echo "$arg"
      done
      echo 'Test $*: '
      for arg in $*
      do
        echo "$arg"
      done
      echo 'Test "$@" : '
      for arg in "$@"
      do
        echo "$arg"
      done
      echo 'Test "$*": '
      for arg in "$*"
      do
        echo "$arg"
      done
      
      ## 下面是几种不同调用输出的结果
      # use nothing
      ./a.sh a b c d e
      The passed in parameters are : a b c d e
      Test $@ : 
      a
      b
      c
      d
      e
      Test $*: 
      a
      b
      c
      d
      e
      Test "$@" : 
      a
      b
      c
      d
      e
      Test "$*": 
      a b c d e
      # use " "
      ./a.sh "a b c d e"
      The passed in parameters are : a b c d e
      Test $@ : 
      a
      b
      c
      d
      e
      Test $*: 
      a
      b
      c
      d
      e
      Test "$@" : 
      a b c d e
      Test "$*": 
      a b c d e
      # use ' '
      ./a.sh 'a b c d e'
      The passed in parameters are : a b c d e
      Test $@ : 
      a
      b
      c
      d
      e
      Test $*: 
      a
      b
      c
      d
      e
      Test "$@" : 
      a b c d e
      Test "$*": 
      a b c d e
    • 当然,这个种表示还有很多细微的差别不太常用,具体的可以参考这里:http://www.tsnc.edu.cn/default/tsnc_wgrj/doc/abs-3.9.1_cn/html/internalvariables.html#APPREF
  • ${}: 变量替换
    • ${#var}:输出var变量的字符串长度。
      • var=123456789;echo ${#var}
        9  
    • ${var#string}/${var##string}/${var%string}/${var%%string}:字符串截取(string中支持Globbing风格的通配符*和?)
      • var1=abcd12345abc6789
        pattern1=a*c  # *(通配符)匹配a - c之间的任意字符.
        #从左到右非贪婪式匹配,即匹配到最短的一个就停止,将匹配到的串截掉。
        echo '${var1#$pattern1} =' "${var1#$pattern1}"  # Output : ${var1#$pattern1} = d12345abc6789
        #从左到右贪婪式匹配,即匹配到最长一个才停止,将匹配到的串截掉。
        echo '${var1##$pattern1} =' "${var1##$pattern1}" # Output :  ${var1##$pattern1} = 6789
        
        pattern2=b*9    # 匹配'b'到'9'之间的任意字符
        #从右到左非贪婪式匹配,即匹配到最短的一个就停止,将匹配到的串截掉。 
        echo '${var1%$pattern2} =' "${var1%$pattern2}" # Output : ${var1%$pattern2} = abcd12345a
        #从右到左贪婪式匹配,即匹配到最长的一个才停止,将匹配到的串截掉。
        echo '${var1%%$pattern2} =' "${var1%%$pattern2}" # Output : ${var1%%$pattern2} = a
        # 这关于#和%谁截掉左边,谁截掉右边的记法,最简单的就是看看键盘上他们在$符号的左右位置。
        #使用这种方式实现的basename的功能
        path_name=/home/bozo/ideas/thoughts.for.today
        t=${path_name##/*/}
        echo $t #Output: thoughts.for.today  
    • ${var:pos}/${var:pos:len}: 相当于Java中String的subString方法,也是bash中以index方式截取字符串的方法。但这种方式即使下标越界,也不会有任何错误输出。。。
      • var1=abcd12345abc6789
        echo ${var1:0} # abcd12345abc6789
        echo ${var1:1} # bcd12345abc6789
        echo ${var1:0:1} # a
    • ${var/Pattern/Replacement}: 使用Replacement来替换变量var中第一个匹配Pattern的字符串。
      • var1=abcd-1234-defg;t=${var1/abcd/m*m-};echo $t
        # Output : m*m--1234-defg
        # pattern中同样支持*通配符,但同样也是Globbing风格。
        var1=abcd-1234-defg;t=${var1/a*d/m*m-};echo $t
        # Output : m*m-efg
    • ${var//Pattern/Replacement}:同上,当时多了一个斜杠之后,标识不仅仅替换第一个匹配的字符串,而是全局替换。
    • ${var/#Pattern/Replacement}/${var/%Pattern/Replacement}: 这里#和%表示Pattern只匹配字符串的开头或者结尾,功能有点类似于RE中的^和$。
      • v0=abc1234zip1234abc
        v1=${v0/#abc/ABCDEF}
        echo "v1 = $v1" # v1 = ABCDEF1234zip1234abc
        v2=${v0/%abc/ABCDEF}
        echo "v2 = $v2" # v2 = abc1234zip1234ABCDEF
        # 123 is neither the prefix nor suffix, so the String will not be replaced.
        v3=${v0/#123/000}
        echo "v3 = $v3" # v3 = abc1234zip1234abc
        v4=${v0/%123/000}
        echo "v4 = $v4" # v4 = abc1234zip1234abc
    • ${array[@]}/${array[*]}: 获取数组的全部元素(${array[index]为获取数组中下标为index的那一个元素)。
      • my_array=(a b c d e)
        echo 'Test ${my_array[@]}'
        for my in ${my_array[@]}
        do
          echo $my
        done
        echo 'Test ${my_array[*]}'
        for my in ${my_array[*]}
        do
          echo $my
        done
        
        #The following is the output
        Test ${my_array[@]}
        a
        b
        c
        d
        e
        Test ${my_array[*]}
        a
        b
        c
        d
        e 
  • $ : 在正则表达式(RE)中作为行结束符。

2、关于Globbing

Bash本身并不会识别正则表达式,在脚本中,使用RE的是命令和工具。比如sed、awk等工具。Bash仅仅做的是文件名的扩展(当然还有上面提到的关于字符串的替换时使用的通配符),也就是所谓的Globbing。Globbing所使用的通配符最常用的有这3个:*/?/^。

这里面最容易和标准正则表达式产生混淆的就是前面两个通配符。

在RE中,*和?的通配作用主要是基于某个特定的字符的,而在Globbing中,他们的通配作用却可以代表任何字符。具体看个例子:

 

ls -l
-rw-r--r--   1 sky  staff   3269  9 29 15:29 velocity.log
-rw-r--r--   1 sky  staff      0 10  6 19:10 velocityab.log
-rw-r--r--   1 sky  staff      0 10  6 19:10 velocityyy.log

##Globbing style
ls -l *.log
-rw-r--r--  1 sky  staff  3269  9 29 15:29 velocity.log
-rw-r--r--  1 sky  staff     0 10  6 19:10 velocityab.log
-rw-r--r--  1 sky  staff     0 10  6 19:10 velocityyy.log
##RE style, nothing output to the console
ls -l|egrep "*.log"
egrep: repetition-operator operand invalid
##RE style again
ls -l|egrep "velocity*.log"
-rw-r--r--   1 sky  staff   3269  9 29 15:29 velocity.log
-rw-r--r--   1 sky  staff      0 10  6 19:10 velocityyy.log

从上面的例子可以看出来,RE中,*是不能作为一个独立的“通配单元”存在的,但是Globbing中没有问题,但是需要注意的是,Globbing中*是不会通配以.开头的隐藏文件的。?会有上面类似的解释规则。所以在Globbing中,velocity.???可以被解释为velocity.log,但同样也可以被解释为velocity.123等。 

3、关于各种[](包括[[]])

 

  • []用作条件测试:基本上等价于shell内建的test。最常见的就是if/while等后面的判断条件。
  • []用作数组元素:直接看例子
    • my_array=(a b c d e);
      echo "The first element is : ${my_array[0]}" #The first element is : a 
  • []用作RE中的字符范围:匹配中括号中字符集中的某一个字符。常用的可以参考如下的这些形式:[xyz]/[c-n]/[B-Pa-m]/[a-z0-9]/[^b-d](这里的^标识取反了)
  • [[]]用作表达式的条件测试:它和[]的最大区别就是,内部支持&&/||/>/<等符号,这也是使得它的结构相对于[]更加通用。

4、关于整数运算扩展操作符(())

该结构允许整数运算和赋值。是Bash(注意,原生shell并不支持该结构)中C语言结构风格的一个表达式。常用的可以参考如下几种用法:((a=12))/((a++))/((++a))/((a--))/((--a))/((t = a<123?0:1)),这里需要注意两点:

 

  • 由于(())本身的C语言语法风格,(())内部在使用Bash变量时,是不需要$前缀标识它的引用形式的。
  • 注意上面例子中的最后一个,可以看出它支持多数编程语言中的典型的三元表达式哦。

5、关于重定向

Linux下,重定向输出的基本符号大家一般比较熟悉了。>表示覆盖式重定向,>>表示追加式重定向。这里需要强调的是:0,1,2这三个特殊的数字,作为3个特殊的文件描述符,已经被默认分配给了,stdin,stdout,stderr这3个特殊的“文件”。下面列出几个常用的利用重定向完成的小功能tip:

 

  • 清空文件内容: : >a.txt 清空a.txt文件的内容。(这里某些shell下,省略前面的冒号,直接使用 >a.txt也可以,但不是所有shell都支持这种语法,所以通用性不用加上冒号的版本)
  • 重定向错误输出到特定的文件中:比如,command 2>a.txt,执行command命令,同时将产生的错误输出信息,从终端中冲定向到a.txt中。
  • 将标准和错误输出全部输出到特定文件中:比较典型的用法就是不关心命令的任何输出,将所有输出信息都丢入垃圾桶:command >/dev/null 2>&1。这个表达式如果不理解含义,很容易记错,误用成下面这个样子:
    • command 2>&1 >/dev/null 这个表达式只能保证将stdout丢掉,但是如果有错误产生,还是会输出到屏幕上。原因是:命令解释到command 2>&1是,表示将stderr输出到stdout上(就是屏幕上),后面执行到>/dev/null时,表示将stdout输出丢掉。但是,在这之前,stderr已经被重定向到了当时的stdout(就是屏幕),所以这时的错误信息并不会丢掉,还是会输出到屏幕。
    • 如果你已经决定不关心任何命令的输出信息,又觉得上面的写法有点啰嗦,那可以试试这个写法: command &>/dev/null 这个写法会不管三七二十一,直接把所有输出信息全部重定向到/dev/null,这里完全省略了默认的文件描述符1和2.

你可能感兴趣的:(Shell,Linux)