欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

脚本编程(一)

程序员文章站 2022-03-20 15:52:32
脚本编程(一) 啰里啰唆:每周的最后一天都特别难受。墨迹扣不出文章。 一、概况 注释是以#开都的,#开头不一定都是注释 SHELL是解释型语言 SHELL脚本第一句以#!/bin/bash开头 SHELL脚本需要具有执行权限 一般以.sh结尾 别名在脚本中无效。在脚本中不能使用别名。 bash使用技 ......

 

脚本编程(一)

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

一、概况

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

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式通配符模式

* 任意长度任意字符

任意一个字符

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

^ 取反