龙之介大人

shell的进阶上
11.1 script 的编写与执行shell script 其实就是纯文本档,我们可以编辑这个文件,然后让这个文...
扫描右侧二维码阅读全文
28
2019/08

shell的进阶上

11.1 script 的编写与执行

shell script 其实就是纯文本档,我们可以编辑这个文件,然后让这个文件来帮我们一次执行多个指令,或者是利用一些运算与逻辑判断来帮我们达成某些功能。所以,要编辑这个文件的内容时,当然就需要具备有 bash 指令下达的相关认识。

  • 注意事项:
  1. 指令的执行是从上而下、从左而右的分析与执行;
  2. 指令的下达就如同第四章内提到的: 指令、选项与参数间的多个空白都会被忽略掉;
  3. 空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空格键;
  4. 如果读取到一个 Enter 符号 (CR) ,就尝试开始执行该行 (或该串) 命令;
  5. 至于如果一行的内容太多,则可以使用『 [Enter] 』来延伸至下一行;
  6. 『 # 』可做为批注!任何加在 # 后面的资料将全部被视为批注文字而被忽略!

    如此一来,我们在 script 内所撰写的程序,就会被一行一行的执行。现在我们假设你写的这个程序 文件名是 /home/xiaoqi/shell.sh 那如何执行这个文件?有底下几个方法:

  • 直接指令下达: shell.sh 文件必须要具备可读与可执行 (rx) 的权限,然后:
  1. 绝对路径:使用 /home/dmtsai/shell.sh 来下达指令;
  2. 相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来执行
  3. 变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/
  • 以 bash 程序来执行:透过『 bash shell.sh 』或『 sh shell.sh 』来执行

由于 CentOS 默认用户家目录下的 ~/bin 目录会被设定到 ${PATH} 内,所以你也可以将 shell.sh 建立在 /home/xiaoqi/bin/ 底下 ( ~/bin 目录需要自行设定) 。此时,若 shell.sh 在 ~/bin 内且具有 rx 的权限, 那就直接输入 shell.sh 即可执行该脚本程序!

  • 那为何sh shell.sh也可以执行呢?

这是因为 /bin/sh 其实就是 /bin/bash (连结档),使用 sh shell.sh 亦即告诉系统,我想要直接以 bash 的功能来执行 shell.sh 这个文件内的相关指令的意思,所以此时 你的 shell.sh 只要有 r 的权限即可被执行喔!而我们也可以利用 sh 的参数,如 -n 及 -x 来检查与 追踪 shell.sh 的语法是否正确!

11.2 shell写hello world

[xiaoqi@study bin]$ mkdir bin;cd bin 
[xiaoqi@study bin]$ vim hello.sh
#!/bin/bash
#Program:
#!       This progeam shows "Hello World" in your screen.
#History
#2019/08/25     xiaoqi          First release

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

export PATH
echo -e  "Hello World \a \n"
exit 0

请将所有编写的 script 放置到你家目录的 ~/bin 这个目录内,以后比较好管理!

  • 整个程序的撰写分成数段,大致是这样:
  1. 第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:

因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来宣告这个文件内的语法使用 bash 的语法! 那么当这个程序被执行时,他就能够加载 bash 的相关环境配置文件 (一般来说就是 non-login shell 的 ~/.bashrc), 并且执行 bash 来使我们底下的指令能够执行!这很重要的!(在很多状况中,如果没有设定好 这一行, 那么该程序很可能会无法执行,因为系统可能无法判断该程序需要使用什么 shell 来执行!

  1. 程序内容的说明:

整个 script 当中,除了第一行的『 #! 』是用来宣告 shell 的之外,其他的 # 都是『批注』用途! 所以 上面的程序当中,第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本信息; 3. 作者与联络方式; 4. 建立日期;5. 历史纪录 等等。这将有助于未来程序的改写与 debug !

  1. 主要环境变量的宣告:

建议务必要将一些重要的环境变量设定好,个人认为,PATH 与 LANG (如果有使用到输出相关的信息时) 是当中最重要的!如此一来,则可让我们这支程序在进行时,可以直接下达一些外部指令,而不必 写绝对路径!

  1. 主要程序部分

就将主要的程序写好即可!在这个例子当中,就是 echo 那一行!

  1. 执行成果告知 (定义回传值)

是否还记得之前讨论一个指令的执行成功与否,可以使用 $? 这个变量来观察~ 那么我们也可以利用 exit 这个指令来让程序中断,并且回传一个数值给系统。在我们这个例子当中,使用 exit 0 ,这代表离开 script 并且回传一个 0 给系统,所以我执行完这个 script 后,若接着下达 echo $? 则可得到 0 的值!利用这个 exit n (n 是数字) 的功能,我们还可以自定义错误讯息!

练习简单的shell

利用 date 进行文件的建立

假设我的服务器内有数据库,数据库每天的数据都不太一样,因此当我备份时,希望将每天的资料都备份成不同的档名, 这样才能够让旧的数据也能够保存下来不被覆盖。

所以我可以将档名取成类似: backup.2019-08-16.data ,不就可以每天一个不同档名了吗?那个 2019-08-28 怎么来的?那就是重点啦!接下来出个相关的例子:

假设我想要建立三个空的文件 (透过 touch) ,档名最开头由使用者输入决定, 假设使用者输入 filename 好了,那今天的日期是 2019-08-28 , 我想要以前天、昨天、今天的日期 来建立这些文件,亦即 filename_20190528, filename_20190527, filename_20190526 ,该如何建立呢?

[root@study xiaoqi]# vim create_3_filename.sh
#!/bin/bash


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

#1. 让使用者输入文件名,并取得filename这个变量
echo -e "I will use 'touch' command to create 3 files."
read -p "Please input your filename: " fileuser

#2. 为了避免使用者随意按 Enter ,利用变量功能分析档名是否有设定
filename=${fileuser:-"filename"}

#3. 开始利用 date 指令来取得所需要的档名了;
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}

