====== 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