【LINUX 运维】第二章 shell 基础003之Bash 的变量和运算符
四、Bash 的变量和运算符
1、什么是变量
在定义变量时,有一些规则需要遵守:
² 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是“2name”
则是错误的。
² 在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为
数值型。
² 变量用等号连接值,等号左右两侧不能有空格。
² 变量的值如果有空格,需要使用单引号或双引号包括。如:“test="hello world!"”。其
中双引号括起来的内容“$”、“\”和反引号都拥有特殊含义,而单引号括起来的内容都是
普通字符。
² 在变量的值中,可以使用“\”转义符。
² 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"$变量名
"或用${变量名}包含变量名。例如:
[root@localhost ~]# test=123
[root@localhost ~]# test="$test"456
[root@localhost ~]# echo $test
123456
#叠加变量 test,变量值变成了 123456
[root@localhost ~]# test=${test}789
[root@localhost ~]# echo $test
123456789
#再叠加变量 test,变量值编程了 123456789
变量值的叠加可以使用两种格式:“$变量名”或${变量名}
² 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令。例如:
[tester@TesterPC ShellProData]$ test=$(date)
[tester@TesterPC ShellProData]$ echo $test
Sun Apr 3 17:21:34 JST 2022
[tester@TesterPC ShellProData]$
² 环境变量名建议大写,便于区分。
2、变量的分类
² 用户自定义变量:这种变量是最常见的变量,由用户自由定义变量名和变量的值。
² 环境变量:这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在 Windows 中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows 的操作环境,可以当做是 Windows 的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。
² 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
² 预定义变量:是 Bash 中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
3、用户自定义变量
1)、变量定义
[tester@TesterPC ShellProData]$ 6name="yuchuanhuaying"
bash: 6name=yuchuanhuaying: command not found...
#变量名不能用数字开头
[tester@TesterPC ShellProData]$ name = "yuchuanhuaying"
bash: name: command not found...
#等号左右两侧不能有空格
[tester@TesterPC ShellProData]$ name=yuchuanhuaying
[tester@TesterPC ShellProData]$ name=yuchuan huaying
bash: huaying: command not found...
#变量的值如果有空格,必须用引号包含
[tester@TesterPC ShellProData]$
2)、变量调用
[tester@TesterPC ShellProData]$ name="yuchuan huaying"
#定义变量 name
[tester@TesterPC ShellProData]$ echo $name
yuchuan huaying
#输出变量 name 的值
[tester@TesterPC ShellProData]$
3)、变量查看
[root@localhost ~]# set [选项]
选项:
-u: 如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x: 如果设定此选项,在命令执行之前,会把命令先输出一次
#直接使用 set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
[tester@TesterPC ShellProData]$ set | grep -n -A5 -B5 "PS1"
55-OSTYPE=linux-gnu
56-PATH=/home/tester/.local/bin:/home/tester/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
57-PIPESTATUS=([0]="0" [1]="0")
58-PPID=330703
59-PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"'
60:PS1='[\u@\h \W]\$ '
61-PS2='> '
62-PS4='+ '
63-PWD=/home/tester/MasterData/ShellProData
64-SELINUX_LEVEL_REQUESTED=
65-SELINUX_ROLE_REQUESTED=
[tester@TesterPC ShellProData]$
#当设置了-u 选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
[tester@TesterPC ShellProData]$ echo $huaying
[tester@TesterPC ShellProData]$ set -u
[tester@TesterPC ShellProData]$ echo $huaying
-bash: huaying: unbound variable
[tester@TesterPC ShellProData]$
[root@localhost ~]# set -x
[root@localhost ~]# ls
+ ls --color=auto
anaconda-ks.cfg install.log install.log.syslog sh tdir test testfile
#如果设定了-x 选项,会在每个命令执行之前,先把命令输出一次
4)、变量删除
[root@localhost ~]# unset 变量名
4 、环境变量
1)、环境变量设置
#使用 export 声明的变量即是环境变量
[tester@TesterPC ShellProData]$ export age="20"
[tester@TesterPC ShellProData]$
2)、环境变量查询和删除
env 命令和 set 命令的区别是,set 命令可以查看所有变量,而 env 命令只能查看环境变量。
#删除环境变量 gender
[tester@TesterPC ShellProData]$ export gender="Manl"
[tester@TesterPC ShellProData]$
[tester@TesterPC ShellProData]$ unset gender
[tester@TesterPC ShellProData]$
[tester@TesterPC ShellProData]$ env | grep gender
[tester@TesterPC ShellProData]$
3)、系统默认环境变量
[tester@TesterPC ShellProData]$ env
#定义颜色的显示
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;3
SSH_CONNECTION=10.81.234.13 56502 10.81.234.130 22
#语系
LANG=en_US.UTF-8
HISTCONTROL=ignoredups
DISPLAY=localhost:10.0
#主机名
HOSTNAME=TesterPC
OLDPWD=/home/tester/MasterData
which_declare=declare -f
XDG_SESSION_ID=25
#当前登录的用户
USER=tester
SELINUX_ROLE_REQUESTED=
#当前所在目录
PWD=/home/tester/MasterData/ShellProData
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
#当前登录用户的家目录
HOME=/home/tester
#当前操作环境是用ssh连接的,这里记录客户端IP
SSH_CLIENT=10.81.234.13 56502 22
#自定义的环境变量
age=20
SELINUX_LEVEL_REQUESTED=
XDG_DATA_DIRS=/home/tester/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
#ssh 连接的终端是pts/0
SSH_TTY=/dev/pts/0
#用户邮箱
MAIL=/var/spool/mail/tester
#终端环境
TERM=xterm
#当前的shell
SHELL=/bin/bash
TC_LIB_DIR=/usr/lib64/tc
SELINUX_USE_CURRENT_RANGE=
#当前在第一层shell中
SHLVL=1
GDK_BACKEND=x11
#登录用户
LOGNAME=tester
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
XDG_RUNTIME_DIR=/run/user/1000
#系统查找命令的路径
PATH=/home/tester/.local/bin:/home/tester/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
#历史命令条数
HISTSIZE=1000
LESSOPEN=||/usr/bin/lesspipe.sh %s
BASH_FUNC_which%%=() { ( alias;
eval ${which_declare} ) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@"
}
#上一次执行命令的最后一个参数或命令本身
_=/usr/bin/env
[tester@TesterPC ShellProData]$
env 命令可以查询到所有的环境变量,可是还有一些变量虽然不是环境变量,却是和 Bash 操作接口相关的变量,这些变量也对我们的 Bash 操作终端起到了重要的作用。这些变量就只能用 set 命令来查看了,我只列出重要的内容吧:
[root@localhost ~]# set
BASH=/bin/bash <--Bash 的位置
BASH_VERSINFO=([0]="4" [1]="1" [2]="2" [3]="1" [4]="release"
[5]="i386-redhat-linux-gnu") <--Bash 版本
BASH_VERSION='4.1.2(1)-release' <--bash 的版本
COLORS=/etc/DIR_COLORS <--颜色记录文件
HISTFILE=/root/.bash_history <--历史命令保存文件
HISTFILESIZE=1000 <--在文件当中记录的历史命令最大条数
HISTSIZE=1000 <--在缓存中记录的历史命令最大条数
LANG=zh_CN.UTF-8 <--语系环境
MACHTYPE=i386-redhat-linux-gnu <--软件类型是 i386 兼容类型
MAILCHECK=60 <--每 60 秒去扫描新邮件
PPID=2166 <--父 shell 的 PID。我们当前 Shell 是一个子 shell
PS1='[\u@\h \W]\$ ' <--命令提示符
PS2='> ' <--如果命令一行没有输入完成,第二行命令的提示符
UID=0 <--当前用户的 UID
² PATH 变量:系统查找命令的路径先查询下 PATH 环境变量的值:
[tester@TesterPC ShellProData]$ echo $PATH
/home/tester/.local/bin:/home/tester/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
[tester@TesterPC ShellProData]$
PATH 变量的值是用“:”分割的路径,这些路径就是系统查找命令的路径。也就是说当我们输入了一个程序名,如果没有写入路径,系统就会到 PATH 变量定义的路径中去寻找,是否有可以执行的程序。如果找到则执行,否则会报“命令没有发现”的错误。那么是不是我们把自己的脚本拷贝到 PATH 变量定义的路径中,我们自己写的脚本也可以不输入路径而直接运行呢?
[root@localhost ~]# cp /root/sh/hello.sh /bin/
#拷贝 hello.sh 到/bin 目录
[root@localhost ~]# hello.sh
Mr. Shen Chao is the most honest man.
#hello.sh 可以直接执行了
那么我们是不是可以修改 PATH 变量的值,而不是把程序脚本复制到/bin/目录中。当然是可以的,我们通过变量的叠加就可以实现了:
[root@localhost ~]# PATH="$PATH":/root/sh
#在变量 PATH 的后面,加入/root/sh 目录
[root@localhost ~]# echo $PATH
/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/
bin:/root/sh
#查询 PATH 的值,变量叠加生效了当然我们这样定义的 PATH 变量只是临时生效,一旦重启或注销就会消失,如果想要永久生效,需要写入环境变量配置文件,我们在“环境变量配置文件”小节中再详细介绍。
² PS1 变量:命令提示符设置PS1 是一个很有意思的变量(这可不是 SONY 的游戏机哦),是用来定义命令行的提示符的,可以安装我们自己的需求来定义自己喜欢的提示符。PS1 可以支持以下这些选项:
Ø \d:显示日期,格式为“星期 月 日”
Ø \H:显示完整的主机名。如默认主机名“localhost.localdomain”
Ø \h:显示简写主机名。如默认主机名“localhost”
Ø \t:显示 24 小时制时间,格式为“HH:MM:SS”
Ø \T:显示 12 小时制时间,格式为“HH:MM:SS”
Ø \A:显示 24 小时制时间,格式为“HH:MM”
Ø \@:显示 12 小时制时间,格式为“HH:MM am/pm”
Ø \u:显示当前用户名
Ø \v:显示 Bash 的版本信息
Ø \w:显示当前所在目录的完整名称
Ø \W:显示当前所在目录的最后一个目录
Ø \#:执行的第几个命令
Ø \$:提示符。如果是 root 用户会显示提示符为“#”,如果是普通用户会显示提示符为“$”
这些选项该怎么用啊?我们先看看 PS1 变量的默认值吧:
[root@localhost ~]# echo $PS1
[\u@\h \W]\$
#默认的提示符是显示“[用户名@简写主机名 最后所在目录]提示符”
在 PS1 变量中,如果是可以解释的符号,如“\u”、“\h”等,则显示这个符号的作用。如果是不能解释的符号,如“@”或“空格”,则原符号输出。那么我们修下 PS1 变量,看看会出现社么情况吧:
[root@localhost ~]# PS1='[\u@\t \w]\$ '
#修改提示符为‘[用户名@当前时间 当前所在完整目录]提示符’
[root@04:46:40 ~]#cd /usr/local/src/
#切换下当前所在目录,因为家目录是看不出来区别的
[root@04:47:29 /usr/local/src]#
#看到了吗?提示符按照我们的设计发生了变化
这里要小心,PS1 变量的值要用单引号包含,否则设置不生效。再举个例子吧:
[root@04:50:08 /usr/local/src]#PS1='[\u@\@ \h \# \W]\$'
[root@04:53 上午 localhost 31 src]#
#提示符又变了。\@:时间格式是 HH:MM am/pm;\#:会显示执行了多少个命令
PS1 变量可以自由定制,好像看到了点 Linux 可以自由定制和修改的影子,还是很有意思的。不过说实话一个提示符已经使用习惯了,如果换一个还是非常别扭的,还是改回默认的提示符吧:
[root@04:53 上午 localhost 31 src]#PS1='[\u@\h \W]\$ '
[root@localhost src]#
² LANG 语系变量
LANG 变量定义了 Linux 系统的主语系环境,这个变量的默认值是:
[root@localhost src]# echo $LANG
zh_CN.UTF-8
这是因为我们 Linux 安装时,选择的是中文安装,所以默认的主语系变量是“zh_CN.UTF-8”。那么 Linux 中到底支持多少语系呢?我们可以使用以下命令查询:
[root@localhost src]# locale -a | more
aa_DJ
aa_DJ.iso88591
aa_DJ.utf8
aa_ER
…省略部分输出…
#查询支持的语系
[root@localhost src]# locale -a | wc -l
735
#是在太多了,统计一下有多少个吧
我们支持这么多的语系,当前系统到底是什么语系呢?使用 locale 命令直接查询:
[tester@TesterPC ShellProData]$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
[tester@TesterPC ShellProData]$
我们还要通过文件/etc/sysconfig/i18n 定义系统的默认语系,查看下这个文件的内容:
[root@localhost src]# cat /etc/sysconfig/i18n
LANG="zh_CN.UTF-8"
这又是当前系统语系,又是默认语系,有没有快晕倒的感觉。解释下吧,我们可以这样理解,默认语系是下次重启之后系统所使用的语系,而当前系统语系是当前系统使用的语系。如果系统重启,会从默认语系配置文件/etc/sysconfig/i18n 中读出语系,然后赋予变量 LANG 让这个语系生效。也就是说,LANG 定义的语系只对当前系统生效,要想永久生效就要修改/etc/sysconfig/i18n 文件了。说到这里,我们需要解释下 Linux 中文支持的问题。是不是我们只要定义了语系为中文语系,如zh_CN.UTF-8 就可以正确显示中文了呢?这要分情况,如果我们是在图形界面中,或者是使用远程连接工具(如 SecureCRT),只要正确设置了语系,那么是可以正确显示中文的。当然远程连接工具也要配置正确的语系环境,具体配置方式可以参考 Linux 系统安装章节。那么如果是纯字符界面(本地终端 tty1-tty6)是不能显示中文的,因为 Linux 的纯字符界面时不能显示中文这么复杂的编码的。如果我们非要在纯字符界面显示中文,那么只能安装中文插件,如zhcon 等。我们举个例子吧:
[root@localhost src]# echo $LANG
zh_CN.UTF-8
#我当前使用远程工具连接,只要语系正确,则可以正确显示中文
[root@localhost src]# df
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/sda3 19923216 1813532 17097616 10% /
tmpfs 312672 0 312672 0% /dev/shm
/dev/sda1 198337 26359 161738 15% /boot
#df 命令可以看到中文是正常的
但如果是纯字符界面呢?虽然我们是中文安装的,但纯字符界面的语系可是“en_US.UTF-8”,如图 11-2 所示:
[tester@TesterPC ShellProData]$ echo $LANG
en_US.UTF-8
[tester@TesterPC ShellProData]$ df
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 32789408 0 32789408 0% /dev
tmpfs 32819636 0 32819636 0% /dev/shm
tmpfs 32819636 26728 32792908 1% /run
tmpfs 32819636 0 32819636 0% /sys/fs/cgroup
/dev/mapper/cl-root 536604672 8937344 527667328 2% /
/dev/sda1 493568 311124 182444 64% /boot
/dev/mapper/cl-u 536604672 11650524 524954148 3% /u
/dev/mapper/cl-home 1005625344 7137944 998487400 1% /home
tmpfs 6563924 44 6563880 1% /run/user/1000
[tester@TesterPC ShellProData]$
图 11-2 字符界面语系那么我们更改下语系为中文,看看会出现什么情况吧:
图 11-3 字符界面设置中文语系
如果我们非要在纯字符界面中设置中文语系,那么就会出现乱码。怎么解决呢?安装 zhcon 中文插件吧,安装并不复杂,查询下安装说明应该可以轻松安装。
5 、位置参数变量
位置参数变量 |
作 用 |
$n |
n 为数字,$0 代表命令本身,$1-$9 代表第一到第九个参数,十以上 的参数需要用大括号包含,如${10}. |
$* |
这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体 |
$@ |
这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待 |
$# |
这个变量代表命令行中所有参数的个数 |
[root@localhost sh]# vi count.sh
#!/bin/bash
# Author: shenchao (E-mail: shenchao@atguigu.com)
num1=$1
#给 num1 变量赋值是第一个参数
num2=$2
#给 num2 变量赋值是第二个参数
sum=$(( $num1 + $num2))
#变量 sum 的和是 num1 加 num2
#Shell 当中的运算还是不太一样的,我们 Shell 运算符小节中详细介绍
echo $sum
#打印变量 sum 的值
那么还有几个位置参数变量是干嘛的呢?我们在写个脚本来说明下:
[root@localhost sh]# vi parameter.sh
#!/bin/bash
# Author: shenchao (E-mail: shenchao@atguigu.com)
echo "A total of $# parameters"
#使用$#代表所有参数的个数
echo "The parameters is: $*"
#使用$*代表所有的参数
echo "The parameters is: $@"
#使用$@也代表所有参数
那么“$*”和“$@”有区别吗?还是有区别的,$*会把接收的所有参数当成一个整体对待,而$@则会区分对待接收到的所有参数。还是举个例子:
[root@localhost sh]# vi parameter2.sh
#!/bin/bash
# Author: shenchao (E-mail: shenchao@atguigu.com)
for i in "$*"
#定义 for 循环,in 后面有几个值,for 会循环多少次,注意“$*”要用双引号括起来
#每次循环会把 in 后面的值赋予变量 i
#Shell 把$*中的所有参数看成是一个整体,所以这个 for 循环只会循环一次
do
echo "The parameters is: $i"
#打印变量$i 的值
done
x=1
#定义变量 x 的值为 1
for y in "$@"
#同样 in 后面的有几个值,for 循环几次,每次都把值赋予变量 y
#可是 Shell 中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
do
echo "The parameter$x is: $y"
#输出变量 y 的值
x=$(( $x +1 ))
#然变量 x 每次循环都加 1,为了输出时看的更清楚
done
6 、预定义变量
预定义变量 |
作 用 |
$? |
最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。 |
$$ |
当前进程的进程号(PID) |
$! |
后台运行的最后一个进程的进程号(PID) |
我们先来看看“$?”这个变量,看起来不好理解,我们还是举个例子:
[root@localhost sh]# ls
count.sh hello.sh parameter2.sh parameter.sh
#ls 命令正确执行
[root@localhost sh]# echo $?
0
#预定义变量“$?”的值是 0,证明上一个命令执行正确
[root@localhost sh]# ls install.log
ls: 无法访问 install.log: 没有那个文件或目录
#当前目录中没有 install.log 文件,所以 ls 命令报错了
[root@localhost sh]# echo $?
2
#变量“$?”返回一个非 0 的值,证明上一个命令没有正确执行
#至于错误的返回值到底是多少,是在编写 ls 命令时定义好的,如果碰到文件不存在就返回数值 2
接下来我们来说明下“$$”和“$!”这两个预定义变量,我们写个脚本吧:
[root@localhost sh]# vi variable.sh
#!/bin/bash
# Author: shenchao (E-mail: shenchao@atguigu.com)
echo "The current process is $$"
#输出当前进程的 PID。
#这个 PID 就是 variable.sh 这个脚本执行时,生成的进程的 PID
find /root -name hello.sh &
#使用 find 命令在 root 目录下查找 hello.sh 文件
#符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍
echo "The last one Daemon process is $!"
#输出这个后台执行命令的进程的 PID,也就是输出 find 命令的 PID 号
7、接收键盘输入
[root@localhost ~]# read [选项] [变量名]
选项:
-p “提示信息”: 在等待 read 输入时,输出提示信息
-t 秒数: read 命令会一直等待用户输入,使用此选项可以指定等待时间
-n 字符数: read 命令只接受指定的字符数,就会执行
-s: 隐藏输入的数据,适用于机密信息的输入
变量名:
变量名可以自定义,如果不指定变量名,会把输入保存入默认变量 REPLY
如果只提供了一个变量名,则整个输入行赋予该变量
如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上
的最后一个变量取得剩余的所有字
还是写个例子来解释下 read 命令:
[root@localhost sh]# vi read.sh
#!/bin/bash
# Author: shenchao (E-mail: shenchao@atguigu.com)
read -t 30 -p "Please input your name: " name
#提示“请输入姓名”并等待 30 秒,把用户的输入保存入变量 name 中
echo "Name is $name"
#看看变量“$name”中是否保存了你的输入
read -s -t 30 -p "Please enter your age: " age
#提示“请输入年龄”并等待 30 秒,把用户的输入保存入变量 age 中
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e "\n"
#调整输出格式,如果不输出换行,一会的年龄输出不会换行
echo "Age is $age"
read -n 1 -t 30 -p "Please select your gender[M/F]: " gender
#提示“请选择性别”并等待 30 秒,把用户的输入保存入变量 gender
#使用“-n 1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Sex is $gender"
8 、Shell 的运算符
1)数值运算的方法
那如果我需要进行数值运算,可以采用以下三种方法中的任意一种:
² 使用 declare 声明变量类型
既然所有变量的默认类型是字符串型,那么只要我们把变量声明为整数型不就可以运算了吗?使用 declare 命令就可以实现声明变量的类型。命令如下:
[root@localhost ~]# declare [+/-][选项] 变量名
选项:
-: 给变量设定类型属性
+: 取消变量的类型属性
-a: 将变量声明为数组型
-i: 将变量声明为整数型(integer)
-r: 讲变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值,
也不能删除变量,甚至不能通过+r 取消只读属性
-x: 将变量声明为环境变量
-p: 显示指定变量的被声明的类型
例子 1:数值运算
那我们只要把变量声明为整数型不就可以运算了吗?试试吧:
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量 aa 和 bb 赋值
[root@localhost ~]# declare -i cc=$aa+$bb
#声明变量 cc 的类型是整数型,它的值是 aa 和 bb 的和
[root@localhost ~]# echo $cc
33
#这下终于可以相加了
这样运算好麻烦啊,没有办法啊,Shell 在数值运算这里确实是比较麻烦,习惯就好了-_-!。
例子 2:数组变量类型
数组这个东东只有写一些较为复杂的程序才会用到,大家可以先不用着急学习数组,当有需要的时候再回来详细学习。那么数组是什么呢?所谓数组,就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标。组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量。一看定义就一头雾水,更加不明白数组是什么了。那么换个说法,变量和数组都是用来保存数据的,只是变量只能赋予一个数据值,一旦重复复制,后一个值就会覆盖前一个值。而数组是可以赋予一组相同类型的数据值。大家可以把变量想象成一个小办公室,这个办公室只能容纳一个人办公,办公室名就是变量名。而数组是一个大办公室,可以容纳很多人同时办公,在这个大办公室办公的每个人是通过不同的座位号来区分的,这个座位号就是数组的下标,而大办公室的名字就是数组名。
还是举个例子吧:
[root@localhost ~]# name[0]="shen chao"
#数组中第一个变量是沈超(大办公室第一个办公桌坐最高大威猛帅气的人)
[root@localhost ~]# name[1]="li ming"
#数组第二个变量是李明(大办公室第二个办公桌坐头发锃亮的人)
[root@localhost ~]# name[2]="tong gang"
#数组第三个变量是佟刚(大办公室第三个办公桌坐眼睛比超哥还小的老师)
[root@localhost ~]# echo ${name}
shen chao
#输出数组的内容,如果只写数组名,那么只会输出第一个下标变量
[root@localhost ~]# echo ${name[*]}
shen chao li ming tong gang
#输出数组所有的内容
注意数组的下标是从 0 开始的,在调用数组值时,需要使用${数组[下标]}的方式来读取。不过好像在刚刚的例子中,我们并没有把 name 变量声明为数组型啊,其实只要我们在定义变量时采用了“变量名[下标]”的格式,这个变量就会被系统认为是数组型了,不用强制声明。
例子 3: 环境变量
我们其实也可以使用 declare 命令把变量声明为环境变量,和 export 命令的作用是一样的:
[root@localhost ~]# declare -x test=123
#把变量 test 声明为环境变量
例子 4:只读属性
注意一旦给变量设定了只读属性,那么这个变量既不能修改变量的值,也不能删除变量,甚至不能使用“+r”选项取消只读属性。命令如下:
[root@localhost ~]# declare -r test
#给 test 赋予只读属性
[root@localhost ~]# test=456
-bash: test: readonly variable
#test 变量的值就不能修改了
[root@localhost ~]# declare +r test
-bash: declare: test: readonly variable
#也不能取消只读属性
[root@localhost ~]# unset test
-bash: unset: test: cannot unset: readonly variable
#也不能删除变量
不过还好这个变量只是命令行声明的,所以只要重新登录或重启,这个变量就会消失了。
例子 5:查询变量属性和取消变量属性变量属性的查询使用“-p”选项,变量属性的取消使用“+”选项。命令如下:
[root@localhost ~]# declare -p cc
declare -i cc="33"
#cc 变量是 int 型
[root@localhost ~]# declare -p name
declare -a name='([0]="shen chao" [1]="li ming" [2]="tong gang")'
#name 变量是数组型
[root@localhost ~]# declare -p test
declare -rx test="123"
#test 变量是环境变量和只读变量
[root@localhost ~]# declare +x test
#取消 test 变量的环境变量属性
[root@localhost ~]# declare -p test
declare -r test="123"
#注意,只读变量属性是不能取消的
² 使用 expr 或 let 数值运算工具
要想进行数值运算的第二种方法是使用 expr 命令,这种命令就没有 declare 命令复杂了。命令如下:
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量 aa 和变量 bb 赋值
[root@localhost ~]# dd=$(expr $aa + $bb)
#dd 的值是 aa 和 bb 的和。注意“+”号左右两侧必须有空格
[root@localhost ~]# echo $dd
33
使用 expr 命令进行运算时,要注意“+”号左右两侧必须有空格,否则运算不执行。至于 let 命令和 expr 命令基本类似,都是 Linux 中的运算命令,命令格式如下:
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量 aa 和变量 bb 赋值
[root@localhost ~]# let ee=$aa+$bb
[root@localhost ~]# echo $ee
33
#变量 ee 的值是 aa 和 bb 的和
[root@localhost ~]# n=20
#定义变量 n
[root@localhost ~]# let n+=1
#变量 n 的值等于变量本身再加 1
[root@localhost ~]# echo $n
21
expr 命令和 let 命令大家可以按照习惯使用,不过 let 命令对格式要求要比 expr 命令宽松,所以推荐使用 let 命令进行数值运算。
² 使用“$((运算式))”或“$[运算式]”方式运算其实这是一种方式“$(())”和“$[]”这两种括号按照个人习惯使用即可。命令如下:
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
[root@localhost ~]# ff=$(( $aa+$bb ))
[root@localhost ~]# echo $ff
33
#变量 ff 的值是 aa 和 bb 的和
[root@localhost ~]# gg=$[ $aa+$bb ]
[root@localhost ~]# echo $gg
33
#变量 gg 的值是 aa 和 bb 的和
这三种数值运算方式,大家可以按照自己的习惯来进行使用。不过我们推荐使用“$((运算式))”的方式
2)、Shell 常用运算符
优先级 |
运算符 |
说明 |
13 |
-, + |
单目负、单目正 |
12 |
!, ~ |
逻辑非、按位取反或补码 |
11 |
* , / , % |
乘、除、取模 |
10 |
+, - |
加、减 |
9 |
<< , >> |
按位左移、按位右移 |
8 |
< =, > =, < , > |
小于或等于、大于或等于、小于、大于 |
7 |
== , != |
等于、不等于 |
6 |
& |
按位与 |
5 |
^ |
按位异或 |
4 |
| |
按位或 |
3 |
&& |
逻辑与 |
2 |
|| |
逻辑或 |
1 |
=,+=,-=,*=,/=,%=,&=, ^=, |=, <<=, >>= |
赋值、运算且赋值 |
运算符优先级表明在每个表达式或子表达式中哪一个运算对象首先被求值,数值越大优先级越高,具有较高优先级级别的运算符先于较低级别的运算符进行求值运算。
例子 1: 加减乘除
[root@localhost ~]# aa=$(( (11+3)*3/2 ))
#虽然乘和除的优先级高于加,但是通过小括号可以调整运算优先级
[root@localhost ~]# echo $aa
21
例子 2:取模运算
[root@localhost ~]# bb=$(( 14%3 ))
[root@localhost ~]# echo $bb
2
#14 不能被 3 整除,余数是 2
例子 3:逻辑与
[root@localhost ~]# cc=$(( 1 && 0 ))
[root@localhost ~]# echo $cc
0
#逻辑与运算只有想与的两边都是 1,与的结果才是 1,否则与的结果是 0
9 、变量的测试与内容置换
变量置换方式 |
变量 y 没有设置 |
变量 y 为空值 |
变量 y 设置值 |
x=${y-新值} |
x=新值 |
x 为空 |
x=$y |
x=${y:-新值} |
x=新值 |
x=新值 |
x=$y |
x=${y+新值} |
x 为空 |
x=新值 |
x=新值 |
x=${y:+新值} |
x 为空 |
x 为空 |
x=新值 |
x=${y=新值} |
x=新值 y=新值 |
x 为空 y 值不变 |
x=$y y 值不变 |
x=${y:=新值} |
x=新值 y=新值 |
x=新值 y=新值 |
x=$y y 值不变 |
x=${y?新值} |
新值输出到标准错误 输出(就是屏幕) |
x 为空 |
x=$y |
x=${y:?新值} |
新值输出到标准错误 输出 |
新值输出到标准错误 输出 |
x=$y |
如果大括号内没有“:”,则变量 y 是为空,还是没有设置,处理方法是不同的;如果大括号内有“:”,则变量 y 不论是为空,还是没有没有设置,处理方法是一样的。如果大括号内是“-”或“+”,则在改变变量 x 值的时候,变量 y 是不改变的;如果大括号内是“=”,则在改变变量 x 值的同时,变量 y 的值也会改变。如果大括号内是“?”,则当变量 y 不存在或为空时,会把“新值”当成报错输出到屏幕上。看的头都晕了吧,举几个例子说明下吧:
例子 1:
[root@localhost ~]# unset y
#删除变量 y
[root@localhost ~]# x=${y-new}
#进行测试
[root@localhost ~]# echo $x
new
#因为变量 y 不存在,所以 x=new
[root@localhost ~]# echo $y
#但是变量 y 还是不存在的
和表 11-12 对比下,这个表是不是可以看懂了。这是变量 y 不存在的情况,那如果变量 y 的值是空呢?
[root@localhost ~]# y=""
#给变量 y 赋值为空
[root@localhost ~]# x=${y-new}
#进行测试
[root@localhost ~]# echo $x
[root@localhost ~]# echo $y
#变量 x 和变量 y 值都是空
那如果变量 y 有值呢?
[root@localhost ~]# y=old
#给变量 y 赋值
[root@localhost ~]# x=${y-new}
#进行测试
[root@localhost ~]# echo $x
old
[root@localhost ~]# echo $y
old
#变量 x 和变量 y 的值都是 old
例子 2:
那如果大括号内是“=”号,又该是什么情况呢?先测试下变量 y 没有设置的情况:
[root@localhost ~]# unset y
#删除变量 y
[root@localhost ~]# x=${y:=new}
#进行测试
[root@localhost ~]# echo $x
new
[root@localhost ~]# echo $y
new
#变量 x 和变量 y 的值都是 new
一旦使用“=”号,那么变量 y 和变量 x 都会同时进行处理,而不像例子 1 中只改变变量 x 的值。那如果变量 y 为空又是什么情况呢?
[root@localhost ~]# y=""
#设定变量 y 为空
[root@localhost ~]# x=${y:=new}
#进程测试
[root@localhost ~]# echo $x
new
[root@localhost ~]# echo $y
new
#变量 x 和变量 y 的值都是 new
一旦在大括号中使用“:”,那么变量 y 为空或者不设定,处理方式都是一样的了。那如果 y 已经赋值了,又是什么情况:
[root@localhost ~]# y=old
#给 y 赋值
[root@localhost ~]# x=${y:=new}
#进行测试
[root@localhost ~]# echo $x
old
[root@localhost ~]# echo $y
old
#原来变量 x 和变量 y 的值都是 old
例子 3:
再测试下大括号中是“?”的情况吧:
[root@localhost ~]# unset y
#删除变量 y
[root@localhost ~]# x=${y?new}
-bash: y: new
#会把值“new”输出到屏幕上
那如果变量 y 已经赋值了呢:
[root@localhost ~]# y=old
#给变量 y 赋值
[root@localhost ~]# x=${y?new}
#进行测试
[root@localhost ~]# echo $x
old
[root@localhost ~]# echo $y
old
#变量 x 和变量 y 的值都是 old
- 点赞
- 收藏
- 关注作者
评论(0)