#4. 建立文件
touch "${file1}"
touch "${file2}"
touch "${file3}"

『 $(command) 』的取得讯息、变量的设定功能、变量的累加以及利用 touch 指令辅助!如果你开始执行这个 create_3_filename.sh 之 后,你可以进行两次执行:一次直接按 [Enter] 来查阅档名是啥? 一次可以输入一些字符,这样可以判断你的脚本是否设计正确!

简单的加减乘除

使用 declare 来定义变量的类型,当变量定义成为整数后才能够进 行加减运算!此外,我们也可以利用『 $((计算式)) 』来进行数值运算的。 可惜的是,bash shell里头预设仅支持到整数的数据而已。

[root@study xiaoqi]# vim multiplying.sh 
#!/bin/bash

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

echo -e "You SHOULD input 2 numbers, I will multiplying them! \n"
read -p "first number: " firstnu
read -p "second number: " secnu
total=$((${firstnu}*${secnu}))
echo -e "\nThe result of ${firstnu} x ${secnu} = ${total}"

在数值的运算上,我们可以使用『 declare -i total=${firstnu}*${secnu} 』 也可以使用上面的方式来进行!

比较建议使用这样的方式来进行运算:var=$((运算内容))不但容易记忆,而且也比较方便的多,因为两个小括号内可以加上空格符.

至于数值运算上的处理,则有:『 +, -, *, /, % 』等等。 那个 % 是取余数!

另外,如果你想要计算含有小数点的数据时,其实可以透过 bc 这个指令的协助喔!例如可以这样做:

[root@study xiaoqi]# echo "12.2*4.33" | bc                   
52.82

透过 bc 计算 pi

其实计算 pi 时,小数点以下位数可以无限制的延伸下去!而 bc 有提供一个运算 pi 的函式,只是 想要使用该函式必须要使用 bc -l 来使用.也因为这个小数点以下位数可以无线延伸运算的特性存在,所以我们可以透过底下这只小脚本来让使用者输入一个『小数点为数值』, 以让 pi 能够更准确!

[root@study xiaoqi]# vim cal_pi.sh  
#!/bim/bash

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

echo -e "This program will calculate pi value. \n"
echo -e "You should input a float number to calculate pi value.\n"
read -p "The scale number (10~10000) ? " checking

num=${checking:-"10"}

echo -e "Starting calcuate pi value. Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq

上述数据中,那个 4*a(1) 是 bc 主动提供的一个计算 pi 的函数,至于 scale 就是要 bc 计算几个 小数点下位数的意思。当 scale 的数值越大, 代表 pi 要被计算的越精确,当然用掉的时间就会越多!

11.3 script 的执行方式差异 (source, sh script, ./script)

不同的 script 执行方式会造成不一样的结果喔!尤其影响 bash 的环境很大呢!脚本的执行方式除了 前面小节谈到的方式之外,还可以利用 source 或小数点 (.) 来执行.那么这种执行方式有何不同呢?

利用直接执行的方式来执行 script

