2016年8月3日星期三

bash的一些memo

  • 很好的东西: bash可以用<(命令) 或者 >(命令) 来生成一个暂时管道文件,用于任何需要文件的地方。 例如
    gcc -x c <(cat <<EOF
    #include <stdio.h>
    int main() {
        printf("hello\n");
        return 0;
    }
    EOF
    )
    
    就相当于做了个临时文件ttt然后执行gcc -x c ttt。这个-x c仅仅是告诉他按照C代码编译。
  • while语句里的变量范围
    while语句放在管道后面时,其中的循环里的变量无法传到外部,例如:
    XXX=1
    (echo 2; echo 3) | while read v; do XXX=$v; done; 
    echo $XXX
    
    结果依然是1
    为了解决这个毛病,得把while放在前面,同时得用 <空格<() Process Substitution。例如:
    XXX=1
    while read v; do XXX=$v; done < <(echo 2; echo 3)
    echo $XXX
    
    结果显示3。
  • 似乎鲜为人知:想要文件名展开时,把通配符之外的部分扩起来,例如"$DIR"/*,而不能全体扩起来"$DIR/*"
    同理,HOME目录标记~也是不能扩起来。哪怕是双引号。
  • 取得当前文件所在路径,考虑到符号链接,可以用这段代码。
    支持一层符号链接。支持被包含调用(就是被source或者.命令调用)。
    thisDir=${BASH_SOURCE[0]%/*}
    if target=`readlink "${BASH_SOURCE[0]}"`; then
        if [[ $target == /* ]]; then     #absolute path target
            thisDir=${target%/*}
        elif [[ $target == */* ]]; then  #relative path target
            thisDir+=/${target%/*}
        fi
    fi
    #如果只想得到引用当前文件所在的目录,不在乎是否绝对路径和空路径(表示/),拿到此就够了。要访问该目录下的东西就$thisDir/xx。
    
    if [[ ! $thisDir ]]; then
        thisDir=/
    else
        thisDir=$(cd $thisDir; pwd)    #如果返回相对路径也行,那就不用这句了。
    fi
    echo $thisDir
    
  • 自己做的which_函数是4倍速于SOME_VAR=`type -p some_exe_name`
    #!/bin/bash
    
    IFS=: pathList=($PATH); unset IFS
    function which_ { x=""; for d in "${pathList[@]}"; do [[ -x $d/$1 ]] && { x=$d/$1; return 0; }; done; return 1; }
    
    echo 'x=`type -p aapt`'
    time {
        for i in {1..5000}; do
            x=`type -p aapt`
        done
    }
    echo $x
    
    echo which_
    time {
        for i in {1..5000}; do
            which_ aapt
        done
    }
    echo $x
    
    结果
    x=`type -p aapt`
    
    real    0m3.684s
    user    0m1.086s
    sys 0m2.382s
    /Users/q/Library/Android/sdk/build-tools/24.0.1/aapt
    which_
    
    real    0m0.788s
    user    0m0.686s
    sys 0m0.095s
    /Users/q/Library/Android/sdk/build-tools/24.0.1/aapt
    
  • 内部命令type -p是50倍速于which命令! 和which一样也有-a选项,列出所有在$PATH里的目标exe。
  • 文字分隔符IFS,可以用来分隔$PATH里的冒号
    IFS=":"; foo="12:34::78"; echo $foo
    
    结果是12 34 78
    IFS=":"; for p in $PATH; do echo $p; done; unset IFS
    
    结果是一个个的路径。
    可以生成一个数组
    IFS=":"; PATH_LIST=($PATH); unset IFS
    echo ${#PATH_LIST[@]}
    
    结果就是路径个数。
  • 变量文字列替换
    例如${X/$Y}把$X里含有$Y的部分删除。
    • 变量替换有好几种方式,吝啬(/,#,%)或贪婪(//,##,%%),头(#)或尾(%)。
    • 即使$X,$Y里含有空格和/都可以
      X=/abc/OK
      Y=/abc/
      echo ${X/$Y} 
      
      结果是OK。
    • 但是通配符会被针对$X范围内自动展开,要小心。
      X=/abc/OK
      Y="*/"
      echo ${X/$Y} 
      
      结果是OK。
      可笑的是,UNIX/Linux下的文件名称里是可以包含这些通配符的, 一旦真有了,那就乱套了。Windows那边是不允许有的。
  • 数组
    • array用法里的下标*@是不一样的
      如同"$*"会整体一个文字列而"$@"会按个数展开成若干文字列甚至含有空格的都不会乱,"${array[*]}""${array[@]}"也是类似的区别。
      array=("a b" c) 是含有2个元素的数组,${#array[@]}是2。
      for item in "${array[@]}"; do echo $item; done 会显示正常,"a b"和c。
      但是for item in "${array[*]}"; do echo $item; done 就成了"a bc"
      for item in ${array[*]}; do echo $item; done 则成了三个了a和b和c
    • 数组的展开做得很理想"${array[@]}"当一个元素都没有时,他不会产生任何输出,连空文字列都没有。
    • 数组添加array+=(newItem),合并array+=("${newArray[@]}")
  • bash里的here document功能强大
    • <<<文字列表示输入文字列,如果有空格什么的得扩起来。 例如bash <<<"echo hi"结果为hi。
    • <<\EOF里的那个\表示here document里的内容不要被展开,即无视$,*等东西。
    • <<-EOF里的那个-支持TAB锁进。
  • 命令行解析模版:
    function _agcc-dbg { echo "$@" >&2; }
    
    function main {
        local ARCH APIL STL FORCE  #如果不在function里,那就换成ARCH=""之类的。
    
        _agcc-dbg "analysing args {"
        while [[ $# -gt 0 ]]; do
            _agcc-dbg "  \"$1\""
            case $1 in
                --arch    ) case $2 in [!-]*|"") ARCH=$2; _agcc-dbg "  =\"$2\""; shift;; esac;;
                --api     ) case $2 in [!-]*|"") APIL=$2; _agcc-dbg "  =\"$2\""; shift;; esac;;
                --stl     ) case $2 in [!-]*|"") STL=$2;  _agcc-dbg "  =\"$2\""; shift;; esac;;
                --force   ) FORCE=$1 ;;
                arm|arm64|x86|x86_64|mips|mips64) ARCH=$1 ;;
                min|max|0 ) APIL=$1 ;;
                gnustl|libc++|stlport ) STL=$1 ;;
                -c|-C     ) break ;; #stop parse due to the later args is command and its args
                -*        ) : ;; #skip unrelated option keyword
                *         ) [[ $1 -gt 0 ]] && APIL=$1 ;;
            esac
            shift
        done
        _agcc-dbg "}"
    
        #剩下的所有参数就是想要执行的外部命令和参数了,用"$@"来代表就行了。
        exec "$@"
    }
    
  • :表示什么都不做的命令。
  • 检测是否整数的判断[[ $X != *[!0-9]* ]]
  • [[ ]]里也可以暧昧匹配,和case语句里的匹配特性和一样,但是不能写多个 a|b|c这样的不行。
  • [[ $X = +([0-9]之类的扩展匹配 ]]时需要extglob的shopt被设定上才能用。默认的非交互的bash里是用不了的。
    bash的pattern match有些功能和shopt有关。看http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_04_03.html 用shopt |grep on看看就知道了。
    checkwinsize    on  #非交互bash里没有
    cmdhist         on
    expand_aliases  on  #非交互bash里没有
    extglob         on  #非交互bash里没有,所以导致一些 $V == +([0-9]) 之类的整数匹配语法错误
    extquote        on
    force_fignore   on
    interactive_comments    on
    login_shell     on
    progcomp        on
    promptvars      on
    sourcepath      on
    
  • [[ ]]替代[ ]测试,避免种种意外。
    • 变量里的空格不会被乱分割,用不着加引号了。
    • 能够模糊匹配。
      变量里的通配符不会按照文件名展开,而是通配符是在比较目标变量内展开。
      X=2ab3
      [[ $X == [1-3]*3 ]] && echo OK
      
      结果OK。
    • [[ $X ]][[ ! $X ]]可以直接判断X变量有没有值。
    • [[ $X == 1 && $Y == 2 || $Z ]]之类的&&||写法是符合想象的。
    这些换成[ ]就不行了,$X会编程当前目录下的所有文件名,语法都会出错。
  • 变量default值:${1-arm}表示如果$1为空那么值为arm。
    但是不能写成${1:=arm},这是试图对$1进行默认赋值。${X:=arm}是可以的。
  • X=值很安全, 右边不用加双引号也OK,即使值里含有空格和通配符,也不会被自动展开。
    不需要啰嗦的加上双引号:X="...",这种写法在里面含有双引号等时多重escape累死了。
    测试: X=*,然后echo $X肯定会显示出一堆当前目录下的东西,被展开了。 但是X内部实际的确只保存了*一个字符,可以用echo ${#X}来证实。 如果就想打印一个星号出来,那就echo "$X"
  • bash的job判断很糟糕,or 和and并不如想象,要小心。
    ls non-exist-file || echo 1 && echo 2  
    
    结果会是2,而不是常识的1+2。
  • { }之中的最后一个命令后面一定要加个;,{之后一定要有一个空格。真变态。
    最短的函数定义是function a { echo hi;}一个空格都不能省略。其实最后一个;}之间最好加个空格。

没有评论:

发表评论