so true

          心懷未來(lái),開創(chuàng)未來(lái)!
          隨筆 - 160, 文章 - 0, 評(píng)論 - 40, 引用 - 0
          數(shù)據(jù)加載中……

          Shell編程積累

          ls -lr反向排序結(jié)果
          ==============================
          ls ${PATH//:/\ } | grep <searchword>
          ==============================
          echo $RANDOM
          ==============================
          [[ $# -ne 3 ]] && {echo "Usage: ${0##.*/} <param>"; exit 1}
          ==============================
          awk '/'$VAR'/{print $0}' file
          ==============================
          #得到絕對(duì)路徑
          bin=`dirname "$0"`
          bin=`cd "$bin"; pwd`
          ==============================
          echo $[23*34] <==> echo $((23*34))
          ==============================
          echo $[7#23] #7是底,23是在這個(gè)底上的數(shù)字,因此最終的結(jié)果為17
          ==============================
          echo ${!P*} #列出當(dāng)前變量中,所有以P開頭的變量的名稱
          ==============================
          shell的處理過(guò)程:alias la='ls -A'->{1..100}->~admin->$PATH->$(ls)->$((23*34))->rm a*o?[a-zA-Z]*
          ==============================
          #下面這種處理方式等同于for f in "$(ls)",但是可以應(yīng)對(duì)文件名中有空格的情況,而for f in "$(ls)"則不行。
          [ $# -eq 0 ] && ls | while read f
          do
              ll-2.sh "$f"
          done
          ==============================
          可以在一行內(nèi)定義一個(gè)函數(shù),寫在shell腳本里可以,還可以直接寫在命令行上,比如:
          root@pc1:~#testfunc(){ echo "$# parameters;echo "$@";}
          而且,如果你在命令行直接這么定義的,你想查看該函數(shù)的內(nèi)容時(shí),可以用type testfunc,你會(huì)看到:
          testfunc is a function
          testfunc ()
          {
              echo "$# params";
              IFS=;
              echo "$*"
          }
          需要特別注意的是:{和echo之間的那個(gè)空格,其他地方有沒(méi)有空格無(wú)所謂,但是這個(gè)空格如果沒(méi)有的話,是必然會(huì)出錯(cuò)的;還有個(gè)地方是}前面那條命令的;必須有,否則也會(huì)有問(wèn)題;最后,還需要提醒的是,如果你不是寫在一行內(nèi),那么}前的;不必有,{后的空格也不必有。
          ===============================
          對(duì)$*和$@作些說(shuō)明:
          其實(shí)要分四種情況:$*, $@, "$*", "$@"
          對(duì)于前兩種情況,都等同于$1 $2 $3 ...,如果某個(gè)參數(shù)內(nèi)有了空格或者換行(不要覺(jué)得不可能,參數(shù)中是可以有換行符的,下面就有例子),比如a "b c" d,那么替換后的結(jié)果是a b c d,看上去就是四個(gè)參數(shù)了;
          對(duì)于"$@",等同于"$1" "$2" "$3" ...,這下就不必?fù)?dān)心參數(shù)中有空格或者換行符號(hào)了;
          對(duì)于"$*",還要受到IFS的影響,其實(shí)是IFS中的第一個(gè)字符的影響,假定IFS中第一個(gè)字符是|的話,那么替換后的結(jié)果是"$1|$2|$3...";這里對(duì)IFS還得作下說(shuō)明,如果 IFS 為空,則沒(méi)有間隔空格。IFS 的默認(rèn)值是空白、制表符和換行符。如果沒(méi)有設(shè)置 IFS,則使用空白作為分隔符(僅對(duì)默認(rèn) IFS 而言),這里特別提到的是,如果你想把參數(shù)都串接起來(lái),那么必須得顯示設(shè)置IFS=''或者IFS=""或者IFS=即可。
          下面給出一個(gè)驗(yàn)證上述描述的超級(jí)牛b的例子:
          [ian@pinguino ~]$ type testfunc2
          testfunc2 is a function
          testfunc2 ()
          {
              echo "$# parameters";
              echo Using '$*';
              for p in $*;
              do
                  echo "[$p]";
              done;
              echo Using '"$*"';
              for p in "$*";
              do
                  echo "[$p]";
              done;
              echo Using '$@';
              for p in $@;
              do
                  echo "[$p]";
              done;
              echo Using '"$@"';
              for p in "$@";
              do
                  echo "[$p]";
              done
          }

          [ian@pinguino ~]$ IFS="|${IFS}" testfunc2 abc "a bc" "1 2
          > 3"
          3 parameters
          Using $*
          [abc]
          [a]
          [bc]
          [1]
          [2]
          [3]
          Using "$*"
          [abc|a bc|1 2
          3]
          Using $@
          [abc]
          [a]
          [bc]
          [1]
          [2]
          [3]
          Using "$@"
          [abc]
          [a bc]
          [1 2
          3]
          看到參數(shù)中使用換行符號(hào)了吧,哈哈。
          ===============================
          函數(shù)返回值return 0~255必須為一個(gè)整數(shù),而且范圍只能是0~255,如果大于255,那么會(huì)返回$((x%256)),而且不能返回負(fù)數(shù),但是允許你寫負(fù)數(shù),負(fù)數(shù)會(huì)被強(qiáng)制轉(zhuǎn)換成整數(shù)。
          ===============================
          #!/bin/sh會(huì)提供系統(tǒng)默認(rèn)的shell解釋器,會(huì)帶來(lái)一些想不到的異常情況,建議都使用#!/bin/bash,在bash中定義函數(shù)時(shí)function關(guān)鍵字是可選的。
          ===============================
          #!/bin/bash

          showopts () {
            while getopts ":pq:" optname
              do
                case "$optname" in
                  "p")
                    echo "Option $optname is specified"
                    ;;
                  "q")
                    echo "Option $optname has value $OPTARG"
                    ;;
                  "?")
                    echo "Unknown option $OPTARG"
                    ;;
                  ":")
                    echo "No argument value for option $OPTARG"
                    ;;
                  *)
                  # Should not occur
                    echo "Unknown error while processing options"
                    ;;
                esac
              done
            return $OPTIND
          }

          showargs () {
            for p in "$@"
              do
                echo "[$p]"
              done
          }

          optinfo=$(showopts "$@")
          argstart=$?
          arginfo=$(showargs "${@:$argstart}")
          echo "Arguments are:"
          echo "$arginfo"
          echo "Options are:"
          echo "$optinfo"

           getopts 命令使用了兩個(gè)預(yù)先確定的變量。OPTIND 變量開始被設(shè)為 1。之后它包含待處理的下一個(gè)參數(shù)的索引。如果找到一個(gè)選項(xiàng),則 getopts 命令返回 true,因此常見的選項(xiàng)處理范例使用帶 case 語(yǔ)句的 while 循環(huán),本例中就是如此。getopts 的第一個(gè)參數(shù)是一列要識(shí)別的選項(xiàng)字母,在本例中是 p 和 r。選項(xiàng)字母后的冒號(hào) (:) 表示該選項(xiàng)需要一個(gè)值;例如,-f 選項(xiàng)可能用于表示文件名,tar 命令中就是如此。此例中的前導(dǎo)冒號(hào)告訴 getopts 保持靜默(silent)并抑制正常的錯(cuò)誤消息,因?yàn)榇四_本將提供它自己的錯(cuò)誤處理。

          此例中的第二個(gè)參數(shù) optname 是一個(gè)變量名,該變量將接收找到選項(xiàng)的名稱。如果預(yù)期某個(gè)選項(xiàng)應(yīng)該擁有一個(gè)值,而且目前存在該值,則會(huì)把該值放入 OPTARG 變量中。在靜默模式下,可能出現(xiàn)以下兩種錯(cuò)誤情況。

             1. 如果發(fā)現(xiàn)不能識(shí)別的選項(xiàng),則 optname 將包含一個(gè) ? 而 OPTARG 將包含該未知選項(xiàng)。
             2. 如果發(fā)現(xiàn)一個(gè)選項(xiàng)需要值,但是找不到這個(gè)值,則 optname 將包含一個(gè) : 而 OPTARG 將包含丟失參數(shù)的選項(xiàng)的名稱。

          如果不是在靜默模式,則這些錯(cuò)誤將導(dǎo)致一條診斷錯(cuò)誤消息而 OPTARG 不會(huì)被設(shè)置。腳本可能在 optname 中使用 ? 或 : 值來(lái)檢測(cè)錯(cuò)誤(也可能處理錯(cuò)誤)。
          此外,getopts ":pq:" optname后面還可以配置上可選的getopts ":pq:" optname "$@"

          [ian@pinguino ~]$ ./testargs.sh -p -q qoptval abc "def ghi"
          Arguments are:
          [abc]
          [def ghi]
          Options are:
          Option p is specified
          Option q has value qoptval
          [ian@pinguino ~]$ ./testargs.sh -q qoptval -p -r abc "def ghi"
          Arguments are:
          [abc]
          [def ghi]
          Options are:
          Option q has value qoptval
          Option p is specified
          Unknown option r
          [ian@pinguino ~]$ ./testargs.sh "def ghi"
          Arguments are:
          [def ghi]
          Options are:
          ===============================
          父shell設(shè)置的alias是不能傳到子shell中的,但是在shell script中依然可以使用alias,不過(guò)要自己手動(dòng)設(shè)定了,然后還要注意在腳本中顯示開啟alias的使用,即 shopt -s expand_aliases;有時(shí)候會(huì)希望alias能帶參數(shù)就好了,其實(shí)可以用函數(shù)代替即可,在.bashrc中增加:cpdev1(){ [ $# -eq 1 ] && scp $1 yanbin@dev1.asc.cnz.alimama.com: ; },具體啥意思,自己去琢磨吧。
          ===============================
          while [ "${i:-1}" -lt 30 ];do a="-"${a:="-"};((i++));done && echo $a
          知識(shí)點(diǎn):while的條件,不新建變量還能正常使用,while是個(gè)整體,可以有返回值,可以重定向其輸出
          ===============================
          如果想實(shí)現(xiàn)對(duì)shell變量的二次解析,那么可以用eval完成,例如:eval cd "\"\$$#\""
          ===============================
          for var in "$@"; <==> for var;
          ===============================
          在shell總一般extglob都是開著的,你可以用shopt查看一下,如果沒(méi)開你可以用shopt -s extglob設(shè)置一下,在這個(gè)選項(xiàng)打開的狀態(tài)下,我們可以使用一些擴(kuò)展的glob選項(xiàng),在shell中是這么使用的:
          ?(pattern-list)Matches zero or one occurrence of the given patterns
          *(pattern-list)Matches zero or more occurrences of the given patterns
          +(pattern-list)Matches one or more occurrences of the given patterns
          @(pattern-list)Matches one of the given patterns
          !(pattern-list)Matches anything except one of the given patterns
          ===============================
          find . -regextype posix-extended -regex '.*\.(h|cpp)'
          ===============================
          findc(){ [ $# -eq 1 ] && compgen -c | grep --color=auto -i $1; }
          ===============================
          在.bashrc中添加:export PS1='\[\e[1;32;40m\]\u@\h:\w\$ '達(dá)到的效果:綠色高亮顯示,且折行時(shí)不會(huì)在同一行,如果去掉\[和\]則會(huì)在同一行折行。
          本人目前在用的一個(gè)PS1是:export PS1='\[\e[0;35m\]>>>>[\[\e[0;33m\]\t\[\e[0;35m\]][\[\e[0;31m\]\u@\h\[\e[0;35m\]:\[\e[0;33m\]\[\e[0;34m\]\w\[\e[0;35m\]]\n\[\e[1;$((31+3*!$?))m\]\$ \[\e[0;32m\]' ,這個(gè)PS1會(huì)換行,而且新行的開始部分($或者#)會(huì)顯示紅色高亮(上次的命令執(zhí)行失敗)或者藍(lán)色高亮(上次的命令執(zhí)行正常)。
          對(duì)于配色,可以參考:
             前景             背景              顏色
             ---------------------------------------
             30                40               黑色
             31                41               紅色
             32                42               綠色
             33                43               黃色
             34                44               藍(lán)色
             35                45               紫紅色
             36                46               青藍(lán)色
             37                47               白色

             代碼              意義
             -------------------------
             0                 OFF
             1                 高亮顯示
             4                 underline
             5                 閃爍
             7                 反白顯示
             8                 不可見
          此外,我在ubuntu上設(shè)置了上述PS1之后,當(dāng)我在終端里打開vi再關(guān)閉vi之后,發(fā)現(xiàn)命令行出現(xiàn)了亂碼,此時(shí)輸入reset即可恢復(fù)正常,此后再使用vi將不再出現(xiàn)亂碼,不過(guò)每次都這么搞一通實(shí)在是不爽,不知道在其他發(fā)行版上會(huì)不會(huì)有這個(gè)問(wèn)題。
          現(xiàn)在終于解決了這個(gè)問(wèn)題,有兩種方法,一種可以在虛擬終端的title設(shè)置中給原先的終端二字前后分別添加一個(gè)英文空格即可;另一種方法是重新設(shè)置PS1為\[\e]0;\u@\h: \w\a\]\[\e[1;31m\]\u\[\e[1;32m\]@\h\[\e[1;31m\]:\[\e[1;32m\]\w\[\e[1;31m\]\$ \[\e[0m\]
          ==========================
          echo "export LS_COLORS='$LS_COLORS'" >> .bashrc之后打開.bashrc,將di=01;34改為di=01;33即可使得目錄以黃色顯示
          ==========================
          shell腳本調(diào)試,可以先設(shè)置
          export PS4='+{$LINENO:${FUNCNAME[0]}} ',再用sh -x來(lái)執(zhí)行腳本,即可在+號(hào)后面打印行號(hào)和函數(shù)名;使用trap  <cmd> DEBUG/ERR/EXIST可以分別在執(zhí)行每一行前/某個(gè)命令返回值不為0時(shí)/函數(shù)或程序退出時(shí)執(zhí)行cmd;可以在腳本里面的腳本段落前后分別用sh -x和sh +x來(lái)使得只有該段落的內(nèi)容執(zhí)行sh -x;可以通過(guò)變量DEBUG作為開關(guān)來(lái)統(tǒng)一控制調(diào)試與否,當(dāng)然需要有個(gè)函數(shù),比如也叫做DEBUG的話,那這個(gè)函數(shù)的定義大概為:DEBUG() { [ "$DEBUG" = "true" ] && $@ ; },然后你就可以肆無(wú)忌憚的DEBUG echo $a,$b或者在一段落前后分別加上DEBUG set -x和DEBUG set +x來(lái)進(jìn)行控制了;還可以使用類似于Gdb調(diào)試的bash調(diào)試工具bashdb,官網(wǎng)地址為:http://bashdb.sourceforge.net/;上述總結(jié)來(lái)自于http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html
          ============================
          export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "可以讓history顯示出執(zhí)行的時(shí)間
          ============================
          date -d "1970-01-01 0:0:0 UTC `date +%s` sec" +'%F %T' 可以將時(shí)間戳轉(zhuǎn)換為指定的時(shí)間表示形式
          用date -d @1313418365 +%F\ %T也可以實(shí)現(xiàn)將時(shí)間戳轉(zhuǎn)換為指定的時(shí)間格式
          ============================
          find . \( -path './rrds*' -o -path './bash*' \) -prune -o -maxdepth 3 -name '*m*' -printf '%f\n'
          該命令實(shí)現(xiàn)的功能是查找當(dāng)前目錄下,除了rrds*和bash*文件夾之外的名字中含有m字符的且深度不超過(guò)3層的, 將符合上述條件的目錄或文件名打印出來(lái), 不打印全部路徑, 只打印文件名或目錄名. 寫該命令時(shí)需要注意的幾點(diǎn):\(和-path之間以及\)之前都必須有空格;針對(duì)path或name定義它們需要符合的pattern時(shí)必須要./開頭或者*開頭,因?yàn)檫@里的匹配都是全匹配,而不是包含即可的意思. 對(duì)該命令的幾點(diǎn)解釋:find查找文件名或目錄名(其實(shí)目錄也是文件)符合pattern所有項(xiàng), 如果只是中間路徑中含有pattern, 并不能保證該目錄下的所有文件都能和pattern匹配,因?yàn)槠ヅ浒l(fā)生在整個(gè)路徑的最后,也就是文件名或目錄名上;prune本意為刪除或砍掉的意思,基本上可以理解為和print恰恰相反即可,它們都是action,find命令其實(shí)類似于一個(gè)復(fù)合的if語(yǔ)句, action前面的默認(rèn)連接是使用-a(即and的意思, 類似于我們常見的command1 && command2 || command3中的&&的作用), 當(dāng)然也可以顯示的指定-o(即or的意思, 類似于||的作用), 既然談到了and和or,那么自然就有not了,在find命令里,not是!,舉個(gè)例子就清楚了:find . ! -name '*.sh'會(huì)找到所有不是以.sh結(jié)尾的文件, 此外還有就是通過(guò)\(和\)能夠把一些復(fù)合起來(lái)的條件作為一個(gè)整體, 因此對(duì)于該條命令, 執(zhí)行過(guò)程是這樣的, if ( -path './rrds*' && -path './bash*' ); then -prune else { if (-maxdepth 3 && -name '*m*' ); then -printf '%f\n' fi } fi
          此外對(duì)于find命令, 最常用的幾個(gè)參數(shù)是 -type和-name,其實(shí)還有-iname(忽略大小寫的匹配), 還有-regex和-iregex,注意-name只是用基本的shell的pattern,即*,?,[]三種, 如果真想用正則來(lái)匹配,還是用-regex吧; 此外對(duì)于find最神奇的莫過(guò)于-exec或者-ok或者xargs了,這里給出兩個(gè)例子即可:
          find . -name '*my*' -exec rm '{}' \;   <==>  find . -name '*my*' | xargs rm
          find. -name '*my*' -exec cp '{}' ~ \;   <==> find . -name '*my*' | xargs -i cp '{}' ~
          對(duì)于rm這種單參數(shù)的命令, find找到的結(jié)果直接會(huì)塞給rm的; 對(duì)于cp這種雙參數(shù)的命令, 可以用'{}'這個(gè)東西來(lái)代表find找到的部分, 但對(duì)于xargs中需要注意使用-i參數(shù), 該參數(shù)其實(shí)和-I參數(shù)作用是一樣的,但是特別之處在于當(dāng)不指定replace-str時(shí), -i這個(gè)參數(shù)會(huì)把{}作為replace-str, 因此使用-i就用經(jīng)管道|過(guò)來(lái)的標(biāo)準(zhǔn)輸入去替換{},因此cp命令就生效了.
          ================
          注釋大段shell腳本的方法:
          :||:<<\{{
          ...
          {{
          這里對(duì)here document也多少作些討論:該文檔其實(shí)是個(gè)臨時(shí)文檔,被生成到/tmp目錄下,你用命令bash -c 'lsof -a -p $$ -d0' <<EOF
          EOF就可以很清楚的看到這一點(diǎn);該臨時(shí)文檔為交互式shell腳本或者可執(zhí)行程序提供輸入,最典型的是為cat和ed這樣的命令feed輸入;最典型的應(yīng)用是在shell中通過(guò)cat<<來(lái)大段輸出文本, 還有一些比較巧妙的用法,比如為腳本中的函數(shù)塞入數(shù)據(jù)(注意不是為函數(shù)塞入?yún)?shù), 而是當(dāng)函數(shù)中有類似于read這樣的命令時(shí)通過(guò)here文檔來(lái)feed給它), 在比如為變量賦值variable=$(cat <<SETVAR
          This variable
          runs over multiple lines.
          SETVAR)
          再比如
          vi $TARGETFILE <<\EOF
          i
          This is line 1 of the example file.
          This is line 2 of the example file.
          ^[
          ZZ
          EOF
          here document自身所特有的幾點(diǎn)包括:
          1.用<<-代替<<可以使內(nèi)容中的leading tabs被忽略,對(duì)leading spaces無(wú)效
          2.用’HERE’, “HERE”, \HERE代替<<后面的HERE,可以使內(nèi)容中變量不被替換,諸如$HOME
          3.如果只希望輸入一行數(shù)據(jù),可以用<<<即可,例如cat <<<'hello world' (說(shuō)到這里, 還真覺(jué)得蠻有意思的,一個(gè)<表示0號(hào)標(biāo)準(zhǔn)輸入; 兩個(gè)<<表示here document的臨時(shí)性輸入;三個(gè)<<<表示單行的here document輸入)
          更多詳細(xì)例子,可以參考:http://tldp.org/LDP/abs/html/here-docs.html
          ===============
          對(duì)于cat這種可以接受標(biāo)準(zhǔn)輸出或者標(biāo)準(zhǔn)輸入流的程序來(lái)說(shuō), 可以通過(guò)一個(gè)-來(lái)承載送過(guò)來(lái)的流, 大致可分為通過(guò)管道|過(guò)來(lái)的標(biāo)準(zhǔn)輸出流和通過(guò)<送進(jìn)來(lái)的標(biāo)準(zhǔn)輸入流, 通過(guò)這樣一個(gè)例子就可以看出ls | cat - t.C - -   < t.sh    <<<'single here document'   <<{{
          multiple
          here
          document
          {{
          結(jié)果只是<<發(fā)揮作用了, 它們的優(yōu)先級(jí)別是: <<最高, <<<次之, < 排第三, | 排第四
          這樣就可以利用該特性為某t.C這樣的文檔添加頁(yè)眉和頁(yè)腳了:
          頁(yè)眉: cat - t.C <<<'header'
          頁(yè)腳: cat t.C - <<<'footer'
          =======================================
          在腳本中,對(duì)于那些腳本依賴的變量,如果沒(méi)定義的話就不希望繼續(xù)執(zhí)行腳本,那么可以使用:
          set -u
          使用了那些你關(guān)注的變量的代碼段
          set +u
          這樣就可以保證腳本在執(zhí)行過(guò)程中沒(méi)有發(fā)現(xiàn)這些變量時(shí)就自動(dòng)報(bào)錯(cuò)退出
          ==========================================
          按照字符串的長(zhǎng)度進(jìn)行排序:
          #! /bin/sh
          awk 'BEGIN { FS=RS } { print length, $0}' $1 |
          sort -k 1n,1 |
          sed 's/^[0-9][0-9]* //' | tee $1 >/de
          ==========================================
          有關(guān)shell腳本的第一行,這一行可不是隨便寫的,是有講究的,首先要從這一行中提取出的是一個(gè)可執(zhí)行程序的全路徑,比需要全路徑,在#!后面可以跟空格;其次,會(huì)為這個(gè)命令要么附加一個(gè)選項(xiàng),要么附加一個(gè)參數(shù),記住,有且只能有一個(gè),而且是option和arg二選一,如果有多個(gè)參數(shù),其實(shí)可以合并,舉個(gè)例子,比如ls,可以把-a和-f合并為-af,因此在這條規(guī)則下:#!/usr/bin/env sed -f就不能work了,因?yàn)閟ed本身就已經(jīng)是env的一個(gè)arg了,其實(shí)在這種情況下,系統(tǒng)會(huì)認(rèn)為'sed -f'整體為一個(gè)arg,系統(tǒng)判斷你指定的究竟是一個(gè)arg還是一個(gè)option的依據(jù)就是:是否以-開頭。
          =============================================
          cd ~1 與cd ~-1代表cd到dirs命令列表中左數(shù)/右數(shù)第1個(gè)目錄(從0開始計(jì)數(shù)); 注意這種用法并不會(huì)與pushd沖突, 因?yàn)閜ushd會(huì)對(duì)dirs列表進(jìn)行rotate(一定要好好理解這個(gè)單詞的含義, 它相當(dāng)于把dirs的結(jié)果看作是一個(gè)round robin的環(huán)狀結(jié)構(gòu), pushd要更改的就是從這個(gè)環(huán)上的哪個(gè)地方找到隊(duì)首,然后順時(shí)針就可以找到其他的元素了)操作, 舉個(gè)例子: 假如dirs的結(jié)果為: ~    /usr     /etc    /tmp四個(gè)目錄,那么cd ~1后的結(jié)果是/usr /usr /etc /tmp; cd ~-1的結(jié)果是/etc /usr /etc /tmp; pushd +1的結(jié)果為: /usr /etc /tmp ~; pushd -1的結(jié)果為: /etc /tmp ~ /usr
          =============================================
          查看bash內(nèi)建命令的help內(nèi)容,可以用help ulimit,不過(guò)這樣得到的信息不全面,全面的信息可以在man builtins里面找到;
          ========================================
          echo -e 'a\tb'和echo $'a\tb'的結(jié)果一樣,$'string'形式的字符串會(huì)被特殊處理,字符串會(huì)被展開,并像c語(yǔ)言那樣將反斜杠及緊跟的字符進(jìn)行替換,擴(kuò)展后的結(jié)果將被單引號(hào)包裹,就好像美元符號(hào)一直就不存在一樣,例如$'\n' <=> $'\012' <=> $'\x0a' <=> $'\x0A';$"string"將會(huì)使得字符串被翻譯成符合當(dāng)前l(fā)ocale的語(yǔ)言,如果當(dāng)前l(fā)ocale是 C 或者 POSIX,美元符號(hào)會(huì)被忽略,如果字符串被翻譯并替換,替換后的字符串仍被雙引號(hào)包裹。
          =========================================
          command > out.log 2>&1  可以被command &> out.log代替,二者意義一樣,注意&和>之間不能有空格,否則&就會(huì)被認(rèn)為是要把命令放到后臺(tái)執(zhí)行,有關(guān)bash一些新增的用法可以參見:http://zh.wikipedia.org/wiki/Bash
          ============================================
          grep -e a -e b file能夠匹配到file中的行內(nèi)含有a或者b的行
          ============================================
          chvt可以替代Ctrl+Alt+Fn功能鍵;openvt可以在開滿了6個(gè)tty之后再新開tty
          ============================================
          在man或者less搜索的時(shí)候,用-i來(lái)切換大小寫是否敏感(當(dāng)最左下角顯示為:時(shí)進(jìn)行)
          =============================================
          export -f可以export函數(shù);unset -f/export -f -n可以取消函數(shù)定義;declare -f可以看到當(dāng)前bash里面所有的函數(shù)
          ==========================================================
          函數(shù)中可以再定義函數(shù),但卻不是嵌在函數(shù)里面的子函數(shù),外部依然可以隨意調(diào)用該函數(shù),也就是說(shuō)和放在外面一樣,只不過(guò)看上去能感覺(jué)到:這個(gè)定義在函數(shù)A里面的函數(shù)B,應(yīng)該是專注于為函數(shù)A服務(wù)的。
          ==========================================================
          在bash里fork子shell
          #!/bin/bash
          if [ "$PROC_PID" != "$PPID" ]; then
              export PROC_PID=$$
              var="mype"
              echo "initial $var"
              declare -r var
              export var
              $0 & # child process
          else
              echo "before $var"
              var="netty5"
              echo "after $var"
          fi
          =================================================================================================
          在腳本中$@代表當(dāng)前shell腳本執(zhí)行時(shí)的所有參數(shù)或者代表傳遞到當(dāng)前函數(shù)內(nèi)部的所有參數(shù),通過(guò)shift命令,可以改變$@的內(nèi)容,但是$0始終代表當(dāng)前進(jìn)程的命令名字,而不是函數(shù)名字
          =================================================================================================
          getopts函數(shù)(bash builtin)的功能還是很弱的,比如要解析pq:,那么必須得這么寫命令才行:<your_command> -p -q test arg1 arg2 ...;參數(shù)之間不能斷開,而且還得必須緊跟在command的后面,當(dāng)然,getopts是解析$@,如果你用shift處理后的$@能滿足$1開始就是option,那么也行。
          ======================================================
          在linux下作各種進(jìn)制的轉(zhuǎn)化,有兩種方法比較好用:
          printf "%x\n" 2342  #該方法和c語(yǔ)言中的printf沒(méi)什么區(qū)別
          echo  'obase=16; 2342' | bc  #該方法很強(qiáng)大, 可以轉(zhuǎn)化為任意進(jìn)制, 比如printf不能做到的2進(jìn)制轉(zhuǎn)化
          ============================================================
          echo $content | while read line;
          do
          ...
          done
          上面這段代碼有3個(gè)問(wèn)題:
          1. echo時(shí)應(yīng)該給$content添加雙引號(hào),否則content中的空格換行tab之類的東西就都丟了;
          2. 用管道會(huì)導(dǎo)致while是在一個(gè)新的子shell里, 導(dǎo)致在while中用的變量其實(shí)是新定義的, 而不是context中的;
          3. read會(huì)忽略前導(dǎo)空格, 因?yàn)閞ead會(huì)使用IFS的第一個(gè)字符作為分割符號(hào), 把當(dāng)前l(fā)ine分割為若干個(gè)word, 你用read word1 word2 word3會(huì)讀取到各個(gè)單詞, 默認(rèn)IFS的第一個(gè)字符是空格, 因此假定當(dāng)前l(fā)ine為'  a'的話, 那么會(huì)被切割為3部分(因?yàn)橛?個(gè)空格), 最后組裝的時(shí)候,  會(huì)忽略掉空單詞, 因此最后就只剩下a了.

          正確的做法是:
          IFS=
          while read line;
          do
          ...
          done < <(echo "$content")
          這里提一下<()和>()的用法, 這兩個(gè)東西都是命名管道(但這里是沒(méi)有名字的, 實(shí)則它們是借助于某一個(gè)fd來(lái)完成的), 相當(dāng)于你先mkfifo了一個(gè)命令管道xxx, 然后你echo "$content" > xxx, 同時(shí)你的read函數(shù)已經(jīng)在xxx的另一端候著了while read line; do ... done < xxx. 用<()和>()省略了建立xxx的過(guò)程.
          ==============================================================================
          tr 'a-z' 'A-Z' < t1.txt > t1.txt像這種語(yǔ)句,都會(huì)把t1.txt清空,其實(shí)是bash在這里做了手腳,我們都知道在bash中l(wèi)s *時(shí),其實(shí)bash會(huì)先把*擴(kuò)展為當(dāng)前所有的文件,然后再讓ls去顯示,這里也差不多,bash發(fā)現(xiàn)>時(shí),會(huì)先把該文件清空,然后再交給tr去處理,所以tr啥都不用做就完成了,而此時(shí)t1.txt就變成空文件了,要想避免這種情況發(fā)生,可以tr 'a-z' 'A-Z' < t1.txt >> t1.txt,但這樣會(huì)保留t1.txt之前的內(nèi)容,現(xiàn)在借助于<>,搞出個(gè)簡(jiǎn)單的方法:tr 'a-z' 'A-Z' < t1.txt 1<> t1.txt,這里1<>代表通過(guò)fd為1的文件進(jìn)行read and write,這樣就繞過(guò)了bash的提前解析,因此t1.txt中的內(nèi)容也被成功替換掉了。但要注意的是,同時(shí)從一個(gè)文件既讀取又寫入,而且是以管道這種流的形式來(lái)搞(正因?yàn)槭橇鞯男问剑圩x一點(diǎn)寫一點(diǎn)],所以就不會(huì)全部讀到內(nèi)存中,然后再處理,再把處理后的結(jié)果從內(nèi)存中dump到輸出文件),所以這種只對(duì)那種本位替換有效果,例如tr或者sed這種,其實(shí)說(shuō)到這里,主要是為了引出<>這個(gè)符號(hào)的用法,并讓大家知道bash會(huì)先把>給解析了,其他的都不重要。
          strace是個(gè)太好太好的命令,例如strace sed -i 's/a/A/g' t1.cpp即可看到sed是使用了臨時(shí)文件進(jìn)行替換的。
          ================================================
          #!/bin/bash

          mput() {
              eval "__map_$1"__"$2"='$3'
          }

          mget() {
              eval echo '${__map_'"$1__$2"'}'
          }

          mgetall() {
              for i in $(eval echo '${!__map_'"$1"'__*}'); do
                  echo -n "${i#__map_$1__*}:"
                  eval echo '${'"$i"'}';
              done
          }

          mput capitals France Paris
          mput capitals Spain Madrid

          echo "$(mget capitals France)"
          mgetall capitals
          =================================================
          func () {
              local unset_x=false
              [[ "$(echo $-)" =~ x ]] || unset_x=true
              [ "false" == "$unset_x" ] && set +x
              #procedure here
              [ "false" == "$unset_x" ] && set -x
          }
          =================================================
          ulimit -c unlimited
          export PS4='+{$LINENO($(date +"%F %T")):${FUNCNAME[0]}} '
          exec 1>${0%.*}.log
          exec 2>${0%.*}.err
          mkdir -p .tmp
          export TMPDIR=.tmp
          set -x
          ====================================================
          __created_tmp_files__=""
          delete_tmp_files() {
              if [ -n "$__created_tmp_files__" ]; then
                  rm -f $__created_tmp_files__
              fi
          }

          trap delete_tmp_files EXIT

          create_tmp_file() {
              local unset_x=false
              [[ "$(echo $-)" =~ x ]] || unset_x=true
              [ "false" == "$unset_x" ] && set +x
              if [ -z "$(eval "echo $"$1)" ]; then
                  eval "$1=.tmp/.__${0##*/}_${LINENO}_$1_${RANDOM}__.tmp"
                  rm -f $(eval "echo $"$1)
                  __created_tmp_files__="$__created_tmp_files__ $(eval "echo $"$1)"
              fi
              [ "false" == "$unset_x" ] && set -x
          }
          ===================================================
          awk '
          {
              if (NF>5 || ("noresource" != $NF && "zero" != $NF && "part" != $NF && "all" != $NF)) {
                  print $0 > "/dev/stderr"
              } else if ($4 ~ /^http/) {
                  print $1, (4==$2 ? "y" : "n"), $4, ($5=="all" ? "y" : "n")
              } else {
                  print $1, (4==$2 ? "y" : "n"), "none", ($4=="all" ? "y" : "n")
              }
          }' $2 | awk '
          BEGIN {
              pv=0;
          }
          {
              if (FILENAME==ARGV[1]) {
                  sitepv[$1]=++pv;
              } else {
                  r[sitepv[$1]]=$0;
              }
          }
          END {
              for (i in r) {
                  print i, r[i];
              }
          }' $1 - | sort -n -k 1 | awk '
          {
              for (i=2; i<NF; ++i) {
                  printf "%s ", $i
              }
              printf "%s\n", $NF
          }'
          ==========================================
          awk -v max_qps=$max_qps -v max_qps_time="$max_qps_time" -v bad_request_num=$bad_request_num '
          BEGIN {
              total_request_num=0;
              total_sum=0;
              max_value=0;
              max_value_time=0;
              min_value=0;
              min_value_time=0;
              cur_value=0;
              level8_threshold=(0 != l8 ? l8 : 3000);
              level7_threshold=(0 != l7 ? l7 : 1500);
              level6_threshold=(0 != l6 ? l6 : 1000);
              level5_threshold=(0 != l5 ? l5 : 500);
              level4_threshold=(0 != l4 ? l4 : 200);
              level3_threshold=(0 != l3 ? l3 : 100);
              level2_threshold=(0 != l2 ? l2 : 50);
              level1_threshold=(0 != l1 ? l1 : 30);
              gt_level8_threshold_num=0;
              gt_level7_threshold_num=0;
              gt_level6_threshold_num=0;
              gt_level5_threshold_num=0;
              gt_level4_threshold_num=0;
              gt_level3_threshold_num=0;
              gt_level2_threshold_num=0;
              gt_level1_threshold_num=0;
              le_level1_threshold_num=0;
          }
          1 == NR {
              cur_value=int(substr($14,4));
              max_value=min_value=cur_value;
              max_value_time=min_value_time=int(substr($1,4));
              ++total_request_num;
              total_sum += cur_value;
              next;
          }
          {
              cur_value=int(substr($14,4));
              if (cur_value > max_value) {
                  max_value=cur_value;
                  max_value_time=int(substr($1,4));
              } else if (cur_value < min_value) {
                  min_value=cur_value;
                  min_value_time=int(substr($1,4));
              }
              if (cur_value > level8_threshold) {
                  ++gt_level8_threshold_num;
              } else if (cur_value > level7_threshold) {
                  ++gt_level7_threshold_num;
              } else if (cur_value > level6_threshold) {
                  ++gt_level6_threshold_num;
              } else if (cur_value > level5_threshold) {
                  ++gt_level5_threshold_num;
              } else if (cur_value > level4_threshold) {
                  ++gt_level4_threshold_num;
              } else if (cur_value > level3_threshold) {
                  ++gt_level3_threshold_num;
              } else if (cur_value > level2_threshold) {
                  ++gt_level2_threshold_num;
              } else if (cur_value > level1_threshold) {
                  ++gt_level1_threshold_num;
              } else {
                  ++le_level1_threshold_num;
              }
              ++total_request_num;
              total_sum += cur_value;
          }
          END {
              gt_level7_threshold_num += gt_level8_threshold_num;
              gt_level6_threshold_num += gt_level7_threshold_num;
              gt_level5_threshold_num += gt_level6_threshold_num;
              gt_level4_threshold_num += gt_level5_threshold_num;
              gt_level3_threshold_num += gt_level4_threshold_num;
              gt_level2_threshold_num += gt_level3_threshold_num;
              gt_level1_threshold_num += gt_level2_threshold_num;
              print total_request_num;
              printf "%d(%.2f%%)\n", bad_request_num, bad_request_num * 100 / total_request_num;
              printf "%d(occur at: %s)\n", max_qps, max_qps_time;
              cmd="date -d @"max_value_time" +\"%F %T\""; cmd | getline max_value_time_str; close(cmd);
              printf "%d ms(occur at: %s)\n", max_value, max_value_time_str;
          #   printf "%d ms(occur at: %s)\n", min_value, strftime("%F %T", min_value_time);
              printf "%.2f ms\n", total_sum / total_request_num;
              print gt_level8_threshold_num;
              print gt_level7_threshold_num;
              print gt_level6_threshold_num;
              print gt_level5_threshold_num;
              print gt_level4_threshold_num;
              print gt_level3_threshold_num;
              print gt_level2_threshold_num;
              print gt_level1_threshold_num;
              print le_level1_threshold_num;
          }' $tmp_analyse_file
          =================================================================
          awk -v threshold=1000 -v debug=n '
          BEGIN {
          }
          {
              if (FILENAME==ARGV[1]) {
                  checked_css[$1]="y";
              } else if ("y" != checked_css[$1]) {
                  if (n[$1] < threshold) {
                      css[$1, n[$1]++] = $2;
                  } else {
                      k = int((rand() * threshold) % threshold);
                      if("y"==debug)print "[DEBUG]over threshold:", $1, "k=", k, "old item:", css[$1, k], "new item:", $2 > "/dev/stderr";
                      css[$1, k] = $2;
                  }
              } else {
                  if("y"==debug)print "[DEBUG]existed css:",$1 > "/dev/stderr";
              }
          }
          END {
              for (i in n) {
                  if (n[i] >= threshold) {
                      printf "%s\t", i;
                      for (j=0; j< threshold; ++j) {
                          printf "%s ", css[i, j];
                      }
                      printf "\n";
                  }
              }
          }' css_ok.txt -
          ========================================================================
          awk 'BEGIN {
              OFS=",";
              array[1,2]=3; array[2,3]=5; array[3,4]=8; 
              for (comb in array) {
                  split(comb,sep,SUBSEP);
                  print sep[1], sep[2], array[sep[1],sep[2]];
              }
          }'
          ========================================================================
          $ awk 'BEGIN{a["one"]=1;a["two"]=2;a["three"]; if("one2" in a)print a["one"]; for(i in a) print i, a[i]}'
          three 
          one 1
          two 2
          =========================================================================
          $ awk 'BEGIN{a=3;b=4; print a,b; print a, b; print a b;print a  b;}'
          3 4
          3 4
          34
          34
          ==========================================================================
          $ awk -F$'\t' 'BEGIN{OFS="\t"}{if($3 ~ /ECSHOP/)print $1,$2;}' t3.4 #否則會(huì)用空格來(lái)分隔fields,print $0不受OFS的影響
          ==========================================================================
          bash里面的大小寫轉(zhuǎn)換:
          $ a="abCdEF"
          $ echo ${a^} #process the first character
          AbCdEF
          $ echo ${a^^} #process all characters
          ABCDEF
          $ echo ${a,} #process the first character
          abCdEF
          $ echo ${a,,} #process all characters
          abcdef
          bash里的substr:
          $ a="abCdEF"
          $ echo ${a:1}
          bCdEF
          $ echo ${a:2:1}
          C
          bash里的數(shù)組操作:
          a=() #empty array
          a=(one two three) #three elements in array, with subscripts: 0 1 2
          ${#a[*]} #length of array
          ${a[*]} #all elements of array
          ${!a[*]} #all subscripts of array
          ${a[*]:2:1} #extract elements, offset index is 2, and length is 1
          ${a[*]:2} #extract elements, offset index is 2, and length is big enough to fetch all residue elements
          ${a[*]: -1} #extract last element, note that the space between : and -, by the way, ${@:3:2} will extract $3 and $4
          unset a[0] #destory the element with 0 subscript
          unset a[*] #destory array
          bash里的associated數(shù)組(即map):
          這個(gè)特性需要bash的版本>=4
          declare -A a
          $ a[one]=1
          $ a["two"]=2
          $ a[3]=three
          $ echo ${!a[*]}
          one two 3
          bash里數(shù)組元素是否存在的檢測(cè)方法:
          $ ${a[two]+:} false && echo ok || echo fail #${var+XX} will be XX if var is set, or empty; so ${a[two]+:} false will be ': false'
          ok
          $ [[ ${a[two]+set} ]] && echo ok || echo fail #${a[two]+set} will be 'set', so [[ set ]] is true;
          ok
          $ ${a[2]+:} false && echo ok || echo fail #${a[2]+:} false will be ' false'
          fail
          更多詳情可參見:http://mywiki.wooledge.org/BashFAQ/083
          bash里的復(fù)合命令(Compound Commands):
          (( expression ))用于檢測(cè)數(shù)值運(yùn)算(ARITHMETIC EVALUATION),例如
          $ (( 3+3 )) && echo ok || echo fail
          ok
          $ (( 3-3 )) && echo ok || echo fail
          fail
          也包括數(shù)值類的比較運(yùn)算符:< > >= !=等
          [[ expression ]]用于檢測(cè)字符串相關(guān)的表達(dá)式,例如 [[ hello ]], [[ -f test.dat ]], 也可用于帶BashPattern的匹配,例如[[ abc == ab* ]],或者正則字符串匹配[[ abc =~ ^ab ]]
          [ expression ]不屬于bash,因?yàn)閇其實(shí)是一個(gè)外部命令,和ls一樣
          bash里忽略大小寫的字符串比較:
          $ shopt -s nocasematch
          $ [[ aBc == abc ]] && echo ok || echo fail
          ok
          set -o會(huì)列出控制bash的所有選項(xiàng),其實(shí)還有一些其他的選項(xiàng)需要通過(guò)shopt來(lái)控制,shopt會(huì)列出全部的選項(xiàng)
          ===========================================================
          a2=99
          a1=88
          b=a2
          echo ${!a*} #list all shell variables that start with a
          a1 a2
          echo ${!b} #this express is equal to: eval 'echo $'$b
          99
          ================================================================
          !net #從歷史命令里搜索以net開頭的最近使用的一條命令
          !?net #從歷史命令里搜索包含net的最近使用的一條命令
          !:0 #上一條命令的command(類似于argv[0])
          !:^ #上一條命令的第一個(gè)參數(shù)
          !:$ #上一條命令的最后一個(gè)參數(shù)(效果等同于Alt+. ,不過(guò)Alt+.按n次,是會(huì)到倒數(shù)第n條命令里取得最后一個(gè)參數(shù)的)
          !:* #上一條命令的所有參數(shù)(效果等同于 !:^-$)
          !:2-4 #上一條命令的第2個(gè)到第4個(gè)參數(shù)
          !:2* #上一條命令從第2個(gè)參數(shù)開始(包括第2個(gè)參數(shù))以后的參數(shù)(效果等同于!:2-$)
          !:2- #上一條命令從第2個(gè)參數(shù)開始(包括第2個(gè)參數(shù))到 倒數(shù)第2個(gè)參數(shù),即省略掉最后一個(gè)參數(shù)
          ^ll^md5sum #從上一條里把第一次出現(xiàn)的ll替換為md5sum后重新執(zhí)行
          !!:gs/ll/md5sum #從上一條命令里把所有出現(xiàn)的ll都替換為md5sum(這里的語(yǔ)法類似于sed,/也可以用其他符號(hào)例如@來(lái)代替,g表示多次匹配)
          !! #重新執(zhí)行上一條命令(效果等同于Ctrl-p)

          posted on 2009-06-16 00:37 so true 閱讀(1750) 評(píng)論(0)  編輯  收藏 所屬分類: Linux

          主站蜘蛛池模板: 饶阳县| 桂阳县| 昭苏县| 资源县| 会昌县| 海丰县| 田阳县| 二连浩特市| 鄂托克前旗| 民丰县| 双桥区| 东港市| 靖州| 济源市| 海城市| 灵璧县| 和顺县| 巴塘县| 宁夏| 图木舒克市| 道真| 宁都县| 临泽县| 嫩江县| 犍为县| 资阳市| 昆明市| 黄陵县| 陕西省| 绥德县| 昌吉市| 三原县| 米脂县| 台北市| 清水河县| 驻马店市| 颍上县| 吕梁市| 农安县| 巧家县| 安达市|