2017年1月19日 星期四

Shell Script簡易教學

一、概論

在許多的情況之下,我們都需要固定一組可以重覆或判斷資訊的指令,
而把這些指令存被在文字檔中,再交由Shell執行,就是Script。
一般會將Shell Script的副檔名命名為.sh,雖然副檔名在Linux中並非必要,
但是有副檔名可以讓我們更容易管理這些檔案。

假設有一個名為test.sh 的 Shell Script,首先用文字編輯器來撰寫內容如下:
#!/bin/sh
echo "Hello World"

第一行是必需的,它是用來定義你要使用的 shell。Linux中有許多的Shell可以使用,
如:ksh、bash、sh,但是彼此之間語法有所差異,所以我們首先需要定義使用哪一個Shell。
而第二行的 echo 代表列出一個字串,預設會把後面的字串「Hello World」顯示在螢幕上。
將test.sh存檔後,可以用下列其中一種方式執行它:
1、直接輸入 sh test.sh
2、改變test.sh的權限,加上可以執行的權限,
   chmod a+x test.sh
   接著直接執行它:
   ./test.sh

在Shell Script中,「#」表示註解,在#後面將視為註解並且被程式忽略。
例如:
#pwd
ls -l
Shell只會執行ls -l,而不會執行ls -l

而「;」 則代表指令的分隔,例如:
pwd;ls -l

pwd
ls -l
都是一樣執行pwd及ls -l。

二、變數的使用

在Shell Script中,所有的變數都視為字串,因此不需要在定義變數類型。
在Shell中定義和使用變數時並不一樣。
例如,定義一個變數id並且設定值為2013001,接著還要將印出變數的值:
id=2013001 -> 定義變數時前面不加「$」符號
echo $id   -> 使用變數時前面要加「$」符號
注意,在等號的二邊不可以有空白,否則將出現錯誤。

再介紹一個範例:
dir=/home/danny
ls $dir

這裡我們定義了變數dir的值為/home/danny,接著用ls指令,
此時指令會變為ls /home/danny,所以就把目錄中所有檔案都列出來。


我們再來看一個例子,說明如何使用變數來定義變數:
tmppath=/tmp

tmpfile=$tmppath/abc.txt
echo $tmpfile
結果會印出   =====>  /tmp/abc.txt