当使用前一小节提到的直接指令下达 (不论是绝对路径/相对路径还是 ${PATH} 内),或者是利用 bash (或 sh) 来下达脚本时,该 script 都会使用一个新的 bash 环境来执行脚本内的指令!也就是 说,使用这种执行方式时, 其实 script 是在子程序的 bash 内执行的!我们在第十章 BASH 内谈 到 export 的功能时,曾经就父程序/子程序谈过一些概念性的问题, 重点在于:『当子程序完成后, 在子程序内的各项变量或动作将会结束而不会传回到父程序中』!这是什么意思呢?

我们举刚刚提到过的 showname.sh 这个脚本来说明好了,这个脚本可以让用户自行设定两个变量, 分别是 firstname 与 lastname,想一想,如果你直接执行该指令时,该指令帮你设定的 firstname 会 不会生效?看一下底下的执行结果:

[root@study xiaoqi]# echo ${firstname} ${lastname}
            <--变量不存在
[root@study xiaoqi]# sh showname.sh           
Please input your first name: xiao
Please input your second name: qi
You Full name is : xiaoqi        <--在 script 运作中,这两个变量有生效
[root@study xiaoqi]# echo ${firstname} ${secname} 
        <--事实上,这两个变量在父程序的 bash 中还是不存在的!

上面的结果你应该会觉得很奇怪,怎么我已经利用 showname.sh 设定好的变量竟然在 bash 环境底下无效!怎么回事呢?

如果将程序相关性绘制成图的话,我们以下图来说明。当你使用直接执行的 方法来处理时,系统会给予一支新的 bash 让我们来执行 showname.sh 里面的指令,因此你的 firstname, lastname 等变量其实是在下图中的子程序 bash 内执行的。当 showname.sh 执行完毕后, 子程序 bash 内的所有数据便被移除,因此上表的练习中,在父程序底下 echo ${firstname} 时,就利用 source 来执行脚本:在父程序中执行如果你使用 source 来执行指令那就不一样了!同样的脚本我们来执行看看:看不到任何东西了!

利用 source 来执行脚本:在父程序中执行

如果你使用 source 来执行指令那就不一样了!同样的脚本我们来执行看看:

[root@study xiaoqi]# 
[root@study xiaoqi]# source showname.sh                  
Please input your first name: xiao
Please input your second name: qi
You Full name is : xiaoqi
[root@study xiaoqi]# echo ${firstname} ${secname}   
xiao qi        <--变量有内容

竟然生效了!没错!因为 source 对 script 的执行方式可以使用底下的图示来说明! showname.sh 会在父程序中执行的,因此各项动作都会在原本的 bash 内生效!这也是为啥你不注销系统而要让某 些写入 ~/.bashrc 的设定生效时,需要使用source ~/.bashrc而不能使用bash ~/.bashrc 是一样的!

11.4 善用判断式

在上一章中,我们提到过 $? 这个变量所代表的意义, 此外,也透过 && 及 || 来作为前一个指令执 行回传值对于后一个指令是否要进行的依据。如果想要判断一个目录是否存在, 当 时我们使用的是 ls 这个指令搭配数据流重导向,最后配合 $? 来决定后续的指令进行与否。 但是 否有更简单的方式可以来进行『条件判断』呢?有的~那就是『 test 』这个指令。

11.4.1 利用 test 指令的测试功能

当我要检测系统上面某些文件或者是相关的属性时,利用 test 这个指令来工作真是好用得不得了, 举例来说,我要检查 /dmtsai 是否存在时,使用:

[root@study xiaoqi]# test -e /root/

执行结果并不会显示任何讯息,但最后我们可以透过 $? 或 && 及 || 来展现整个结果呢! 例如我 们在将上面的例子改写成这样:

[root@study xiaoqi]# test -e /xiaoqi && echo "exist" || echo "Not exist"
Not exist        <--结果输出不存在

最终的结果可以告知我们是『exist』还是『Not exist』呢!那我知道 -e 是测试一个『东西』在不在, 如果还想要测试一下该档名是啥玩意儿时,还有哪些标志可以来判断?有底下这些参数!

  • 关于某个档名的『文件类型』判断,如 test -e filename 表示存在否
测试的标志代表意义
-e该『档名』是否存在?(常用)
-f该『档名』是否存在且为文件(file)?(常用)
-d该『文件名』是否存在且为目录(directory)?(常用)
-b该『档名』是否存在且为一个 block device 装置?
-c该『档名』是否存在且为一个 character device 装置?
-S该『档名』是否存在且为一个 Socket 文件?
-p该『档名』是否存在且为一个 FIFO (pipe) 文件? -L 该『档名』是否存在且为一个连结档?
  • 关于文件的权限侦测,如 test -r filename 表示可读否 (但 root 权限常有例外)
