linux shell scripts

一. 编写shell脚本的准备

1. shell脚本的内部运行机制

  • 命令的运行是从上而下、从左而右的分析与运行; 
  • 命令的下达: 命令、选项与参数间的多个空白都会被忽略掉; 
  • 空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空白键; 
  • 如果读取到一个 Enter 符号 (CR) ,就尝试开始运行该行 (或该串) 命令; 
  • 至於如果一行的内容太多,则可以使用『 \[Enter] 』来延伸至下一行; 
  • 『 # 』可做为注解!任何加在 # 后面的数据将全部被视为注解文字而被忽略!

2. 如何启动shell脚本

  • 直接命令下达: shell.sh 文件必须要具备可读与可运行 (rx) 的权限,然后: (实际上是在bash的子程序里运行)
    • 绝对路径:使用 /home/dmtsai/shell.sh 来下达命令; 
    • 相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来运行 
    • 变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/ 
  • 以 bash 程序来运行:透过『 bash shell.sh 』或『 sh shell.sh 』来运行.(实际上是在bash的子程序里运行)
  • 以source来运行脚本(是在父bash程序运行脚本,不推荐)。

3. shell脚本头部内容

  • script 的功能; 
  • script 的版本资讯; 
  • script 的作者与联络方式; 
  • script 的版权宣告方式; 
  • script 的 History (历史纪录); 
  • script 内较特殊的命令,使用『绝对路径』的方式来下达; 
  • script 运行时需要的环境变量预先宣告与配置。

4. 编写shell脚本的习惯

  • shell脚本头部建议按照第3点进行完善;
  • 个人建议务必要加上注解说明,可以帮助你非常非常多;
  • 此外,程序码的撰写最好使用巢状方式,在包覆的内部程序码最好能以 [tab] 按键的空格向后推, 这样你的程序码会显的非常的漂亮与有条理!在查阅与 debug 上较为轻松愉快喔;
  • 使用撰写 script 的工具最好使用 vim 而不是 vi ,因为 vim 会有额外的语法检验机制,能够在第一阶段撰写时就发现语法方面的问题。

二. 简单脚本功能实践

1. 提示用户输入并拼接用户输入范例

#!/bin/bash
# study shell scripts
# 2020.03.04 auth:jet

PATH=`echo $PATH`
export PATH

read -p "please input your first name:" first_name
read -p "please input your second name:" second_name
echo -e "\n your full name is: $first_name$second_name"

2. 根据用户输入创建不同日期文件

#!/bin/bash
# study touch different date file
# 2020.03.05 auth:jet

PATH=`echo $PATH`
export PATH #提示用户输入文件名
echo -e "I will touch three dinfferent date file." read -p "please input your filename:" fileuser #判断用户是否输入了文件名 filename=${fileuser:-"filename"} #将用户输入的文件名进行不同日期处理 date1=`date --date '2 days ago' +%Y%m%d` date2=`date --date '1 days ago' +%Y%m%d` date3=`date +%Y%m%d` file1=$filename$date1 file2=$filename$date2 file3=$filename$date3 #创建不同日期的文件名 touch $file1 touch $file2 touch $file3

 

 

3. 计算器的演示

#!/bin/bash
# this is study cal
# 2020.03.05 auth:jet

PATH=`echo $PATH`
export PATH

echo -e "this is a * cal"
read -p "pls input a:" a
read -p "pls input b:" b

x=$(($a*$b))

echo -e "a*b=$x"

 

 

三. 学习判断工具

1. test判断工具