我們也可以把指令的輸出傳回給變數,指令前後加上關鍵字「`」,
它是位於鍵盤左上角「數字鍵1」左邊的按鍵:
now=`date` 
echo now
結果會秀出今天的日期與時間

此外,雙引號與單引號字串對變數解釋有不同的意義,
如果是使用單引號,則變數會當成一般文字;雙引號才會解譯成變數,例如:
a=123
echo "$a" ->結果是123
echo '$a' ->結果是$a


Script也可以要求使用者輸入資料,利用read指令來讀取輸入的資料。如下:
#!/bin/sh
echo -n "Please input your name:"
read Name
echo "Your name is $Name"

由於echo指令預設會自動換行,所以我們使用參數-n 來不讓他換行。
我們將上述內容存成檔案test01.sh,接著執行它:
sh test01.sh
Please input your name: Danny ->提示要輸入名字,此時輸入的是Danny
Your name is Danny

Shell Script有一些預設的特殊變數如下:
$? :表示上一個指令的離開狀況,一般指令正常離開會傳回 0。不正常離開則會傳回 1、2 等數值。
$1 :表示輸入的第一個參數,$2 則為第二個參數,依此類推。
$0 :shell script的檔名。
$@ :即代表 $1, $2,....直到所有參數結束。也就是說 $@ 代表了 "$1" "$2" "$3"....。
$* :所有參數無間隔的連在一起,成為單一個參數。也就是說 $* 代表了 "$1 $2 $3..."

三、運算符號

在 shell 中的四則運算必須使用 expr 這個指令來輔助。注意,在 + - * / 的二邊都有空白,如果沒有空白將產生錯誤:
expr 6 - 2
sum=`expr 10 + 20`
echo $sum
結果:
4
30

還有要特別注意的是乘號 * 。因為 * 有其他意義,所以要使用 \* 來代表。另外,也可以用 % 來求餘數。
count=`expr 9 \* 3`
echo $count
echo `expr $count % 3`
結果:
27
0

簡單的條件判斷是以 && 及 || 這二個符號來表示。
a && b如果 a 是真,則執行 b。如果 a 是假,則不執行 b。
a || b如果 a 是假,則執行 b。如果 a 是真,則不執行 b。
例如:
ls /home && echo YES
ls /dev/aaaa && echo YES
結果:
YES
ls: /dev/aaaa: No such file or directory

接著我們使用 test 及 [ 來做運算,運算所傳回的結果是真 (true) 或假 ( false)。
我們可以將它應用在條件判斷上。test 和 [ 都是一個指令,我們可以使用 test 並在其後加上參數來判斷真假。
或者也可以使用 [ 表達式 ] 來替代 test,要注意的是 [ ] 中的必須有空白間隔。

表達式說明如下:
-n str1      :如果字串 str1 的長度大於 0 則傳回 true。
-z str1      :如果字串 str1 的長度等於 0 則傳回 true。
str1  = str2 :如果字串str1等於字串str2則傳回 true,等號二邊有空白。
str1 != str2 :如果 str1 不等於 str2 則傳回 true。!= 的二邊有空白。
a -eq b      :Equal,a 等於 b 則傳回真 (true)。
a -ne b      :Not equal,a 不等於 b 則傳回真 (true)。
a -gt b      :Grwater than,a 大於 b 則傳回真 (true)。
a -lt b      :Less Than,a 小於 b 則傳回真 (true)。
a -ge b      :Greater or equal,a 大於或等於 b 則傳回真 (true)。
a -le b      :Less or equal,a 小於或等於 b 則傳回真 (true)。

我們舉例來說明:
test 5 -eq 5 && echo true
test abc!=cde && echo true
[ 6 -lt 10 ] && echo true
結果:
true
true
true

四、流程控制

1、if 的條件判斷

範例:
if [ $1 = "aa" ]; then
  echo "YES"
else
  echo "NO"
fi

範例二:
#!/bin/sh
if [ $1 -gt 10 ]; then
      echo " $1 is bigger then 10"
elif [ $1 -ge 0 ]; then
      echo " $1 is between 5 and 0. "
else 
      echo "$1 is less then 0."
fi


2、while 及 until 迴圈
基本語法:
while condition-list
   do list
   done

until condition-list
   do list
   done
範例一:
#!/bin/sh
i=1
while [ $i -le 10 ]
   do
       echo $i
       i=$(($i+1))
   done
說明:首先令變數 i=1,接著在當i小於等於10執行迴圈的內容,而每次執行迴圈一次,則i加1。

範例二:
#!/bin/sh
i=1
until [ $i -gt 10 ]
   do
       echo $i
       i=$(($i+1))`
   done

說明:首先令變數 i=1,接著迴圈會判斷,一直執行到i大於10才停止。
      每跑一次迴圈就i就加1。
 
3、for 迴圈
基本語法:
for name in word1 word2 …
   do do-list
   done

for name
   do do-list
   done
  
範例一:
#!/bin/sh
for color_name in blue red green
    do
       echo $color_name
    done

說明:這個Script中,會在每一次迴圈中將關鍵字in後面的字串分配給變數color_name,
      然後印出變數 color。
 
4、case 判斷
基本語法:
case word in
    pattern1) list1 ;;
    pattern2) list2 ;;
    …
esac

範例:
case $num1 in
  1|2|3)       echo $num1 is between 0~3;;
  4|5|6|7)       echo $num1 is between 4~7;;
  8|9)           echo $num1 is 8 or 9;;
  *)             echo $num1 is not on the list;;
esac

6、函式的運用
Shell Script中也可以使用函式 (function) 來將程式模組化。
基本語法:
name ( )
{
    statement
}

函式有幾個要注意的地方:
  A、在使用函式之前一定要先定義它,也就是在一個 Shell Script 中,一定要先寫函式的內容,
     在函式之後再寫一般的程式部份。
  B、在Script中的變數全部都是全域變數 (Global),所以函式中的變數會影響函式之外的其他部份。
  C、在命令列中輸入的參數是以 $1,$2來讀取,但是這些參數並沒有辦法在函式中使用。
     傳給函式的方法和在命令列中一樣,例如:[函式名稱] arg1 arg2..。
 傳進函式的變數一樣會以 $1,$2來儲存並使用。

