shell script - BASH
BASH (Bourne Again Shell) 是 GNU 開發的 Shell (外殼),是以由 Steve Bourne 開發的 Shell 為名,開發的目的,是希望借 BASH 代替一般商業化的 Shell。 Shell 的意念在 UNIX Version 7 時已經出現,其版本是 Bourne Shell ,也就是 sh。
後來,由 Bill Joy (名字很熟識吧?他就是 vi 的作者,亦是 BSD 早期開發的重要人物之一) 開發的 C Shell 更受廣泛使用,因為 C Shell 的語法很像 C,管理員只需懂得 C 程式就可以簡單使用 C Shell。對於建立 GNU 系統,撰寫 Shell 外殼是有必要的,而 BASH 是由 Brian Fox (GNU 出名的人物) 撰寫,並由 Chet Ramey 所維持,現時最新版本為 2.04。
現在,絕大多數的 Linux distribution 預設都使用 BASH,而在其它系統像 OpenBSD 及 FreeBSD 等都使用傳統的 csh (C Shell) 或是 ksh (Korn Shell)。而筆者今次介紹的是 BASH,集中在 bash 上的程式 (即 shell script) 寫作,而 bash 環境的應用只會提及而不深入講解,因為撰寫 shell script 對管理上有很大幫助。
第一個的 shell script
在很多情況上系統都會使用 shell script,例如 /etc/rc.d/rc.local、.bash_login 等檔案,甚至定時檢查與 ISP 的連線等都可以使用 shell script 的幫忙。請看例子一,'echo' 就是把字句或變數列印出來,在 bash 的編程中,以$開頭的就是變數,相信有使用 Perl 的朋友都看得懂吧!例子一所做的就是要打印該句出來:
例子一 (test-1.sh):
#!/bin/bash Var1="How are you today ?" echo $Var1
首先請留意一點,在 test-1.sh 的第一行,筆者把之設為交給 /bin/bash 執行,而不是 /bin/sh,原因可能部份 Unix 系統中的 /bin/sh 並不是 bash,這樣執行可能發生問題,所以您還是檢查一下,如果 /bin/sh 是連到 /bin/bash 的 symbolic link 就兩者都可以使用。
在 bash 中的變數是大少寫敏感的,即是說 $Var 不等同 $VAR,這點很多的程式語言都一樣。把 test-1.sh 儲存成檔案後,執行的方法有兩種,您可以直接使用 bash 來執行這個檔案,或是把檔案設為可執行(executable):
例子一執行方法一:
shell@www :~# sh test-1.sh
How are you today ?
例子一執行方法二:
shell@www :~# chmod 0700 test-1.sh shell@www :~# ./test-1.sh How are you today ?
兩種方法都可行,在不同情況上,您可以選擇哪個方法較好。
Bash 的編程支援參數(parameters)。在 C 的編程上,您可使用 argc 及 argv 來找尋輸入至程式的參數;而在 bash 上,$# 就是 argc,是顯示參數的數目;而 $1、$2、$3 等等就是參數值,$@ 是所有參數的陣列(Array)。請參考例子二,它示範了這幾個內置變數的功能:
例子二 (test-2.sh):
#!/bin/bash echo "Total Parameters of $0 : $#" echo "They are : $@"; echo "First parameter is : $1"
例子二執行方法:
shell@www :~# chmod 700 test-2.sh shell@www :~# ./test-2.sh 1st 2nd 3rd Total Parameters of ./test.sh : 3 They are : 1st 2nd 3rd First parameter is : 1st
在例子三中,我們嘗試使用 shell script 執行外部的程式。它是一個在某檔案中找尋字串的 shell script:
例子三 (test-3.sh):
#!/bin/bash echo "Now searching file : ${1##/*/}" grep $2 $1
例子三執行方法:
shell@www :~# ./test-3.sh /etc/services ftp Now searching file : services ftp-data 20/tcp ftp 21/tcp tftp 69/udp sftp 115/tcp bftp 152/tcp
參數一是檔案位置,而參數二就是字串。在這個只有兩行的 shell script中,當然沒有甚麼特別的功能,目的只是顯示在 shell script 中如何執行外部指令。或者我們可以使例子變得複雜一點,當然,請繼續看下去。例子中的 ${1##/*/} 就是把 /etc/ 等刪去,請參考表一。
表一:
變數 | 結果 |
${path} | /usr/local/bin/emacs |
${path#/*/} | local/bin/emacs |
${path##/*/} | emacs |
${path%/*} | /usr/local/bin |
${path:15} | emacs |
${path:10:4} | /bin |
此外,在 bash 的變數中,還有些能即時回傳測試,例如測試變數是否存在,如果沒有,會做別的事情,這些在撰寫功能多的 shell script 時很常用,請參考表二(這裡先假設 $Var1 的值是 "Yes",而 $Var2 沒有定義):
表二:
用法 | 結果 |
${Var1:-No} | 印出 "No" |
${Var2:-Yes} | Var2 沒有變數值,印出 "Yes" 字串 |
${Var1:=No} | 印出 "Yes" 字串 |
${Var2:=Yes} | Var2 現在變數值為 "Yes" |
${Var1:?No} | 印出 "Yes" 字串 |
${Var2:?Yes} | 印出 "Var2: Yes" 字句 |
順帶一提,如果在Shell的環境下直接輸入 echo -e ${PATH//:/' '},印出的就是在 $PATH 變數內的值,而 /:/' ' 會把分隔 $PATH 的 ':'字串變為 newline 印出。
編者註︰要清楚以上所有變數的替換,請看 bash 的 manpage 裡有關 "Parameter Expansion" 的一段。
if... then... else... fi 語法: if <條件 1>; then 執行指令 elif <條件 2>; then 執行指令 else 執行指令 if
在例子四中,我們加入了 if 的條件控制,令 shell script 功能變得好一點;或許在看例子前參考表三,表三是 bash 內可使用的比較字元及測試字元:
表三:
用法 | 解釋 |
str1 = str2 |
比較字串變數是否相同 |
str1 != str2 |
比較字串變數是否不同 |
str1 < str2 |
比較 str1 是否大於 str2 |
str1 > str2 |
比較 str1 是否小於 str2 |
-n str1 |
檢查 str1 是否有定義 |
-z str1 |
檢查 str1 是否沒有定義 |
例子四 (test-4.sh):
#!/bin/bash if [ -z "$1" ]; then echo "Usage : test-4.sh [File] [Keyword]" exit 1; fi echo "Now searching file : ${1##/*/}" grep $2 $1
我們使用了 -z 檢查 $1,即是第一個參數,如果沒有輸入參數,程式會回傳使用的方法,然後結束程式執行;當然我們還可以加入其它更多的檢查,使這個程式變得更強。在例子五中,我們加入對檔案狀態的檢查。
例子五 (test-5.sh):
#!/bin/bash if [ $# != "2" ]; then echo "Usage : test-5.sh [File] [Keyword]" exit 1; fi if [ ! -r "$1" ]; then echo "No such files or permission denied !" fi echo "Now searching file : ${1##/*/}" grep $2 $1
在例子五中加入了一段 if 的條件,"!"的意思是 NOT,即是檢查 $1 是否不能讀 (! -r),就像是 Slackware 的 rc 檔案會檢驗在 /etc/rc.d/ 內有沒有 rc.* 的檔案,並且檢查是否可以執行,有關這些檢查的運算子,可以參考表四:
表四:
選項 | 解釋 |
-d | 檢查是否目錄 |
-e | 檢查是否檔案 |
-f | 檢查是否檔案(非特別檔案如 /dev/*) |
-r | 檢查是否可讀 |
-s | 檢查檔案是否存在及內容不是空的 |
-w | 檢查是否可寫 |
-x | 檢查是否可執行 |
-O | 檢查 Owner 是執行者 |
-G | 檢查 Group 是執行者的群組 |
編者註︰若想知更多,請參考 test 的 manpage。
例子六 (test-6.sh):
#!/bin/bash filename="test-6.sh" echo -n "File owner of $filename is " if [ -O $filename ]; then echo "you !" else echo "not you !" fi
例子六是另一個有關檢查檔案的例子,與前例差不多,唯獨要留意的就是在 echo 中的 -n 參數,-n 的意思就是令 echo 不會在句尾加入 ,這樣,在印出字句時,便不會開新的一行。
for ... in ...; do ...; done
語法:
for x in list do 執行指令 done
for loop 在某些情況很常用,例如要在很多的目錄工作時。請參考例子七;它是一個很簡單的 shell script,作用只是在數個目錄內找尋符合字串的檔案名稱:
例子七 (test-7.sh):
#!/bin/bash IFS=: PATH=/usr/bin:/usr/sbin:/sbin:/bin:/usr/local/sbin:/usr/local/bin if [ -z "$1" ]; then echo "Usage : test-7.sh [Keyword]" exit 1 fi for dir in $PATH do echo "Working in $dir ..." ls -al $dir | grep $1 done exit 0
例子七執行方法︰
shell@www :~# ./test-7.sh login Working in /usr/bin ... -r-sr-xr-x 1 root bin 20480 Dec 24 13:29 login -r-sr-xr-x 1 root bin 20480 Dec 24 13:18 rlogin -r-sr-xr-x 2 root bin 184320 Feb 1 10:13 slogin Working in /usr/sbin ... -r-sr-xr-x 1 root bin 12288 Dec 24 13:19 sliplogin Working in /sbin ... -r-xr-xr-x 1 root bin 20480 Dec 24 13:18 nologin Working in /bin ... Working in /usr/local/sbin ... Working in /usr/local/bin ... case... in ... esac
語法:
case <變數> in pattern 1 ) 執行指令 ;; pattern 2 ) 執行指令 ;; esac
case 就像是在 C 語言內的 select 一樣,其流程就是在 [expression] 中與各 [pattern] 作配對,如果找尋得到便會執行 [pattern] 內的語句;使用 case 的最常見例子就是 RedHat 等的 init script,當輸入 '/etc/rc.d/init.d/network restart' 時,程式會因應 'start'、'stop'、'restart' 等字眼而執行不同的語句。
在例子八中就是一個使用 case 來控制網絡介面卡的 shell script。
例子八 (test-8.sh):
#!/bin/bash IP=202.181.234.40 Eth=eth0 case "$1" in up ) ifconfig $Eth up ;; down ) ifconfig $Eth down ;; test ) ping $IP ;; * ) echo "Usage { up | down | test }" echo esac exit 0 while 與 until while 語法: while <條件> do <語句> done
在某些工作上,您會需要使用一個不斷的迴路以檢查變數的變化。如果您有使用別的程式語言的經驗,相信 while 對您來說絕不陌生;請參考例子九,它使用到了新的功能,就是 getopts,getopts 與 C 程式中的 getopts 有相同功能,用以取得程式的參數。
例子九 (test-9.sh):
#!/bin/bash while getopts "a:b:c" opt; do case $opt in a) echo -n "Argv is -a ! " ;; b) echo -n "Argv is -b ! " ;; c) echo -n "Argv is -c ! " ;; *) echo "Usage : $0 -[abc] text" exit 1;; esac done echo $2
例子九執行方法:
shell@www :~# ./test-9.sh -a "The sting here" rgv is -a ! The string here
當然,例子九並不是一個很好的程式,只是想介紹 while 怎樣與 getopts 一併使用。while 與 getopts 一併使用的情況並不罕見。
until 語法: until <條件>; do <語句> done
until 與 while 的用法很相似,所以筆者亦不加以介紹,但是一般 until 會用在如檢查指令執行的狀態上。
Function
在文章開端時,筆者提及到在 bash 內有 function 功能,也就是子程序,在一般程式寫作時很常到用,但是在撰寫 shell script 時會較少;使用子程序有很多好處,例如在程式比較複雜時,或是某些語句需要重複,這時候子程序是有必要的。
語法:
function name { <語句> } 或 是 name() { <語句> }
'name' 就是子程序的名稱,而執行的方法是直接呼叫 'name',任何輸入至子程序的參數可以加在 'name' 的後面,如:
#!/bin/bash function test { echo "test..." } test
或者
#!/bin/bash function test { echo "Argument is $1" } test abc
結語
其實在 bash 的程式設計中,還有很多的功能筆者並未介紹,希望下次有機會時再為大家介紹;如果您希望學得更多有關 shell script 的程式設計時,您可以參考 O'Reilly 出版的Learning the Bash shell 或是參考系統上原有的 shell script 和 manpage,當中您會學到其它功能的使用。
文: Shell Hung shellhung@linux.org.hk
shell script - BASH相关推荐
- 小白linux bash 学习二 shell Script
之前说了一些脚本和变量,其实都是为了现在讲shell Script也就是shell脚本做铺垫.按照我自己的理解来讲.其实shell 脚本就是一些基础的语法和命令的总和, 所以要学shell 脚本,那么 ...
- linux bash语法检查,ShellCheck — Shell Script 语法检查工具
如果在使用一些指令 (例如 rm) 不小心, ShellCheck 是一个 Shell Script 分析及除错工具, 之后要花很多时间修复, 可以在尝试执行 Shell Script 前先找出 Wa ...
- 「学习笔记-Linux」学习Shell Script
学习Shell Script Table of Contents 1 什么是Shell Scipt 1.1 程序书写 1.2 程序执行 2 简单Shell练习 2.1 例1 接收用户输入 2.2 例2 ...
- Linux08--Shell程序设计03 shell script
第一个Shell脚本--HelloWorld [root@localhost ~]# vi sh01.sh #!/bin/bash #!表明使用哪种shell # this is my first s ...
- 那些年我们一起追过的Shell Script
原本这是自己在几个月前为公司的一个分享活动写的一个投影片,今天趁大脑负荷比较小,把这个话题拿到blog上面来分享一下.从知道shell算起至今也就几个年头而已,如今勉强算是入门了.对某一个新事物的掌握 ...
- Linux shell script 的语法汇总
2019独角兽企业重金招聘Python工程师标准>>> 条件判断式 if判断式 if [ 条件判断式1 ]:then执行动作 elif [ 条件判断式2 ]:then执行动作 els ...
- linux-basic(13)学习shell script
[13.1]什么是shell script? 1)shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文字档,将一些 shell 的语法与命令( ...
- linux 脚本批量删除,Linux Shell使用Bash脚本批量删除文件
PHP每天生成一个存有系统要删除的临时文件的.out文件列表,由bash脚本逐行通过读取.out文件(每一行代表一个待删除的文件)挨个删除,代码如下(dl.sh): #!/bin/bash # She ...
- 好用的shell_Linux系统安全 | Linux中的Shell和Bash
目录 shell shell script sh bash Dash tty.pty和pts 学安全的我们,经常会听到说获得某服务器的shell,就是指获得某个服务器的操作权限.我们学习linux时, ...
最新文章
- Python 3.10的几个好用的新特性
- python文件对象提供了3个读方法、分别是-python3基础之文件对象操作
- php根据时间段分组,php按月分组(时间戳)
- jps查看java进程以及pwdx通过pid查看进程所在位置
- LeetCode 1652. 拆炸弹(前缀和)
- centos7 oracle_Centos7主机名变成bogon的原因及解决方法
- 如何做到免驱打印_证卡打印机云打印方案
- python3哪个版本稳定-Python 3.9 发布稳定版本,八大特性学起来!
- oracle怎么查导入导出记录,Oracle实验记录——数据的导入和导出
- how-to-set-java_home-environment-variable-on-mac-os-x
- 苹果智能家居—HomeKit,带给您不一样的生活体验
- 电脑蓝屏后的文件数据怎么恢复?电脑蓝屏的原因有哪些
- mysql不停库全量备份,mysql全量备份数据
- 如何将Spotify音乐下载并保存为MP3
- 统计——假设检验与p值
- Android FFmpeg集成
- worldpress小工具自定义html,WorldPress Grid html代码设置浅析,代码非常简洁
- 机器指令、操作类型、寻址方式
- 订阅号和服务号有什么区别?哪种更好?企业该如何选择?
- 2022年,中国餐饮数字化进行到哪一步了?