测试的标志    代表意义
1. 关於某个档名的『文件类型』判断,如 test -e filename 表示存在否
-e    该『档名』是否存在?(常用)
-f    该『档名』是否存在且为文件(file)?(常用)
-d    该『档名』是否存在且为目录(directory)?(常用)
-b    该『档名』是否存在且为一个 block device 装置?
-c    该『档名』是否存在且为一个 character device 装置?
-S    该『档名』是否存在且为一个 Socket 文件?
-p    该『档名』是否存在且为一个 FIFO (pipe) 文件?
-L    该『档名』是否存在且为一个连结档?
2. 关於文件的权限侦测,如 test -r filename 表示可读否 (但 root 权限常有例外)
-r    侦测该档名是否存在且具有『可读』的权限?
-w    侦测该档名是否存在且具有『可写』的权限?
-x    侦测该档名是否存在且具有『可运行』的权限?
-u    侦测该档名是否存在且具有『SUID』的属性?
-g    侦测该档名是否存在且具有『SGID』的属性?
-k    侦测该档名是否存在且具有『Sticky bit』的属性?
-s    侦测该档名是否存在且为『非空白文件』?
3. 两个文件之间的比较,如: test file1 -nt file2
-nt    (newer than)判断 file1 是否比 file2 新
-ot    (older than)判断 file1 是否比 file2 旧
-ef    判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩!
4. 关於两个整数之间的判定,例如 test n1 -eq n2
-eq    两数值相等 (equal)
-ne    两数值不等 (not equal)
-gt    n1 大於 n2 (greater than)
-lt    n1 小於 n2 (less than)
-ge    n1 大於等於 n2 (greater than or equal)
-le    n1 小於等於 n2 (less than or equal)
5. 判定字串的数据
test -z string    判定字串是否为 0 ?若 string 为空字串,则为 true
test -n string    判定字串是否非为 0 ?若 string 为空字串,则为 false。
注: -n 亦可省略
test str1 = str2    判定 str1 是否等於 str2 ,若相等,则回传 true
test str1 != str2    判定 str1 是否不等於 str2 ,若相等,则回传 false
6. 多重条件判定,例如: test -r filename -a -x filename
-a    (and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r 与 x 权限时,才回传 true-o    (or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r 或 x 权限时,就可回传 true!    反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true
test工具详解
#!/bin/bash
# this is study test
# 2020.03.05 auth:jet

PATH=`echo $PATH`
export=PATH

# 1. 让使用者输入档名,并且判断使用者是否真的有输入字串?

echo -e "Please input a filename, I will check the filename's type and \

permission. \n\n"

read -p "Input a filename : " filename

test -z $filename && echo "You MUST input a filename." && exit 0

# 2. 判断文件是否存在?若不存在则显示信息并结束脚本

test ! -e $filename && echo "The filename '$filename' DO NOT exist" && exit 0

# 3. 开始判断文件类型与属性

test -f $filename && filetype="regulare file"

test -d $filename && filetype="directory"

test -r $filename && perm="readable"

test -w $filename && perm="$perm writable"

test -x $filename && perm="$perm executable"

# 4. 开始输出资讯!

echo "The filename: $filename is a $filetype"

echo "And the permissions are : $perm"
test使用范例

 

2. 利用判断符号 [ ]

中括号的使用方法与 test的用法一致,区别在于表现形式,中括号的使用需要注意如下几点:

  • 在中括号 [] 内的每个组件都需要有空白键来分隔; 
  • 在中括号内的变量,最好都以双引号括号起来; 
  • 在中括号内的常数,最好都以单或双引号括号起来。

举个例子,判断两个变量是否相等。

举例1:
[ "$HOME" == "$MAIL" ] [□"$HOME"□==□"$MAIL"□] ↑ ↑ ↑ ↑

举例2:
read -p "Please input (Y/N): " yn [ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0 [ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0 echo "I don't know what your choice is" && exit 0
 

 

3. shell scripts的默认变量

脚本的默认变量的位置

/path/to/scriptname  opt1  opt2  opt3  opt4 

       $0             $1    $2    $3    $4
#!/bin/bash
PATH=`echo $PATH`
export PATH

echo "The script name is        ==> $0"

echo "Total parameter number is ==> $#"    # "$#"代表后接的参数『个数』

[ "$#" -lt 2 ] && echo "The number of parameter is less than 2.  Stop here." && exit 0

echo "Your whole parameter is   ==> '$@'"   # "$@"代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);
echo
"Your whole parameter is ==> '$*'" # "$*"代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字节,默认为空白键
echo "The 1st parameter         ==> $1"
echo "The 2nd parameter         ==> $2"

 

4. shell scripts 变量偏移

#代码:
echo "Total parameter number is ==> $#"

echo "Your whole parameter is   ==> '$@'"

shift   # 进行第一次『一个变量的 shiftecho "Total parameter number is ==> $#"

echo "Your whole parameter is   ==> '$@'"

shift 3 # 进行第二次『三个变量的 shiftecho "Total parameter number is ==> $#"

echo "Your whole parameter is   ==> '$@'"

#运行结果:
[root@www scripts]# sh sh08.sh one two three four five six <==给予六个参数

Total parameter number is ==> 6   <==最原始的参数变量情况

Your whole parameter is   ==> 'one two three four five six'

Total parameter number is ==> 5   <==第一次偏移,看底下发现第一个 one 不见了

Your whole parameter is   ==> 'two three four five six'

Total parameter number is ==> 2   <==第二次偏移掉三个,two three four 不见了

Your whole parameter is   ==> 'five six'

 

四. 条件判断语句

1. if判断语句

基础if语句语法:

if [ 条件判断式 ]; then

    当条件判断式成立时,可以进行的命令工作内容;

fi   <==将 if 反过来写,就成为 fi 啦!结束 if 之意!

#注意,在中括号判断中&& 代表 AND ;|| 代表 or ;但是亲自测试结果是&& ||不好用,会报语法错误,所以还是老老实实的用-a -o 把。
# 一个条件判断,分成功进行与失败进行 (else)

if [ 条件判断式 ]; then

    当条件判断式成立时,可以进行的命令工作内容;

else

    当条件判断式不成立时,可以进行的命令工作内容;

fi
# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况运行

if [ 条件判断式一 ]; then

    当条件判断式一成立时,可以进行的命令工作内容;

elif [ 条件判断式二 ]; then

    当条件判断式二成立时,可以进行的命令工作内容;

else

    当条件判断式一与二均不成立时,可以进行的命令工作内容;

fi
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH



if [ "$1" == "hello" ]; then

    echo "Hello, how are you ?"

elif [ "$1" == "" ]; then

    echo "You MUST input parameters, ex> {$0 someword}"

else

    echo "The only parameter is 'hello', ex> {$0 hello}"

fi
if范例

 

2. case判断语句

case  $变量名称 in   <==关键字为 case ,还有变量前有钱字号

  "第一个变量内容")   <==每个变量内容建议用双引号括起来,关键字则为小括号 )

    程序段

    ;;            <==每个类别结尾使用两个连续的分号来处理!

  "第二个变量内容")

    程序段

    ;;

  *)                  <==最后一个变量内容都会用 * 来代表所有其他值

    不包含第一个变量内容与第二个变量内容的其他程序运行段

    exit 1

    ;;