範例:
addnum1 ( )
{
  echo 'Function $1 is' $1
  echo 'Function $2 is' $2 
}

echo 'Command $1 is' $1
echo 'Command $2 is' $2

a=$(($1 + 10))
b=$(($2 + 10))
addnum1 $a $b

程式執行:
./test01.sh 2 3
結果:
Command $1 is 2
Command $2 is 3
Function $1 is 12
Function $2 is 13



五、Shell script Debug

可以使用以下的方式來做Debug用途
sh -x ./test.sh
他會印出test.sh實際運作的過程

2017年1月10日 星期二

開啟 VMWare Workstation 出現 internal error 的解決方法

原因是因為服務開啟時,VMWare的服務沒有開啟成功. 所以執行VMWare時會出現下列錯誤訊息:










解決方法如下所示:
1.在Windows啟動的地方輸入services.msc






















2.點選VMware的服務,點滑鼠右鍵,選擇啟動
















再開啟VMware就可以成功了!

2016年12月9日 星期五

把ethtool交叉編譯在arm平台上使用

首先去網站抓一包Source Code:
         http://ftp.kernel.org/pub/software/network/ethtool/

解壓縮之後,先在資料夾中新增一個檔案cross_configure.sh內容如下:
================= cross_configure.sh ==================

#!/bin/sh
ARM_TOOLCHAIN_DIR=/opt/arm/bin     #please type your toolchain path
export CROSS_COMPILE="${ARM_TOOLCHAIN_DIR}/arm-linux-gnueabi-"
export AR="${CROSS_COMPILE}ar"
export AS="${CROSS_COMPILE}as"
export LD="${CROSS_COMPILE}ld"
export NM="${CROSS_COMPILE}nm"
export CC="${CROSS_COMPILE}gcc"

export FC="${CROSS_COMPILE}gfortran "
export RANLIB="${CROSS_COMPILE}ranlib"
export STRIP="${CROSS_COMPILE}strip"
export OBJCOPY="${CROSS_COMPILE}objcopy"  
export CFLAGS="-Os -pipe -O2 -static"

./configure --target=arm-linux --host=arm-linux
===================================================

接下來幾個步驟分別為:
1. ./cross_configure.sh
2. make
3. cp ethtool 到目標板上
4. use "./ethtool -S eth0" to watch network status.


完成!!!


2016年12月7日 星期三

Cross-compile程式之後,放到板子上執行,卻顯示"/bin/ash: ./phyAccess: not found"(以我編譯phyAccess工具為範例)

今天編譯phyAccess tool準備要測試網路功能,編譯完成之後,放到板子執行時卻出現以下的錯誤訊息:
     



於是我先回到編譯的ubuntu電腦上使用file看一下編譯完成的檔案,是把程式編譯成甚麼CPU架構的,確認是ARM的無誤:




另外注意到了這個執行檔編譯完成後,是屬於dynamically linked(用file可看到如上圖),因此我用 readelf -l phyAccess | grep "program interpreter"(readelf -d phyAccess 也可看出需要哪些library) 來看看他動態連結了哪些library,發現他需要"ld-linux.so.3"的library(如下圖)
    



原來無法在板子上執行,是因為板子上缺少了這個library,因此最後的解決方法就是,編譯的時候在Makefile的 CFLAG參數裡面加上 -static(編譯成靜態)就解決此問題了。

2016年12月6日 星期二

Linux中devmem直接讀寫記憶體位置的用法

讀取
在位置0x98000000讀取32bit值
# devmem 0x98000000
0x11111111


在位置0x98000000讀取16bit值
# devmem 0x98000000 16
0x1111





寫入
在位置0x98000000寫入32bit值
# devmem 0x98000000 32 0x1111AAAA
# devmem 0x98000000
0x1111AAAA



另外如果出現以下訊息:
    devmem: can't open '/dev/mem': No such file or directory
表示Kernel端沒有開啟功能,請開啟下列選項:

