脚本编程(一)

 

脚本编程(一)

啰里啰唆:每周的最后一天都特别难受。墨迹扣不出文章。

一、概况

注释是以#开都的,#开头不一定都是注释

SHELL是解释型语言

SHELL脚本第一句以#!/bin/bash开头

SHELL脚本需要具有执行权限

一般以.sh结尾

别名在脚本中无效。在脚本中不能使用别名。

 

bash使用技巧:

-n 检查语法,无法无法检测处命令错误,同时只是检查语法不会真正执行脚本。

-x 逐行执行,逐行显示执行结果

 

脚本中的错误:

1、语法错误,会导致后续的命令无法继续执行。可以通过bash -n选项来检查

提示的错误行不一定是准确的。

2、命令错误,后续的命令还可以继续执行。无法通过bash -n 选项来检查错误,可以使用set -e或者set -o errexit来设定遇到错误命令后退出

3、逻辑错误,只能使用bash -x选项来检查错误。

 

变量:

变量表示命名的内存空间,讲数据放在内存空间中,通过 "$变量名" 引用,从而获取数据

 

内置环境变量:

PS1 SHELL HASTNAME $$ $?

自定义变量:

[set] NAME=VALUE set可以省略

 

变量类型:

字符型:默认都是字符型

数值:整型、 bash不支持浮点型

 

静态编译语言,使用变量前,先声明变量类型,之后类型不能改变,在编译时执行检查,JAVA C

动态编译语言:不用事先声明,可以随时改变变量类型,SHELL PYTHON

 

强类型语言:不同类型的操作数,必须经过强制转换成同一类型的变量后才能运算

弱类型语言:不同类型的操作数,会隐式转换数据类型,如SHELL

 

shell中变量命令规则

变量名不用实现声明

变量名不能和系统已经定义好的变量同名

变量名不能和系统的内外部命令同名

变量名不能和系统定义好的关键字同名,如:if else case for

变量名不能以数字开头

变量名不支持短横线-

变量名需要见名知意,最好用英语名,最好不要命拼音,不要用拼英缩写

统一命名规则:驼峰命名法,大驼峰studentName     小驼峰studentName

变量名大写

局部变量小写

函数名小写

 

普通变量,生效范围是当前shell进程;对当前shell之外的其它进程(包括当前shell的子进程)均无效。

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

本地变量,生效范围进士当前shell进程中某个代码片段,通常指函数。

 

变量赋值的时候等号左右不要有空格: NAME="value"    如果赋值中有空格需要使用引号引起来。

可以把文件路径赋值给变量。

特殊用法参考以下示例:

[root@CentOS8 /]# FILE=/*
[root@CentOS8 /]# echo $FILE
/bin /boot /data /dev /etc /home /lib /lib64 /media /mnt /opt /proc /root /run /sbin /srv /sys /@System.solv /tmp /usr /var
/*
[root@CentOS8 /]# ls -d $FILE
/bin   /data  /etc   /lib    /media  /opt   /root  /sbin  /sys           /tmp  /var
/boot  /dev   /home  /lib64  /mnt    /proc  /run   /srv   /@System.solv  /usr
[root@CentOS8 /]# ls -d "$FILE"

 

 

变量引用  $NAME   ${NAME}

 

变量引用的时候,加上双引号会保留原始的回车符等隐式字符,

不加上双引号默认会去掉回车符并以空格代替。

看以下示例 {

[root@CentOS8 ~]# NUM=`seq 10`
[root@CentOS8 ~]# echo "$NUM"
1
2
3
4
5
6
7
8
9
10
[root@CentOS8 ~]# echo $NUM
1 2 3 4 5 6 7 8 9 10
[root@CentOS8 ~]#

注意:变量赋值时临时生效,当退出终端后,变量会自动删除;脚本中的变量会随着脚本结束自动删除。

 

env可以显示所有已经定义好的环境变量

export不跟任何选项,也可以显示所有已经定义的环境变量

declare -x 也可以显示所有已经定义的环境变量

set可以显示所有已经定义好的所有变量

 

unset NAME可以取消变量定义

unset NAME1 NAME2 NAME3...取消多个变量

 

无法在子shell中使用父shell中定义的本地变量,可以使用exportdeclare -x把变量声明成环境变量,就可以在子shell(包括孙子进程)中调用父shell中定义的环境变量。但是无法让父进程继承子进程的

变量,孙子进程可以继承子进程的环境变量。

 

$BASHPID 显示当前进程的PID

$PPID 显示父进程的PID

LANG 语言系环境变量

MAIL 邮箱

SHLVL shell的嵌套深度

 

变量除了可以存字符,还可以存命令。$CMD的结果SHELL会当成命令执行。

 

只读变量,定义后的只读变量不能修改和删除,只能退出重新登

readonly NAME 定义只读变量

declare -r NAME 定义只读变量

 

位置变量:

$0 表示命令本身,会包括路径

$1 表示对应第一个参数,使用shift n替换

$2 表示对应第二个参数,使用shift n替换

...

${10} 表示对应的第十个参数,不能直接写成$10

${11} 表示对应的第十一个参数,不能直接写成$11

$* 表示所有参数,会把各个变量看成统一整体,只有双引号$*"$@"才有区别

$@ 表示所有参数,会把各个变量看成独立个体,只有双引号$*"$@"才有区别

$# 表示参数个数

$? 判断上一条执行状态的执行结果

$_ 前一个命令的最后一个参数

 

set -- 清楚所有位置变量

[root@CentOS7 2]# cat arg.sh
#!/bin/bash
#
echo "1st is $1"
echo "2st is $2"
echo "3st is $3"
echo "10st is ${10}"
echo "10st is $10"
echo "11st is ${11}"
echo "11st is $11"
echo "The number of `basename $0` is $#"
echo "All args are $*"
echo "All args are $@"
.
/arg2.sh $* ;echo '$*' ./arg2.sh $@ ;echo '$@' ./arg2.sh "$*" ;echo '"$*"' ./arg2.sh "$@" ;echo '"$@"'
[root@CentOS7 2]# cat arg2.sh #!/bin/bash # echo -n "$1 " [root@CentOS7 2]# ./arg.sh {a..z} 1st is a 2st is b 3st is c 10st is j 10st is a0 11st is k 11st is a1 The number of arg.sh is 26 All args are a b c d e f g h i j k l m n o p q r s t u v w x y z All args are a b c d e f g h i j k l m n o p q r s t u v w x y z a $* a $@ a b c d e f g h i j k l m n o p q r s t u v w x y z "$*" a "$@" [root@CentOS7 2]#

 

命令行展开

命令行展开优先级

1、把命令行分成单个命令词

2、展开别名

3、展开大括号的声明{}

4、展开波浪线声明~

5、命令替换$()或··

6、再次把命令行分成命令词

7、展开文件通配符

8、准备I/O重定向

9、运行命令

 

防止扩展

使用反斜线\会是随后的字符按愿意执行

 

脚本安全和set

she命令,地址shell环境

$- 以关键字形式显示系统启用功能

+表示禁用,-表示启用

h 表示hash,可以set +h选项关闭hash

i 交互式,说明当前shell是个交互是shell

m 表示启用job control来控制进程的停止

B 表示启用大括号扩展

H HISTORY,表示可以展开命令历史列表

  $_ 前一个命令的最后一个参数

set命令 修改环境变量

-o 打开关闭某些选项,如果后面不跟任何选项表示显示以shell启用的某些功能

set -o 表示启用

set +o  表示禁用

-e 等同于 -o errexit遇到命令出错就退出

-u 是否使用没有定义的变量,相当于-o nounset

-x 单步执行

 

  exit命令

    用户可以在脚本中使用以下命令自定义退出状态码

        exit [n]

    脚本中一旦遇到exit命令,脚本会立即终止,然后返回状态码

    如果exit未指定退出码,整个脚本的最终状态码取决于最后一条命令的状态码。

    例:{示例说明演示未指定退出码的情况

[root@CentOS7 2]# cat exit.sh
#!/bin/bash
#
echo haha
ech haha
exit
[root@CentOS7 2]# ./exit.sh
haha
./exit.sh: line 4: ech: command not found
[root@CentOS7 2]# echo $?
127

 

printf命令

printf "指定的格式" 文本1  文本2.。。 按照指定格式显示指定文本

%s 字符串格式,%-#.##s中,#表示显示字符宽度,数字不足的用空格补足,##表示显示小数个数,-表示左对齐

%f 浮点型格式

%b 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义

%d  | %i 十进制整数

%c ASCII字符,即显示对应参数的第一个字符

%o 八进制显示

%u 不带正负号的十进制显示

%x 小写的十六进制显示,即a-f

%X 大写的十六进制显示,即A-F

%% 表示%本身

 

转义字符:

\a 警告字符,会发一声响

\b 后退一格

\f 换页

\n 换行

\r 回车

\t 横向制表符

\v 纵向制表符

\\ 表示\本身

 

二、算数运算

算数运算符

+

-

*

/

% 取模,求余数

** 乘方

 

进行算数表达式

let $M+$N 不用带$,带上也没问题

let M++ M自增,与++M的区别是前者是先引用后自增,后者是先自增后引用

let N-- N自减,与--N的区别是前者是先引用后自减,后者是先自减后引用

$[$M+$N] 不用带$,带上也没问题

$(($M+$N)) 不用带$,带上也没问题

expr $M + $N 在运算符两边要有空格,在运算乘法的时候需要转移即\*

declare -i VAR 可以使用declare -i把变量声明成整数,然后就可以直接运算了

 

三、逻辑运算

 1&&1=1   1&&0=0   0&&1=0    0&&0=0 任何数与0相与都为假

 1||1=1   1||0=1   0||1=1    0||0=0 任何数与1相或都为真

 !1=0     !0=1

异或  1^0=1   1^1=0    0^0=0     0^1=0 异或是用二进制进行运算。异或的两个值,相同为假,不同为真

两个运算数异或得出的值,此值在与两个运算数其中任何一个再异或,必定得出另一个运算数

示例演示用异或互换两个变量的值

#!/bin/bash
# i
=10 j=20 echo "i=$i;j=$j" #i中的值现在已经是i与j的异或值 i=$[i^j] #下句的意思是用上一句得出的异或值i,与运算数j 再次进行异或,会得出另一个运算数 j=$[i^j] #下句的意思是用上一句得出的异或值j再次与第一句中得出的异或值i进行异或,会得出另一个 运算数 i=$[i^j] echo "i=$i;j=$j"

 

短路运算

短路与:CMD1&&CMD2

第一个CMD1结果为0,总的结果必定为0,因此不需要执行CMD2

第一个CMD1结果为1,第二个CMD2必须要参与运算,才能得到最终结果

短路或:CMD1||CMD2

第一个CMD1结果为1,总的结果必定为1,因此不需要执行CMD2

第一个CMD1结果为0,第二个CMD2必须要参与运算,才能得到最终结果

 

四、条件测试

条件测试命令

test EXPRESSION

[ EXPRESSION ] 判断式必须有空格。等同于test

[[ EXPRESSION ]]

 

变量测试

[ -v VAR ] 判断变量VAR是否已经定义,True if the shell variable VAR is set.

[ -R VAR ] 判断变量VAR是否已经定义并且已经引用,True if the shell variable VAR is set and is a name reference.

 

数值测试     进行数值判断的时候不能有非数字

-eq 相等

-ne 不相等

-lt 小于

-le 小于等于

-gt 大于

-ge 大于等于

 

字符串测试

-z 测试变量是否为空或者没赋值,变量未定义 变量里面是空值 都为真,但是在以用变量的时候需要加上双引号。True if string is empty.例:n="";[ -z "$n" ];echo $?

-n 判断变量是否为非空,在引用变量的时候必须加上双引号 True if string is not empty.: [ -n "$mm" ]

[STRING] 判断式中什么也不见,默认是指判断变量是否为非空。True if string is not empty.

= 判断字符串是否相等,等号两边都有空格,True if the strings are equal.

!= 判断字符串是否不相等,不等号两边都有空格,True if the strings are not equal.

> 前一个字符串的长度大于后一个字符串,True if STRING1 sorts after STRING2 lexicographically.

< 前一个字符串的长度小于后一个字符串,True if STRING1 sorts before STRING2 lexicographically.

[ $mm ] 如果什么也不跟,是为了判断变量mm长度是否非零,True if string is not empty.

示例演示[ $mm ]

[root@CentOS8 7]# mm=""
[root@CentOS8 7]# [ $mm ] && echo haha
[root@CentOS8 7]# mm="123"
[root@CentOS8 7]# [ $mm ] && echo haha
haha}

 

  [[ ]] 双中括号的时候里面可以用正则表达式,双中括号也支持通配符,一般情况下使用单中括号

   [[ "$FILE" == *.log ]] 在双中括号中== 后面接通配符,判断FILE文件是不是log结尾

   [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]  在双中括号中=~ 后面接正则表达式

   结论:[[ == ]]这种写法中,==右侧的* 如果想做通配符,不要加"",只想做*本身含义,需要转移或者加上双引号

    示例演示通配符 {[root@CentOS8 7]# mm=hh.tt

[root@CentOS8 7]# [ "$mm" == "*.tt" ] && echo haha
[root@CentOS8 7]# [ $mm == "*.tt" ] && echo haha
[root@CentOS8 7]# [[ $mm == "*.tt" ]] && echo haha
[root@CentOS8 7]# [[ "$mm" == "*.tt" ]] && echo haha
[root@CentOS8 7]# [[ "$mm" == "*\.tt" ]] && echo haha
[root@CentOS8 7]# [[ "$mm" == *.tt ]] && echo haha
haha

 

  =~==详细使用介绍{

#通配符

[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#FILE=test.txt
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[[ "$FILE" != *.log ]]
[root@centos8 ~]#echo $?
0

#正则表达式
[root@centos8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" =~ \.log$ ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#N=100
[root@centos8 ~]#[[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#N=Magedu10
[root@centos8 ~]#[[ "$N" =~ ^[0-9]+$ ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#IP=1.2.3.4
[root@centos8 ~]#[[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#IP=1.2.3.4567
[root@centos8 ~]#[[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}
([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[root@centos8 ~]#echo $?
1

#通配符
[root@centos8 ~]#NAME="linux1"
[root@centos8 ~]#[[ "$NAME" == linux* ]]
[root@centos8 ~]#echo $?
0
[root@centos8 ~]#[[ "$NAME" == "linux*" ]]
[root@centos8 ~]#echo $?
1
[root@centos8 ~]#NAME="linux*"
[root@centos8 ~]#[[ "$NAME" == "linux*" ]]
[root@centos8 ~]#echo $?
0

#结论:[[ == ]] == 右侧的 * 做为通配符,不要加“”,只想做为*, 需要加“” 或转义

 

文件测试

-a 文件是否存在,True if file exists.

-e 文件是否存在 等同于-a, -e选项常用,True if file exists.

-b 文件是否是块文件,True if file is block special.

-c 文件是否是字符文件,True if file is character special.

-d 判断是不是目录,True if file is a directory.

-f 判断是不是普通文件,在判断软连接的时候,会追踪软连接指向的文件,True if file exists and is a regular file.

-h -L 判断是不是软连接,True if file is a symbolic link.

-p 判断是不是管道文件,True if file is a named pipe.

-S 大写,判断是不是套接字文件

文件新旧比较

-ot old thanTrue if file1 is older than file2.

-nt new thanTrue if file1 is newer than file2

-ef 判断是不是硬链接,True if file1 is a hard link to file2.

文件权限测试

-w 判断文件是否有写权限,判断最终的权限,而不是看表面ll的显示结果,True if the file is writable by you.

-x 判断文件是否有执行权限,判断最终的权限,而不是看表面ll的显示结果,True if the file is executable by you.

-r 判断文件是否有读权限,判断最终的权限,而不是看表面ll的显示结果,True if file is readable by you.

-u FILE 是否存在且拥有suid权限

-g FILE 是否存在且拥有sgid权限

-k FILE 是否存在且拥有sticky权限

 

文件属性测试

-s 小写,是否存在且非空,True if file exists and is not empty.

-t 判断文件的文件描述符是否已经打开,True if FD is opened on a terminal.

-N FILE 文件自从上一次被读取之后是否被修改过

-O FILE 当前有效用户是否为文件属主

-G FILE 当前有效用户是否为文件属组

 

 

() 小括号中变量会在子进程中运行,同时会继承父进程中的变量值,但是起产生的结果不会影响父进程中的值

{} 花括号中的变量不会在子进程中运行,会在当前进程中执行,其结果会影响话括号外面的变量,花括号内的最后公式需要以分号;结尾,花括号内两边有空格

(){}都能将多个命令组合在一起,批量执行.

 

组合测试条件

[ EXPRESSION1 -a EXPRESSION2 ] 并且,表达式1和表达式2,结果才为真,True if both expr1 AND expr2 are true.

[ EXPRESSION1 -o EXPRESSION2 ]  或者,表达式1和表达式2只要一个为真,结果就为真,会进行短路运算,True if either expr1 OR expr2 is true.

[ ! EXPRESSION ]   取反,True if expr is false.

 

  -a-o选项只能在单中括号内使用,不能再[[ ]]内使用。

 

 

  COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN。如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2

  COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE。如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2

  ! COMMAND ,取反

  如果&&||同时使用,一般情况下&&放在前面给,||放在后面。

 

六、条件判断

顺序执行:

  ;分号

if语句

  单分支:

if COMMANDS; then

COMMANDS

fi

 

  双分支:

if EXPRIESSION;then

COMMANDS

else

COMMANDS

fi

 

  多分支:

if EXPRIESSION;then

COMMANDS

elif EXPRIESSION; then

COMMANDS

elif EXPRIESSION; then

COMMANDS

fi

 

 

case语句

case WORD in

[PATTERN])

COMMAND

;;

[PATTERN])

COMMAND

;;

*)

COMMAND

;;

esac

 

PATTER式通配符模式

* 任意长度任意字符

任意一个字符

[] 匹配中括号中的单个字符

^ 取反

 

posted @ 2020-04-05 00:54  讽刺暧昧  阅读(735)  评论(0编辑  收藏  举报