esac                  <==最终的 case 结尾!『反过来写』思考一下!
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH



case $1 in

  "hello")

    echo "Hello, how are you ?"

    ;;

  "")

    echo "You MUST input parameters, ex> {$0 someword}"

    ;;

  *)   # 其实就相当於万用字节,0~无穷多个任意字节之意!

    echo "Usage $0 {hello}"

    ;;

esac
case范例
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH



echo "This program will print your selection !"

# read -p "Input your choice: " choice # 暂时取消,可以替换!

# case $choice in                      # 暂时取消,可以替换!

case $1 in                             # 现在使用,可以用上面两行替换!

  "one")

    echo "Your choice is ONE"

    ;;

  "two")

    echo "Your choice is TWO"

    ;;

  "three")

    echo "Your choice is THREE"

    ;;

  *)

    echo "Usage $0 {one|two|three}"

    ;;

esac
case进阶范例

 

3. shell scripts的函数

function fname() {

    程序段

}
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH



function printit(){

    echo -n "Your choice is "     # 加上 -n 可以不断行继续在同一行显示

}



echo "This program will print your selection !"

case $1 in

  "one")

    printit; echo $1 | tr 'a-z' 'A-Z'  # 将参数做大小写转换!

    ;;

  "two")

    printit; echo $1 | tr 'a-z' 'A-Z'

    ;;

  "three")

    printit; echo $1 | tr 'a-z' 'A-Z'

    ;;

  *)

    echo "Usage $0 {one|two|three}"

    ;;

esac
函数的使用范例
#!/bin/bash

# function 也是拥有内建变量的~他的内建变量与 shell script 很类似, 函数名称代表示 $0 ,而后续接的变量也是以 $1, $2... 来取代的~ 这里很容易搞错喔~因为『 function fname() { 程序段 } 』内的 $0, $1... 等等与 shell script 的 $0 是不同的。

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH



function printit(){

    echo "Your choice is $1"   # 这个 $1 必须要参考底下命令的下达

}



echo "This program will print your selection !"

case $1 in

  "one")

    printit 1  # 请注意, printit 命令后面还有接参数!

    ;;

  "two")

    printit 2

    ;;

  "three")

    printit 3

    ;;

  *)

    echo "Usage $0 {one|two|three}"

    ;;

esac

#在上面的例子当中,如果你输入『 sh sh12-3.sh one 』就会出现『 Your choice is 1 』的字样~ 为什么是 1 呢?因为在程序段落当中,我们是写了『 printit 1 』那个 1 就会成为 function 当中的 $1 .
理解函数变量范例

 

五. 循环语句

1. while循环

while [ condition ]  <==中括号内的状态就是判断式

do            <==do 是回圈的开始!

    程序段落

done          <==done 是回圈的结束
until [ condition ]

do

    程序段落

done
while [ "$yn" != "yes" -a "$yn" != "YES" ]

do

    read -p "Please input yes/YES to stop this program: " yn

done

echo "OK! you input the correct answer."
while范例
until [ "$yn" == "yes" -o "$yn" == "YES" ]

do

    read -p "Please input yes/YES to stop this program: " yn

done

echo "OK! you input the correct answer."
until范例
s=0  # 这是加总的数值变量

i=0  # 这是累计的数值,亦即是 1, 2, 3....

while [ "$i" != "100" ]

do

    i=$(($i+1))   # 每次 i 都会添加 1 

    s=$(($s+$i))  # 每次都会加总一次!

done

echo "The result of '1+2+3+...+100' is ==> $s"
1累加到100范例

 

2. for循环

for var in con1 con2 con3 ...

do

    程序段

done
#范例1
for animal in dog cat elephant

do

    echo "There are ${animal}s.... "

done

#范例2
users=$(cut -d ':' -f1 /etc/passwd)  # 撷取帐号名称

for username in $users               # 开始回圈进行!

do

        id $username

        finger $username

done


#范例3
network="192.168.1"              # 先定义一个网域的前面部分!

for sitenu in $(seq 1 100)       # seq 为 sequence(连续) 的缩写之意

do

    # 底下的程序在取得 ping 的回传值是正确的还是失败的!

        ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1

    # 开始显示结果是正确的启动 (UP) 还是错误的没有连通 (DOWN)

        if [ "$result" == 0 ]; then

                echo "Server ${network}.${sitenu} is UP."

        else

                echo "Server ${network}.${sitenu} is DOWN."

        fi

done


#范例4
# 1. 先看看这个目录是否存在啊?

read -p "Please input a directory: " dir

if [ "$dir" == "" -o ! -d "$dir" ]; then

    echo "The $dir is NOT exist in your system."

    exit 1

fi



# 2. 开始测试文件罗~

filelist=$(ls $dir)        # 列出所有在该目录下的文件名称

for filename in $filelist

do

    perm=""

    test -r "$dir/$filename" && perm="$perm readable"

    test -w "$dir/$filename" && perm="$perm writable"

    test -x "$dir/$filename" && perm="$perm executable"

    echo "The file $dir/$filename's permission is $perm "

done
for循环用法1范例
for (( 初始值; 限制值; 运行步阶 ))

do

    程序段

done
read -p "Please input a number, I will count for 1+2+...+your_input: " nu



s=0

for (( i=1; i<=$nu; i=i+1 ))

do

    s=$(($s+$i))

done

echo "The result of '1+2+3+...+$nu' is ==> $s"
for循环用法2范例

 

六. shell脚本debug

scripts 在运行之前,最怕的就是出现语法错误的问题了,可以在脚本运行之前通过以下方法进行debug。

[root@www ~]# sh [-nvx] scripts.sh

选项与参数:

-n  :不要运行 script,仅查询语法的问题;,没有问题不会有任何显示。

-v  :再运行 sccript 前,先将 scripts 的内容输出到萤幕上;

-x  :将使用到的 script 内容显示到萤幕上,这是很有用的参数!

 

posted @ 2020-03-04 21:40  IamJet  阅读(268)  评论(0编辑  收藏  举报