【Linux系统管理】10 Shell 基础概念篇
一 Shell 概述
1.1 什么是 Shell
- Shell 只要可以实现功能(不考虑效率的情况)即可。Shell 需要对 Linux系统比较熟悉才能使用得当。
- Shell 编程主要为了帮助运维人员实现自动化重复的工作
- Python 是一个面向对象的程序语言,Shell 则是一个面向过程的简单语言。
- Shell 编程尽量使用非人机交互式的操作
如何学习Shell?
- Shell 编程熟能生巧,多敲 Shell 程序
- 将注释写清楚完整
- 重新编写代码
1.2 Shell 的分类
Shell 类别 | 易学性 | 可移植性 | 编辑行 | 快捷性 |
---|---|---|---|---|
Bourne shell (sh) | 容易 | 好 | 较差 | 较差 |
Korn shell (ksh) | 较难 | 较好 | 好 | 较好 |
Bourne Again (Bash) | 难 | 较好 | 好 | 好 |
POSIx Shell (psh) | 较难 | 好 | 好 | 较好 |
c Shell (csh) | 较难 | 差 | 较好 | 较好 |
TC Shell (tcsh) | 难 | 差 | 好 | 好 |
Shell 确实是命令解释器。Linux 系统是类Unix系统。脚本是一堆Linux命令的集合,Shell 可以调用。Bash Shell 是Linux的主流Shelll。
Shell 的两种主要语法类型有Bourne和C,这两种语法彼此不兼容。Bourne家族主要包括sh、ksh、Bash、psh、zsh;C家族主要包括:csh、tcsh (Bash和zsh在不同程度上支持csh 的语法)。
我们可以通过 /etc/shells 文件来查询Linux支持的Shell。命令如下:
~ cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh
二 Shell 脚本的执行方式
2.1 echo 命令
echo [选项] [输出内容]
选项:-e:支持反斜线控制的字符转换(具体参见表)-n:取消输出后行末的换行符号(就是内容输出后不换行)
范例:
#例子1:
~ echo 'Mr Zhong zhi wei is the most honest man!'
Mr Zhong zhi wei is the most honest man!
#echo 的内容就会打印到屏幕上#例子2:
echo -n 'Mr Zhong zhi wei is the most honest man!'
Mr Zhong zhi wei is the most honest man![root@localhost ~]#
#如果加入了"-n"选项,输出内容结束后,不会换行直接显示新行的提示符
在 echo 命令中如果使用了“-e”选项,则可以支持控制字符,如表所示:
控制字符 | 作用 |
---|---|
\ | 输出\本身 |
\a | 输出警告音 |
\b | 退格键,也就是向左删除键 |
\c | 取消输出行末的换行符。和“-n”选项一致 |
\e | ESCAPE键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车键 |
\t | 制表符,也就是Tab键 |
\v | 垂直制表符 |
\0nnn | 按照八进制ASCII码表输出字符。其中0为数字零,nnn是三位八进制数 |
\xhh | 按照十六进制 ASCII码表输出字符。其中 hh是两位十六进制数 |
2.1.1 ASCII 表
范例:
~ echo -e "\\ \a"
\
#这个输出会输出 \ ;同时会在系统音响中输出一声提示音~ echo -e "ab\bc"
ac
#这个输出中,在b键左侧有"\b",所以输出时只有ac~ echo -e "a\tb\tc\nd\te\tf"
a b c
d e f
#我们加入了制表符"\t"和换行符"\n",所以会按照格式输出
~ echo -e "\0141\t\0142\t\0143\t\n\0144\t\0145\t\0146"
a b c
d e f
#还是会输出上面的内容,不过时按照八进制ASCII码输出的。
也就是说141这个八进制,在ASCII码中代表小写的“a”,其他的以此类推。
~ echo -e "\x61\t\x62\t\x63\t\n\x64\t\x65\t\x66"
a b c
d e f
#如果按照十六进制ASCII码也同样可以输出
echo 命令还可以进行一些比较有意思的东西,比如:
#例子
echo -e "\e[1;31m abcd \e[0m"
这条命令会把abcd按照红色输出。解释下这个命令\e[1是标准格式,代表颜色输出开始,\e[Om代表颜色输出结束,31m定义字体颜色是红色。
echo能够识别的颜色如下:30m=黑色,31m=红色,32m=绿色,33m=黄色,34m=蓝色,35m=洋红,36m=青色,37m=白色。
echo -e "\e[1;42m abcd \e[0m"abcd
这条命令会给abcd加入一个绿色的背景。echo可以使用的背景颜色如下:40m=黑色,41m=红色,42m=绿色,43m=黄色,44m=蓝色,45m=洋红,46m=青色,47m=白色。
范例:取消系统的注释音
~ vi /etc/inputrc
set bell-style none
#取消该参数的注释后,重启系统即可。
2.2 Shell 脚本的执行
cat > hello.sh <<EOF
#!/bin/bash
#The first program
#Author: zhongzhiwei <Email: zhongzhiwei@kubesphere.io>echo -e "Mr. Zhongzhiwei is the most honest man."
EOF
Shell脚本写好了,那么这个脚本该如何运行呢?
在Linux中脚本的执行主要有这样两种种方法:赋予执行权限,直接运行
这种方法是最常用的shell脚本运行方法,也最为直接简单。就是赋予执行权限之后,直接运行。
当然运行时可以使用绝对路径(推荐使用绝对路径),也可以使用相对路径运行。命令如下:
~ chmod 755 hello.sh
#赋予执行权限~ /root/shell/hello.sh
Mr. Zhongzhiwei is the most honest man.
#使用绝对路径运行~ ./hello.sh
Mr. Zhongzhiwei is the most honest man.
#使用相对路径运行
通过Bash 调用执行脚本
这种方法也非常简单,命令如下:
~ bash hello.sh
Mr. Zhongzhiwei is the most honest man.
三 Bash 的基本功能
3.1 历史命令
1)历史命令的查看
history [选项] [历史命令保存文件]
选项:-c: 清空历史命令-w: 把缓存中的历史命令写入历史命令保存文件。如果不手工指定历史命令保存文件,则放入默认历史命令保存文件~/.bash_history中
~ vim /etc/profile
...省略部分输出...
HISTSIZE=1000
...省略部分输出...
我们使用history命令查看的历史命令和/.bash_history文件中保存的历史命令是不同的。那是因为当前登录操作的命令并没有直接写入/.bash_history文件,而是保存在缓存当中的。需要等当前用户注销之后,缓存中的命令才会写入~/.bash_history 文件。如果我们需要把内存中的命令直接写入~l.bash_history文件,而不等用户注销时再写入,就需要使用“-w”选项了。命令如下:
~ history -w
#把缓存中的历史命令直接写入~/.bash_history
这时再去查询~/.bash_history文件,历史命令就和history命令查询的一致了。
如果需要清空历史命令,只需要执行:
~ history -c
#清空历史命令
历史命令是存放在 硬盘Disk 中的。
2)历史命令的调用
如果想要使用原先的历史命令有这样几种方法:
- 使用上、下箭头调用以前的历史命令
- 使用“!n”重复执行第n条历史命令
- 使用“!!”重复执行上一条命令
- 使用“!字串”重复执行最后一条以该字串开头的命令
- 使用“!$”重复上一条命令的最后一个参数
3.2 命令与文件的补全
在Bash中,命令与文件补全是非常方便与常用的功能,我们只要在输入命令或文件时,按“Tab”键就会自动进行补全
- 快速输入
- 帮助排错
3.3 命令别名
#命令格式
~ alias
#查询命令别名~ alias 别名="原命令"
#设定命令别名#例如:
~ alias
#查询系统中已经定义好的别名
alias cdnet='cd /etc/sysconfig/network-scripts/'
alias cdyum='cd /etc/yum.repos.d/'
alias disepel='sed -ri /enabled/s/enabled=1/enabled=0/ /etc/yum.repos.d/base.repo'
alias egrep='egrep --color=auto'
alias epel='sed -ri /enabled/s/enabled=0/enabled=1/ /etc/yum.repos.d/base.repo'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias i.='ls -d .* --color=auto'
alias l.='ls -d .* --color=auto'
alias lh='ls -lh --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias more='more -d'
alias nm='systemctl restart NetworkManager'
alias p='poweroff'
alias restart='systemctl restart '
alias restartnet='systemctl restart network'
alias scandisk='echo "- - -" > /sys/class/scsi_host/host0/scan;\echo "- - -" > /sys/class/scsi_host/host1/scan;\echo "- - -" > /sys/class/scsi_host/host2/scan'
alias vi='vim'
alias vie0='vim /etc/sysconfig/network-scripts/ifcfg-eth0'
alias vie1='vim /etc/sysconfig/network-scripts/ifcfg-eth1'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
alias xzfgrep='xzfgrep --color=auto'
alias xzgrep=' xzgrep --color=auto'
alias yr='yum remove'
alias yy='yum -y install'
alias zegrep='zegrep --color=auto'
alias zfgrep='zfgrep --color=auto'
alias zgrep='zgrep --color=auto'~ alias vi='vim'
#定义vim命令的别名是 vi
既然我们说别名的优先级比命令高,那么命令执行时具体的顺序是什么呢?命令执行时的顺序是这样的:
1、第一顺位执行用绝对路径或相对路径执行的命令。
2、第二顺位执行别名。
3、第三顺位执行Bash的内部命令。
4、第四顺位执行按照$PATH环境变量定义的目录查找顺序找到的弟一个命令。为了让这个别名永久生效,可以把别名写入环境变量配置文件“~/.bashrc”,命令如下:
~ vi /root/.bashrc
3.4 Bash常用快捷键
快捷键 | 作用 |
---|---|
ctrl+A | 把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。 |
ctrl+E | 把光标移动到命令行结尾。 |
ctri+C | 强制终止当前的命令。 |
ctrl+L | 清屏,相当于clear命令。 |
ctrl+U | 删除或剪切光标之前的命令。我输入了一行很长的命令,不用使用退格键一个一个字符的删除,使用这个快捷键会更加方便 |
ctrl+K | 删除或剪切光标之后的内容。 |
ctrl+Y | 粘贴ctrl+U或ctrl+K剪切的内容。 |
ctrl+R | 在历史命令中搜索,按下ctrl+R之后,就会出现搜索界面,只要输入搜索内容,就会从历史命令中搜索。 |
ctrl+D | 退出当前终端。 |
ctrl+Z | 暂停,并放入后台。这个快捷键牵扯工作管理的内容,我们在系统管理章节详细介绍。 |
ctrl+S | 暂停屏幕输出。 |
ctrl+Q | 恢复屏幕输出。 |
3.5 输入输出重定向
1)Bash的标准输入输出
设备 | 设备文件名 | 文件描述符 | 类型 |
---|---|---|---|
键盘 | /dev/stdin | 0 | 标准输入 |
显示器 | /dev/stdout | 1 | 标准输出 |
显示器 | /dev/stderr | 2 | 标准错误输出 |
2)Bash的输出重定向
类型 | 符号 | 作用 |
---|---|---|
标准输出重定向 | 命令 > 文件 | 以覆盖的方式,把命令的正确输出输出到指定的文件或设备当中。 |
命令 >> 文件 | 以追加的方式,把命令的正确输出输出到指定的文件或设备当中。 | |
标准错误输出重定向 | 错误命令 2> 文件 | 以覆盖的方式,把命令的错误输出输出到指定的文件或设备当中。 |
错误命令 2>> 文件 | 以追加的方式,把命令的错误输出输出到指定的文件或设备当中。 | |
正确输出和错误输出同时保存 | 命令 > 文件 2>&1 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。 |
命令 >> 文件 2>&1 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中。 | |
命令 &> 文件 | 以覆盖的方式,把正确输出和错误输出都保存到同一个文件当中。 | |
命令 &>> 文件 | 以追加的方式,把正确输出和错误输出都保存到同一个文件当中。 | |
命令>>文件1 2>>文件2 | 把正确的输出追加到文件1中,把错误的输出追加到文件2中。 |
3)输入重定向
~ wc [选项] [文件名]
选项:-c:统计字节数-w:统计单词数-l:统计行数
3.6 多命令顺序执行
多命令执行符 | 故式 | 作用 |
---|---|---|
; | 命令1 ; 命令2 | 多个命令顺序执行,命令之间没有任何逻辑联系 |
&& 短路与 | 命令1 && 命令2 | 当命令1正确执行(?=0),则命令2才会执行当命令1执行不正确(?=0),则命令2才会执行当命令1执行不正确(?=0),则命令2才会执行当命令1执行不正确(?≠0),则命令2不会执行 |
|| 短路或 | 命令1 || 命令2 | 当命令1正确执行(?=0),则命令2不会执行当命令1执行不正确(?=0),则命令2不会执行当命令1执行不正确(?=0),则命令2不会执行当命令1执行不正确(?≠0),则命令2才会执行 |
范例:
~ ls ; date ; cd /etc~ ls && echo yes
~ scandisk && echo yes~ ls || echo on
~ scandisk || echo on
编程语言:
- 支持变量
- 支持流程控制支持循环
- 支持运算
- 支持函数
3.7 管道符
1)行提取命令 grep
~ grep [选项] "搜索内容" 文件名
选项:-A数字:列出符合条件的行,并列出后续的n行-B数字:列出符合条件的行,并列出前面的n行-c:统计找到的符合条件的字符串的次数-i:忽略大小写-n:输出行号-v:反向查找--color=auto:搜索出的关键子用颜色显示
举几个例子:
~ grep "/bin/bash" /etc/passwd
#查找用户信息文件/etc/ passwd中,有多少可以登录的用户
再举几个例子:
~ grep -A 3 "root" /etc/passwd
#查找包含有“root”的行,并列出后续的3行
root:x:0:0:root:/root:/bin/zsh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin~ grep -n "/bin/bash" /etc/passwd
#查找可以登录的用户,并显示行号~ grep -v "bin/bash" /etc/passwd
#查找不含有“/bin/bash”的行,其实就是列出所有的伪用户
2)find 和 grep 的区别
find命令是在系统当中搜索符合条件的文件名,如果需要模糊查询,使用通配符(通配符我们下一小节进行介绍)进行匹配,搜索时文件名是完全匹配。
~ touch abc abcd
#建立文件abc 建立文件abcd~ find . -name "abc"
#搜索文件名是abc 的文件,只会找到abc文件,而不会找到文件abcd
#虽然abcd文件名中包含abc,但是 find是完全匹配,只能和要搜索的数据完全一样才能找到
注意:find命令是可以通过-regex选项识别正则表达式规则的,也就是说find命令可以按照正则表达式规则匹配,而正则表达式是模糊匹配。但是对于初学者而言,find命令和grep命令本身就不好理解,所以我们这里只按照通配符规则来进行find查询。
grep命令是在文件当中搜索符合条件的字符串,如果需要模糊查询,使用正则表达式进行匹配,搜索时字符串是包含匹配。
3)管道符
ls -al /etc/ | more
netstat -an | grep "ESTABLISHED"
#查询下本地所有网络连接,提取包含ESTABLISHED(已建立连接)的行
#就可以知道我们的服务器上有多少已经成功连接的网络连接
netstat -an | grep "ESTABLISHED" | wc -l
#如果想知道具体的网络连接数量,就可以再使用wc命令统计行数
rpm -qa | grep httpd
3.8 通配符
通配符 | 作用 |
---|---|
? | 匹配一个任意字符 |
* | 匹配0个或任意多个任意字符,也就是可以匹配任何内容 |
[] | 匹配中括号中任意一个字符。例如:[abc]代表一定匹配一个字符,或者是a,或者是b,或者是c。 |
[-] | 匹配中括号中任意一个字符,-代表一个范围。例如: [a-z]代表匹配一个小写字母。 |
[^] | 逻辑非,表示匹配不是中括号内的一个字符。例如:[^o-9]代表匹配一个不是数字的字符。 |
范例:
~ touch abc abcd 012 0abc
#建立几个测试文件~ ls *
012 0abc abc abcd
#"*"代表所有的文件~ ls ?abc
0abc
#"?"匹配任意一个字符,所以会匹配0abc
#但是不能匹配abc,因为"?"不能匹配空~ ls [0-9]*
012 0abc
#匹配任何以数据开头的文件~ ls [^0-9]*
abc abcd
#匹配不已数字开头的文件
3.9 Bash中其他特殊符号
符号 | 作用 |
---|---|
‘’ | 单引号。在单引号中所有的特殊符号,如“$”和“”(反引号)都没有特殊含义。 |
“” | 双引号。在双引号中特殊符号都没有特殊含义,但是“$”、“”和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。 |
`` | 反引号。反引号括起来的内容是系统命令,在 Bash中会先执行它。和()作用一样,不过推荐使用()作用一样,不过推荐使用()作用一样,不过推荐使用(),因为反引号非常容易看错。 |
$() | 和反引号作用一样,用来引用系统命令。 |
() | 用于一串命令执行时,()中的命令会在子Shell中运行 |
{} | 用于一串命令执行时,手中的命令会在当前Shell中执行。也可以用于变量变形与替换。 |
[] | 用于变量的测试。 |
# | 在 Shell脚本中,#开头的行代表注释。 |
$ | 用于调用变量的值,如需要调用变量name 的值时,需要用$name 的方式得到变量的值。 |
\ | 转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如$将输出“$”符号,而不当做是变量引用。 |
1)单引号和双引号
~ name=sc
#定义变量name的值是sc
~ echo '$name'
$name
#如果输出时使用单引号,则 $name 原封不动的输出
~ echo "$name"
sc
#如果输出时使用双引号,则会输出变量name的值sc~ echo `date`
Thu Aug 18 09:59:53 CST 2022
#反引号括起来的命令会正常执行
范例:
~ name=zzw
~ echo name
name
~ echo $name
zzw
#调用变量时需要 $
~ echo "$name"
zzw
~ echo '$name'
$name
#使用''单引号直接输出字符~ cmd=date
~ echo "$cmd"
date
~ echo `$cmd` #echo $($cmd)效果一致
Thu Aug 18 09:53:00 CST 2022
#使用``反引号直接调用命令
~ echo '`date`'
`date`
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,date`会被当成普通字符输出
~ echo "`date`"
Thu Aug 18 10:00:56 CST 2022
#如果是双引号括起来,那么这个命令又会正常执行
2)反引号
~ echo ls
ls
#如果命令不用反引号包含,命令不会执行,而是直接输出~ echo `ls`
012 0abc abc abcd
#只有用反引号包括命令,这个命令才会执行~ echo $(date)
Thu Aug 18 10:02:13 CST 2022
#使用$(命令)的方式也是可以的
3)小括号、中括号和大括号
在介绍小括号和大括号的区别之前,我们先要解释一个概念,那就是父Shell和子Shell。在我们的 Bash 中,是可以调用新的Bash的,比如;
~ bash
~
这时,我们通过pstree命令查看一下进程数:
~ pstree | grep sshd
init─┬─
...省略部分输出...|-sshd-+-sshd---bash| |-2*[sshd---sftp-server]| `-sshd---bash---bash-+-grep
知道了父Shell和子Shell,我们接着解释小括号和大括号的区别。如果是用于一串命令的执行,那么小括号和大括号的主要区别在于:
- ()执行一串命令时,需要重新开一个子shell进行执行;
- {}执行一串命令时,是在当前shell执行;
- () 和 {} 都是把一串的命令放在括号里面,并且命令之间用;号隔开;
- ()最后一个命令可以不用分号;
- {}最后一个命令要用分号;
- {}的第一个命令和左括号之间必须要有一个空格;
- ()里的各命令不必和括号有空格;
- ()和{}中括号里面的某个命令的重定向只影响该命令,但括号外的重定向则影响到括号里的所有命令。
范例:
~ name=zzw ; ( echo "$name"; name=lm; echo "$name"; ) ; echo "$name"
zzw
lm
zzw~ name=zzw ; { echo "$name"; name=lm; echo "$name"; } ; echo "$name"
zzw
lm
lm~ echo "\$name"
$name
四 Bash 的变量和运算符
4.1 什么是变量
在定义变量时,有一些规则需要遵守;
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是“2name”则是错误的。
- 在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型。
- 变量用等号连接值,等号左右两侧不能有空格。
- 变量的值如果有空格,需要使用单引号或双引号包括。如:“test=“hello world!””。其中双引号括起来的内容“$”、“\”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
- 在变量的值中,可以使用“\”转义符。
- 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含"变量名"或用变量名"或用变量名"或用{变量名}包含变量名。例如:
~ _name=zhongzhiwei
~ echo "$_name"
zhongzhiwei~ 2name=zhongzhiwei
bash: 2name=zhongzhiwei: command not found~ name = zhongzhiwei
bash: name: command not found
#Linux格式:命令 选项 参数
范例:
~ test=123
~ test="$test"456
~ echo $test
123456
#叠加变量test,变量值变成了123456~ test=${test}789
~ echo $test
123456789
#再次叠加变量test,变量值变成了123456789
变量值的叠加可以使用两种格式:"变量名"或变量名"或变量名"或{变量名}
- 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令。例如:
~ test=$(date)
~ echo $test
Thu Aug 18 11:31:12 CST 2022
- 环境变量名建议大写,便于区分(便于和命令区分)。
4.2 变量的分类
- 用户自定义变量:这种变量是最常见的变量,由用户自由定义变量名和变量的值。
- 环境变量:这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在Windows 中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows 的操作环境,可以当做是Windows的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。用户也可以自定义环境变量。环境变量分为全局变量和局部变量。
- 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
- 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
Linux 下的变量分类总结:
用户自定义变量:名称(自定义);作用(自定义);内容(自定义)
环境变量:
- 用户自定义环境变量:名称(自定义);作用(自定义);内容(自定义)
- 系统自带环境变量:名称(确定);作用(确定);内容(自定义)
位置参数变量:名称(确定);作用(确定);内容(自定义)
预定义变量:名称(确定);作用(确定);内容(自定义)
4.3 用户自定义变量
1)变量定义
~ 2name="zhong zhi wei"
bash: 2name=zhong zhi wei: command not found
#变量名不能用数字开头~ name = "zhong zhi wei"
bash: name: command not found
#等号左右两侧不能有空格~ name=zhong zhi wei
bash: zhi: command not found
#变量的值如果有空格,必须要用引号包含
2)变量调用
~ name="zhong zhi wei"
#定义变量name
~ echo $name
zhong zhi wei
#输出变量name的值,变量调用的时候需要添加 $
3)变量查看
[root@localhost ~]# set [选项]
选项:
-u:如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x:如果设定此选项,在命令执行之前,会把命令先输出一次
范例:
~ echo $age#发现打印age变量没有信息,会有两种情况
##1.变量age没有定义
##2.变量age定义为 "" 空串~ set -u #临时生效,永久生效需要写入到环境变量配置文件
~ echo $age
bash: age: unbound variable
#当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
~ echo $name
zhong zhi wei~ set
#查看系统下所有的变量(用户自定义变量和环境变量)
BASH=/bin/bash
...省略部分输出...
name='zhong zhi wei'
#直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量~ env
#查看系统下所有的环境变量~ set -x
~ ls
+ ls --color=auto
anaconda-ks.cfg install.log install.log.syslog reset_centos.sh shell
#如果设定了-x选项,会在每个命令执行之前,先把命令输出一次
4)变量删除
~ unset 变量名
范例:
~ unset name
4.4 环境变量
1)环境变量设置
~ export age="18"
#使用 export 声明的变量即是环境变量
2)环境变量查询和删除
env 命令和 set 命令的区别是,set 命令可以查看所有变量(用户自定义变量和环境变量),而 env 命令只能查看环境变量。
unset gender
env | grep gender
#删除环境变量gender
3)系统默认环境变量
~ env
HOSTNAME=centos6server #当前主机名
SHELL=/bin/bash #当前的Shell
TERM=xterm #终端环境
HISTSIZE=1000 #历史命令条数
SSH_CLIENT=10.0.0.1 12021 22 #当前操作环境是用ssh连接的,这是记录客户端IP
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
SSH_TTY=/dev/pts/1 #ssh连接的终端时pts/1
USER=root #当前登录的用户
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:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
#定义颜色显示
age=18 #我们刚定义的环境变量
PATH=/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
#系统查找命令的路径
MAIL=/var/spool/mail/root #用户邮箱
PWD=/root #当前所在的目录路径
LANG=en_US.UTF-8 #语系
HOME=/root #当前登录用户的家目录
SHLVL=2 #当前在第二层子Shell中,我们刚加入了一个子Shell,如果时第一层Shell,这里就是1
LOGNAME=root #登录的用户
_=/bin/env #上次执行命令的最后一个参数或者命令本身
env命令可以查询到所有的环境变量,可是还有一些变量虽然不是环境变量,却是和Bash 操作接口相关的变量,这些变量也对我们的Bash操作终端起到了重要的作用。这些变量就只能用set 命令来查看了,我只列出重要的内容吧:
~ set
BASH=/bin/bash #Bash的位置
BASH_VERSINFO=([0]="4" [1]="1" [2]="2" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu") #Bash的版本
BASH_VERSION='4.1.2(2)-release' #Bash的版本
COLORS=/etc/DIR_COLORS #颜色记录文件
HISTFILE=/root/.bash_history #历史命令保存文件
HISTFILESIZE=1000 #在文件当中记录的历史命令最大条数
HISTSIZE=1000 #在缓存当中记录的历史命令最大条数
LANG=en_US.UTF-8 #语系环境
MACHTYPE=x86_64-redhat-linux-gnu #软件类型是 x64 兼容类型
MAIL=/var/spool/mail/root #用户邮箱路径
MAILCHECK=60 #每60秒去扫描新邮件
PPID=3989 #父Shell的PID。我们当前Shell是一个子Shell
PS1='\[\e[1;33m\][\u@\h \W]\$\[\e[0m\]' #命令提示符
PS2='> ' #第一行没输完,等待第二行输入的提示符
PS4='+ ' #"set -x"用来修改跟踪输出的前缀
PWD=/root #当前目录的路径
- PATH变量:系统查找命令的路径
先查询下PATH环境变量的值:
~ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
PATH变量的值是用“:”分割的路径,这些路径就是系统查找命令的路径。也就是说当我们输入了一个程序名,如果没有写入路径,系统就会到 PATH变量定义的路径中去寻找,是否有可以执行的程序。如果找到则执行,否则会报“命令没有发现”的错误。
那么是不是我们把自己的脚本拷贝到PATH变量定义的路径中,我们自己写的脚本也可以不输入路径而直接运行呢?
~ cp /root/shell/hello.sh /bin/
#拷贝hello.sh到/bin/目录下
~ hello.sh
Mr. Zhongzhiwei is the most honest man.
#hello.sh可以直接执行
所有的执行文件都是通过绝对路径或者相对路径找到后执行。
那么我们是不是可以修改PATH变量的值,而不是把程序脚本复制到/bin/目录中。当然是可以的,我们通过变量的叠加就可以实现了:
~ vim /etc/profile
PATH="$PATH":/root/shell
#在变量PATH的后面,加入/root/shell 目录~ echo $PATH
/usr/local/apache/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/shell
#查询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变量的默认值吧:
$ echo $PS1
\[\e[1;34m\]# \[\e[1;36m\]\u \[\e[1;0m\]@ \[\e[1;32m\]\h \[\e[1;0m\]in \[\e[1;33m\]\w \[\e[1;0m\][\[\e[1;0m\]\t\[\e[1;0m\]]\n\[\e[1;31m\]$\[\e[0m\]
#默认的提示符是显示“[用户名@简写主机名最后所在目录]提示符”
在PS1变量中,如果是可以解释的符号,如“\u”、“\h”等,则显示这个符号的作用。如果是不能解释的符号,如“@”或“空格”,则原符号输出。那么我们修改下PSl变量,看看会出现社么情况吧:
[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="\[\e[1;34m\]# \[\e[1;36m\]\u \[\e[1;0m\]@ \[\e[1;32m\]\h \[\e[1;0m\]in \[\e[1;33m\]\w \[\e[1;0m\][\[\e[1;0m\]\t\[\e[1;0m\]]\n\[\e[1;31m\]$\[\e[0m\] "PS1="\[\e[37;40m\][\[\e[32;40m\]\u\[\e[37;40m\]@\h \[\e[36;40m\]\w\[\e[0m\]]\\$ "
PS1变量可以自由定制,好像看到了点Linux可以自由定制和修改的影子,还是很有意思的。不过说实话一个提示符已经使用习惯了,如果换一个还是非常别扭的,还是改回默认的提示符吧:
[root@04:53上午 localhost 31 src]# PS1='[\u@\h \W]\$ '
[root@localhost src]#
- LANG语系变量
LANG变量定义了Linux系统的主语系环境,这个变量的默认值是:
~ echo $LANG
zh_CN.UTF-8
Linux 中文支持:
1)安装了中文字体和编码
2)操作终端必须支持中文编码
这是因为我们Linux安装时,选择的是中文安装,所以默认的主语系变量是“zh_CN.UTF-8”。那么Linux中到底支持多少语系呢?我们可以使用以下命令查询;
~ locale -a | more
aa_DJ
aa_DJ.iso88591
aa_DJ.utf8
aa_ER
...省略部分输出...
#查询支持的语系~ locale -a | wc -l
791
#实在是太多了,统计一下有多少个
我们支持这么多的语系,当前系统到底是什么语系呢?使用locale命令直接查询:
~ 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=
我们还要通过文件 /etc/sysconfig/i18n 定义系统的默认语系,查看下这个文件的内容:
~ ~ cat /etc/sysconfig/i18n
LANG="en_US.UTF-8"
SYSFONT="latarcyrheb-sun16"
这又是当前系统语系,又是默认语系,有没有快晕倒的感觉。解释下吧,我们可以这样理解,默认语系是下次重启之后系统所使用的语系,而当前系统语系是当前系统使用的语系。如果系统重启,会从默认语系配置文件/etc/sysconfig/il8n中读出语系,然后赋予变量LANG让这个语系生效。也就是说,LANG定义的语系只对当前系统生效,要想永久生效就要修改/etc/sysconfig/il8n文件了。
说到这里,我们需要解释下Linux中文支持的问题。是不是我们只要定义了语系为中文语系,如zh_CN.UTF-8就可以正确显示中文了呢?这要分情况,如果我们是在图形界面中,或者是使用远程连接工具(如SecureCRT),只要正确设置了语系,那么是可以正确显示中文的。当然远程连接工具也要配置正确的语系环境,具体配置方式可以参考Linux系统安装章节。
那么如果是纯字符界面(本地终端tty1-tty6)是不能显示中文的,因为Linux 的纯字符界面时不能显示中文这么复杂的编码的。如果我们非要在纯字符界面显示中文,那么只能安装中文插件,如zhcon 等。我们举个例子吧:
~ echo $LANG
en_US.UTF-8
#我当前使用远程工具连接,只要语系正确,则可以正确显示中文~ df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/vg_centos6server-lv_root 51475068 3511260 45342368 8% /
tmpfs 953092 72 953020 1% /dev/shm
/dev/sda1 487652 41219 420833 9% /boot
/dev/mapper/vg_centos6server-lv_home 150147332 61028 142452592 1% /home
/dev/sdb1 5039592 10280 4766652 1% /disk
/dev/mapper/scvg-userlv 2547504 2308 2414140 1% /disklvm
#df命令可以看到中文是正常的
但如果是纯字符界面呢?虽然我们是中文安装的,但纯字符界面的语系可是“en_US.UTF-8”,如图所示:
4.5 位置参数变量
位置参数变量 | 作用 |
---|---|
$n | n为数字,$0代表命令本身,$1-9代表第一到第九个参数,十以上的参数需要用大括号包含,如9代表第一到第九个参数,十以上的参数需要用大括号包含,如9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10} |
$* | 这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体(类似于字符串) |
$@ | 这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待(类似于数组) |
$# | 这个变量代表命令行中所有参数的个数 |
范例:
~ cat > count.sh <<-'EOF'
#!/bin/bash#使用位置变量进行num1和num2的赋值
num1=$1
#给num1变量赋值就是第一个参数
num2=$2
#给num2变量赋值就是第一个参数#对数值进行加法运算
SUM=$[ $num1 + $num2 ] #SUM=$(( $num1 + $num2 )) #可以进行算术运算
#变量sum的和就是num1和num2
#Shell当中的运算还是不太一样的,Shell运算符小节会详细介绍
echo $SUM
#打印变量sum的值
echo $0
#打印脚本本身
EOF~ chmod 755 count.sh
~ ./count.sh 1 2
3
那么还是几个位置参数的变量是干嘛的?我们在写个脚本来说明下:
范例:
~ vim parameter.sh
#!/bin/bashecho "A total of $# parameters"
#使用$#代表所有参数的个数echo "The parameters is: $*"
#使用$*代表所有的参数echo "The parameters is: $@"
#使用$@代表所有的参数
那么“∗”和“*”和“∗”和“@”有区别吗?还是有区别的,∗会把接收的所有参数当成一个整体对待,而*会把接收的所有参数当成一个整体对待,而∗会把接收的所有参数当成一个整体对待,而@则会区分对待接收到的所有参数。还是举个例子:
范例:
~ vim parameters-for.sh
#!/bin/bashfor i in "$*" ;do
#定义 for循环,in后面有几个值,for 会循环多少次,注意“$*”要用双引号括起来
#每次循环会把in后面的值赋予变量i
#Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次echo "\$* is: $i"#打印变量$i的值
done
echo -e "\E[1;41m-------------------------\E[0m"# for y in "$@" ;do
# #同样in后面的有几个值,for循环几次,每次都把值赋予变量y
# #可是Shell中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
# echo "\$@ is: $y"
# done
x=1
#定义变量x的值为1
for y in "$@" ;do
#同样in后面的有几个值,for循环几次,每次都把值赋予变量y
#可是Shell中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次echo "The parameter$x is $y"#输出变量y的值x=$(( $x + 1 ))#变量x每次循环都加1,为了输出时看的更清楚
done~ ./parameters-for.sh 11 22 33 44
$* is: 11 22 33 44
-------------------------
$@ is: 11
$@ is: 22
$@ is: 33
$@ is: 44
4.6 预定义变量
预定义变量 | 作用 |
---|---|
$? | 最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。 |
$$ | 当前进程的进程号(PID) |
$! | 后台运行的最后一个进程的进程号(PID) |
我们先来看看“$?”这个变量,看起来不好理解,我们还是举个例子:
~ ls
count.sh parameters-for.sh parameter.sh
#ls命令正确执行
~ echo $?
0
#预定义变量“$?”的值是0,证明上一个命令执行正确
~ ls install.log
ls: cannot access install.log: No such file or directory
#ls命令执行有误
~ echo $?
2
#预定义变量“$?”的值是2,证明上一个命令执行或者输出结果有误
范例:
~ echo $$
9142
#当前进程的进程号(PID)
4.7 接收键盘输入
[root@localhost~]# read [选项] [变量名]
选项:-p “提示信息” :在等待read输入时,输出提示信息-t 秒数 :read命令会一直等待用户输入,使用此选项可以指定等待时间-n 字符数 :read命令只接受指定的字符数,就会执行-s :隐藏输入的数据,适用于机密信息的输入
变量名:变量名可以自定义,如果不指定变量名,会把输入保存入默认变量 REPLY如果只提供了一个变量名,则整个输入行赋予该变量如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字
read 命令范例:
~ vim read.sh
#!/bin/bash
#Author: zhongzhiwei <Email: zhongzhiwei@kubesphere.io>read -t 30 -p "Please input your name: " name
#提示“请输入姓名”并等待30秒,把用户的输入保存入变量 name 中
echo "Name is $name"
#看看变量“$name”中是否保存了你的输入read -s -t 30 -p "Please input your age: " age
#提示“请输入年龄”并等待30秒,把用户的输入保存入变量 age 中
#年龄是隐私,所以我们用“-s”选项隐藏输入
echo -e "\n"
#调整输出格式,如果不输出换行,一会的年龄输出不会换行
echo "Age is $age"
echo -e "\n"read -n 1 -t 30 -p "Please select your gender(M/F): " gender
#提示“请选择性别”并等待30秒,把用户的输入保存入变量gender
#使用“-n1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Sex is $gender"
范例:
~ vim read-num.sh
#!/bin/bashread -t 30 -p "Please input your number1: " number1
read -t 30 -p "Please input your number2: " number2SUM=$[ $number1 + $number2 ]
echo "SUM=$SUM"~ chmod 755 ./read-num.sh
~ ./read-num.sh
Please input your number1: 10
Please input your number2: 20
SUM=30
4.8 Shell 运算符
1)数值运算的方法
那如果我需要进行数值运算,可以采用以下三种方法中的任意一种:
- 使用declare声明变量类型。
既然所有变量的默认类型是字符串型,那么只要我们把变量声明为整数型不就可以运算了吗?使用declare命令就可以实现声明变量的类型。命令如下:
root@localhost ~]# declare [+/-] [选项] 变量名
选项:
- :给变量设定类型属性
+ :取消变量的类型属性
-a :将变量声明为数组型
-i :将变量声明为整数型(integer)
-r :将变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值,也不能删除变量,甚至不能通过+r取消只读属性-
-x :将变量声明为环境变量
-p :显示指定变量的被声明的类型
例子1:数值运算
那我们只要把变量声明为整数型不就可以运算了吗?试试吧:
aa=11
bb=22
#给变量aa和bb赋值
declare -i cc=${aa}+${bb}
#声明变量cc的类型是整数型,它的值是aa和bb的和
echo ${cc}
33
#这下终于可以相加了
这样运算好麻烦啊,没有办法啊,Shell在数值运算这里确实是比较麻烦,习惯就好了-_-!
例子2:数组变量类型
数组这个东东只有写一些较为复杂的程序才会用到,大家可以先不用着急学习数组,当有需要的时候再回来详细学习。那么数组是什么呢?所谓数组,就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名.然后用编号区分他们的变量的集合,这个名字称为数组名,编号称为下标。组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量。
一看定义就一头雾水,更加不明白数组是什么了。那么换个说法,变量和数组都是用来保存数据的,只是变量只能赋予一个数据值,一旦重复复制,后一个值就会覆盖前一个值。而数组是可以赋予一组相同类型的数据值。大家可以把变量想象成一个小办公室,这个办公室只能容纳一个人办公,办公室名就是变量名。而数组是一个大办公室,可以容纳很多人同时办公,在这个大办公室办公的每个人是通过不同的座位号来区分的,这个座位号就是数组的下标,而大办公室的名字就是数组名。
数组会明显降低编写程序的难度。例如冒泡程序等。但是数组在 Shell 编程中比较少使用。
还是举个例子吧:
name[0]="zhong zhi wei"
#数组中第一个变量是zhongzhiwei(大办公室第一个办公桌坐最高大威猛帅气的人)name[1]="zhang san"
#数组第二个变量是zhangsan(大办公室第二个办公桌坐头发铿亮的人)name[2]="li si"
#数组第三个变量是lisi(大办公室第三个办公桌坐眼睛比zhongzhiwei还小的老师)。echo ${name}
zhong zhi wei
#输出数组的内容,如果只写数组名,那么只会输出第一个下标变量echo ${name[*]}
zhong zhi wei li si li si
#输出数组所有的内容
注意数组的下标是从 0 开始的,在调用数组值时,需要使用 ${数组[下标]} 的方式来读取。
不过好像在刚刚的例子中,我们并没有把 name变量声明为数组型啊,其实只要我们在定义变量时采用了“变量名[下标]”的格式,这个变量就会被系统认为是数组型了,不用强制声明。
例子3:环境变量
我们其实也可以使用declare命令把变量声明为环境变量,和Pexport命令的作用是一样的:
declare -x test=123
#把变量 test 声明为环境变量declare -x
#查看环境变量
例子4:只读属性
注意一旦给变量设定了只读属性,那么这个变量既不能修改变量的值,也不能删除变量,甚至不能使用“+r”选项取消只读属性。命令如下:
~ declare -r test
#会test斌予只读渴性~ test=456
-bash: test: readonly variable
#test变量的值就不能进行修改了~ declare +r test
-bash: declare: test: readonly variable
#也不能取消只读属性~ unset test
-bash: unset: test: cannot unset: readonly variable
#也不能删除只读变量~ echo $test
123
不过还好这个变量只是命令行声明的、所以只要重新登录或重启,这个变量就会消失了。
例子5:查询变量属性和取消变量属性
变量属性的查询使用“-p”选项,变量属性的取消使用“+”选项。命令如下:
~ declare -p cc
declare -i cc="33"
#cc变量是int类型~ declare -p name
declare -a name='([0]="zhong zhi wei" [1]="li si" [2]="li si")'
#name变量是数组型~ declare -p test
declare -rx test="123"
#test变量是环境变量和只读变量~ declare +x test
#取消test变量的环境变量属性~ declare -p test
declare -r test="123"
#注意:只读变量是不能取消的
4.8.1 数值运算
- 使用 declare
范例:
~ a=11
~ b=22
~ declare -i c=${a}+${b}
~ echo ${c}
33
- 使用expr或let数值运算工具
要想进行数值运算的第二种方法是使用 expr 命令,这种命令就没有declare命令复杂了。命令如下:
~ aa=11
~ bb=22
#给变量aa和变量bb赋值~ dd=$(expr $aa + $bb)
#dd的值是aa和bb的和。注意“+”号左右两侧必须有空格
~ echo $dd
33
使用expr命令进行运算时,要注意“+”号左右两侧必须有空格,否则运算不执行。
至于 let 命令和 expr 命令基本类似,都是Linux中的运算命令,命令格式如下:
~ aa=11
~ bb=22
#给变量aa和变量bb赋值~ let ee=${aa}+${bb}
~ echo ${ee}
33
#变量ee的值是aa和bb的总和~ n=20
#定义变量n
~ let n+=1
#变量n的值等于变量本身再加1
~ echo ${n}
21
expr命令和let命令大家可以按照习惯使用,不过let命令对格式要求要比 expr命令宽松,所以推荐使用let命令进行数值运算。
- 使用“((运算式))”或“((运算式))”或“((运算式))”或“[运算式]”方式运算
其实这是一种方式“(())”和“(())”和“(())”和“[”这两种括号按照个人习惯使用即可。命令如下:
~ aa=11
~ bb=22
#给变量aa和变量bb赋值
~ ff=$(( ${aa} + ${bb} ))
~ echo ${ff}
33#变量ff的值是aa 和 bb的和
~ gg=$[ ${aa} + ${bb} ]
~ echo ${gg}
33
#变量gg的值是aa 和 bb的和
这三种数值运算方式,大家可以按照自己的习惯来进行使用。不过我们推荐使用“$((运算式))”的方式。
2)Shell 的常用运算符
优先级 | 运算符 | 说明 |
---|---|---|
13 | -,+ | 单目负、单目正 |
12 | !,~ | 逻辑非、按位取反或补码 |
11 | *,/ ,% | 乘、除、取模 |
10 | +,- | 加、减 |
9 | <<, >> | 按位左移、按位右移 |
8 | <=,> =,<, > | 小于或等于、大于或等于、小于、大于 |
7 | ==,!= | 等于、不等于 |
6 | & | 按位与 |
5 | ^ | 按位异或 |
4 | | | 按位或 |
3 | && | 逻辑与 |
2 | || | 逻辑或 |
1 | =,+=,—=,*=,/一,%=,&=,^=,|=,<<=,>>= | 赋值、运算且赋值 |
运算符优先级表明在每个表达式或子表达式中哪一个运算对象首先被求值,数值越大优先级越高,具有较高优先级级别的运算符先于较低级别的运算符进行求值运算。(用括号,可读性是第一位的,不用为了优先级而难为自己)
例子1:加减乘除
~ aa=$(( (11+3) * 3 / 2 ))
#虽然乘和除的优先级高于加,但是通过小括号可以调整运算优先级~ echo ${aa}
21
例子2:取模运算
~ bb=$(( 14 % 3 ))
~ echo ${bb}
2
#14不能被3整除,余数是2
例子3:逻辑与
~ cc=$(( 1 && 0 ))
~ echo ${cc}
0
#逻辑与运算只有想与的两边都是1,与的结果才是1,否则与的结果是О
4.8.2 四则运算练习题
范例:
~ vim arithmetic.sh
#!/bin/bash
#Author: zhongzhiwei <Email: zhongzhiwei@kubesphere.io>#请输入需要数值的赋值
read -t 30 -p "Please input the number1: " NUM1
if [[ ${NUM1} =~ [0-9].* ]] ; thenecho -e "\E[1;32mNumber is True \E[0m"
elseecho -e "\E[1;31mNumber is False \E[0m"exit;
fi
read -t 30 -p "Please input the number2: " NUM2
if [[ ${NUM2} =~ [0-9].* ]] ; thenecho -e "\E[1;32mNumber is True \E[0m"
elseecho -e "\E[1;31mNumber is False \E[0m"exit;
fi#请输入需要进行四则运算的过程
read -n 1 -t 30 -p "Please enter the procedure that requires four operations(+|-|*|/): " OPTIONS
echo
echo -e "\E[1;42m--------------------------------\E[0m"if [ "${OPTIONS}" == '+' ] ;thenSUM=$(( ${NUM1} ${OPTIONS} ${NUM2} ))echo -e "${NUM1} ${OPTIONS} ${NUM2} = ${SUM}"
elif [ "${OPTIONS}" == '-' ] ;thenSUM=$(( ${NUM1} ${OPTIONS} ${NUM2} ))echo -e "${NUM1} ${OPTIONS} ${NUM2} = ${SUM}"
elif [ "${OPTIONS}" == '*' ] ;thenSUM=$(( ${NUM1} ${OPTIONS} ${NUM2} ))echo -e "${NUM1} ${OPTIONS} ${NUM2} = ${SUM}"
elif [ "${OPTIONS}" == '/' ] ;thenSUM=$(( ${NUM1} ${OPTIONS} ${NUM2} ))echo -e "${NUM1} ${OPTIONS} ${NUM2} = ${SUM}"
elseecho -e "\E[1;31m Error!Please input the (+|-|*|/) \E[0m"
fi
范例:
#极其简单
~ vim count.sh
#!/bin/bashvalue=$(( $1 $2 $3 ))
echo ${value}~ vim count.sh
#!/bin/bash#请输入需要数值的赋值
read -t 30 -p "Please input the number1: " NUM1
if [[ ${NUM1} =~ [0-9].* ]] ; thenecho -e "\E[1;32mNumber is True \E[0m"
elseecho -e "\E[1;31mNumber is False \E[0m"exit;
fi
read -t 30 -p "Please input the number2: " NUM2
if [[ ${NUM2} =~ [0-9].* ]] ; thenecho -e "\E[1;32mNumber is True \E[0m"
elseecho -e "\E[1;31mNumber is False \E[0m"exit;
fi#请输入需要进行四则运算的过程
read -n 1 -t 30 -p "Please enter the procedure that requires four operations(+|-|*|/): " OPTIONSecho
echo -e "\E[1;42m--------------------------------\E[0m"
[ "$OPTIONS" == '+' ] && echo "SUM=$(( $NUM1 + $NUM2 ))" && exit
[ "$OPTIONS" == '-' ] && echo "SUM=$(( $NUM1 - $NUM2 ))" && exit
[ "$OPTIONS" == '*' ] && echo "SUM=$(( $NUM1 * $NUM2 ))" && exit
[ "$OPTIONS" == '/' ] && echo "SUM=$(( $NUM1 / $NUM2 ))" && exitecho -e "\E[1;31mPlease enter the correct operator\E[0m"
4.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=$yy值不变 |
x=${y:=新值} | x=新值y=新值 | x=新值y=新值 | x=$yy值不变 |
x=${y?新值} | 新值输出到标准错误输出(就是屏幕) | x为空 | x=$y |
x=${y:?新值} | 新值输出到标准错误输出 | 新值输出到标准错误输出 | x=$y |
如果大括号内没有“:”,则变量y是为空,还是没有设置,处理方法是不同的;如果大括号内有“:”,则变量y不论是为空,还是没有没有设置,处理方法是一样的。
如果大括号内是“-”或“+”,则在改变变量x值的时候,变量y是不改变的;如果大括号内是“=”,则在改变变量x值的同时,变量y的值也会改变。
如果大括号内是“?”,则当变量y不存在或为空时,会把“新值”当成报错输出到屏幕上。看的头都晕了吧,举几个例子说明下吧:
例子1;
~ unset y
#删除变量y
~ x=${y-new}
#进行测试
~ echo $x
new
#因为变量y不存在,所以x=new
~ echo $y#但是变量y还是不存在的
和表对比下,这个表是不是可以看懂了。这是变量y不存在的情况,那如果变量y的值是空呢?
~ y=""
#给变量y赋值为空
~ x=${y-new}
#进行测试
~ echo $x~ echo $y#变量x和变量y值都是空的
那如果变量y有值呢?
~ y=old
#给变量y赋值
~ x=${y-new}
#进行测试
~ echo $x
old
~ echo $y
old
#变量x和变量y的值都是old
例子2:
那如果大括号内是“=”号,又该是什么情况呢?先测试下变量 y 没有设置的情况:
~ unset y
#删除变量y
~ x=${y:=new}
#进行测试
~ echo $x
new
~ echo $y
new
#变量 x 和变量 y 的值都是new
一旦使用“=”号,那么变量y和变量x都会同时进行处理,而不像例子1中只改变变量x的值。那如果变量y为空又是什么情况呢?
~ y=""
#设置变量y为空
~ x=${y:=new}
#进行测试
~ echo $x
new
~ echo $y
new
#变量 x 和变量 y 的值都是new
一旦在大括号中使用“:”,那么变量y为空或者不设定,处理方式都是一样的了。那如果y已经赋值了,又是什么情况:
~ y=old
#给y赋值
~ x=${y:=new}
#进行测试
~ echo $x
old
~ echo $y
old
#变量 x 和变量 y 的值都是old
范例:
~ x=${z-new}
~ echo ${x}
new~ z=""
~ x=${z-new}
~ echo ${x}
五 环境变量配置文件
5.1 source 命令
source 配置文件
或者
. 配置文件
5.2 环境变量配置文件
1)、登录时生效的环境变量配置文件
在Linux系统登录时主要生效的环境变量配置文件有以下五个:
- /etc/profile
- /etc/profile.d/*.sh
- ~/.bash_profile
- ~/.bashrc
- /etc/bashrc
存放在 /etc/ 下的环境变量配置文件是全局用户生效,存放在各自家目录下的环境变量配置文件是只对各自用户生效。
环境变量配置文件调用过程
- 在用户登录过程先调用/etc/profile文件
在这个环境变量配置文件中会定义这些默认环境变量:
- USER变量:根据登录的用户,给这个变量赋值(就是让USER变量的值是当前用户)。
- LOGNAME变量:根据USER变量的值,给这个变量赋值。
- MAIL变量:根据登录的用户,定义用户的邮箱为 /var/spool/mail/用户名。
- PATH变量:根据登录用户的UID是否为o,判断PATH变量是否包含/sbin、/usr/sbin 和 /usr/local/sbin这三个系统命令目录。
- HOSTNAME变量:更加主机名,给这个变量赋值。HISTSIZE变量:定义历史命令的保存条数。
- umask:定义umask 默认权限。注意/etc/profile文件中的umask权限是在“有用户登录过程(也就是输入了用户名和密码)”时才会生效。
- 调用/etc/profile.d/*.sh文件,也就是调用/etc/profile.d/目录下所有以.sh结尾的文件。
由/etc/profile文件调用/etc/profile.d/*.sh文件
这个目录中所有以.sh结尾的文件都会被 /etc/profile 文件调用,这里最常用的就是lang.sh文件,而这个文件又会调用/etc/sysconfig/i18n文件。/etc/ sysconfig/i18n这个文件眼熟吗?就是我们前面讲过的默认语系配置文件啊。
由/etc/profile文件调用~/. bash_profile文件
- ~/.bash_profile文件就没有那么复杂了,这个文件主要实现了两个功能:
- 调用了~/.bashrc文件。
- 在PATH变量后面加入了“:$HOME/bin”这个目录。那也就是说,如果我们在自己的家口目录中建立 bin目录,然后把自己的脚本放入“~/bin”目录,就可以直接执行脚本,而不用通过目录执行了。
由~/ . bash_profile文件调用~/.bashrc文件
- 在~/. bashrc文件中主要实现了:
- 定义默认别名,所以把自己定义的别名也放入了这个文件。
- 调用/etc/bashrc
由~/.bashrc调用了/etc/bashrc文件
- 在 /etc/bashrc文件中主要定义了这些内容:
- PS1变量:也就是用户的提示符,如果我们想要永久修改提示符,就要在这个文件中修改
- umask:定义umask默认权限。这个文件中定义的umask是针对“没有登录过程(也就是不需要输入用户名和密码时,比如从一个终端切换到另一个终端,或进入子Shell)”时生效的。如果是“有用户登录过程”,则是/etc/profile文件中的umask生效。
- PATH变量:会给PATH变量追加值,当然也是在“没有登录过程”时才生效。
- 调用/etc/profile.d/.sh 文件,这也是在“没有用户登录过程”是才调用。在“有用户登录过程”时,/etc/profile.d/.sh文件已经被/etc/profile文件调用过了。
这样这五个环境变量配置文件会被依次调用,那么如果是我们自己定义的环境变量应该放入哪个文件呢?如果你的修改是打算对所有用户生效的,那么可以放入 /etc/profile 环境变量配置文件;如果你的修改只是给自己使用的,那么可以放入/.bash_profile或/.bashrc这两个配置文件中的任一个。
可是如果我们误删除了这些环境变量,比如删除了/etc/bashrc文件,或删除了/.bashrc文件,那么这些文件中配置就会失效(/.bashrc文件会调用/etc/bashrc文件)。那么我们的提示符就会变成:
-bash-4.1#
#有可能是/etc/bashrc配置文件或者是家目录bashrc配置
2)、注销时生效的环境变量配置文件
在用户退出登录时,只会调用一个环境变量配置文件,就是~/.bash_logout。这个文件默认没有写入任何内容,可是如果我们希望再退出登录时执行一些操作,比如清除历史命令,备份某些数据,就可以把命令写入这个文件。
3)、其他配置文件
还有一些环节变量配置文件,最常见的就是~/bash_history文件,也就是历史命令保存文件。这个文件已经讲过了,这里我们只是把它归入环境变量配置文件小节而已。
5.3 Shell 登录信息
1)、/etc/issue
我们在登录tty1-tty6这六个本地终端时,会有几行的欢迎界面。这些欢迎信息是保存在哪里的?可以修改吗?当然可以修改,这些欢迎信息是保存在/etc/issue文件中,我们查看下这个文件:
~ cat /etc/issue
CentOS release 6.10 (Final)
Kernel \r on an \m
可以支持的转义符我们可以通过 man agetty 命令查询,在表中我们列出常见的转义符作用:
转义符 | 作用 |
---|---|
\d | 显示当前系统日期 |
\s | 显示操作系统名称 |
\l | 显示登录的终端号,这个比较常用。 |
\m | 显示硬件体系结构,如i386、i686等 |
\n | 显示主机名 |
\o | 显示域名 |
\r | 显示内核版本 |
\t | 显示当前系统时间 |
\u | 显示当前登录用户的序列号 |
范例:
~ cat /etc/issue
CentOS release 6.10 (Final)
Kernel \r on an \m
TTY in \l#重启机器
~ reboot
2)、/etc/issue.net
/etc/issue是在本地终端登录是显示欢迎信息的,如果是远程登录 ( 如ssh远程登录,或telnet远程登录 ) 需要显示欢迎信息,则需要配置/etc/issue.net这个文件了。使用这个文件时由两点需要注意:
- 首先,在/etc/issue文件中支持的转义符,在/etc/issue.net文件中不能使用。
- 其次,ssh远程登录是否显示/etc/issue.net文件中的欢迎信息,是由ssh 的配置文件决定的。
如果我们需要ssh远程登录可以查看/etc/issue.net的欢迎信息,那么首先需要修改ssh的配置文件/etc/ssh/sshd_config,加入如下内容:
/etc/ssh/ssh_config #客户端的配置文件
/etc/ssh/sshd_config #登录服务器端的配置文件
~ vim /etc/ssh/sshd_config
...省略部分输出...
# no default banner path
#Banner none
Banner /etc/issue.net
...省略部分输出...#重启机器或者重启sshd服务
~ reboot | ~ service sshd restart
这样在ssh远程登录时,也可以显示欢迎信息,只是不再可以识别“\d”和“\l”等信息了。
3)、/etc/motd
/etc/motd 文件中也是显示欢迎信息的,这个文件和 /etc/issue 及 /etc/issue.net 文件的区别是:
/etc/issue 及 /etc/issue.net 是在用户登录之前显示欢迎信息(推荐使用告警信息),而 /etc/motd 是在用户输入用户名和密码,正确登录之后显示欢迎信息。在 /etc/motd 文件中的欢迎信息,不论是本地登录,还是远程登录都可以显示。不需要重启就可以立刻生效。
~ cat /etc/motd
FBI Warning!
5.4 定义 Bash 快捷键
[root@localhost~]# stty -a
#查询所有的快捷键
那么这些快捷键可以更改吗?可以啊,只要执行:
[root@localhost ~]# stty 关键字 快捷键例如:
[root@localhost ~]# stty intr ^p
#定义ctrl+p 快捷键为强制终止,“ ^ ”字符只要手工输入即可[root@localhost ~]# stty -a
speed 38400 baud; rows 21; columns 104; line = 0;
intr = ^P; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol =<undef>; eol2 =<undef>; swtch= <undef> ;
start = Q; stop = S; susp = ^Z; rprnt = R; werase = ^W; lnext = V; flush = ~O; min =l ; time = 0 ;
#强制终止变成了 ctrl + p 快捷键
【Linux系统管理】10 Shell 基础概念篇相关推荐
- 史上最牛最强的linux学习笔记 10.shell基础
史上最牛最强的linux学习笔记 10.shell基础 写在最前面: 本文是基于某站的视频学习所得,第一个链接如下: https://www.bilibili.com/video/BV1mW411i7 ...
- Linux Capabilities 入门教程--基础实战篇
该系列文章总共分为三篇: Linux Capabilities 入门教程:概念篇 Linux Capabilities 入门教程:基础实战篇 Linux Capabilities 入门教程:进阶实战篇 ...
- 2022-07-17 Linux group与user基础概念
Linux group与user基础概念 概念 user 用户 group 用户组 Linux 命令使用 user 用户 useradd 添加用户 userdel 删除用户 usermod 修改账号 ...
- 【软件测试】基础-概念篇
软件测试基础-概念篇 记录 - 慕课网 imooc 软件测试基础-概念篇 简介:系统介绍什么是软件测试,从软件测试的定义.原则以及测试阶段.测试模式.测试手段和测试类型分别详细说明软件测试中的各种测试 ...
- 电子书 鸟哥的Linux私房菜 (基础学习篇 第三版).pdf
<鸟哥的Linux私房菜 (基础学习篇 第三版)>是颇具知名度的Linux入门书<鸟哥的Linux私房菜:基础学习篇>的全新版,全面而详细地介绍了Linux操作系统. 全书分为 ...
- C++面试题(一)——基础概念篇
https://blog.csdn.net/worldwindjp/article/details/18909079 C++面试题--基础概念篇 唐璐 http://blog.csdn.net/wor ...
- linux一号进程和二号进程,Linux系统管理10——进程和计划任务管理(示例代码)
Linux系统管理10--进程和计划任务管理 一.程序和进程的关系 1.程序 ·保存在硬盘.光盘等介质中的可执行代码和数据 ·静态保存的代码 2.进程 ·在CPU及内存中运行的程序代码 ·动态执行的代 ...
- 模型推理部署——基础概念篇
模型推理部署--基础概念篇 训练(training)vs推理(inference) 训练是通过从已有的数据中学习到某种能力,而推理是简化并使用该能力,使其能快速.高效地对未知的数据进行操作,以获得预期 ...
- Python+Dash快速web应用开发——基础概念篇
作者:费弗里 来源:Python大数据分析 ❝本文示例代码与数据已上传至https://github.com/CNFeffery/DataScienceStudyNotes ❞ 1 简介 这是我的新系 ...
最新文章
- 关于学习Python的一点学习总结(7->相关字符串操作)
- java 数据反射_基本操作--反射填充数据
- BZOJ 4555 [Tjoi2016Heoi2016]求和
- spring30: 事务
- QT连接Postgresql数据库
- Code Review 与 结对编程
- python中tmp什么意思_python中temp是什么意思-问答-阿里云开发者社区-阿里云
- CukeTest新版本公测邀请-Windows应用自动化
- wx僵尸粉检测,真实好友1.0(无障碍检测好友状态)
- Android 百度地图定位
- Hive MetaStore java.lang.NoClassDefFoundError: com/facebook/fb303/FacebookService$Iface
- android 重力感应小球 Demo
- 头歌——c++单向链表
- 技术总结-从输入 URL 到页面加载完成的过程中都发生了什么事情?
- Airtest 安装及模拟器连接
- 树莓派上手前的准备工作(一)——格式化sd卡(sd卡格式化工具的使用)
- 达观数据CEO陈运文:“AI+RPA”如何赋能企业数字化转型
- 12连跳后富士康应停产整顿
- win10休眠_转载 || 维修站教你最牛的win10优化设置小技巧!
- Tsai库---分享一个好用的时间序列库
热门文章
- 漫画:骚操作系列(必须掌握的疯子找座问题)
- 减法公式运算法则_数学的运算定律
- Php狼找兔子,狼和兔子的故事 - 仙境传说:守护永恒的爱 - 普隆德拉酒馆 - 心动游戏 官方论坛...
- 免费下载shape_predictor_68_face_landmarks.dat
- vscode报错Failed to save ***,The content of the file is newer.
- Stream Collectors - reducing
- Web前端开发有哪些岗位职责 要掌握什么技能
- health HEALTH_WARN;352 pgs degraded;352 pgs stuck unclean;352 pgs undersized;recovery 20/40 objects
- android mixpanel可视化埋点框架解析
- H3CNE考试模拟试题