首页 > 代码库 > Shell脚本编程基础——Linux基本命令(11)

Shell脚本编程基础——Linux基本命令(11)

1.Shell脚本基础

1shell脚本

包含一些命令或声明,并符合一定格式的文本文件

格式要求:首行shebang机制

#!/bin/bash

#!/usr/bin/python

#!/usr/bin/perl

 

2)格式要求

脚本代码开头约定:

1、第一行一般为调用使用的语言

2、程序名,避免更改文件名为无法找到正确的文件

3、版本号

4、更改后的时间

5、作者相关信息

6、该程序的作用,及注意事项

7、最后是各版本的更新简要说明(也可以不写)

技术分享

其实最重要的是第一行,其他行也可以不写,但是为了一个良好的变成习惯,建议还是写上。

 

3)脚本调试

检测脚本中的语法错误

bash -n/path/to/some_script

调试执行

bash -x/path/to/some_script

技术分享

会一条一条的调试执行,可以找出哪一句错误了

技术分享

如图,显示着最后一条有错误

 


2.变量

1)变量介绍

作用:

1、数据存储格式

2、参与的运算

3、表示的数据范围

类型:

字符

数值:整型、浮点型

变量命名法则:

1、不能使程序中的保留字:例如if, for

2、只能使用数字、字母及下划线,且不能以数字开头

3、见名知义

4、统一命名规则:驼峰命名法

2bash中变量的种类

根据变量的生效范围等标准:

本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效

环境变量:生效范围为当前shell进程及其子进程

局部变量:生效范围为当前shell进程中某代码片断(通常指函数)

位置变量:$1, $2, ...来表示,用于在脚本代码中调用通过命令行传递给它的参数。

特殊变量:$?, $0, $*, $@,$#,$$

 

3)本地变量

变量赋值:name=value

可以使用引用value:

(1) 可以是直接字串;name=root"

(2) 变量引用:name="$USER"

(3) 命令引用:name=`COMMAND` name=$(COMMAND)

变量引用:${name} $name

"":弱引用,其中的变量引用会被替换为变量值

‘‘:强引用,其中的变量引用不会被替换为变量值,而保

持原字符串

  显示已定义的所有变量:set

  删除变量:unset name

 

4)环境变量

变量声明、赋值:

export name=VALUE

declare -x name=VALUE

变量引用:$name, ${name}

显示所有环境变量:

env

printenv

export

declare -x

删除变量:

unset name

bash内建的环境变量:

PATH SHELL  USER  UID  HOME  PWD  SHLVL 

LANG  MAIL  HOSTNAME  HISTSIZE —

 

5)只读和位置变量

只读变量:只能声明,但不能修改和删除

 声明只读变量:

readonly name

declare -r name

 查看只读变量:

readonly -p

位置变量:在脚本代码中调用通过命令行传递给脚本的参数

$1, $2, ...:对应第1、第2等参数。$1表示第一个参数,$2表示第二个参数

技术分享

$0: 命令本身

$#: 传递给脚本的参数的个数

$*: 传递给脚本的所有参数,全部参数合为一个字符串

$@: 传递给脚本的所有参数,每个参数为独立字符串

$@$* 只在被双引号包起来的时候才会有差异

    假设你的脚本运行时你写了三个参数 分别存储在$1 $2 $3中 

    则"$*" 等价于 “$1 $2 $3 $4"  ---》传递了一个参数

    而“$@" 等价于 "$1"  "$2"  "$3"  "$4"   ---》传递了四个参数

技术分享

 如图,分别使用两种方式将1 2 3 4传给aa.sh和bb.sh,然后取第一个参数,果然发现输出的结果表明$@传递了四个参数,因此取第一个才会是1。


set -- 清空所有位置变量

技术分享

如图,使用set --后所有变量被清空

PS:引用一位数以上的变量(如上图的$10)时要加{},不然系统会认为是$10

技术分享

6)参数左移

shift 用于对参数的移动(左移)

技术分享

技术分享

如图,参数向左移动了两位(就是删除最左边两个)。由abcdefg变成cdefg

 


3.退出状态

1)退出状态

进程使用退出状态来报告成功或失败

代表成功,1255代表失败

$? 变量保存最近的命令退出状态技术分享

正常状态,$?值为0

技术分享

最后一条失败,$?值不为0

 

2)自定义退出状态码

exit [n]

使用exit [n]也可以自定义退出状态码。注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字。

注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码。

 


4.算术运算

bash中的算术运算:help let

+, -, *, /, %取模(取余), **(乘方)

实现算术运算:

(1) let var=算术表达式

(2) var=$[算术表达式]

(3) var=$((算术表达式))

(4) var=$(exprarg1 arg2 arg3 ...)

(5) declare i var = 数值

(6) echo ‘算术表达式’ | bc

乘法符号有些场景中需要转义。

bash有内建的随机数生成器:$RANDOM0-32767

echo$[$RANDOM%50] 0-49之间随机数

(同理可以使用echo$[$RANDOM%50+1]随机产生1-50之间的随机数)

 


