编写 shell脚本

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mokang0421/article/details/70833318

之前两周,参考<快乐的linux命令行> 学习了基本的shell命令,虽然好多细节没有记得很清楚。但大致操作了下。

下面,将学习 如何编写shell脚本


<第26章  编写第一个shell脚本>

我们通过前面章节,大概学习了shell命令行的使用. 他们一般单独的出现在 Terminal的命令行中,完成某种单一的功能。

shell脚本是 把这些整合在一起,完成一个 较大的 函数功能。


一个shell脚本,赢具备 基础的三点特征:

(1)  #! /bin/bash                shebang打头

(2) 具有可执行权限,一般是 755 或者700权限

(3) 最好把脚本 放在一个 路径下,  并且加入环境PATRH内。  这样更加便捷。


脚本位置说明:   脚本的存放路径置于 PATH 内,在命令行中调用脚本时,shell会自动搜索各PATH的 可执行文件,给出补全符. 

这样不管在什么路径下,都能直接调用某一脚本,不用费心的输入脚本所在的路径。


<第27章 启动一个项目>


其实呢,整个shell脚本的编写,有点像C程序的编写. 整个结构也是相似,本质上是编程思想。


1.定义 变量


C程序中最常用的就是设置变量:如 a=2;b=1;b=b+1.          shell脚本为了便捷,也会如此处理.如 set printlog= " this step is rigth"