测试的标志代表意义
-r侦测该档名是否存在且具有『可读』的权限?
-w侦测该档名是否存在且具有『可写』的权限?
-x侦测该档名是否存在且具有『可执行』的权限?
-u侦测该文件名是否存在且具有『SUID』的属性?
-g侦测该文件名是否存在且具有『SGID』的属性?
-k侦测该文件名是否存在且具有『Sticky bit』的属性?
-s侦测该档名是否存在且为『非空白文件』?
  • 两个文件之间的比较,如: test file1 -nt file2
测试的标志代表意义
-nt(newer than)判断 file1 是否比 file2 新
-ot(older than)判断 file1 是否比 file2 旧
-ef判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode !
  • 关于两个整数之间的判定,例如 test n1 -eq n2
测试的标志代表意义
-eq两数值相等 (equal)
-ne两数值不等 (not equal)
-gtn1 大于 n2 (greater than)
-ltn1 小于 n2 (less than)
-gen1 大于等于 n2 (greater than or equal)
-len1 小于等于 n2 (less than or equal)
  • 判定字符串的数据
测试的标志代表意义
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
  • 多重条件判定,例如: 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 来帮我们写几个简单的例子。首先,判断一下,让使用者输入一个档名, 我们判断:
  1. 这个文件是否存在,若不存在则给予一个『Filename does not exist』的讯息,并中断程序;
  2. 若这个文件存在,则判断他是个文件或目录,结果输出『Filename is regular file』或 『Filename is directory』
  3. 判断一下,执行者的身份对这个文件或目录所拥有的权限,并输出权限数据!
[root@study xiaoqi]# cat file_perm.sh 
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
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 for you are : ${perm}"

如果你执行这个脚本后,他会依据你输入的档名来进行检查喔!先看是否存在,再看为文件或目录类 型,最后判断权限。 但是你必须要注意的是,由于 root 在很多权限的限制上面都是无效的,所以 使用 root 执行这个脚本时, 常常会发现与 ls -l 观察到的结果并不相同!所以,建议使用一般使用 者来执行这个脚本试看看。

11.4.2 利用判断符号 [ ]

除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ] 』(就是中括号啦) 来进行 数据的判断呢! 举例来说,如果我想要知道 ${HOME} 这个变量是否为空的,可以这样做:

[xiaoqi@study ~]$ [ -z "${HOME}" ] ; echo $?     
1

使用中括号必须要特别注意,因为中括号用在很多地方,包括通配符与正规表示法等等,所以如果要 在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空格符来分 隔喔! 假设我空格键使用『□』符号来表示,那么,在这些地方你都需要有空格键:

[ "$HOME" == "$MAIL" ] 
[□"$HOME"□==□"$MAIL"□]
 ↑       ↑  ↑       ↑

你会发现在上面的判断式当中使用了两个等号『 == 』。其实在 bash 当中使用一个等号与两个等号的结果是一样的! 不过在一般惯用程序的写法中,一个等号代表『变量的设定』,两个等号则是 代表『逻辑判断 (是与否之意)』。 由于我们在中括号内重点在于『判断』而非『设定变量』,因此建议还是 使用两个等号较佳!

上面的例子在说明,两个字符串 ${HOME} 与 ${MAIL} 是否相同的意思,相当于 test ${HOME} == ${MAIL} 的意思啦! 而如果没有空白分隔,例如 [${HOME}==${MAIL}] 时,我们的 bash 就会显 示错误讯息了!这可要很注意啊! 所以说,你最好要注意:

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

为什么要这么麻烦啊?直接举例来说,假如我设定了 name="VBird Tsai" ,然后这样判定:

[root@study xiaoqi]# [ ${name} == "Vbird" ]   
bash: [: 参数太多

怎么会发生错误?bash 还跟我说错误是由于『太多参数 (arguments)』所致! 为什么呢? 因为 ${name} 如果没有使用双引号刮起来,那么上面的判定式会变成:

[ VBird Tsai == "VBird" ]

上面这个是错的.因为一个判断式仅能有两个数据的比对,上面 VBird 与 Tsai 还有 "VBird" 就有 三个资料! 这不是我们要的!我们要的应该是底下这个样子:

[ "VBird Tsai" == "VBird" ]

这可是差很多的喔!另外,中括号的使用方法与 test 几乎一模一样啊~ 只是中括号比较常用在条件 判断式 if ..... then ..... fi 的情况中就是了。

那我们也使用中括号的判断来做一个小案例好了,案 例设定如下:

  1. 当执行一个程序的时候,这个程序会让用户选择 Y 或 N ,
  2. 如果用户输入 Y 或 y 时,就显示『 OK, continue 』
  3. 如果用户输入 n 或 N 时,就显示『 Oh, interrupt !』
  4. 如果不是 Y/y/N/n 之内的其他字符,就显示『 I don't know what your choice is 』

利用中括号、 && 与 || 来完成!

[root@study xiaoqi]# vim ans_yn.sh 
 #!/bin/bash

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

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

由于输入正确 (Yes) 的方法有大小写之分,不论输入大写 Y 或小写 y 都是可以的,此时判断式内 就得要有两个判断才行! 由于是任何一个成立即可 (大写或小写的 y) ,所以这里使用 -o (或) 连结 两个判断!利用这个字符串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类!

11.4.3Shell script的默认变量($0, $1...)

我们知道指令可以带有选项与参数,例如 ls -la 可以察看包含隐藏文件的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢?

举例来说,如果你想要重新启动系统的网络,可以这样做:

[root@study xiaoqi]# file /etc/init.d/network 
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
#使用 file 来查询后,系统告知这个文件是个 bash 的可执行 script !

[root@study xiaoqi]# /etc/init.d/network restart
Restarting network (via systemctl):                        [  确定  ]

restart 是重新启动的意思,上面的指令可以『重新启动 /etc/init.d/network 这个程序』
那么如果你在 /etc/init.d/network 后面加上 stop 呢?就可以直接关闭该服务了!

如果你要依据程序的执行给予一些变量去进行不同的任务时,本章一开始是使用 read 的功 能!但 read 功能的问题是你得要手动由键盘输入一些判断式。如果透过指令后面接参数, 那么一个指令就能够处理完毕而不需要手动再次输入一些变量行为!这样下达指令会比较简单方便啦!

script 是怎么达成这个功能的呢?其实 script 针对参数已经有设定好一些变量名称了!对应如下:

/path/to/scriptname opt1 opt2 opt3 opt4
        $0             $1      $2   $3    $4

执行的脚本档名为 $0 这个变量,第一个接的参数就是 $1 所以,只要我们 在 script 里面善用 $1 的话,就可以很简单的立即下达某些指令功能了!
了这些数字的变量之外, 我们还有一些较为特殊的变量可以在 script 内使用来呼叫这些参数!

  • $# :代表后接的参数『个数』,以上表为例这里显示为『 4 』;
  • $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);
  • $* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字符,默认为空格键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。

那个 $@ 与 $* 基本上还是有所不同啦!不过,一般使用情况下可以直接记忆 $@ 即可!

来做个例子吧~假设我要执行一个可以携带参数的 script ,执行该脚本后屏幕会显示如下的数据:

  • 程序的文件名为何?
  • 共有几个参数?
  • 若参数的个数小于 2 则告知使用者参数数量太少
  • 全部的参数内容为何?
  • 第一个参数为何?
  • 第二个参数为何
[root@study xiaoqi]# vim how_paras.sh
#!/bin/bash

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 
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  ==> '$@'"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"

执行结果:

[root@study xiaoqi]# sh how_paras.sh theone haha quit
The script name is ==> how_paras.sh        <--文件名
Total parameter number is ==> 3            <--传入的3个参数
Your whole parameter is  ==> 'theone haha quit'    <--传入的全部参数    
The 1st parameter       ==> theone        <--第一个参数
The 2nd parameter       ==> haha        <--第二个参数
  • shift:造成参数变量号码的便宜

除此之外,脚本后面所接的变量是否能够进行偏移 (shift) 呢?我们直接以底下的范 例来说明好了, 用范例说明比较好解释!我们将 how_paras.sh 的内容稍作变化一下,用来显示每次 偏移后参数的变化情况:

[root@study xiaoqi]# cat shift_paras.sh 
#!/bin/bash

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

echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'" 
shift # 进行第一次『一个变量的 shift 』 
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'" 
shift 3 # 进行第二次『三个变量的 shift 』 
echo "Total parameter number is ==> $#" 
echo "Your whole parameter is ==> '$@'"

执行结果:

[root@study xiaoqi]# sh shift_paras.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'
 

光看结果你就可以知道啦,那个 shift 会移动变量,而且 shift 后面可以接数字,代表拿掉最前面的 几个参数的意思。 上面的执行结果中,第一次进行 shift 后他的显示情况是『 one two three four five six』,所以就剩下五个啦!第二次直接拿掉三个,就变成『 two three four five six 』

最后修改:2019 年 08 月 28 日 11 : 06 PM

发表评论