鄭重聲明,這個文章不是我寫的,是我的同事——劉彬做的學習筆記.我覺得寫的太好了,但他又沒有發到網上,只好自己貼一下了.所以轉載必須帶上原作者的名字,以及本段聲明.
Contents
此文分享我在bash學習,使用,編程中的碰到的一些問題,以及一些體會.
CTRL 鍵相關的快捷鍵:
ALT 鍵相關的快捷鍵:
可通過bash內鍵命令 bind 修改按鍵綁定,具體可查幫助: help bind
對於使用readline庫的程序,可修改 /etc/inputrc 或 ~/.inputrc 修改綁定關係.
ps: 使用vim的建議交換 CapsLock 與 Esc 鍵
我的 ~/.inputrc
"e[A": history-search-backward "e[B": history-search-forward "C-p": history-search-backward "C-n": history-search-forward # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving "e[1;5C": forward-word "e[1;5D": backward-word "e[5C": forward-word "e[5D": backward-word "ee[C": forward-word "ee[D": backward-word
# 忽略重複的命令 export HISTCONTROL=ignoredups # 忽略由冒號分割的這些命令 export HISTIGNORE="[ ]*:&:bg:fg:exit" export HISTFILESIZE=1000000000 export HISTSIZE=1000000 export HISTFILE=~/.lb_bash_history shopt -s histappend
ls
eval "`dircolors -b ~/.dircolors`" alias ls='ls --color=auto'
grep, fgrep, egrep
export GREP_OPTIONS='--color=auto'
man
export LESS_TERMCAP_mb=$'E[01;31m' export LESS_TERMCAP_md=$'E[01;31m' export LESS_TERMCAP_me=$'E[0m' export LESS_TERMCAP_se=$'E[0m' export LESS_TERMCAP_so=$'E[01;44;33m' export LESS_TERMCAP_ue=$'E[0m' export LESS_TERMCAP_us=$'E[01;32m'
if [ -f /etc/bash_completion ]; then . /etc/bash_completion fi
中文目錄拼音補全: chsdir
介紹怎樣編寫補全腳本: An introduction to bash completion part 1 , An introduction to bash completion part 2
Bash completion for discoverer and retriever
function _discoverer() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="-r --home -v --version -h --help -w --website -k --keyword -K --linksite-keyword -c --category --related -u --userid -m --max-videos -M --max-pages -F --linksite-fullsite -C --linksite-category --log-level" case "$prev" in -w | --website) local running="youtube dailymotion myspace aol_movie metacafe msn tudou veoh yahoo youku megavideo ku6 s56 pomoho room6 pandora salloumi nicovideo myvideo tu 66stage blogspot campusist chewymovies fastpasstv freetvonline legalmovies mediateevee movie6 moviesondemand movierumor moviesdesk moviesforfree moviesister movietvonline mymovies nabolister omegatube quickss sevenscreen sidereel tvdash tvpapa watchnewmovies watchmovies watchtvsitcoms wizmovies xoxomovie live-video msn-video sogou-video bing-video-cn bing-video-com baidu-video google-video-cn google-video-com gougou-video soso-video alluc nabolister tvshack tvlinks watchmoviesfree taringa thepiratecity cinemawiki joox tvpapa familyguy freeonlineepisode moviealien tubezoom videofactor redcurtainmovies findthatshow watcheveryshow watchgossipgirl bynik watchtvsitcoms likestreaming allomovies alloshowtv diziport surfthechannel delatv estrenosonline momomesh movie2k ls2megaupload yesfilmes ninjavideo yidio casttv videosurf" running ="youporn slutload xnxx gaywatch megaporn xvideos itsallgay tube8 tnaflix keezmovies xhamster gayforit redtube empflix pornhub jerkyourtube youporngay" COMPREPLY=( $(compgen -W "$running" -- "$cur") ) return 0 ;; -r | --home) local running="$(dirname $PWD)" COMPREPLY=( $(compgen -W "$running" -- "$cur") ) return 0 ;; *) ;; esac if [[ ("$cur" == -*) || ( ("$cur" = "") && ("$prev" != -*) ) ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) fi } complete -F _discoverer discoverer function _retriever() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="-r --home -v --version -h --help -d --detail -D --download -k --keyid --removal --resource --log-level" opts =" --need-multi-clip --max-total-size --max-clip-num" case "$prev" in -r | --home) local running="$(dirname $PWD)" COMPREPLY=( $(compgen -W "$running" -- "$cur") ) return 0 ;; *) ;; esac if [[ ("$cur" == -*) ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) fi } complete -F _retriever retriever
腳本程序通常第一行以 #! 開頭指定一個腳本解釋器.如
#!/bin/bash #!/bin/sh #!/usr/bin/awk -f #!/bin/sed -f #!/usr/bin/expect -f #!/usr/bin/env perl
注意 : linux 裡面,解釋器之後只能有一個參數,如有多個,也只做為一個.
例子: test.pl
#!/usr/bin/env LC_CTYPE=zh_CN.UTF-8 perl print "hellon"
perl test.pl 可以正常運行,./test.pl則不會正常執行,且不會退出.
運行 ./test.pl 相當於 /usr/bin/env "LC_CTYPE=zh_CN.UTF-8
perl" ./test.pl . evn會先設置LC_CTYPE="zh_CN.UTF-8 perl"這樣一個環境變數,然後執行 ./test.pl,這就回到了最開始,於是一不斷重複這個過程,不退出.ps: 關於腳本程序第一行的相關信息可見於:man execve
Bash 有以下保留字:
! case do done elif else esac fi for function if in select then until while { } time [[ ]]
注意:
ps: 可用 type 查看是否為保留字,內建命令等
A parameter is an entity that stores values. A variable is a parameter denoted by a name.
指 $1, $2, $3 等
注意: $0, $1, .. $9 沒有問題,$10 將被解釋為 ${1}0.要使用 >= 10 的位置參數,需用 { } 括起來,如 ${15}.
From bash manual
The shell treats several parameters specially. These parameters may only be referenced; assignment to them is not allowed. * Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable. That is, "$*" is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators. @ Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" ... If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed). # Expands to the number of positional parameters in decimal. ? Expands to the exit status of the most recently executed foreground pipeline. - Expands to the current option flags as specified upon invocation, by the set builtin command, or those set by the shell itself (such as the -i option). $ Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the current shell, not the subshell. ! Expands to the process ID of the most recently executed background (asynchronous) command. 0 Expands to the name of the shell or shell script. This is set at shell initialization. If bash is invoked with a file of commands, $0 is set to the name of that file. If bash is started with the -c option, then $0 is set to the first argument after the string to be executed, if one is present. Otherwise, it is set to the file name used to invoke bash, as given by argument zero. _ At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command exe cuted and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file currently being checked.
使用較多的有 $*, $@, $#, $?, $$, $!, $0
* 與 @ 的區別,在別的地方法了是這樣.如在數組中 "${array[*]}" 與 "${array[@]}" .
if list; then list; [ elif list; then list; ] ... [ else list; ] fi
這是最常用的分支語句,通常用 [ ], [[ ]], (( )) 進行條件測試.事實上一切語句都可以用,如
if echo "$var" | tr '[:upper:]' '[:lower:]' | grep -q 'regex expression' ; then echo matched fi
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
case 使用 pattern 匹配進行測試,變得非常好用. 如檢查一個字元串是否包含別一個字元串
case "$1" in ) *"$2"* ) true ;; * ) false ;; esca
&&, ||
如 [ $# -ne 2 ] && exit 1 或 [ $# -eq 2 ] || exit 1
for name [ in word ] ; do list ; done
for host in $(<host_list) ; do ... ; done for i in {1..100} ; do ... ; done for f in /aaa/bbb/cc* ; do ... ; done for v in "$@" ; do ... ; done
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
跟 c/c 中 for 語義一致,好用.如
for (( i=0; i<100; i )) ; do ... ; done
while list; do list; done
while true ; do ...; done while read line ; do ...; done < input_file
until list; do list; done
語義跟while相反,沒見人用過.
$ echo abc{d,e,f} abcd abce abcf $ echo {a,b,c}{d,e,f} ad ae af bd be bf cd ce cf $ echo {1..10} 1 2 3 4 5 6 7 8 9 10 $ echo {1..10..2} 1 3 5 7 9
ps:
cp filename{,.bak} 好像是某個網站選出來的十大最酷的linux單行命令之一.
$ echo ~ /home/liubin $ echo ~liubin /home/liubin $ echo ~tracker /home/tracker $ echo ~no ~no $ echo ~ /tmp $ echo ~- /home/liubin
最基本的
根據parameter未定義或值為空進行操作/擴展
${parameter:-word}
${parameter:=word}
${parameter:?word}
${parameter: word}
較為有用,可用於為輸入參數置默認值等.如
out_file=${1:-/dev/null}
size=$(stat -c %s $file) if [ ${size:-0} -lt 100 ] ; then ... fi
數組相關
字元串操作
${#parameter}
${parameter:offset}
${parameter:offset:length}
${parameter#word}
${parameter##word}
${parameter%word}
${parameter%%word}
非常有用,如 basename 可近似於 ${path##*/} , dirname 可近似於 ${path$/*}
${parameter/pattern/string}
${parameter^pattern}
${parameter^^pattern}
${parameter,pattern}
${parameter,,pattern}
上面四個大小寫轉換的應該是在 bash 4.0 中引入的.寫入腳本中要慎重.
另外
$(command) or `command`
會去掉的newline,但中間的會保留.
var=$(<file) 跟var=$(cat file) 是相同的,但要快.
``與$()處理 '' 的方式是不同的.
`'sed -e 's/\/\\/g'` 是不可用的,而 $(sed -e 's/\/\\/g') 是可用的
$ echo "`echo '\'`" echo "$(echo '\')" \ echo "`echo sed -e 's/\/\\/g'`" sed -e s//\/g $ echo "$(echo sed -e 's/\/\\/g')" sed -e s/\/\\/g
使用``嵌套時要將裡面的 `` 轉義.
``是比較舊的方式,現已漸被$()替換
ps: 個人認為 `` 寫在腳本中也不好看,符號太小,且易與 ' 混淆,建議不要使用.
$(( expression ))
<(list) or >(list)
Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files.
例子
$ diff <(printf "%sn" a b c) <(printf "%sn" a b c) $ diff <(printf "%sn" a b c) <(printf "%sn" a bb c) 2c2 < b --- > bb $ paste <(printf "%sn" a b c) <(printf "%sn" a bb c) a a b bb c c
可用於將 list | while read l ; do ...; done 替換為 while read l ; do ...; done < <(list) 這種形式,從而避免 while 循環於子 shell 執行.
通過 pattern matching 進行擴展.
From bash manual
* Matches any string, including the null string. When the globstar shell option is enabled, and * is used in a filename expansion context, two adja cent *s used as a single pattern will match all files and zero or more directories and subdirectories. If followed by a /, two adjacent *s will match only directories and subdirectories. ? Matches any single character. [...] Matches any one of the enclosed characters. A pair of characters separated by a hyphen denotes a range expression; any character that sorts between those two characters, inclusive, using the current locale's collating sequence and character set, is matched. If the first character following the [ is a ! or a ^ then any character not enclosed is matched. The sorting order of characters in range expressions is determined by the current locale and the value of the LC_COLLATE shell variable, if set. A - may be matched by including it as the first or last character in the set. A ] may be matched by including it as the first character in the set. Within [ and ], character classes can be specified using the syntax [:class:], where class is one of the following classes defined in the POSIX stan dard: alnum alpha ascii blank cntrl digit graph lower print punct space upper word xdigit A character class matches any character belonging to that class. The word character class matches letters, digits, and the character _. Within [ and ], an equivalence class can be specified using the syntax [=c=], which matches all characters with the same collation weight (as defined by the current locale) as the character c. Within [ and ], the syntax [.symbol.] matches the collating symbol symbol.
路徑擴展是在單詞割之後進行,所以如路徑名中有空格,該路徑也只會做為一個參數傳入.
所以,一般來說這種寫法 for f in $(ls $dir) ; do ... ; done 不如 for f in $dir/* ; do ...; done
After the preceding expansions, all unquoted occurrences of the characters , ', and " that did not result from one of the above expansions are removed.
$ var= $ [ -z "$var" ] ; echo $? 0 $ [ -n "$var" ] ; echo $? 1 $ [ -z $var ] ; echo $? 0 $ [ -n $var ] ; echo $? 0
在用 -n 進行字元串不為空的測試時,一定要將所測試的變數用 "" 引起來.
bash 一般的做法是通過外部命令來實現,或自己寫個函數來做.
在較高版本的bash中可用以下方法
$RANDOM 產生 0 - 32767 之間隨機數,要產生更大的隨要數,可以以 $RANDOM 為基礎做一個函數. 也可以像 hexdump -e '"%un"' -n 4 /dev/random 這樣去讀 /dev/random
read 讀一行時會對 轉義,一般來說是沒用的,需加上 -r 參數.
read時會去掉頭尾的空白,如需要保留,則read后不要跟變數,用 $REPLY 訪問讀到的內容.
如對一個文本文件進行通過read進行複製可以:
while read -r ; do echo "$REPLY" ; doen <input >output需防止 while 中的命令去讀 stdin
如
printf "%sn" localhost localhost | while read host ; do ssh $host whoami ; done
這樣是有問題的,正確的方法可為
printf "%sn" localhost localhost | while read host ; do ssh -n $host whoami ; done printf "%sn" localhost localhost | while read host ; do ssh $host whoami </dev/null ; done exec 3< <(printf "%sn" localhost localhost) while read -u3 host ; do ssh $host whoami ; done exec 3<&-
見: http://fvue.nl/wiki/Bash:_Why_use_eval_with_variable_expansion?
$cmd 一般會有問題,大概是這樣理解的 :
$cmd 會最先做一個syntax parser,然後再參數擴展,然後會到 word splitting. cmd='echo "a b"' , $cmd 在word splitting後會變成 echo '"a' 'b"'
要只被交到 word splitting 的就是按IFS來分的了,如
$ echo $(echo '"a b"') "a b" $ echo "$(echo '"a b"')" "a b"
所以會被分成那個鳥樣.
如cmd中有參數,那麼 "$cmd" 這樣直接執行一般不會有結果,返回值是127,就是找不到命令.
而 eval 是相當於對 擴展好的 cmd 再走了一下解釋流程,所以沒有問題.
結論:不要用 $cmd 要用 eval "$cmd"
不要用 local a="a_value" b="${a} b value"
local 是個命令,跟bash中賦值語句的執行是不一樣的.
例子
$ cat t.sh #!/bin/bash function t1() { local a=t1 b=$a.bb echo "a:[$a] b:[$b]" } function t2() { a=t2 b=$a.bb echo "a:[$a] b:[$b]" } t1 t2 t1 $ ./t.sh a:[t1] b:[.bb] a:[t2] b:[t2.bb] a:[t1] b:[t2.bb]
同理,export也是一樣的.
命令前有賦值(如 PATH=/usr/bin env ),那個這個賦值只對這個命令本身有效,不會影響當前環境. 所以這個賦值一般要是環境變數才行,不然應該一點用都沒有.
常用的形式有 IFS=: read v1 v2 v3
echo hello # stdout echo hello 1>&2 # stderr
上面是必需必需的, 還可以通過重定向到以下文件來指定:
/dev/stdout /dev/stderr
/dev/fd/1 /dev/fd/2
/proc/$$/fd/1 /proc/$$/fd/2
/proc/self/fd/1 /proc/self/fd/2
子shell中的變數對於子shell之外的代碼塊來說, 是不可見的. 當然, 父進程也不能訪問這些變數.
(command;command;...; command)
可用於進入一個目錄操作再退出
( cd $dir .... # do something )
From bash manual
Each command in a pipeline is executed as a separate process (i.e., in a subshell)
常見的一個問題
num=1 seq 1 5 | while read l ; do num=$((num 1)); done echo $num
可使用進程替換解決
num=1 while read l ; do num=$((num 1)); done < <(seq 1 5) echo $num
不支持進程替換的可用命名管道解決
mkfifo t_fifo seq 1 5 >t_fifo & num=1 while read l ; do num=$((num 1)); done <t_fifo echo $num
$ a=aa $ echo $(a=bb;echo $a) bb $ echo $a aa
fork炸彈,造成死機,危險.
解釋
:() { : | : & } :
定義了一個名為 ':' 的遞歸函數,然後調用.
ps: ':' 是一個啥事不幹的內建命令,可用於佔位,如
if [ $v1 -lt v2 ] ; then : else echo do something fi
[火星人 ] Bash Use And Programming已經有838次圍觀