需要注意的shell 定义变量的 语法:  =号前后都不能出现空格.      (原因;  变量=表达式.    变量名称必须是字母下划线数字不能有空格符,所以=前面不能有空格

          后面可以有空格,但必须用 双引号 ,因为shell遇到空格认为,变量字符串结束了。

默认  大写字母表示 常量; 小写字母表示 变量。


2. 调用变量

 定义过的变量,可以使用$符号置换,  常见的使用方法为  $a  $b

其实这是 省略写法, 真正的变量调用是:  ${a}  其中花括号作用是 : 告诉命令解析器,变量名字的边界位置。  推荐给所有变量加上{},这是一个好的习惯


3. 定义函数

有两种方法定义

function   name {                              name () {                       即 关键词 function可以省略掉

       cmd                                                       cmd

      [ return ]                                                [ return ]

}                                                                    }

return可以加,也可以不加;不加时,会吧最后一个cmd的结果返回出来.  如果加,必须是0-255之间的一个数字.通常用1和0.

 returen返回值  保存在 $? 这个变量中


调用函数:    name  【参数】....   会自动把参数命名为 $1 $2 $3 .....   如果参数数目不够,就相当于 $2 $3..为空

                   

4. 函数内部变量;

  在函数内部,我们希望定义一些 可读性较好的变量名.  这些值仅在函数内部使用即可. 为了避免和外面变量混淆.起名字将会非常困难.

我们只需要在 函数内部定义下:   local a;

                                                         a=hhh;   就可以在该函数内,安全的使用变量a


<第 二十八章  if 结构>

  在上面一小节中,我们知道了变量的定义方式,包括局部变量、全局变量。 也了解了一些 shell函数的定义方式。

我们知道 shell函数会在最后 返回一个值。  (但我们还不是很清楚的知道这个返回值 究竟是什么,代表什么意义)


1.   当shell命令执行后,不论对错都会有一个返回值,写入 "? "这个全局变量中。

     特别注意:   0 代表 命令执行正确;其他值表示命令执行失败. 而且这个返回值,是0-255之间的。

2.  shell内部有两个 命令:  true 和 false.      true一定能执行成功,使得 $?=0 .

                                                                           false一定执行失败, 使得 $? =1


需要特别提醒的是:   shell 中,  0代表Ture .  非零代表false.   这一点于很多语言都不一样。


3.  再看 if 语句.

    shell中的if结构:  if   commands 1 ;   then 

                                       operation1...

                                 【else if cmomands2 ; then

                                       operation2 ...

                                  】

                                   【else    operation3 ... 】

                                 fi


if 先判断 commands1 命令系类的 最后一个命令的执行结果.   如果正确执行了,或者返回值是 0.(代表True)  .    就进行operation1.

   否则再去判断 command2......   







4.  我们常用的 if 条件

    (1) if test expression  的意思是   test 是一个命令, 判断expression表达式的真伪.   也就是说   command1 =  test expression

    (2) 当然了,这样书写,对于shell脚本可读性不太好,它的等效写法是    if [ expression ]  注意[] 前后要有空格。

          本质上就是  test expression = 【 expression 】

   

常用的表达式 expression 主要有一下:

        -e file    :存在  (注意file是  路径+文件名)

        -f file : 存在且是一个普通文件。

        -w file  : 某文件可写

       -x file   : 某文件可以被执行

      -r  file  :  某文件可读


      -z  string :  字符串长度为0

      -n  string:  字符串长度大于0

     string1 == string2  : 字符串1 和字符串2相同

     string1 != string2  : 字符串1 和字符串2相同

    string1  \> string2 :   ascii 排序 string1 排在string2前面

   sting1 \< string2 :   排序 string1 排在 string2 后面


     integer1  -eq      integer2   : 整数1 等于整数2 

     integer1  -ne      integer2   : 整数1 不等于整数2 

     -lh  :   less than  小于

     -le  : less equal  小于等于

    -gh  :  greater than 大于

    -ge  :  greater equal 大于等于


if  [ -e file   -a    -r  file ]   表示文件存在 并且可读

if [ $a -eq 2 || $a > 5]   表示数据a等于2 或者大于5时 为真 执行... 

if  [ ! -e file ]  文件不存在



5. [[ ]] 和[] 的区别  

[ ] 是一个命令  [ expression ] 里面的expression 是 命令[ ] 的参数。(等价于 test expression) 。  并且呢, 这个[ ] 命令在 很多shell中都是支持的。

比如 bash、csh、tsh等等。  

我们知道shell 运行时, 会对 命令的参数执行  字符预扩展。  所以 expression 里面出现 特殊字符时候,需要注意。  最好前面加上 反斜杠,使其变成正常字符、


[[ ]]  不是一个命令,而仅仅是一个 特殊的书写符号。用作关键字。 并且并不是所有的shell 都支持 这样书写。

      bash、ksh支持,而 ash、bsh不支持。 两者完成的功能基本相同、  下面将比较两个的异同点


  类别                                                                                 [  ]                                                         [[  ]]

(1)     整数测试  -eq -lt -gt -ge -le -ne                          支持                                                     支持

(2)    文件测试  -e -w -r -x 等等...                                  支持                                                     支持

(3)    字符串测试   == !=  -z -n  < >                             <  > 前面需要加反斜杠转义               支持

(4)   逻辑表达式                                                             -a -o  !                                                  &&   ||  !     (两者描述 条件1 ...条件2 的语法不一样) 

(5)  简单模式匹配.                                     仅支持 * . 正则,不支持? +等其他                         支持

(6)  string  =~ regep                                                       不支持                                                  特别技能!!!

(7) 算数扩展 如 99+1                                                     不支持                                                    支持   (但是不推荐使用)            


6. 上面我们比较了[] 和 [[ ]] 的异同点, 一版而言 [[ ]] 语法不易出错且宜读。推荐使用 [[ ]]  

     上面第(7)  算数扩展项,我们不推荐使用[[ ]] 。那用什么好??   后面章节会再做补充



<< 第29章    和键盘进行交互>> 

我们在使用 shell脚本时候,都是直接调用就可以了。   但是有一些脚本,需要处理键盘输入数据。  这个时候 键盘和脚本怎么交互呢? 

read命令就能完成此任务。

read [ option]...   [Varname]   :    read + 选项 + 参数名


当在shell命令行中输入:   read    var1 之后.    shell命令行会自动的另起一行,   让我们我们输入信息.   我们输入完信息之后,按下回车键。read命令就执行完毕了。如下所示

[caokang@caokang Prj]$ read var1
asdasd
[caokang@caokang Prj]$ 

这个命令完成的功能是:   把输入的信息"asdasd" 赋值给 Var1变量。


当输入多个 变量名,如下:

[caokang@caokang Prj]$ read var1 var2 var3
a b d d e
[caokang@caokang Prj]$ echo $var1 
a
[caokang@caokang Prj]$ echo $var2
b
[caokang@caokang Prj]$ echo $var3
d d e

read命令的机制,是把  第一个空格前输入信息  赋值给变量1 ,  第1~2个空格之间的数据赋值给变量2. 在把最后的数据  全都给变量3.

 

如果输入信息如下                        

[caokang@caokang Prj]$ read var1 var2 var3
a
[caokang@caokang Prj]$ 
这时候var1 =a  var2、var3都是空。    


同样,当我们 只输入 read,不带有 参数名时候:  这个时候会把  下面一整行的输入信息,存入REPLY变量中

[caokang@caokang Prj]$ read
hahahsh
[caokang@caokang Prj]$ echo $REPLY 
hahahsh
[caokang@caokang Prj]$ 



所以为了避免   输入数据信息和 变量名字不一样,最好能加入一些提醒信息:  告诉我们要输入什么信息,输入几个。这个功能就有 -p 选项完成

[caokang@caokang Prj]$ read -p "please enter 3 num :" var1 var2 var3
please enter 3 num :1 2 3
[caokang@caokang Prj]$ echo $var1
1
[caokang@caokang Prj]$ 

-p sting  选项完成的功能是,   输入信息栏,前面 打印一些信息(string)


以上 完成的是基本的 键盘交互的变量存储问题.  那么如果我们 脚本执行到此后, 键盘提示我们输入信息,我们忘记了,这时候shell脚本就会卡死在这里,死等。

为了解决这个问题, 有了 -t  num这个选项.  表示 超过  num秒,  read命令执行无效. 

[caokang@caokang Prj]$ read -t 5 var1
[caokang@caokang Prj]$ 

-s选项:  有时候啊,我们输入信息, 但是不想在  shell命令行中显示出来。比如 输入 root密码。  -s选项就是为了实现这个功能。

[caokang@caokang Prj]$ read -s var1
[caokang@caokang Prj]$ echo $var1
123456
[caokang@caokang Prj]$ 


从文件中读入数据:  除了上文介绍的从键盘输入信息, read也支持从文件中读取内容. 但是感觉这个功能有些鸡肋...


<< 第三十章  流程控制 >> 

在这一小节中, 我们将要介绍 while 循环语句的用法.

 while  条件 ;  do

   操作; [ break; continue]

done

其中 break代表,挑出while循环;  continue 跳过此次循环,进行下一次while(重新判断)



条件 一般跟 if 一样,使用 test命令,或者是[] 、 [[ ]] .

 跟c语言里面类似,如下 while(i >=0) { i--;}    我们知道 操作里面需要更新 条件i的值,才不至于整个程序卡死。  

那么我们经常会用 数字运算, 来表示循环次数. 

那么shell中进行数学运算的途径有哪些呢?

(1) let命令   +   -    *    /   %  ++    --     +=      -=  

           比如 let   a++       let b=a-2            (注意变量a、b前面不能加$)

 (2) $[ ] 命令,

          i=$[ a -1]       i=$[$a-$b]       也就是说 a、b变量前面的$可有可无。

(3)  $(( express )) 模式

        只要里面的表达式express 符号 c语言规则,就能使用.   比如

      $(( a +b )) 


经典应用   从文件a.v 中读取每一行内容,并打印出;

while read  linetxt ; do

echo "$linetxt"

done < a.v                    

 这里呢 , < a.v表示重定向,a.v到键盘输入,就是相当于  read  文件a.v    read 读文件,最后一行之后,会返回False值。 即while (false)。



<第 三十二章  流程控制  case语句>

还是借鉴c语言构架,本小节介绍 case语句的使用.

对比C语言,  

  case(xxx)

    8‘h01 : 。。。。;break;

    8'h02: .。。。。; break

   ................

    default :  。。。。; break

  endcase


同样的  shell中 case语句结构大概相同。


case ( 变量 ) in

 pattern1 )   commands      ;;

 pattern2 )   commands      ;;

