SHELL(bash)腳本編程四:其餘擴展

在以前的文章中咱們講述了變量擴展數學擴展命令替換。本篇接着介紹shell中用到的其餘擴展。shell

歷史擴展

默認時,在交互式shell環境下,bash容許對歷史命令進行記錄和擴展。
環境變量HISTSIZE的值定義了記錄歷史命令的條數,HISTFILE的值指明瞭交互式shell啓動時須要加載的歷史命令的配置文件。在交互式shell退出時(exit),當前環境下執行過的命令會保存在此配置文件中。
當不帶任何選項執行內置命令history時,將輸出全部記錄的歷史命令(共$HISTSIZE條)。vim

[root@centos7 ~]# history
    4  type true
    5  help true
    6  man bash
    7  vim test.sh 
    8  bash -x test.sh
    ...
 1003  history

環境變量HISTTIMEFORMAT的做用是控制輸出和記錄歷史命令的時間格式(和date命令的時間格式一致)。
如:centos

[root@centos7 ~]# export HISTTIMEFORMAT="[%F %T] "
[root@centos7 ~]# history |tail -2
 1012  [2017-01-16 20:16:41] export HISTTIMEFORMAT="[%F %T] "
 1013  [2017-01-16 20:16:52] history |tail -2

因爲在bash腳本中,默認是不能使用歷史命令的,咱們這裏只簡要介紹一些經常使用的用法。
歷史擴展操做符:!數組

事件
!n                  #第n條命令
!-n                 #當前命令以前第n條命令
!!                  #上一條命令,和!-1等價
!string             #最近執行過的一條以string開頭的命令
!?string[?]         #最近執行過的一條包含string的命令,當string後面就是換行符時,結尾的?能夠省略。
^string1^string2^   #用string2替換上一條命令中的string1,並執行它。結尾的^能夠省略。
!#                  #表示本條命令字符!#以前鍵入的全部字符
事件以後能夠跟冒號分隔的以下字符,表示選擇特定的參數(當冒號後是 ^, $, *, -, 或 %時,冒號能夠省略)
如:
!^                  #前一條命令的第一個參數
!435:0              #第435條命令的命令名
!$                  #前一條命令的最後一個參數
!*                  #前一條命令的全部參數
冒號後還能夠是以下字符,表示對指定命令的修改:
s/old/new/          #替換第一個old,!!:s/string1/string2/ 和 ^string1^string2^ 表示一樣的意思
g                   #用於全局替換,如 !!:gs/string1/string2/

別名擴展

另外一個默認時只能在交互式shell中使用的擴展是別名擴展
當單詞做爲簡單命令的第一個單詞時,bash容許用字符串來替換這個單詞(別名)。
內置命令aliasunalias用來定義和撤銷別名。
單獨執行命令alias時會列出系統中全部的別名,alias命令接受形如變量賦值格式的參數來設定別名。但別名的名稱並不像變量名的要求那樣嚴格,別名能夠包含除了 /$反引號=元字符引用字符以外的任意字符。而別名的替代字符串能夠是任何shell輸入。bash

[root@centos7 ~]# alias 
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

能夠看到當咱們執行ls命令時,之因此輸出結果文件的類型均用顏色來區分,是由於lsls --color=auto的別名。
默認時shell腳本中不能使用別名。別名擴展是徹底基於文本的,於是別名能夠改變shell語法。幾乎任何別名的做用,均可以用shell函數來實現。併發

大括號擴展

