====== Linux Shell Scripting Tutorial ====== [[http://freeos.com/guides/lsst/|Linux Shell Scripting Tutorial v1.05r3 - A Beginner's handbook]] ====== bash 環境設定 ====== 可用 set 指令查看現有的環境變數 ===== 設定檔 ===== ==== 讀取順序 ==== - /etc/profile - ~/.bash_profile - ~/.bash_login - ~/.profile ^ 環境變數 ^ 說明 ^ | HOME |家目錄| | MAIL |郵件儲存位置| | PS1 |第一個提示符號,等待使用者命令| | PS2 |第二個提示符號,收到的指令不完整,等待使用者繼續輸入| PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ PS2='> ' ^ 控制字 ^ 功能 ^ | \a |一嗶聲| | \e |跳脫字元| | \H |主機名稱(較長格式)| | \h |主機名稱(較短格式)| | \n |換行| | \s |Shell Name| | \T |以 12 小時制顯示時間| | \t |以 24 小時制顯示時間| | \@ | | | \u |使用者名稱| | \W |完整工作目錄| | \w |目前目錄| | \$ |root 會顯示「#」,一般使用者則為「$」| | \ \ |印出反斜線| ===== bash 記憶指令 ===== 在 ~/.bashrc 加上 bind '"\x1b\x5b\x41":history-search-backward' bind '"\x1b\x5b\x42":history-search-forward' 參考 zxvc.bbs@ptt.cc 寫的 如果想知道bash有多少好用的hotkey, 只要man bash,然後搜尋『history-search-backward』, 就可以在history-search-backward附近找到一堆hotkey。 或者在bash中輸入 $ bind -p 也可以看到很多hotkey,只不過沒有詳細說明。 另外『\M-』這個prefix在一般PC鍵盤上代表的是ESC key, 這man bash也是可以查得到。 如果想知道某個按鍵的keymap(例如Up鍵),可以在『純終端機』 (我不清楚為什麼虛擬終端機會不能用showkey)輸入 $ showkey -m 查到,但是這是10進位的值,要把它轉成16進位再加上『\x』 才可以跟某個function bind在一起。 例如:Up鍵與history-search-backward bind在一起: $ bind ‘”\x1b\x5b\x41″:history-search-backward’ [[http://www.thegeekstuff.com/2011/08/bash-history-expansion/|15 Linux Bash History Expansion Examples You Should Know]] ====== Shell Script ====== ===== 變數 ===== 直接指定變數的值,使用時前面加 $ vari = 5 echo "var = $vari" ===== 指令參數 ===== $ ./script.sh ~/temp.txt temp2.txt 此時,在程式中 $0 = ./script.sh $1 = ~/temp.txt $2 = temp2.txt $* = ~/temp.txt temp2.txt // 所有參數 $# = 2 // 參數個數 ==== 設定參數 set ==== 將原本的參數換成新指令的輸出 set `date` echo "$4 $5 $1 $3 $2 $6" ===== 輸入 read ===== read input echo $input ===== 數學運算 expr ===== a=1 b=2 c=`expr $a + $b` # 等號前後不可以有空白 echo $c ===== 條件敘述 ===== ==== 數值比較 ==== 若為 true 則傳回 1,false 為零 test a -eq b ^ 符號 ^意義^ | -eq | = | | -ne | != | | -lt | < | | -le | < = | | -gt | > | | -ge | >= | ==== 字串比較 ==== ^ 符號 ^意義^ | = |str1 = str2| | != |str1 != str2| | -n |不是空字串| | -z |空字串| ==== 檔案屬性測試 ==== ^ 選項 ^意義^ | -l |是否為連結檔案| | -d |目錄| | -e |存在| | -f |存在且為一般檔案| | -r |存在且可讀| | -w |存在且可寫| | -x |存在且可執行| | -nt |比較兩檔案修改時間,如: file1 -nt file2| ==== if 的結構 ==== if 條件判斷 then ... fi 關鍵字要自成一行,若硬要同行,要用分號分隔,但 then 後面不用分號 if 條件 ; then ... ; fi if test -f $1 ; then echo "$1 is a reqular file." ; fi if [ $1 -eq $2 ] ; then echo "equal" ; else echo "not equal" ; fi if [ ! -e $1 ] ; then ... ; fi # 如果 $1 的檔案不存在 以下兩行相同 if [ -r $1 ] && [ -x $1 ] if (test -r $1) && (test -x $1) if 條件 then ... else ... fi if 條件 then ... elif then ... else ... fi ==== case 結構 ==== 每一 pattern 用兩個分號分隔,每一敘述用一個分號。 case variable in pattern_1) [statement] ;; pattern_2) [statement1]; [statement2];; ... esac case $1 in 1) echo "One";; 2) echo "Two"; echo "2";; esac pattern 可使用正規表示式: case $1 in *a) echo "End with a";; *b) echo "b";; esac ==== for 迴圈 ==== List 中所有的元素接執行一次 for var in List do ........ done for file in `ls` do if [ -f $var ] ; then echo "$file is a reqular file."; done ==== while 迴圈 ==== while 條件 do ...... done ==== until 迴圈 ==== 和 while 相反,在條件不成立時執行。 until 條件 do ...... done ==== select 迴圈 ==== 系統將 List 中的所有元素當作選項,並以環境變數 PS3 作為 Prompt,提示使用者輸入,當使用者輸入某個選項時執行 do done 間的程式。 select var in List do ...... done #!/bin/bash PS3="INPUT: " select var in `ls` do if [ $var ]; then if [ -f $var ];then echo "$var reqular." else echo "$var no reqular." fi else echo "Bad input" fi done ==== break , continue ==== 可在後面接數字,代表要跳出哪個迴圈,如:break2 指要中斷第二個迴圈。 ===== function ===== 函數宣告必須在呼叫之前完成,因為 Shell script 是直譯程式,且需注意:1. 全域和區域變數的範圍;2. 傳參數的用法。 Function name { ...... } 或 function_name() { ...... } ==== 變數的生命週期 ==== 有三種不同性質的變數。 === 環境變數 === 由系統產生,必定是全域變數。 === 位置變數 === 由函式被呼叫時所附加的參數而定,因此必為區域變數,在不同的函式用同樣的方法使用,但數值不同。 $1, $2, $3, ... === 自訂變數 === 除非特別指定成 local,不然皆為全域變數。 local var1="string" #!/bin/bash funct () { echo "Inside -- funct: $# args: $1 $2 $3" } echo "Outside -- $0: $# args: $1 $2 $3" funct input1 input2 input3 // 呼叫函式 $ ./test.sh in1 in2 in3 Outside -- ./test.sh: 3 args: in1 in2 in3 Inside -- funct: 3 args: input1 input2 input3 ==== 參數傳遞 ==== * 傳入函式:呼叫時直接寫在函式名稱後面,即可成為函式的位置變數 * 傳出函式:使用全域變數來儲存跨函式間的資料 ===== 上一個指令的執行結果 ===== http://blog.miniasp.com/post/2009/02/Writing-Bash-script-should-care-about-error-handling-issues.aspx 「$?」代表上個指令的執行結果,若成功執行則回傳「0」。 因此可寫成: SUCCESS=0 mv some.log $BAKDIR/some-$TODAY.log if [ "$?" -ne $SUCCESS ] then echo "Can NOT move some.log to $BAKDIR/some-$TODAY.log!!" exit 1 fi ====== date 指令 ====== 取得昨天的日期: date -d 'yesterday' +%Y-%m-%d.log 取得幾天前的日期: date -d '6 days ago' +%Y-%m-%d 下週一、上週三的日期: date -d 'next Mon' date -d 'last Wed' ====== 定期確認 apache 的 error log ====== 定期分析 apache 的 error log 中,找不到檔案的部份,郵寄給管理者參考。 #! /bin/bash ( for x in `grep "File does not exist:" /var/log/apache2/error.log | awk '{print $13}' | sort | uniq`; do grep $x /var/log/apache2/error.log | wc -l | tr -d '\n'; echo " : $x"; done | sort -rn | head -20 ) | mail -s "Missing file report" webmaster@domain.name 並在 crontab 中加入(需 root 權限) 0 21 * * 5 /root/apache_file_no_found_error.sh ====== zsh ====== http://friedcpu.wordpress.com/2007/07/24/zsh-the-last-shell-youll-ever-need/ http://grml.org/zsh/zsh-lovers.html http://www.acm.uiuc.edu/workshops/zsh/toc.html http://rayninfo.co.uk/tips/zshtips.html ===== zshrc by vgod ===== export PATH="$PATH:~/bin:/usr/local/bin:/usr/local/texlive/2007/bin/i386-darwin:/opt/flex_sdk_3/bin:/usr/texbin:/opt/local/bin" export EDITOR=vim export LANG=en_US.UTF-8 UNAME=`uname` if [[ $UNAME == "Darwin" ]] then export LSCOLORS="gxfxcxdxbxegedabagacad" alias ls='ls -Gv' elif [[ $UNAME == "Linux" ]] then alias ls='ls --color=auto' fi # path alias, e.g. cd ~XXX #hash -d WWW="/home/lighttpd/html" # HISTORY # number of lines kept in history export HISTSIZE=10000 # # number of lines saved in the history after logout export SAVEHIST=10000 # # location of history export HISTFILE=~/.zhistory # # append command to history file once executed setopt INC_APPEND_HISTORY # Disable core dumps limit coredumpsize 0 # vi key binding bindkey -v bindkey '^R' history-incremental-search-backward # mapping del bindkey "\e[3~" delete-char setopt AUTO_PUSHD WORDCHARS='*?_-[]~=&;!#$%^(){}<>' # auto-completion setopt COMPLETE_ALIASES setopt AUTO_LIST setopt AUTO_MENU #setopt MENU_COMPLETE setopt MULTIBYTE autoload -U compinit compinit # Completion caching zstyle ':completion::complete:*' use-cache on zstyle ':completion::complete:*' cache-path .zcache #zstyle ':completion:*:cd:*' ignore-parents parent pwd #Completion Options zstyle ':completion:*:match:*' original only zstyle ':completion::prefix-1:*' completer _complete zstyle ':completion:predict:*' completer _complete zstyle ':completion:incremental:*' completer _complete _correct zstyle ':completion:*' completer _complete _prefix _correct _prefix _match _approximate # Path Expansion zstyle ':completion:*' expand 'yes' zstyle ':completion:*' squeeze-shlashes 'yes' zstyle ':completion::complete:*' '\\' #zstyle ':completion:*:*:*:default' menu yes select #interactive zstyle ':completion:*:*:default' force-list always # require /etc/DIR_COLORS to display colors in the completion list [ -f /etc/DIR_COLORS ] && eval $(dircolors -b /etc/DIR_COLORS) export ZLSCOLORS="${LS_COLORS}" zmodload zsh/complist zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31' zstyle ':completion:*' completer _complete _match _approximate zstyle ':completion:*:match:*' original only zstyle ':completion:*:approximate:*' max-errors 1 numeric bindkey -M menuselect '^M' .accept-line compdef pkill=kill compdef pkill=killall zstyle ':completion:*:*:kill:*' menu yes select interactive zstyle ':completion:*:kill:*' force-list always zstyle ':completion:*:processes' command 'ps -au$USER' # Group matches and Describe zstyle ':completion:*:matches' group 'yes' zstyle ':completion:*:options' description 'yes' zstyle ':completion:*:options' auto-description '%d' zstyle ':completion:*:descriptions' format $'\e[01;33m -- %d --\e[0m' zstyle ':completion:*:messages' format $'\e[01;35m -- %d --\e[0m' zstyle ':completion:*:warnings' format $'\e[01;31m -- No Matches Found --\e[0m' alias ll='ls -l' alias grep='grep --color=auto' function precmd { local TERMWIDTH (( TERMWIDTH = ${COLUMNS} - 1 )) ### # Truncate the path if it's too long. PR_FILLBAR="" PR_PWDLEN="" local promptsize=${#${(%):---(%n@%m)----}} local pwdsize=${#${(%):-%~}} if [[ "$promptsize + $pwdsize" -gt $TERMWIDTH ]]; then ((PR_PWDLEN=$TERMWIDTH - $promptsize)) else PR_FILLBAR="\${(l.(($TERMWIDTH - ($promptsize + $pwdsize)))..${PR_HBAR}.)}" fi ### # Get APM info. #if which ibam > /dev/null; then #PR_APM_RESULT=`ibam --percentbattery` #elif which apm > /dev/null; then #PR_APM_RESULT=`apm` #fi } setopt extended_glob preexec () { if [[ "$TERM" == "screen" ]]; then local CMD=${1[(wr)^(*=*|sudo|-*)]} echo -n "\ek$CMD\e\\" fi } setprompt () { ### # Need this so the prompt will work. setopt prompt_subst ### # See if we can use colors. autoload colors zsh/terminfo if [[ "$terminfo[colors]" -ge 8 ]]; then colors fi for color in RED GREEN YELLOW BLUE MAGENTA CYAN WHITE; do eval PR_$color='%{$terminfo[bold]$fg[${(L)color}]%}' eval PR_LIGHT_$color='%{$fg[${(L)color}]%}' (( count = $count + 1 )) done PR_NO_COLOUR="%{$terminfo[sgr0]%}" ### # See if we can use extended characters to look nicer. typeset -A altchar set -A altchar ${(s..)terminfo[acsc]} PR_SET_CHARSET="%{$terminfo[enacs]%}" PR_SHIFT_IN="%{$terminfo[smacs]%}" PR_SHIFT_OUT="%{$terminfo[rmacs]%}" PR_HBAR=${altchar[q]:--} #PR_HBAR=" " PR_ULCORNER=${altchar[l]:--} PR_LLCORNER=${altchar[m]:--} PR_LRCORNER=${altchar[j]:--} PR_URCORNER=${altchar[k]:--} ### # Decide if we need to set titlebar text. case $TERM in xterm*) PR_TITLEBAR=$'%{\e]0;%(!.-=*[ROOT]*=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\a%}' ;; screen) PR_TITLEBAR=$'%{\e_screen \005 (\005t) | %(!.-=[ROOT]=- | .)%n@%m:%~ | ${COLUMNS}x${LINES} | %y\e\\%}' ;; *) PR_TITLEBAR='' ;; esac ### # Decide whether to set a screen title if [[ "$TERM" == "screen" ]]; then PR_STITLE=$'%{\ekzsh\e\\%}' else PR_STITLE='' fi ### # APM detection #if which ibam > /dev/null; then #PR_APM='$PR_RED${${PR_APM_RESULT[(f)1]}[(w)-2]}%%(${${PR_APM_RESULT[(f)3]}[(w)-1]})$PR_LIGHT_BLUE:' #elif which apm > /dev/null; then #PR_APM='$PR_RED${PR_APM_RESULT[(w)5,(w)6]/\% /%%}$PR_LIGHT_BLUE:' #else PR_APM='' #fi ### # Finally, the prompt. PROMPT='$PR_SET_CHARSET$PR_STITLE${(e)PR_TITLEBAR}\ $PR_CYAN$PR_SHIFT_IN$PR_ULCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT [\ $PR_GREEN%(!.%SROOT%s.%n)$PR_GREEN@%m\ $PR_BLUE]$PR_SHIFT_IN $PR_SHIFT_OUT$PR_BLUE\ $PR_MAGENTA%$PR_PWDLEN<..<%~%<<\ $PR_BLUE$PR_SHIFT_IN$PR_HBAR${(e)PR_FILLBAR}$PR_CYAN$PR_URCORNER$PR_SHIFT_OUT\ $PR_CYAN$PR_SHIFT_IN$PR_LLCORNER$PR_BLUE$PR_HBAR$PR_SHIFT_OUT \ %(?..$PR_LIGHT_RED%?$PR_BLUE:)\ $PR_LIGHT_BLUE%(!.$PR_RED#.$PR_WHITE\$)$PR_SHIFT_IN$PR_SHIFT_OUT\ $PR_CYAN$PR_SHIFT_IN$PR_SHIFT_OUT\ $PR_NO_COLOUR ' RPROMPT=' $PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_BLUE$PR_HBAR$PR_SHIFT_OUT\ ($PR_YELLOW%D{%H:%M}$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_CYAN$PR_LRCORNER$PR_SHIFT_OUT$PR_NO_COLOUR' PS2='$PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\ $PR_BLUE$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT(\ $PR_LIGHT_GREEN%_$PR_BLUE)$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT\ $PR_CYAN$PR_SHIFT_IN$PR_HBAR$PR_SHIFT_OUT$PR_NO_COLOUR ' } setprompt