。。。。。

 * )  commands  ;;

esac


其中 pattern 注意 ,只支持字符串、通配符。     如  a 字符a     ?一个字符     * 任意字符        [a-z] 小写字母 这种,支持POSIX表达。 不支持正则表达式。

 如果想用 if [[ sting =~ regep ]]

应该注意到, bash本身是不支持 正则表达式的,只有少数命令的 参数上可以使用,如 sed \grep\ if [[ ]] .  这些支持Regep的地方都会特别的标注出来.

        

也可是使用:   pattern1  |   pattern 2 )  表明是  或条件,满足其1即可


 

<< 第 三十三章    位置参数 >> 

习惯上,我们在shell命令行中,调用shell脚本时候. 一般都是直接调用就完事了.  

比如:  创造了一个脚本. 用于把 log文件中的Error信息行抓取下来,    为了能兼容其他.  我们最好做成这样的设计:   

    [caokang@ Prj] $      pick_err   dc.log

    [caokang@ Prj] $      pick_err   verdi.log           在后面直接尾缀上,需要处理的文件.   

是不是想起来  公司里面跑 综合\PT的脚本,都是   sync_dc  BK5121 umc11f SPEF  尾缀好几个参数。


本章所讲述的内容就是 ,如何把这些参数  传送到shell脚本里面来。

 本质上,通过变量传递的方法进行传输:  把从shell命令行中输入的众多参数,依次存储在 特定的变量中,然后shell脚本可以直接使用该变量。


$0  :   脚本名称(含路径)   即在shell命令行中,输入的脚本名称,

