使用者工具

網站工具


linux:shell

Linux Shell Scripting Tutorial

bash 環境設定

可用 set 指令查看現有的環境變數

設定檔

讀取順序

  1. /etc/profile
  2. ~/.bash_profile
  3. ~/.bash_login
  4. ~/.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’

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

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
linux/shell.txt · 上一次變更: 2011/08/09 09:43 由 wenpei