shell脚本之变量与状态码
目录:
前言
在linux管理中,shell脚本很是重要,它可以帮助我们完成很多繁琐的工作,专注于更重要的事情上来,脚本的学习也是我们学习linux中所要遇到的比较困难的部分,因为它需要对vim,正则,逻辑,程序化语言有一定的熟悉,shell编程是过程式,解释执行的。它包括各种系统指令的组合,数据存储(变量,数组)、表达式、语句。
在shell脚本中,包含一些命令或声明,并符合一定格式的文本文件,在首行是shebang机制,如:
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
脚本在开头查找执行本脚本的程序,此程序就在第一行命令。Shell脚本经常用于自动化常用命令,执行系统管理和故障排除,创建简单的应用程序,处理文本或文件。
如何创建一个脚本
第一步,使用文本编辑器(vim)创建一个文件,一般都是以.sh结尾,在第一行必须包括shell声明#!(即shebang机制)我们一般使用#!/bin/bash
[root@CT71 bin]# vim test.sh
1 #!/bin/bash 2 ~ 3 ~ 4 ~ 5 ~ 6 ~ 7 ~ 8 ~ 9 ~ 10 ~ 11 ~ 12 13 -- INSERT -- 1,12 All
第二步,添加注释,shell脚本中的注释都是以#开头,后面跟上注释内容(一般注释写什么,我们一会儿就会说到)
1 #!/bin/bash 2 #Filename: test.sh 3 #Revision: 1.0 4 #Date: 2017-8-2 5 #Author: hahaha 6 #Description: ################ ~ ~ ~
第三步,编写脚本内容,脚本内容可以写我们常用的一些命令,比如查找我们磁盘中使用量最大的百分比等
1 #!/bin/bash 2 #Filename: disk.sh 3 #Revision: 1.0 4 #Date: 2017-8-2 5 #Author: hahaha 6 #Dessciption: 7 8 df | grep "dev/sd" | tr -s " " % | cut -d"%" -f5 | sort -nr | head -1 ~ ~ ~ ~ ~
第四步,给予脚本执行权限,即chmod +x shellname.sh,最后我们就可以执行我们的脚本了
[root@CT71 bin]# ll | grep "test.sh" -rw-r--r--. 1 root root 0 Aug 3 16:06 test.sh [root@CT71 bin]# chmod +x test.sh [root@CT71 bin]# ll | grep "test.sh" -rwxr-xr-x. 1 root root 0 Aug 3 16:06 test.sh
接下来我们说说们可以在注释里写啥东西。
1. 第一行一般为调用使用的语言
2. 程序名,避免更改文件名为无法找到正确的文件
3. 版本号
4. 更改后的时间
5. 作者相关信息
6.该程序的作用,及注意事项
7. 最后是各版本的更新简要说明
比如说:
1 #!/bin/bash
2 # Filename: hello.sh
3 # Revision: 1.1
4 # Date: 2017/06/01
5 # Author: wang
6 # Description: This is the first script
脚本调试
我们咋运行脚本之前,可以对脚本进行调试,这样可以保证我们的脚本出错的几率更低,一般我们常用的有两个参数:
检测脚本中的语法错误
bash -n /path/to/some_script
调试执行
bash -x /path/to/some_script
[root@CT71 bin]# bash -x link.sh + netstat -tn + grep tcp + cut -d: -f6 + sort -nr + uniq -c + tr -s ' ' : 1 192.168.111.1
接下来我们开始与脚本内容相关的知识,基础知识包括变量(变量的种类,变量的命令规则,本地变量,环境变量,位置变量),执行后的状态及状态码,算数运算与赋值。
变量相关
在shell脚本中的变量是弱类型的变量,语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换,变量无须事先定义可直接调用。
变量的命令规则
1. 不能使程序中的保留字:例如if, for
2. 只能使用数字、字母及下划线,且不能以数字开头
3. 见名知义
4. 统一命名规则:驼峰命名法
1 for:错误 2 23var:错误 3 ip_w:正确 4 var:正确 5 _min:正确 6 someIp:正确
bash中变量的种类
根据变量的生效范围等标准:
本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
环境变量:生效范围为当前shell进程及其子进程局部变量:生效范围为当前shell进程中某代码片断(通常指函数)
位置变量: $1, $2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数
特殊变量:
$?, $0, $*, $@, $#,$$
在这些变量中,本地变量仅对当前shell有效,对子shell是没有效的。而环境变量对当前shell及其子shell都有效,我们可以用env查询所有的环境变量,用set查询本地变量和环境,我们有一点需要知道,在linux中变量声明以后会一直存在,除非我们把他们给删除掉,删除变量我们可以用unset varname
[root@CT71 bin]# echo $A string [root@CT71 bin]# unset A [root@CT71 bin]# echo $A [root@CT71 bin]# set | grep "^A" ABRT_DEBUG_LOG=/dev/null 删除了A变量
本地变量
设置变量:
(1) 可以是直接字串; name=“root"
(2) 变量引用: name="$USER"
(3) 命令引用: name=`COMMAND` name=$(COMMAND)
引用变量:
${varname}
$varname
“$varname”
[root@CT71 bin]# myName=BUG [root@CT71 bin]# echo $myName BUG [root@CT71 bin]# echo "$myName" BUG [root@CT71 bin]# echo ${myName} BUG
"":弱引用,其中的变量引用会被替换为变量值
'':强引用,其中的变量引用不会被替换为变量值,而保持原字符串
[root@CT71 bin]# echo "$USER" root [root@CT71 bin]# echo '$USER' $USER
转译:
\ 完全转译
""部分转译 \ ` ! $(""无法转译的字符)
'' 完全转译
转译的原因是因为有些字符在linux下有特殊含义,但是,我们又需要用到它普通意义,所以我们需要对他们转译成非特殊字符供我们使用。
[root@CT71 app]# echo "$20.0"
0.0
[root@CT71 app]# echo "\$20.0"
$20.0
[root@CT71 app]# echo '$20.0'
$20.0
[root@CT71 app]# echo "!ll" echo "ll" ll [root@CT71 app]# echo '!ll' !ll
环境变量
环境变量的声明:
export name=value
declare -x name=value
环境变量的引用:
$name
${name}
在前面我们已经说过如何查看我们的变量,但是,查看环境变量的命令还有几个,如:env,printenv,export,declare –x,在bash中还有一些内建的环境变量:
PATH:命令查找的路径
SHELL:我们使用的shell
USER:当前用户名
UID:当前用户UID
HOME:家目录
PWD:当前工作目录
SHLVL:当前所在的几级shell
LANG:使用的字符集
MAIL:邮箱路径
HOSTNAME:主机名
HISTSIZE:历史命令记录长度
_:上次执行的命令
[root@CT71 app]# env XDG_SESSION_ID=612 HOSTNAME=CT71 -----------------------------------------------------------------------主机名 SELINUX_ROLE_REQUESTED= TERM=xterm SHELL=/bin/bash ---------------------------------------------------------------------使用的shell HISTSIZE=1000 -----------------------------------------------------------------------历史命令记录长度 SSH_CLIENT=192.168.111.1 52880 22 SELINUX_USE_CURRENT_RANGE= SSH_TTY=/dev/pts/2 USER=root ---------------------------------------------------------------------------当前用户名 LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00 ... ... MAIL=/var/spool/mail/root --------------------------------------------------------------邮箱路径 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin --------------------命令查找路径 PWD=/app ----------------------------------------------------------------------------当前的工作目录 LANG=en_US.UTF-8 --------------------------------------------------------------------使用的字符集 ... ...
SHLVL=1 -----------------------------------------------------------------------------当前所在的几级shell
HOME=/root --------------------------------------------------------------------------当前用户家目录
LOGNAME=root
SSH_CONNECTION=192.168.111.1 52880 192.168.111.110 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
OLDPWD=/root/bin
_=/usr/bin/env ----------------------------------------------------------------------上次执行的命令
只读和位置变量
我们可以在linux中设置只读变量,它的特点是只能声明,但不能修改和删除
声明只读变量:
readonly name
declare –r name
查看只读变量:
readonly –p
[root@CT71 app]# readonly H=2134 [root@CT71 app]# H=werq -bash: H: readonly variable [root@CT71 app]# readonly -p declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath" declare -ir BASHPID declare -r BASH_COMPLETION_COMPAT_DIR="/etc/bash_completion.d" declare -ar BASH_REMATCH='()' declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="1" [4]="release" [5]="x86_64-redhat-linux-gnu")' declare -ir EUID="0" declare -r H="2134" declare -ir PPID="57929" declare -r S declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor" declare -ir UID="0"
[root@CT71 app]# unset -f S 删除只读变量 [root@CT71 app]# echo $S [root@CT71 app]#
declare是一个shell的内建命令,用来显示所有变量属性和和值的,它还有一些常用的参数,比如上面的declare –r是用来设置只读变量的,declare –f不仅显示变量,还显示函数,还有declare –x设置环境变量等
位置变量
位置变量其实就是在脚本代码中调用通过命令行传递给脚本的参数。我们可以通过外在的参数传递给脚本以实现与脚本的交互操作,增强脚本功能。
在脚本中的$1,$2,$3…分别对应着脚本外面的第1个,第2个,第3个参数,$0代表的是命令本身,$*是传递给脚本的所有参数,全部参数合为一个字符串
$@是传递给脚本的所有参数,每个参数为独立字符串,$#是传递给脚本的参数的个数($@ $* 只在被双引号包起来的时候才会有差异)
set -- 清空所有位置变量
1 #!/bin/bash 2 3 echo "$1" ~ ~ ------------------------------------------- [root@CT71 app]# chmod +x test.sh [root@CT71 app]# ll total 4 -rwxr-xr-x. 1 root root 23 Aug 3 19:22 test.sh [root@CT71 app]# ./test.sh Hello Hello
1 #!/bin/bash 2 echo "filename :$(basename $0)" ~ ~ ~
-----------------------------------------
[root@CT71 app]# ./test2.sh
filename :test2.sh
1 #!/bin/bash #tt1.sh 2 ./tt2.sh "$@" ~ ~ 1 #!/bin/bash #tt2.sh 2 3 echo "$@" ~ [root@CT71 app]# ./tt1.sh 1 2 3 4 5 1 2 3 4 5 --------------------------------------------------------------------- 1 #!/bin/bash #tt1.sh 2 ./tt2.sh "$#" ~ ~ 1 #!/bin/bash #tt2.sh 2 3 echo "$#" ~ [root@CT71 app]# ./tt1.sh 1 2 3 4 5 1
由于我不是在PATH所在的路径下执行的脚本所以直接执行脚本是找不到的,必须要跟上确定的路径./tt2.sh就是在本目录下执行tt2.sh脚本
查询变量
本地变量的查询
set (即查本地,也查环境)
[root@CT71 app]# set | grep -C 4 "^PWD" PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"' PS1='[\u@\h \W]\$ ' PS2='> ' PS4='+ ' PWD=/app SELINUX_LEVEL_REQUESTED= SELINUX_ROLE_REQUESTED= SELINUX_USE_CURRENT_RANGE= SHELL=/bin/bash
环境变量的查询
env
printenv
export
declare -x
[root@CT71 app]# printenv XDG_SESSION_ID=612 HOSTNAME=CT71 SELINUX_ROLE_REQUESTED= TERM=xterm SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=192.168.111.1 52880 22 SELINUX_USE_CURRENT_RANGE= ... ...
进程的退出状态与状态码
我们在系统中没执行一个命令都会开辟一个进程,配置一个PID,当程序运行结束后,即进程结束后,都会返回一个值给变量“?”,通过返回的值,我们就可以了解到这个进程执行的结果如何,在返回的中,0 代表执行成功,1-255代表着不同的失败,$?变量保存着最近的命令执行后的状态。
除了程序自动的赋值外,我们也可以手动自定义程序退出的状态码,特别是在我们写脚本时,通过条件语句进行判断时,自定义的退出码就派上大用场。
自定义退出状态码:
exit[n] n={1..255}
注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
1 #!/bin/bash 2 3 echo "Myname is Tony!" 4 5 exit 1 6 7 echo "Show my name" ~ ~ -------------------------------------------------- [root@CT71 app]# chmod +x tt3.sh [root@CT71 app]# ./tt3.sh Myname is Tony!
在这我们多说一点东西,如何查看进程以及PID,这里有几个变量和命令,我们来看一下:
变量$$:查看当前的PID
PPID:查看父进程的PID
SHLVL:显示当前的shell是几级shell
pstree:显示进程树
-p:同时显示进程的PID
ps –ef :显示进程信息
[root@CT71 app]# echo $$ 57933 [root@CT71 app]# echo $PPID 57929 [root@CT71 app]# pstree -p | grep -C 2 "bash" | `-{rsyslogd}(2281) |-smartd(577) |-sshd(1401)---sshd(57929)---bash(57933)-+-grep(61485) | `-pstree(61484) |-systemd-journal(367) [root@CT71 app]# echo $SHLVL 1 [root@CT71 app]# ps -ef | grep "bash" root 607 1 0 Jul31 ? 00:00:14 /bin/bash /usr/sbin/ksmtuned root 57933 57929 0 14:09 pts/2 00:00:01 -bash root 61495 57933 0 20:05 pts/2 00:00:00 grep --color=auto bash