5.赋值

增强型赋值:

+=, -=, *=, /=,%=

let varOPERvalue

例如:let count+=3

自加3后自赋值

自增,自减:

let var+=1

let var++

let var-=1

let var--

 


6.逻辑运算

1)与

真与真 

真与假 

假与真 

假与假 

总结:只要有一个假则为假

 

短路与:若前一个为假,则不再判断第二个值,因为结果必为假。

 

2)或

真或真 真

真或假 真

假或真 真

假或假 假

总结:只要有一个真则为真

 

短路或:若前一个为真,则不再判断第二个值,因为结果必为真。

 


7.条件测试

判断某需求是否满足,需要由测试机制来实现。专用的测试表达式需要由测试命令辅助完成测试过程。

评估布尔声明,以便用在条件性执行中

 若真,则返回0

  若假,则返回1

测试命令:

 test EXPRESSION

 [ EXPRESSION ]

 [[ EXPRESSION ]]

注意:EXPRESSION前后必须有空白字符

技术分享


根据退出状态而定,命令可以有条件地运行

 && 代表条件性的AND THEN

||  代表条件性的OR ELSE

就是说如果前一条命令输出为0,就执行&&后面的内容,否则执行||后面的内容。

使用这种方法就不用每次使用$?调取结果了

技术分享


另外,使用[]可以判断是否有值,如有则输出0,如没有则输出1

技术分享

技术分享

 


8.bash的测试

1)数值测试

-gt 是否大于

-ge 是否大于等于

-eq 是否等于

-ne 是否不等于

-lt 是否小于

-le 是否小于等于

技术分享

2)字符串测试

== 是否等于

>  ascii码是否大于ascii

<  是否小于

!= 是否不等于

 

=~ 左侧字符串是否能够被右侧的PATTERN所匹配(部分内容被匹配即可)

注意: 此表达式一般用于[[ ]]中;扩展的正则表达式

技术分享


-z "STRING 字符串是否为空,空为真,不空为假

-n "STRING 字符串是否不空,不空为真,空为假

 

注意:用于字符串比较时的用到的操作数都应该使用引号

 

3)文件测试

使用方式为选项加文件:-x FILE


存在性测试:

  -a 判断文件存在则为真

  -e -a

技术分享


存在性及类别测试:

  -b 判断文件为块设备文件则为真

  -c 判断文件为字符文件则为真

  -d 判断文件为目录文件则为真

  -f 判断文件为普通文件则为真

技术分享


  -h 判断文件为软链接文件则为真

  -L -h

技术分享

       

  -p 是否存在且为命名管道文件

  -S 是否存在且为套接字文件

 

文件权限测试:

  -r 判断当前用户对该文件可读时为真(root永远为真)

  -w 判断当前用户对该文件可写时为真(root永远为真)

  -x 判断当前用户对该文件可执行为真(只要有任意位置有xroot则为真)

 

文件特殊权限测试:

  -u 判断该文件有suid权限为真(对于非二进制文件加上suid即使为真也无效)

  -g 判断该文件有sgid权限为真(对于非二进制文件加上sgid即使为真也无效,目录有效)

  -k 判断文件是否有sticky权限(对于普通文件没有意义)

 

文件大小测试:

-s 判断文件(非目录)为非空时为真

 

文件是否打开:

-N 判断文件在上一次读取后被改过则为真。

-O 判断文件的owner为当前用户时为真

-G 判断文件的group是当前用户的主组时为真

 

双文件测试:

使用方式为FILE1 -ef FILE2

-ef 判断两个文件是硬链接时为真

技术分享

  -nt 判断前一个文件的mtime新于后一个文件时为真

  -ot 判断前一个文件的mtime旧于后一个文件时为真

 

4)组合测试条件

第一种方式:

COMMAND1 && COMMAND2 并且

COMMAND1 || COMMAND2 或者

! COMMAND 

如:[[ -r FILE ]]&&[[ -w FILE ]]

第二种方式:

EXPRESSION1 -a EXPRESSION2 并且

EXPRESSION1 -o EXPRESSION2 或者

! EXPRESSION

必须使用测试命令进行

技术分享

 


9.接受输入

使用read来把输入值分配给一个或多个shell变量

-p 指定要显示的提示

技术分享

  read -p “Enter a filename: “ FILE

技术分享


-s 静默输入,一般用于密码