$1 :  第一个参数(如BK5121)

$2:第二个参数(如 umc11f)

$3---n 依次为后面的内容。如果没有,该变量就是空。   特别说明,如果 n大于10了,使用${10} .  前文中介绍过。大括号用于告知shell解析器,变量名的边界。无特殊意义

$# :  变量个数,即n值。(不包括$0)


$*  :  直接使用$*的时候,  $* = $1 $2 $3 ......$n 用空格链接起来

$@ :  直接使用$*的时候, $@ = $1 $2 $3 ......$n 用空格链接起来


"$* "    用双引号 引用起来后。 变成一个统一的变量   “ $0 $1 $2 ....$n”

"$@ "    用双引号 引用起来后。 变成一个统一的变量   “ $0” “ $1”  “ $2 ”  ....  “$n”    

当要把参数列成表格使用时候, 推荐使用 ”$@“



2.      在这里学习下:  basename dirname这两个命令。 它们是为了获取某一文件的 路径、或者仅仅文件名的 命令。

dirname很好使用:    获取其路径参数

[caokang@caokang Prj]$ dirname /home/caokang/Prj/stderr.txt 
/home/caokang/Prj
[caokang@caokang Prj]$  
  注意:  如果特别 指明了 绝对路径.则获得.   否则获得的是相对路径.  如下"  .  "

[caokang@caokang Prj]$ dirname stderr.txt.ssd.ae 
.
[caokang@caokang Prj]$ 


basename 用于去除后缀.    basename  -flie  ssffix 

去除一个文件的  后缀。  如果没有指定 尾缀是什么东西,就去除空。

                                            如果指定了尾缀,则去除文件名中的 该尾缀。

[caokang@caokang Prj]$ basename /home/caokang/Prj/stderr.txt.ssd.ae .ae
stderr.txt.ssd
[caokang@caokang Prj]$ 
上面的例子, 去除的尾缀是" .ae"             下面是指定空 尾缀的情况

[caokang@caokang Prj]$ basename /home/caokang/Prj/stderr.txt.ssd,ae 
stderr.txt.ssd,ae
[caokang@caokang Prj]$ 


3.   常用的 提示信息;

上面我们说了参数如何传递进入脚本。 那么在命令行中输入时候,我们会经常性的漏掉、或者次序颠倒。  这一小节,就介绍如何做提示信息。



<< 第三十四章   for循环>>

依旧跟C语言类似。   前面 if判断、while循环、case选项。还有一个for循环没有介绍。

shell中的for循环有两种表述形式: 

(1)  传统描述

  for var   in   1 2 3 4 5 ...N ; do 

     cmds

done

即 var分别等于 1 2 3 4 5 ...N时候 ,执行 cmds命令。   1 2 3 4 5 ...N 相当于是个参数集合,是一个列表        .用空格隔开就会被认为是一个参数。

这个命令真正强大的地方在于,  使用别的命令得到 列表, 用于 for循环.


比如 :   for var in *.txt ;do

echo "$var"

done

shell命令行遇到for列表处,进行元字符替换,得到一个文件列表。


或者是使用 命令结果用作列表. 

比如

for  var  in $(ls) ; do

echo $var

done

shell命令行,会先执行ls命令,得到的结果充当 列表,供for循环使用


在这里不得不提下:  shell中如何获得  某一命令的结果。

  (1) `cmd`

   (2)  $(cmd)


C语言类型的 for 命令表达:


for(( expr1 ; expr2 ;expr3 )) ; do

    cmds

done


比如:   

for(( i=0 ; i<5; i=i+1 )) ; do

  echo $i

done


<< 第三十五章   字符串和数字>>

之前讲述的很多命令,更多的是以文件为操作对象的.   本小节,谈论下 字符串和数字.

通常不论字符串还是数字,我们都会把它放到一个变量中去.  当需要使用的时候,去调用变量即可. 这就涉及到了 变量置换.


(1)  默认是 $a  参数   $a=${a} . 我们之前讲过, 花括号的用处是告诉shell命令解析器  变量名字的范围。


