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的處理過程: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è)空格,其他地方有沒有空格無所謂,但是這個(gè)空格如果沒有的話,是必然會(huì)出錯(cuò)的;還有個(gè)地方是}前面那條命令的;必須有,否則也會(huì)有問題;最后,還需要提醒的是,如果你不是寫在一行內(nèi),那么}前的;不必有,{后的空格也不必有。
===============================
對(duì)$*和$@作些說明:
其實(shí)要分四種情況:$*, $@, "$*", "$@"
對(duì)于前兩種情況,都等同于$1 $2 $3 ...,如果某個(gè)參數(shù)內(nèi)有了空格或者換行(不要覺得不可能,參數(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還得作下說明,如果 IFS 為空,則沒有間隔空格。IFS 的默認(rèn)值是空白、制表符和換行符。如果沒有設(shè)置 IFS,則使用空白作為分隔符(僅對(duì)默認(rèn) IFS 而言),這里特別提到的是,如果你想把參數(shù)都串接起來,那么必須得顯示設(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ì)帶來一些想不到的異常情況,建議都使用#!/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 語句的 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 中使用 ? 或 : 值來檢測(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,不過要自己手動(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查看一下,如果沒開你可以用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)亂碼,不過每次都這么搞一通實(shí)在是不爽,不知道在其他發(fā)行版上會(huì)不會(huì)有這個(gè)問題。
現(xiàn)在終于解決了這個(gè)問題,有兩種方法,一種可以在虛擬終端的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來執(zhí)行腳本,即可在+號(hào)后面打印行號(hào)和函數(shù)名;使用trap <cmd> DEBUG/ERR/EXIST可以分別在執(zhí)行每一行前/某個(gè)命令返回值不為0時(shí)/函數(shù)或程序退出時(shí)執(zhí)行cmd;可以在腳本里面的腳本段落前后分別用sh -x和sh +x來使得只有該段落的內(nèi)容執(zhí)行sh -x;可以通過變量DEBUG作為開關(guān)來統(tǒng)一控制調(diào)試與否,當(dāng)然需要有個(gè)函數(shù),比如也叫做DEBUG的話,那這個(gè)函數(shù)的定義大概為:DEBUG() { [ "$DEBUG" = "true" ] && $@ ; },然后你就可以肆無忌憚的DEBUG echo $a,$b或者在一段落前后分別加上DEBUG set -x和DEBUG set +x來進(jìn)行控制了;還可以使用類似于Gdb調(diào)試的bash調(diào)試工具bashdb,官網(wǎng)地址為:http://bashdb.sourceforge.net/;上述總結(jié)來自于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字符的且深度不超過3層的, 將符合上述條件的目錄或文件名打印出來, 不打印全部路徑, 只打印文件名或目錄名. 寫該命令時(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語句, action前面的默認(rèn)連接是使用-a(即and的意思, 類似于我們常見的command1 && command2 || command3中的&&的作用), 當(dāng)然也可以顯示的指定-o(即or的意思, 類似于||的作用), 既然談到了and和or,那么自然就有not了,在find命令里,not是!,舉個(gè)例子就清楚了:find . ! -name '*.sh'會(huì)找到所有不是以.sh結(jié)尾的文件, 此外還有就是通過\(和\)能夠把一些復(fù)合起來的條件作為一個(gè)整體, 因此對(duì)于該條命令, 執(zhí)行過程是這樣的, 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,即*,?,[]三種, 如果真想用正則來匹配,還是用-regex吧; 此外對(duì)于find最神奇的莫過于-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è)東西來代表find找到的部分, 但對(duì)于xargs中需要注意使用-i參數(shù), 該參數(shù)其實(shí)和-I參數(shù)作用是一樣的,但是特別之處在于當(dāng)不指定replace-str時(shí), -i這個(gè)參數(shù)會(huì)把{}作為replace-str, 因此使用-i就用經(jīng)管道|過來的標(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中通過cat<<來大段輸出文本, 還有一些比較巧妙的用法,比如為腳本中的函數(shù)塞入數(shù)據(jù)(注意不是為函數(shù)塞入?yún)?shù), 而是當(dāng)函數(shù)中有類似于read這樣的命令時(shí)通過here文檔來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無效
2.用’HERE’, “HERE”, \HERE代替<<后面的HERE,可以使內(nèi)容中變量不被替換,諸如$HOME
3.如果只希望輸入一行數(shù)據(jù),可以用<<<即可,例如cat <<<'hello world' (說到這里, 還真覺得蠻有意思的,一個(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)輸入流的程序來說, 可以通過一個(gè)-來承載送過來的流, 大致可分為通過管道|過來的標(biāo)準(zhǔn)輸出流和通過<送進(jìn)來的標(biāo)準(zhǔn)輸入流, 通過這樣一個(gè)例子就可以看出ls | cat - t.C - - < t.sh <<<'single here document' <<{{
multiple
here
document
{{
結(jié)果只是<<發(fā)揮作用了, 它們的優(yōu)先級(jí)別是: <<最高, <<<次之, < 排第三, | 排第四
這樣就可以利用該特性為某t.C這樣的文檔添加頁眉和頁腳了:
頁眉: cat - t.C <<<'header'
頁腳: cat t.C - <<<'footer'
=======================================
在腳本中,對(duì)于那些腳本依賴的變量,如果沒定義的話就不希望繼續(xù)執(zhí)行腳本,那么可以使用:
set -u
使用了那些你關(guān)注的變量的代碼段
set +u
這樣就可以保證腳本在執(zhí)行過程中沒有發(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,不過這樣得到的信息不全面,全面的信息可以在man builtins里面找到;
========================================
echo -e 'a\tb'和echo $'a\tb'的結(jié)果一樣,$'string'形式的字符串會(huì)被特殊處理,字符串會(huì)被展開,并像c語言那樣將反斜杠及緊跟的字符進(jìn)行替換,擴(kuò)展后的結(jié)果將被單引號(hào)包裹,就好像美元符號(hào)一直就不存在一樣,例如$'\n' <=> $'\012' <=> $'\x0a' <=> $'\x0A';$"string"將會(huì)使得字符串被翻譯成符合當(dāng)前l(fā)ocale的語言,如果當(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來切換大小寫是否敏感(當(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ù),也就是說和放在外面一樣,只不過看上去能感覺到:這個(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ù),通過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語言中的printf沒什么區(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è)問題:
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è)東西都是命名管道(但這里是沒有名字的, 實(shí)則它們是借助于某一個(gè)fd來完成的), 相當(dāng)于你先mkfifo了一個(gè)命令管道xxx, 然后你echo "$content" > xxx, 同時(shí)你的read函數(shù)已經(jīng)在xxx的另一端候著了while read line; do ... done < xxx. 用<()和>()省略了建立xxx的過程.
==============================================================================
tr 'a-z' 'A-Z' < t1.txt > t1.txt像這種語句,都會(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<>代表通過fd為1的文件進(jìn)行read and write,這樣就繞過了bash的提前解析,因此t1.txt中的內(nèi)容也被成功替換掉了。但要注意的是,同時(shí)從一個(gè)文件既讀取又寫入,而且是以管道這種流的形式來搞(正因?yàn)槭橇鞯男问剑圩x一點(diǎn)寫一點(diǎn)],所以就不會(huì)全部讀到內(nèi)存中,然后再處理,再把處理后的結(jié)果從內(nèi)存中dump到輸出文件),所以這種只對(duì)那種本位替換有效果,例如tr或者sed這種,其實(shí)說到這里,主要是為了引出<>這個(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ì)用空格來分隔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)需要通過shopt來控制,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+. ,不過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(這里的語法類似于sed,/也可以用其他符號(hào)例如@來代替,g表示多次匹配)
!! #重新執(zhí)行上一條命令(效果等同于Ctrl-p)