技术分享

 (注意,必须是sp如果一起使用必须是-sp,不能用-ps

 

-n N 指定输入的字符长度N

技术分享


-d ‘字符’  输入结束符(就是当输入指定结束符时就算输入结束,会自动跳到下一行)

-t N TIMEOUTN (给用户的输入做限时规定。若超出-t参数后所规定的时间值后,脚本将终止用户的输入,可单独使用也可和其它参数配合连用。)

 

read 也可以从标准输入中读取值,给每个单词分配一个变量(注意:不支持管道)。如果变量数少于单词数,则所有剩余单词都被分配给最后一个变量

 

read 变量名1 [变量名2] < 文件名

技术分享

 

也可以使用<<<直接导入具体值:

read 变量名1 [变量名2]<<< "1 [2] ..."

技术分享

 


10.防止扩展

反斜线(\)会使随后的字符按原意解释

技术分享

如图,此时我们只想使用$作为美元符号,因此需要加上\,如果不加则意思为引用变量5。但是我们没有定义变量5,所以为空。

 

也可以加引号来防止扩展

 单引号()防止所有扩展

 双引号()也防止所有扩展,但是以下情况例外:

   $(美元符号) 变量扩展

   `(反引号) 命令替换

   \(反斜线) 禁止单个字符扩展

   !(叹号) 历史命令替换

技术分享

 


11. bash的配置文件

按生效范围划分,存在两类:

 全局配置:

/etc/profile

/etc/profile.d/*.sh

/etc/bashrc

 个人配置:

~/.bash_profile

~/.bashrc

 


12.shell登录的两种方式

1)交互式登录:

(1)直接通过终端输入账号密码登录

(2)使用“su - UserName”切换的用户

执行顺序:/etc/profile--> /etc/profile.d/*.sh ~/.bash_profile --> ~/.bashrc-->/etc/bashrc

 

bash已经定义好了,只要登录bash,就先加载/etc/profile文件

技术分享

通过/etc/profile中的代码调用/etc/profile.d/ 下所有的.sh文件

技术分享

当读完所有.sh文件后,返回/etc/profile结束读取。然后bash会接着自动读取~/.bash_profile文件

技术分享

如图,~/.bash_profile文件中的命令调用~/.bashrc文件

技术分享

~/.bashrc文件中的命令又调用了/etc/bashrc文件

技术分享

其实在/etc/bashrc文件中也有命令调用/etc/profile.d/*.sh文件,但是由于之前已经调用过了,里面会判断就不再调用了

 

总结:bash自身只会读/etc/profile ~/.bash_profile 其他都是在读取这两个文件时调用读取出来的

 

2)非交互式登录:

(1)su UserName

(2)图形界面下打开的终端

(3)执行脚本

(4)任何其它的bash实例

执行顺序:~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh

总结:非交互式登陆下,bash自身只读取~/.bashrc 文件,然后文件中调用/etc/bashrc文件中又继续调用/etc/profile.d/*.sh

 

举个具体例子加深理解吧:

技术分享

如图,我们分别在这5个文件中加上5段定义变量的话。此时查看5个变量的值,发现为空。因为此时并没有重新登录所以这些文件不重新读取。

技术分享

于是我们退出并重新登陆。

此时可以看到,分别有定义的值了。

技术分享

 

然后我们将C3改成C6

技术分享

(改完之后目前还是原来的结果,因为没有重新登录)

然后我们使用非交互式登陆root

注意:非交互式登陆有一个特点,它不是登陆一个新的环境,会继承之前shell的所有变量,当然了,如果重新赋值了,它当然会被重新覆盖掉。

技术分享

因此我们发现变量的值仍然不变。因为即使修改了C,但是非登录shell并不读取~/.bash_profile文件,所以不会被重新赋值。

然后我们退出这个shell到之前的shell,将/etc/bashrc中的E5改为E7

技术分享

因为从来都没有重新读取过,因此此时读取变量的话,仍然是123455个值。

然后再非交互式登陆到root下,

技术分享

发现E的值改变了,因为非交互式登陆是需要读取/etc/bashrc文件的。

然后我们再退出到上一个shell,将B2改成B8

技术分享

此时读取变量仍然是12345

技术分享

然后使用非交互式登陆到liubei用户下,发现B的值也改变了。因为非交互式登陆也需要读取/etc/profile.d/*.sh文件的。

然后我们再退出,将D4改为D9

技术分享

发现变量仍然为

技术分享

因为我们改的是root下的.bashrc文件,而liubei用户读取的应该是自己家目录的.bashrc文件,因此D的值不变。

然后我们退出并使用交互式登陆liubei用户

此时变量为:

技术分享

因为交互式登录不会继承之前shell的内容而是全部重新读取,而liubei用户又只会读自己家目录下的.bash_profile文件,而不会读取root下的.bash_profile文件,因此C的值为空。同理,也不会读D的值。因此这两个变量均为空。

 

PS:修改文件后需生效有两种方法:

重新启动shell进程(像刚才举例子中的方式)

使用. source  例如:  . ~/.bashrc

 


13. $-变量

hhashall,打开这个选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +hh选项关闭

 iinteractive-comments,包含这个选项说明当前的shell 是一个交互式的 shell。所谓的交互式shell,在脚本中,i选项是关闭的。

 mmonitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等。

 Bbraceexpand,大括号扩展

 HhistoryH选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令

技术分享

正常查询时全部开启,某些变量可以通过命令开启或关闭

技术分享

如图,关闭h选项。

本文出自 “Ty_endless” 博客,请务必保留此出处http://tyendless.blog.51cto.com/12435756/1954576

Shell脚本编程基础——Linux基本命令(11)