${varname:-"repace value“}   :当varname没有定义或者 定义为空时候,  该命令返回 replace value    

${varname:="repace value“}   :当varname没有定义或者 定义为空时候,  该命令返回 replace value   . 于此同时varname会被赋值  varname= repalce value. 


[caokang@caokang Prj]$ foo=
[caokang@caokang Prj]$ echo ${foo:-"hashh"}
hashh
[caokang@caokang Prj]$ echo $foo 

[caokang@caokang Prj]$ 

这里, foo定义成空。   ${foo:-"hashh"}返回值就是 hashh。 但此时 foo变量仍是空


[caokang@caokang Prj]$ foo=
[caokang@caokang Prj]$ echo ${foo:="hashh"}
hashh
[caokang@caokang Prj]$ echo $foo 
hashh
[caokang@caokang Prj]$ 
这里,foo依旧定义成空, := 返回值是 hashh,同时foo变量已经被赋值了。



${varname:?"Note var is null or not defined “}   :当varname没有定义或者 定义为空时候,  命令执行情况是1.(表示运行错误)。 返回值是 Note: var is...........

[caokang@caokang Prj]$ foo=
[caokang@caokang Prj]$ echo ${foo:?"NOte, foo is null or not defined"}
bash: foo: NOte, foo is null or not defined
[caokang@caokang Prj]$ echo $foo 

[caokang@caokang Prj]$ 

 

${varname:-"repace value“}   :当varname没有定义或者 定义为空时候,  该命令返回仍未空

     但是当varname定义时, 此时会返回 repace value.  仅仅只一次.  varname的值依旧一样

[caokang@caokang Prj]$ foo=jjjj
[caokang@caokang Prj]$ echo ${foo:+"repace once"}
repace once
[caokang@caokang Prj]$ echo $foo 
jjjj
[caokang@caokang Prj]$ 


通过上面这四种情况,可以在必要的时候引用,避免来回的修改变量值。




(2)  返回变量名的参数展开

  ${#varname}  返回该变量值,字符串的长度。

  ${#varname:offset:length}  返回varname值  从offset开始,length长度个 字符。 

 ${#varname: offset}  当不指定length时,默认是到字符串结尾。


${varname#pattern}       :把varname值的 字符串,符合正则表达式的最短匹配项,删除.

${varname##pattern}     :把varname值的 字符串,符合正则表达式的最长匹配项,删除.


${varname%pattern}       :把varname值的 字符串,符合正则表达式的最短匹配项,删除. (从尾部开始匹配)

${varname%%pattern}     :把varname值的 字符串,符合正则表达式的最长匹配项,删除. (从尾部开始匹配)


${varname/pattern/string}       :把varname值的 字符串,符合正则表达式的第一个匹配项部分,替换成为 string

${varname//pattern/string}     :把varname值的 字符串,符合正则表达式的全部匹配项部分,替换成为 string

[caokang@caokang Prj]$ echo $test 
word word1 test word2
[caokang@caokang Prj]$ echo ${test/word/temp}
temp word1 test word2
[caokang@caokang Prj]$ echo ${test//word/temp}
temp temp1 test temp2
[caokang@caokang Prj]$ 

本质上 这些命令,都可以使用sed命令进行处理. 但提前了解一点也好.



(3) 变量内容的大小写转换。

${var,,}   把var内容去不变成 小写字母

${var,} 把var内容的第一个 字符变成 小写。

${var^^}   把var内容去不变成大写字母

${var^} 把var内容的第一个 字符变成 大写。                     (bash不支持??)



<< 第三十六章   数组 >>

数组是一种很重要的 数据结构.

(1) 定义数组

     a=(a0 a1 a2 a3 a4 a5)  定义一个长度为6的数组

     a=()  定义了一个空数组

(2) 赋值数组

   单个赋值 a[0] = "hhh"

   注意:   a= 

    代表是元素a[0]为null。 而不是整个数组a 赋null

(3)数组元素操作


整个数组的整体操作

跟之前讲述的 $* $@ "$*" "$@"一样.

${arry[*]  = a[0]  a[1] 。。。。a[n]
${arry[@]  = a[0]  a[1] 。。。。a[n]
"${arry[*]" =" a[0]  a[1] 。。。。a[n]"
"${arry[@]" =" a[0] " " a[1]"  。。。。"a[n]"


${#a[@]  或者 ${#a[*]    获得真个数组 元素的个数  


在数组末尾添加元素:    a +=(a b c)    在数组a后面再追加三个元素。 a b c

删除数组:   unset a         删除整个数组

    unset  'a[2]'   删除元素a[2]



关联数组:   与普通数组的区别在于,    数组下标 可以用字符串表示。  

declare -A  colors  定义了一个colors的关联数组

colors["red"]="ff0000"

colors["blue"]="00ff00"

colors["yellow"]="0000ff"



结语:    在学习 <<快乐的linux命令行>> 这本书, 从基础讲起。系统了介绍了shell命令、打下基础。 对我这种初学者帮助很大. 

给本书的作者点赞。





展开阅读全文

没有更多推荐了,返回首页