大括號擴展是一種生成任意字符串的機制。一個正確的大括號擴展格式必須包含非引用的大括號{},和至少一個非引用的逗號或序列表達式。任何不正確的格式將保持原樣。在大括號中,如須要{,保持它們的字面意思,能夠在字符前添加一個反斜線\。
序列表達式的格式爲:{x..y[..incr]}。其中xy均爲數字或單個英文字母,incr表示增量(必須是整數),..incr能夠省略,若是省略則表示增量爲1或-1。
批量建立文件異步

[root@centos7 tmp]# touch {a..l}.txt
[root@centos7 tmp]# ls
a.txt  b.txt  c.txt  d.txt  e.txt  f.txt  g.txt  h.txt  i.txt  j.txt  k.txt  l.txt
[root@centos7 tmp]#

大括號擴展和文件名的通配符匹配相似,但大括號擴展並不須要文件是存在的。函數

[root@centos7 tmp]# ls [a-n].txt
a.txt  b.txt  c.txt  d.txt  e.txt  f.txt  g.txt  h.txt  i.txt  j.txt  k.txt  l.txt
[root@centos7 tmp]# ls {a..n}.txt
ls: 沒法訪問m.txt: 沒有那個文件或目錄
ls: 沒法訪問n.txt: 沒有那個文件或目錄
a.txt  b.txt  c.txt  d.txt  e.txt  f.txt  g.txt  h.txt  i.txt  j.txt  k.txt  l.txt

大括號也能夠嵌套
如建立目錄測試

[root@centos7 tmp]# mkdir -p ./a{m,n/{1..3},o}x
[root@centos7 tmp]# find . -type d
.
./amx
./an
./an/1x
./an/2x
./an/3x
./aox
[root@centos7 tmp]#

序列表達式中數字以0開頭時,擴展後會在全部數字前添加0以使它們等寬centos7

[root@centos7 tmp]# echo {05..100..5}
005 010 015 020 025 030 035 040 045 050 055 060 065 070 075 080 085 090 095 100

還能夠用在for循環命令中

[root@centos7 tmp]# for i in {1..10..2};do echo $i;done
1
3
5
7
9
[root@centos7 tmp]#

一點小技巧:

#備份
[root@centos7 tmp]# find . -name '*.txt' -exec cp {}{,.bak} \;
[root@centos7 tmp]# ls [a-z].txt{,.bak}
a.txt      b.txt      c.txt      d.txt      e.txt      f.txt      g.txt      h.txt      i.txt      j.txt      k.txt      l.txt
a.txt.bak  b.txt.bak  c.txt.bak  d.txt.bak  e.txt.bak  f.txt.bak  g.txt.bak  h.txt.bak  i.txt.bak  j.txt.bak  k.txt.bak  l.txt.bak
#移動
[root@centos7 tmp]# mv {[a-z].txt.bak,amx}
[root@centos7 tmp]# ls
amx  an  aox  a.txt  b.txt  c.txt  d.txt  e.txt  f.txt  g.txt  h.txt  i.txt  j.txt  k.txt  l.txt
[root@centos7 tmp]# ls amx/
a.txt.bak  b.txt.bak  c.txt.bak  d.txt.bak  e.txt.bak  f.txt.bak  g.txt.bak  h.txt.bak  i.txt.bak  j.txt.bak  k.txt.bak  l.txt.bak

波浪號擴展

shell中以字符~開頭的單詞(不能被引用)也會被做爲一種擴展方式(或者用在變量賦值等號右邊)。
下面給出部分舉例:

#單詞         擴展結果
~             $HOME
~+            $PWD
~-            $OLDPWD
~user_name    #用戶user_name的家目錄

[root@centos7 tmp]# echo ${PWD/#$HOME/~}
/root/temp/tmp
[root@centos7 tmp]# echo "${PWD/#$HOME/~}"
~/temp/tmp
[root@centos7 tmp]#

進程替換

在講語法的時候咱們談到命令替換(格式爲:$(...)或 `...`),是命令執行與變量操縱的結合。shell運行一個命令,收集其輸出,而後將輸出做爲展開的值。
命令替換的一個問題是命令的當即執行而後等待結果,此過程當中shell沒法傳入輸入。bash使用一個稱爲進程替換的功能來彌補這些不足,進程替換其實是命令替換管道的組合,和命令替換相似,bash運行一個命令,但令其運行於後臺而再也不等待其完成。關鍵在於Bash爲這條命令打開了一個用於讀和寫的管道,而且綁定到一個文件名,最後展開爲結果。
進程替換的格式爲:<(command)>(command)。其擴展結果是一個文件(文件描述符):

[root@centos7 tmp]# echo <(ls)
/dev/fd/63
[root@centos7 tmp]#

因此能夠用查看文件的命令來得到進程的輸出:

[root@centos7 tmp]# cat <(ls)
amx
an
aox
a.txt
b.txt
c.txt
d.txt
e.txt
...

能夠執行以下兩個命令試對比命令替換進程替換的區別:

#sleep命令結束後才輸出
echo $(ls;sleep 3)
#輸出先於sleep執行結束
cat <(ls;sleep 3)

腳本舉例:

#!/bin/bash
#進程替換能夠看成文件來使用
#做爲輸入文件
while read line
do
    ARR+=("$line")
done < <(seq 100)
#做爲輸出文件
echo $((`echo -n ${ARR[*]} > >(tr ' ' '+')`))

執行結果:

[root@centos7 temp]# ./test.sh 
5050
[root@centos7 temp]#

任務控制

在容許任務控制的系統上,bash能夠有選擇地掛起某個前臺進程,並使它在後臺異步地繼續執行。
CTRL+Z可使一個正在運行的前臺進程掛起:

[root@centos7 ~]# sleep 300
^Z
[1]+  已中止               sleep 300
[root@centos7 ~]#

[1]中數字1表示第1個後臺進程
內置命令jobs能夠查看當前有哪些後臺進程:

[root@centos7 ~]# jobs
[1]+  已中止               sleep 300
[root@centos7 ~]#

內置命令bg可使掛起的進程在後臺繼續運行:

[root@centos7 ~]# bg %1
[1]+ sleep 300 &
[root@centos7 ~]#

%1表示繼續運行第一個後臺進程,程序運行結束後會顯示:

[1]+  完成                  sleep 300

內置命令fg可使後臺進程返回到前臺繼續運行:

[root@centos7 ~]# fg %1
sleep 300
^C
[root@centos7 ~]#

在交互式shell或腳本中,以控制操做符&結尾的命令也會被做爲後臺命令異步地執行,當前shell不會等待此命令執行結束,命令的返回碼爲0。
在腳本中使用後臺執行命令時須要注意,若是當前shell先於後臺進程退出,會致使後臺進程也隨之退出(此時並無執行完)。若是須要等待後臺進程退出後父進程才退出,可使用內置命令wait
腳本舉例:

#!/bin/bash
#定義C段地址數組
c=(1 2 3 4 5)
#測試連通性函數
function ping_ip() {
    ping -c3 10.0.$i.$j &>/dev/null \
    || echo "10.0.$i.$j is not used" >>result.txt
}
#後臺併發測試
for i in ${c[@]}
do
    for j in {1..254}
    do
        ping_ip &
    done
done
#等待全部後臺進程結束
wait

執行略

相關文章
相關標籤/搜索