=> Device Drivers                                                  
      => Character devices                                        
                       [*] /dev/mem virtual device support

這樣應該就可以了!!




2016年11月24日 星期四

linux中,格式化隨身碟的指令(ext4)

首先可以使用 "fdisk -l" 先確認好自己的隨身碟在檔案系統當中的名稱,可以在 /dev 資料夾底下找到,通常會是 /dev/sda 類似的 (/dev/sdb  or /dev/sdc 等等)

在確認完成之後就開始指令的部分了,假設我們的隨身碟是 /dev/sda:

1. sudo fdisk /dev/sda (作硬碟分割,可以做出partition table),進入fdisk後會要求你再下command,"h" 可以列出所有command,通常我們會先下"n"可以去增加一個partition,然後再下"p"會看到切出一個/dev/sda1的partition,最後再下"w"作儲存的動作

2. sudo mkfs.ext4 /dev/sda1 最後就是對剛剛切出的partition作檔案格式化,這邊的範例是格式化成ext4的格式.


作完以上步驟就可以了!!!

2016年11月17日 星期四

shell script的 字串處理,取代符號的用法,awk 以及 sed

awk

awk這個指令,可以用來處理字串,他的用法可以針對你想找的"符號"或"字串",來做切割然後可以單獨的取出,awk 使用說明如下

awk -F'要找的字符' '{print $1}'  

舉個實際例子,假設有個字串 =>  str="danny:12345:man"
那我現在想要取出danny,該怎麼做呢? 使用awk可以很簡的把他取出來
你只需下這段指令:
        echo $str | awk -F':' '{print $1}'





如果要取出12345,
        echo $str | awk -F':' '{print $2}'




如果要取出man,
        echo $str | awk -F':' '{print $3}'




如果是要取最後,也可以使用
        echo $str | awk -F':' '{print $NF}'

取倒數第二,寫法是
         echo $str | awk -F':' '{print $(NF-1)}'
        
        

sed

sed指令可以用來針對字串作以下功能  
1.以行為單位的新增/刪除   
2.以行為單位的取代與顯示
3.部分資料的搜尋並取代的功能

假設現在有一個檔案,名為hello,他的內容如下:










1.以行為單位的新增/刪除

(新增)我要印出hello的內容,並在第3行後面(也就是第四行)新增一些文字,作法如下:
       cat hello | sed '3a danny' 










(刪除)我要印出hello的內容,並刪除2~5行,作法如下:
       cat hello | sed '2,5d' 








2.以行為單位的取代與顯示

(取代)我要把2~5行取代為別的內容,作法如下:
       cat hello | sed '2,5c HHHHHHHHHHH' 






(顯示)我只要顯示2~5行的內容,作法如下:
       cat hello | sed -n '2,5p'







另外補充 sed 帶入變數的取某一行的方法
例如我有個變數$count="2"
我要用這個變數來取第二行,作法如下:
sed -n "${count}p"  ==> 等同於 sed -n "2p"


3.部分資料的搜尋並取代的功能

作法如下:
sed 's/要被取代的字串/新的字串/g'


例如有個字串 => str="@danny",假設我想要改成 "Hi danny",作法如下:
       echo $str | sed 's/@/Hi /g'





假設我只想要改成"danny",作法如下:
       echo $str | sed 's/@//g'





另外補充 sed 帶入變數的取代法
假設我有有個字串 => str="0.1.20180709", 我有個變數 $Date="20180709",
現在我要把str後面的".20180709"去掉, 作法如下:
       echo $str | sed "s/.$Date//g"



最後附上一個shell script範例,此範例是從一個json格式的字串中截取出user_name, passwd然後再透過smbpasswd指令去更改Samba的密碼

=================== test.sh ==========================
#!/bin/sh

input='{"user":"danny","passwd":"1234"}'
user_name=$(echo $input | awk -F',' '{print $1}' | awk -F':' '{print $2}' | sed 's/"//g')
passwd=$(echo $input | awk -F',' '{print $2}' | awk -F':' '{print $2}' | sed 's/"//g' | sed 's/}//g' )
printf "$passwd\n$passwd\n" | smbpasswd -a -s "$user_name" > /dev/null

==================================================