Jex’s Note

Shell Script 基本語法

接收傳入參數

建立test.sh內容:

#!/bin/bash
echo $0
echo $1
echo $2

執行你的shell檔案,並將要傳入的參數加在後面

$ sh test.sh hello 11 22
test.sh         # 檔名
hello           # 第一個參數
11              # 第二個參數

讀寫檔案內容

建立test.sh內容:

cat > qq.php << EOF
this is line 1
this is line 1
EOF

執行後就會產生qq.php檔案了,檔案內容為EOF框起來的內容

寫檔換行 :

echo -e "line1\nline2" | tee -a /tmp/q.txt

append到檔案最後面

tee -a /tmp/t.txt << EOF
line 1
line 2
EOF

讀取檔案,將每行放入陣列

file=1.txt

seq=1
while read line
do
    lines[$seq]=$line
    ((seq++))
done < $file

for ((i=1;i<=${#lines[@]};i++))
do
    echo ${lines[$i]}
done

使用sh執行會有錯誤,但bash執行沒問題

或 :

array_1=($(cat /tmp/t.txt))
echo ${array_1[0]}

使用sh執行會有錯誤,但bash執行沒問題

set

錯誤直接停止, 在最上面加上:

set -e

錯誤不停止,繼續執行

set +e

其他:

  • set -- : 正常看到-後面是option,現在不再是option 而是一個命令參數。如-1 -2 …
  • set -a : 從這邊以後變數自動變成環境變數。
  • set -f : 不要解釋檔名的特殊字元例如wildcard *不再解釋為所有的意思了。
  • set -x : debug shell scripts
  • set -o ignoreeof : 一定要用exit離開shell,本來按Ctrl-D(eof)也可以
  • set -o noclobber : 關掉I/O導向不準overwrite檔案
  • set -o notify : shell結束時報告background job的status
  • set -o noglob : 關掉wildcard字元解釋 如 * ? [ ]
  • set +o : 把-o的反向操作
  • set - : 關掉-v -x -三種選項
  • set -- : 或者 set - 常常用在shell scripts裡面。 set -o 是很常用的例如set -o vi設定shell的操作方式用vi方法, 取回上個命令就是按ESC再按k囉。

ref:

if else

t.sh :

#!/bin/bash

num=$1

if [ "${num}" == "XD" ]; then
    echo "= XD"
elif [ "${num}" != "QD" ]; then
    echo "!= QD"
else
    echo "Nothing"
fi

執行 :

$ bash t.sh XD
= XD
$ bash t.sh something
!= QD
$ bash t.sh QD
Nothing

AND / OR :

[ "${confirm}" == "y" ] && [ "${confirm}" == "Y" ]
[ "${confirm}" == "y" ] || [ "${confirm}" == "Y" ]

switch

case $1 in
    "aa")
        echo "aa"
        ;;
    "cc")
        echo "cc"
        ;;
    "dd")
        echo "dd"
        ;;
    *)
        echo
        echo "  ***\$1 doesn't match!"
        echo
        exit
        ;;
esac

迴圈

for

for var in con1 con2 con3 ...
do
    // do something
done

or

for ((i=0; i<6; i++)); do
  echo $i
  if [ ${i} == 3 ]; then
        continue
        // break
  fi
  echo "done"
done

while

i=1
while [ $i -le 5 ]      // i < 5
do
    ((i++))
    echo $i
done

until

until [ condition ]
do
    程式段落
done

陣列

lines=('line1' 'line2' 'line3')

${#array[@]} 為array的總數 ${array[0]} 索引從0開始, -1為最後一個值

判斷

檔案是否存在

if test -e /etc/network/if-pre-up.d/firewall; then
    echo "/etc/network/if-pre-up.d/firewall exists.."
fi

or

if [ -f /tmp/${filename}.tar.gz ]; then
    echo "exist"
fi

判斷沒有目錄直接離開:

if [ ! -d "/var/www/$dir" ]; then
    echo "/var/www/$dir doesn't exist."
    exit
fi

判斷變數是否存在

if [ -z $1 ]; then
        # 不存在
else
        # 存在
fi

判斷只能輸入數字

echo "$1" | grep -o "^[0-9]*$"
if [ $? -eq 0 ] ;then
    echo "match found"
else
    echo "match not found"
fi

AND 條件

if (( $hour >= 0 )) && (( $hour <= 9 )); then
    # do something
else
    # do something
fi

亂數 10~19 中其中一個數字

shuf -i 10-19 -n 1

執行command結果給變數

random_num=$(shuf -i 10-19 -n 1)

or

count=$(grep "2017-07-07 $hour_str:$min_str" /var/log/php/ci/log-2017-07-07.php | wc -l)

How to run mysql commands through shell script?

mysql -u root test_database << EOF
SELECT * FROM test
EOF

有設定免密碼所以不用加 -p

在shell script執行 發生Bad substitution錯誤

可能發生的原因 ex: shell script 可能使用 ${str:3} (取得str變數第三個字元以後的字串)

因為sh不支援,所以執行時改用bash執行就可以解決了

bash test.sh

執行 ssh 後要怎麼離開

以下是沒有用的:

ssh qq.com
exit

要改成:

ssh qq.com <<ENDHERE
exit
ENDHERE

shell script 回覆 yes

當我要reload supervisor這個套件,但是會有新的一行yes/no,所以要使用這種方法才可以讓shell script幫我填入yes

sudo supervisorctl <<EOF
reload
y | command-that-asks-for-input
EOF

or

yes | sudo sensors-detect
sudo sensors

shell script 下 sudo echo 發生 permission denied

sudo echo "123" >> /etc/fstab

>> 是bash執行,不具root權限,以tee 代替>>

改成:

echo "123" | sudo tee -a /etc/fstab

-a, –append : append to the given FILEs, do not overwrite

match string

只比對jex, 其餘不要

bash :

#!/bin/bash

jex=$(echo $1 | grep -o "jex[0-9]*")
echo ${jex}

-o, –only-matching

取得指定字串長度

str='123456'
echo ${str:3:2}        # 結果為45

read讓使用者輸入變數

sudo fdisk -l
read -p "Please input XFS partition: " xfs_partition
echo $xfs_partition

執行漸進式選項

EX: fdisk partition

(echo o; echo n; echo p; echo 1; echo ; echo; echo w) | sudo fdisk

判斷 command 是否執行成功

some_command
if [ $? -eq 0 ]; then
    echo OK
else
    echo FAIL
fi

The return value is stored in $?. 0 indicates success, others indicates error.

比對檔案是否有符合字串

grep "127.0.1.1" /tmp/hosts > /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo 'match'
fi

function

的第一種寫法,並返回值

function test ()
{
    return 255
}

test
echo $?

結果 : 255 return 範圍是 0~255,超過的話 : 256 為 0 257 為 1 使用 $? 取得 return 的值

第二種寫法,傳值並返回

test2 () {
 echo $1$2
}

string=$(test2 "Hello" " World!")
echo $string

結果 : Hello World! function 並不像 php 一樣順序可以放在執行後,shell script 的 function 必須放在執行前

背景執行不顯示 output

host www.google.com > /dev/null 2>&1

將 command 結果存入陣列

function test_dev_list()
{
    echo "/dev/sda5"
    echo "/dev/sda6"
    echo "/dev/sda7"
    echo "/dev/sda10"
    echo "/dev/sda11"
    echo "/dev/sda14"
}

dev_array=(`echo $(test_dev_list) | cut  -d " " --output-delimiter=" " -f 1-`)
for ((i=0; i<${#dev_array[@]}; i++)); do
    echo $i : ${dev_array[$i]}
done

結果:

0 : /dev/sda5
1 : /dev/sda6
2 : /dev/sda7
3 : /dev/sda10
4 : /dev/sda11
5 : /dev/sda14

減法

a=5
b=3
c=$(($a-$b))
echo $c

結果 : 2

輸出顏色文字及控制背景顏色

printf "\33[0;35;44m"
echo " Menu of available command:"
printf "\33[0m"
printf "\E[0;31;40m"
echo " Menu of available command:"
printf "\E[0m"
printf "\e[0;36;43m"
echo " Menu of available command:"
printf "\e[0m"

\33 = \E = \eprintf 來輸出,而不是一般的 echo

註 :

  • Syntx : \E[樣式;文字顏色;背景顏色m
  • 輸出文字 : \E[0m

樣式:

  • 0 一般樣式
  • 1 粗體
  • 4 加底線
  • 5 灰底
  • 7 文字及背景顏色對調

文字顏色:

30 黑色    90 暗灰
31 紅色    91 亮紅
32 綠色    92 亮綠
33 黃色    93 亮黃
34 藍色    94 亮藍
35 紫色    95 亮紫
36 青藍綠  96 亮青藍綠
37 亮灰    97 白

背景顏色:

40 黑色    100 暗灰
41 紅色    101 亮紅
42 綠色    102 亮綠
43 黃色    103 亮黃
44 藍色    104 亮藍
45 紫色    105 亮紫
46 青藍綠  106 亮青藍綠
47 亮灰    107 白

ref : 參考顏色 http://mark528.pixnet.net/blog/post/7267334-shell-script%3A-%E6%8E%A7%E5%88%B6%E6%96%87%E5%AD%97%E9%A1%8F%E8%89%B2

printf + read

printf "Choose Monkey, are you sure (y/n) ? "
read xfs_choice_confirm

read 後面不需接 -p

補充 if 參數

  • n1 -lt n2 : n1 小於 n2 (less than)
  • -a file : True if file exists.
  • -b file : True if file exists and is a block special file.
  • -c file : True if file exists and is a character special file.
  • -d file : True if file exists and is a directory.
  • -e file : True if file exists.
  • -f file : True if file exists and is a regular file.
  • -g file : True if file exists and is set-group-id.
  • -h file : True if file exists and is a symbolic link.
  • -k file : True if file exists and its ‘‘sticky’’ bit is set.
  • -p file : True if file exists and is a named pipe (FIFO).
  • -r file : True if file exists and is readable.
  • -s file : True if file exists and has a size greater than zero.
  • -t fd : True if file descriptor fd is open and refers to a terminal.
  • -u file : True if file exists and its set-user-id bit is set.
  • -w file : True if file exists and is writable.
  • -x file : True if file exists and is executable.
  • -O file : True if file exists and is owned by the effective user id.
  • -G file : True if file exists and is owned by the effective group id.
  • -L file : True if file exists and is a symbolic link.
  • -S file : True if file exists and is a socket.
  • -N file : True if file exists and has been modified since it was last read.
  • file1 -nt file2 : True if file1 is newer (according to modification date) than file2, or if file1 exists and file2 does not.
  • file1 -ot file2 : True if file1 is older than file2, or if file2 exists and file1 does not.
  • file1 -ef file2 : True if file1 and file2 refer to the same device and inode numbers.

  • [ -b $file ] True if file exists and is block special.

  • [ -c $file ] True if file exists and is character special.
  • [ -d $file ] True if file exists and is a directory.
  • [ -e $file ] True if file exists.
  • [ -f $file ] True if file exists and is a regular file.
  • [ -g $file ] True if file exists and is set-group-id.
  • [ -k $file ] True if file has its “sticky” bit set.
  • [ -L $file ] True if file exists and is a symbolic link.
  • [ -p $file ] True if file exists and is a named pipe.
  • [ -r $file ] True if file exists and is readable.
  • [ -s $file ] True if file exists and has a size greater than zero.
  • [ -S $file ] True if file exists and is a socket.
  • [ -t $fd ] True if fd is opened on a terminal.
  • [ -u $file ] True if file exists and its set-user-id bit is set.
  • [ -w $file ] True if file exists and is writable.
  • [ -x $file ] True if file exists and is executable.
  • [ -O $file ] True if file exists and is owned by the effective user id.
  • [ -G $file ] True if file exists and is owned by the effective group id.

ref: http://www.troubleshooters.com/linux/quickhacks.htm#ShellscriptFileTests

確認是否要執行以下指令

confirm() {
    echo "Press ENTER to continue, or ^C to cancel.";
    read -e ignored
}
confirm

判斷 Ubuntu 版本

LTS="Ubuntu 10.04"
ISSUE=`cat /etc/issue`
if [[ $ISSUE != Ubuntu* ]]; then
    echo "This script is intended for use on Ubuntu, but this system appears
    echo "to be something else. Your results may vary."
    echo
elif [[ `expr match "$ISSUE" "$LTS"` -eq ${#LTS} ]]; then
    echo "what"
fi

是否擁有 sudo 權限

echo "Testing sudo..."
sudo true
if [ $? -ne 0 ]
then
    echo "ERROR: You must be able to sudo to run this script.";
    exit 1;
fi;

動態偵測輸入變數

for TOKEN in $*
do
   echo $TOKEN
done

判斷第幾個參數

if [ $# -lt 1 ]; then

line 10: php: command not found

在使用 shell 的 bash 指令裡使用 php 指令記得要用絕對路徑

可使用 which php 查路徑在哪

指定 USER 執行命令

/usr/bin/sudo -H -u $USER bash -c "php -l qq.php"

每秒執行一次

while true
do
    /bin/sleep 1s
done

Add cron by command line

先印出已存在的 crontab, 然候新增你要的存進去

(crontab -l; echo "0 * * * * your_command") |uniq - | crontab -

Other way

echo "0   0  *  *  * your_command" | sudo tee -a /var/spool/cron/crontabs/jex

Remove cron by command line

先印出已存在的 crontab, 再用 sed 去刪除

crontab -l | sed "/pattern/d" | crontab -

other way

sudo sed -i "/pattern/.*$/d" /var/spool/cron/crontabs/jex

2>&1

2>&1 表示將stderr導向輸出stdout

0: stdin 1: stdout 2: stderr

out.file 2>&1 : out.file是將command的輸出重定向到out.file文件,即輸出內容不打印到屏幕上,而是輸出到out.file文件中。2>&1是將標准出錯重定向到標準輸出,這裡的標準輸出已經重定向到了out.file文件,即將標准出錯也輸出到out.file文件中。 ref: http://www.cnblogs.com/caolisong/archive/2007/04/25/726896.html

截取目錄名

jex@106-185-47-26:~/dockerfile/33-fsx$ echo ${PWD#*-}
fsx

當使用echo 增加資料發生Permission denied

$ sudo echo 'xxx' >> /etc/projects
-bash: /etc/projects: Permission denied

解決方法:

sudo echo 'xxx' | sudo tee -a /etc/projects

multi commands

alias lock='gnome-screensaver; gnome-screensaver-command --lock'

or

lock() {
    gnome-screensaver
    gnome-screensaver-command --lock
}

Comments