shell基础/shell编程
2021-03-30 10:35:46 4 举报
AI智能生成
linux shell详细学习资料整理
作者其他创作
大纲/内容
Shell基础
一、Shell概述
1.什么是Shell
sh就是命令解释器
脚本就是命令的集合
2、shell的分类
unix使用的csh,单盒bash几乎是一样的
主要语法类型有Bourne和C,彼此不兼容
Bourne
sh
ksh
Bash
psh
zsh
C
csh
tcsh
Bash和zsh在不同程度上支持csh的语法
通过 /etc/shells文件来查询Linux支持的Shell
[root@localhost ~]# vi /etc/shells<br>/bin/sh<br>/bin/Bash<br>/sbin/nologin<br>/bin/tcsh<br>/bin/csh
二、Shell脚本的执行方式
1、echo命令
[root@localhost ~]# echo [选项] [输出内容]
选项:
-e: 支持反斜线控制的字符转换(具体参见表11-2)
-n: 取消输出后行末的换行符号(就是内容输出后不换行)
例子1:
[root@localhost ~]# echo "Mr. Shen Chao is the most honest man!"
#echo的内容就会打印到屏幕上。
Mr. Shen Chao is the most honest man!
[root@localhost ~]#
例子2:
[root@localhost ~]# echo -n "Mr. Shen Chao is the most honest man!"
Mr. Shen Chao is the most honest man![root@localhost ~]#
#如果加入了“-n”选项,输出内容结束后,不会换行直接显示新行的提示符。
在echo命令中如果使用了“-e”选项,则可以支持控制字符,如表11-2所示:
例子3:<br>[root@localhost ~]# echo -e "\\ \a"<br>\<br>#这个输出会输出\,同时会在系统音响中输出一声提示音<br>例子4:<br>[root@localhost ~]# echo -e "ab\bc"<br>ac<br>#这个输出中,在b键左侧有“\b”,所以输出时只有ac<br>例子5:<br>[root@localhost ~]# echo -e "a\tb\tc\nd\te\tf"<br>a b c<br>d e f<br>#我们加入了制表符“\t”和换行符“\n”,所以会按照格式输出<br>例子6:<br>[root@localhost ~]# echo -e "\0141\t\0142\t\0143\n\0144\t\0145\t\0146"<br>a b c<br>d e f<br>#还是会输出上面的内容,不过是按照八进制ASCII码输出的。<br>
echo命令还可以进行一些比较有意思的东西,比如:
例子8:<br>[root@localhost ~]# echo -e "\e[1;31m abcd \e[0m"
这条命令会把abcd按照红色输出。解释下这个命令\e[1是标准格式,代表颜色输出开始,\e[0m代表颜色输出结束,31m定义字体颜色是红色。echo能够识别的颜色如下:30m=黑色,31m=红色,32m=绿色,33m=黄色,34m=蓝色,35m=洋红,36m=青色,37m=白色。
例子9:
[root@localhost ~]# echo -e "\e[1;42m abcd \e[0m"
这条命令会给abcd加入一个绿色的背景。echo可以使用的背景颜色如下:40m=黑色,41m=红色,42m=绿色,43m=黄色,44m=蓝色,45m=洋红,46m=青色,47m=白色。
echo -e 'abc! '感叹号后要加如空格
2、Shell脚本的执行
[root@localhost sh]# vi hello.sh<br>#!/bin/Bash<br>#The first program<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>echo -e "Mr. Shen Chao is the most honest man. "
hell脚本写好了,那么这个脚本该如何运行呢?在Linux中脚本的执行主要有这样两种种方法:
赋予执行权限,直接运行
就是赋予执行权限之后,直接运行。当然运行时可以使用绝对路径,也可以使用相对路径运行。
[root@localhost sh]# chmod 755 hello.sh<br>#赋予执行权限<br>[root@localhost sh]# /root/sh/hello.sh<br>Mr. Shen Chao is the most honest man.<br>#使用绝对路径运行<br>[root@localhost sh]# ./hello.sh<br>Mr. Shen Chao is the most honest man.<br>#因为我们已经在/root/sh目录当中,所以也可以使用相对路径运行<br>
通过Bash调用执行脚本
[root@localhost sh]# bash hello.sh
Mr. Shen Chao is the most honest man.
三、Bash的基本功能
1 历史命令
1)历史命令的查看
[root@localhost ~]# history [选项] [历史命令保存文件]
选项:
-c: 清空历史命令
-w: 把缓存中的历史命令写入历史命令保存文件。如果不手工指定历史命令保存文
件,则放入默认历史命令保存文件~/.bash_history中
设置history显示历史命令数量
[root@localhost ~]# vi /etc/profile
…省略部分输出…
HISTSIZE=1000
…省略部分输出…
使用history命令查看的历史命令和~/.bash_history文件中保存的历史命令是不同的。那是因为当前登录操作的命令并没有直接写入~/.bash_history文件,而是保存在缓存当中的。需要等当前用户注销之后,缓存中的命令才会写入~/.bash_history文件。如果我们需要把内存中的命令直接写入~/.bash_history文件,而不等用户注销时再写入,就需要使用“-w”选项了。命令如下:
[root@localhost ~]# history -w
#把缓存中的历史命令直接写入~/.bash_history
这时再去查询~/.bash_history文件,历史命令就和history命令查询的一致了。
如果需要清空历史命令,只需要执行:
[root@localhost ~]# history -c
#清空历史命令
不建议使用
一般是在设置mysql密码时使用
2)、历史命令的调用
如果想要使用原先的历史命令有这样几种方法:
使用上、下箭头调用以前的历史命令
使用“!n”重复执行第n条历史命令
使用“!!”重复执行上一条命令
使用“!字串”重复执行最后一条以该字串开头的命令
使用“!$”重复上一条命令的最后一个参数
2、命令与文件的补全
Tab键
3 命令别名
命令格式:
命令格式:
[root@localhost ~]# alias
#查询命令别名
[root@localhost ~]# alias 别名='原命令'
#设定命令别名
例如:
[root@localhost ~]# alias
#查询系统中已经定义好的别名
alias cp='cp -i'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
[root@localhost ~]# alias vi='vim'
#定义vim命令的别名是vi
为了让这个别名永久生效,可以把别名写入环境变量配置文件“~/.bashrc”。命令如下:
[root@localhost ~]# vi /root/.bashrc
[root@localhost ~]# source /root/.bashrc
别名的优先级比命令高
命令执行时具体的顺序是什么
1、 第一顺位执行用绝对路径或相对路径执行的命令。<br>2、 第二顺位执行别名。<br>3、 第三顺位执行Bash的内部命令。<br>4 、第四顺位执行按照$PATH环境变量定义的目录查找顺序找到的第一个命令。
4 Bash常用快捷键
ctrl+A
把光标移动到命令行开头。如果我们输入的命令过长,想要把光标移动到命令行开头时使用。
ctrl+E
把光标移动到命令行结尾。
ctrl+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
恢复屏幕输出。
5 输入输出重定向
1)、Bash的标准输入输出
键盘
/dev/stdin
0
标准输入
显示器
/dev/stdout
1
标准输出
/dev/stderr
2
标准错误输出
2)、输出重定向
标准输出重定向
命令 > 文件
命令 >> 文件
标准错误输出重定向
错误命令 2>文件
错误命令 2>>文件
正确输出和错误输出同时保存
命令 > 文件 2>&1
命令 >> 文件 2>&1
命令 &>文件
命令 &>>文件
命令>>文件1 2>>文件2
3)、输入重定向
[root@localhost ~]# wc [选项] [文件名]
选项:
-c 统计字节数
-w 统计单词数
-l 统计行数
6、多命令顺序执行
;
命令1 ;命令2
多个命令顺序执行,命令之间没有任何逻辑联系,即使其中一条命令报错,下面的命令依然会执行
&&
命令1 && 命令2
当命令1正确执行($?=0),则命令2才会执行
当命令1执行不正确($?≠0),则命令2不会执行
||
命令1 || 命令2
当命令1 执行不正确($?≠0),则命令2才会执行
当命令1正确执行($?=0),则命令2不会执行
7、 管道符
1)、行提取命令grep
[root@localhost ~]# grep [选项] "搜索内容" 文件名<br>选项:<br>-A 数字: 列出符合条件的行,并列出后续的n行<br>-B 数字: 列出符合条件的行,并列出前面的n行<br>-c: 统计找到的符合条件的字符串的次数<br>-i: 忽略大小写<br>-n: 输出行号<br>-v: 反向查找<br>--color=auto 搜索出的关键字用颜色显示
举几个例子:
[root@localhost ~]# grep "/bin/bash" /etc/passwd
#查找用户信息文件/etc/passwd中,有多少可以登录的用户
再举几个例子吧:
[root@localhost ~]# grep -A 3 "root" /etc/passwd
#查找包含有“root”的行,并列出后续的3行
[root@localhost ~]# grep -n "/bin/bash" /etc/passwd
#查找可以登录的用户,并显示行号
[root@localhost ~]# grep -v "/bin/bash" /etc/passwd
#查找不含有“/bin/bash”的行,其实就是列出所有的伪用户
2)find和grep的区别
find命令是在系统当中搜索符合条件的文件名,如果需要模糊查询,使用通配符(通配符我们下一小节进行介绍)进行匹配,搜索时文件名是完全匹配。
[root@localhost ~]# touch abc
#建立文件abc
[root@localhost ~]# touch abcd
#建立文件abcd
[root@localhost ~]# find . -name "abc"
./abc
#搜索文件名是abc的文件,只会找到abc文件,而不会找到文件abcd
#虽然abcd文件名中包含abc,但是find是完全匹配,只能和要搜索的数据完全一样,才能找到
注意:find命令是可以通过-regex选项识别正则表达式规则的,也就是说find命令可以按照正则表达式规则匹配,而正则表达式是模糊匹配。但是对于初学者而言,find命令和grep命令本身就不好理解,所以我们这里只按照通配符规则来进行find查询。
grep命令是在文件当中搜索符合条件的字符串,如果需要模糊查询,<br>使用正则表达式进行匹配,搜索时字符串是包含匹配。
[root@localhost ~]# echo abc > test
#在test文件中写入abc数据
[root@localhost ~]# echo abcd >> test
#在test文件中再追加abcd数据
[root@localhost ~]# grep "abc" test
abc
abcd
#grep命令查找时,只要数据行中包含有abc,就会都列出
#所以abc和abcd都可以查询到
3)管道符
[root@localhost ~]# ll -a /etc/ | more
[root@localhost ~]# netstat -an | grep "ESTABLISHED"
#查询下本地所有网络连接,提取包含ESTABLISHED(已建立连接)的行
#就可以知道我们的服务器上有多少已经成功连接的网络连接
[root@localhost ~]# netstat -an | grep "ESTABLISHED" | wc -l
#如果想知道具体的网络连接数量,就可以再使用wc命令统计行数
[root@localhost ~]# rpm -qa | grep httpd
8 通配符
?
匹配一个任意字符
*
匹配0个或任意多个任意字符,也就是可以匹配任何内容
[]
匹配中括号中任意一个字符。例如:[abc]代表一定匹配一个字符,或者是a,或者是b,或者是c。
[-]
匹配中括号中任意一个字符,-代表一个范围。例如:[a-z]代表匹配一个小写字母。
[^]
逻辑非,表示匹配不是中括号内的一个字符。例如:[^0-9]代表匹配一个不是数字的字符。
9 Bash中其他特殊符号
''
单引号。在单引号中所有的特殊符号,如“$”和“`”(反引号)都没有特殊含义。
""
双引号。在双引号中特殊符号都没有特殊含义,但是“$”、“`”和“\”是例外,拥有“调用变量的值”、“引用命令”和“转义符”的特殊含义。
``
反引号。反引号括起来的内容是系统命令,在Bash中会先执行它。和$()作用一样,不过推荐使用$(),因为反引号非常容易看错。
$()
和反引号作用一样,用来引用系统命令。
()
用于一串命令执行时,()中的命令会在子Shell中运行
{}
用于一串命令执行时,{}中的命令会在当前Shell中执行。也可以用于变量变形与替换。
[]
用于变量的测试。
#
在Shell脚本中,#开头的行代表注释。
$
用于调用变量的值,如需要调用变量name的值时,需要用$name的方式得到变量的值。
\
转义符,跟在\之后的特殊符号将失去特殊含义,变为普通字符。如\$将输出“$”符号,而不当做是变量引用。
四、Bash的变量和运算符
1 什么是变量
在定义变量时,有一些规则需要遵守:
变量名称可以由字母、数字和下划线组成,但是不能以数字开头。如果变量名是“2name”则是错误的。
在Bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必修指定变量类型为数值型。
变量用等号连接值,等号左右两侧不能有空格。
变量的值如果有空格,需要使用单引号或双引号包括。如:“test="hello world!"”。其中双引号括起来的内容“$”、“\”和反引号都拥有特殊含义,而单引号括起来的内容都是普通字符。
在变量的值中,可以使用“\”转义符。
如果需要增加变量的值,那么可以进行变量值的叠加。<br>不过变量需要用双引号包含"$变量名"或用${变量名}包含变量名。例如:
[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
变量值的叠加可以使用两种格式:“$变量名”或${变量名}
如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令。例如:
[root@localhost ~]# test=$(date)
[root@localhost ~]# echo $test
2018年 10月 21日 星期一 20:27:50 CST
环境变量名建议大写,便于区分。
2、变量的分类
用户自定义变量
这种变量是最常见的变量,由用户自由定义变量名和变量的值。
环境变量
这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。不是太好理解吧,那么大家还记得在Windows中,同一台电脑可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows的操作环境,可以当做是Windows的环境变量来理解。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。
位置参数变量
这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
预定义变量
是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。
3、用户自定义变量
1)、变量定义
[root@localhost ~]# 2name="shen chao"
-bash: 2name=shen chao: command not found
#变量名不能用数字开头
[root@localhost ~]# name = "shenchao"
-bash: name: command not found
#等号左右两侧不能有空格
[root@localhost ~]# name=shen chao
-bash: chao: command not found
#变量的值如果有空格,必须用引号包含
2)、 变量调用
[root@localhost ~]# name="shen chao"
#定义变量name
[root@localhost ~]# echo $name
shen chao
#输出变量name的值
3)、 变量查看
[root@localhost ~]# set [选项]<br>选项:<br><font color="#c41230">-u: 如果设定此选项,调用未声明变量时会报错(默认无任何提示)</font><br>-x: 如果设定此选项,在命令执行之前,会把命令先输出一次
[root@localhost ~]# set<br>BASH=/bin/bash<br>…省略部分输出…<br>name='shen chao'<br>#直接使用set命令,会查询系统中所有的变量,包含用户自定义变量和环境变量<br>[root@localhost ~]# set -u<br>[root@localhost ~]# echo $file<br>-bash: file: unbound variable<br>#当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。<br>[root@localhost ~]# set -x<br>[root@localhost ~]# ls<br>+ ls --color=auto<br>anaconda-ks.cfg install.log install.log.syslog sh tdir test testfile<br>#如果设定了-x选项,会在每个命令执行之前,先把命令输出一次
-u 参数可以区分变量是否为空还是不存在的情况
这个命令是临时生效的,永久生效需要写入配置文件
4)、 变量删除
[root@localhost ~]# unset 变量名
4 环境变量
1)、环境变量设置
[root@localhost ~]# export age="18"<br>#使用export声明的变量即是环境变量<br>[root@localhost ~]# age="18"<br>[root@localhost ~]# export age<br>
2)、环境变量查询和删除
env命令和set命令的区别是,set命令可以查看所有变量,而env命令只能查看环境变量。
[root@localhost ~]# unset gender
[root@localhost ~]# env | grep gender
#删除环境变量gender
3)、系统默认环境变量 env
[root@localhost ~]# env<br>HOSTNAME=localhost.localdomain 主机名<br>SHELL=/bin/bash 当前的shell<br>TERM=linux 终端环境<br>HISTSIZE=1000 历史命令条数<br>SSH_CLIENT=192.168.4.159 4824 22 当前操作环境是用ssh连接的,这里记录客户端ip<br>SSH_TTY=/dev/pts/1 ssh连接的终端时pts/1<br>USER=root 当前登录的用户<br>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<br>;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: <br>定义颜色显示<br>age=18 我们刚刚定义的环境变量<br>PATH=/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 系统查找命令的路径<br>MAIL=/var/spool/mail/root 用户邮箱<br>PWD=/root 当前所在目录<br>LANG=zh_CN.UTF-8 语系<br>HOME=/root 当前登录用户的家目录<br>SHLVL=2 当前在第二层子shell中。还记得我们刚刚进入了<br>一个子shell吗?如果是第一层shell,这里是1<br>LOGNAME=root 登录用户<br>_=/bin/env 上次执行命令的最后一个参数或命令本身<br>
nv命令可以查询到所有的环境变量,可是还有一些变量虽然不是环境变量,却是和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环境变量的值:
[root@localhost ~]# echo $PATH
/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
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变量的值
[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的值,变量叠加生效了
PS1变量:命令提示符设置
PS1是一个很有意思的变量(这可不是SONY的游戏机哦),是用来定义命令行的提示符的,可以安装我们自己的需求来定义自己喜欢的提示符。PS1可以支持以下这些选项:
选项
\d:显示日期,格式为“星期 月 日”<br> \H:显示完整的主机名。如默认主机名“localhost.localdomain”<br> \h:显示简写主机名。如默认主机名“localhost”<br> \t:显示24小时制时间,格式为“HH:MM:SS”<br> \T:显示12小时制时间,格式为“HH:MM:SS”<br> \A:显示24小时制时间,格式为“HH:MM”<br> \@:显示12小时制时间,格式为“HH:MM am/pm”<br> \u:显示当前用户名<br> \v:显示Bash的版本信息<br> \w:显示当前所在目录的完整名称<br> \W:显示当前所在目录的最后一个目录<br> \#:执行的第几个命令<br> \$:提示符。如果是root用户会显示提示符为“#”,如果是普通用户会显示提示符为“$”
[root@localhost ~]# echo $PS1
[\u@\h \W]\$
#默认的提示符是显示“[用户名@简写主机名 最后所在目录]提示符”
[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;\#:会显示执行了多少个命令
LANG语系变量
LANG变量定义了Linux系统的主语系环境,这个变量的默认值是:
[root@localhost src]# echo $LANG
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命令直接查询:
[root@localhost src]# locale
LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=
我们还要通过文件/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所示:
那么我们更改下语系为中文,看看会出现什么情况吧:
如果我们非要在纯字符界面中设置中文语系,那么就会出现乱码。怎么解决呢?安装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<br>count.sh hello.sh parameter2.sh parameter.sh<br>#ls命令正确执行<br>[root@localhost sh]# echo $?<br>0<br>#预定义变量“$?”的值是0,证明上一个命令执行正确<br>[root@localhost sh]# ls install.log<br>ls: 无法访问install.log: 没有那个文件或目录<br>#当前目录中没有install.log文件,所以ls命令报错了<br>[root@localhost sh]# echo $?<br>2<br><font color="#c41230">#变量“$?”返回一个非0的值,证明上一个命令没有正确执行<br>#至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2</font>
“$$”和“$!”
[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 [选项] [变量名]<br>选项:<br>-p “提示信息”: 在等待read输入时,输出提示信息<br>-t 秒数: read命令会一直等待用户输入,使用此选项可以指定等待时间<br>-n 字符数: read命令只接受指定的字符数,就会执行<br>-s: 隐藏输入的数据,适用于机密信息的输入<br>变量名:<br>变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY<br>如果只提供了一个变量名,则整个输入行赋予该变量<br>如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字
例子
[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声明变量类型
[root@localhost ~]# declare [+/-][选项] 变量名
选项:
-: 给变量设定类型属性
+: 取消变量的类型属性
-a: 将变量声明为数组型
-i: 将变量声明为整数型(integer)
-r: 讲变量声明为只读变量。注意,一旦设置为只读变量,既不能修改变量的值,
也不能删除变量,甚至不能通过+r取消只读属性
-x: 将变量声明为环境变量
-p: 显示指定变量的被声明的类型
[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
#这下终于可以相加了
例子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
#输出数组所有的内容
采用了“变量名[下标]”的格式,这个变量就会被系统认为是数组型了,不用强制声明。
例子3: 环境变量
[root@localhost ~]# declare -x test=123
#把变量test声明为环境变量
也可以使用declare命令把变量声明为环境变量,和export命令的作用是一样的:
例子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<br>declare -i cc="33"<br>#cc变量是int型<br>[root@localhost ~]# declare -p name<br>declare -a name='([0]="shen chao" [1]="li ming" [2]="tong gang")'<br>#name变量是数组型<br>[root@localhost ~]# declare -p test<br>declare -rx test="123"<br>#test变量是环境变量和只读变量<br>[root@localhost ~]# declare +x test<br>#取消test变量的环境变量属性<br>[root@localhost ~]# declare -p test<br>declare -r test="123"<br>#注意,只读变量属性是不能取消的
使用expr或let数值运算工具
要想进行数值运算的第二种方法是使用expr命令,这种命令就没有declare命令复杂了。
[root@localhost ~]# aa=11<br>[root@localhost ~]# bb=22<br>#给变量aa和变量bb赋值<br>[root@localhost ~]# dd=$(expr $aa + $bb)<br>#dd的值是aa和bb的和。注意“+”号左右两侧必须有空格<br>[root@localhost ~]# echo $dd<br>33<br>
至于let命令和expr命令基本类似,都是Linux中的运算命令,命令格式如下:
[root@localhost ~]# aa=11<br>[root@localhost ~]# bb=22<br>#给变量aa和变量bb赋值<br>[root@localhost ~]# let ee=$aa+$bb<br>[root@localhost ~]# echo $ee<br>33<br>#变量ee的值是aa和bb的和<br>[root@localhost ~]# n=20<br>#定义变量n<br>[root@localhost ~]# let n+=1<br>#变量n的值等于变量本身再加1<br>[root@localhost ~]# echo $n<br>21
使用“$((运算式))”或“$[运算式]”方式运算
其实这是一种方式“$(())”和“$[]”这两种括号按照个人习惯使用即可。
推荐使用“$((运算式))”的方式
[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是为空,还是没有设置,处理方法是不同的;如果大括号内有“:”,则变量<br>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还是不存在的
[root@localhost ~]# y=""
#给变量y赋值为空
[root@localhost ~]# x=${y-new}
#进行测试
[root@localhost ~]# echo $x
[root@localhost ~]# echo $y
#变量x和变量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<br>#删除变量y<br>[root@localhost ~]# x=${y:=new}<br>#进行测试<br>[root@localhost ~]# echo $x<br>new<br>[root@localhost ~]# echo $y<br>new<br>#变量x和变量y的值都是new<br>
一旦使用“=”号,那么变量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<br>#给变量y赋值<br>[root@localhost ~]# x=${y?new}<br>#进行测试<br>[root@localhost ~]# echo $x<br>old<br>[root@localhost ~]# echo $y<br>old<br>#变量x和变量y的值都是old<br>
五 环境变量配置文件
1 source命令
[root@localhost ~]# source 配置文件
或
[root@localhost ~]# . 配置文件
2 环境变量配置文件
1)、 登录时生效的环境变量配置文件
在Linux系统登录时主要生效的环境变量配置文件有以下五个:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
~/.bash_profile
~/.bashrc
环境变量配置文件调用过程
在用户登录过程先调用/etc/profile文件
在这个环境变量配置文件中会定义这些默认环境变量:
USER变量:根据登录的用户,给这个变量赋值(就是让USER变量的值是当前用户)。
LOGNAME变量:根据USER变量的值,给这个变量赋值。
MAIL变量:根据登录的用户,定义用户的邮箱为/var/spool/mail/用户名。
PATH变量:根据登录用户的UID是否为0,判断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#
2)、 注销时生效的环境变量配置文件
~/.bash_logout。
使用logout命令
在用户退出登录时,只会调用一个环境变量配置文件,就是~/.bash_logout。这个文件默认没有写入任何内容,可是如果我们希望再退出登录时执行一些操作,比如清除历史命令,备份某些数据,就可以把命令写入这个文件。
3)、 其他配置文件
还有一些环节变量配置文件,最常见的就是~/bash_history文件,也就是历史命令保存文件。这个文件已经讲过了,这里我们只是把它归入环境变量配置文件小节而已。
3 Shell登录信息
1)、 /etc/issue
我们在登录tty1-tty6这六个本地终端时,会有几行的欢迎界面。这些欢迎信息是保存在哪里的?可以修改吗?当然可以修改,这些欢迎信息是保存在/etc/issue文件中,我们查看下这个文件:
[root@localhost ~]# cat /etc/issue
CentOS release 6.8 (Final)
Kernel \r on an \m
可以支持的转义符我们可以通过man agetty命令查询,在表中我们列出常见的转义符作用:
\d
显示当前系统日期
\s
显示操作系统名称
\l
显示登录的终端号,这个比较常用。
\m
显示硬件体系结构,如i386、i686等
\n
显示主机名
\o
显示域名
\r
显示内核版本
\t
显示当前系统时间
\u
显示当前登录用户的序列号
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,加入如下内容:
[root@localhost ~]# cat /etc/ssh/sshd_config
…省略部分输出…
# no default banner path
#Banner none
Banner /etc/issue.net
…省略部分输出…
这样在ssh远程登录时,也可以显示欢迎信息,只是不再可以识别“\d”和“\l”等信息了
3)、 /etc/motd
/etc/motd文件中也是显示欢迎信息的,这个文件和/etc/issue及/etc/issue.net文件的区别是:/etc/issue及/etc/issue.net是在用户登录之前显示欢迎信息,而/etc/motd是在用户输入用户名和密码,正确登录之后显示欢迎信息。在/etc/motd文件中的欢迎信息,不论是本地登录,还是远程登录都可以显示。
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 = 1; time = 0;
#强制终止变成了ctrl+p快捷键
Shell编程
一、正则表达式
1、概述
正则表达式
用来匹配文件中符合条件的字符串
通配符
用来匹配符合条件的文件名
2 基础正则表达式
* 前一个字符匹配 0 次或任意多次。
. 匹配除了换行符外任意一个字符。
^ 匹配行首。例如:^hello 会匹配以 hello 开头的行。
$ 匹配行尾。例如:hello&会匹配以 hello 结尾的行。
[] 匹配中括号中指定的任意一个字符,只匹配一个字符。
例如:[aoeiu] 匹配任意一个元音字母,[0-9] 匹配任意一位数字,
[a-z][0-9]匹配小写字和一位数字构成的两位字符。
[^] 匹配除中括号的字符以外的任意一个字符。
例如:[^0-9] 匹配任意一位非数字字符,[^a-z] 表示任意一位非小写字母。
\ 转义符。用于取消讲特殊符号的含义取消。
\{n\} 表示其前面的字符恰好出现 n 次。
例如:[0-9]\{4\} 匹配 4 位数字,[1][3-8][0-9]\{9\} 匹配手机号码。
\{n,\} 表示其前面的字符出现不小于 n 次。
例如: [0-9]\{2,\} 表示两位及以上的数字。
\{n,m\} 表示其前面的字符至少出现n次,最多出现m次。
例如:[a-z]\{6,8\}匹配 6 到 8 位的小写字母。
实例
1)、 练习文件建立
[root@localhost ~]# vi test_rule.txt<br>Mr. Li Ming said:<br>he was the most honest man.<br>123despise him.<br>But since Mr. shen Chao came,<br>he never saaaid those words.<br>5555nice!<br>because,actuaaaally,<br>Mr. Shen Chao is the most honest man<br>Later,Mr. Li ming soid his hot body.<br>
2)、 “*”前一个字符匹配 0 次,或任意多次
3)、 “.” 匹配除了换行符外任意一个字符
正则表达式“.”只能匹配一个字符,这个字符可以是任意字符
[root@localhost ~]# grep "s..d" test_rule.txt <br>Mr. Li Ming said:<br>Later,Mr. Li ming soid his hot body.<br>#“s..d”会匹配在 s 和 d 这两个字母之间一定有两个字符的单词<br>[root@localhost ~]# grep "s.*d" test_rule.txt <br>Mr. Li Ming said:<br>he never saaaid those words.<br>Later,Mr. Li ming soid his hot body.<br>#最后一句话比较有意思,匹配的是“soid his hot bod”
[root@localhost ~]# grep ".*" test_rule.txt
4)、 “^”匹配行首,“$”匹配行尾
“^”代表匹配行首,比如“^M”会匹配以大写“M”开头的行:
[root@localhost ~]# grep "^M" test_rule.txt <br>Mr. Li Ming said:<br>Mr. Shen Chao is the most honest man
“$”代表匹配行尾,如果“n$”会匹配以小写“n”结尾的行:
[root@localhost ~]# grep "n$" test_rule.txt <br>Mr. Shen Chao is the most honest man
而“^$”则会匹配空白行:
[root@localhost ~]# grep -n "^$" test_rule.txt
5)、 “[]” 匹配中括号中指定的任意一个字符,只匹配一个字符
6)“[^]” 匹配除中括号的字符以外的任意一个字符
“[^]” 匹配除中括号的字符以外的任意一个字符
[root@localhost ~]# grep "^[^a-z]" test_rule.txt
而“^[^a-zA-Z]”则会匹配不用字母开头的行:
[root@localhost ~]# grep "^[^a-zA-Z]" test_rule.txt
7)、 “\” 转义符
[root@localhost ~]# grep "\.$" test_rule.txt
8)、 “\{n\}”表示其前面的字符恰好出现 n 次
9)、 “\{n,\}”表示其前面的字符出现不小于 n 次
10)“\{n,m\}”匹配其前面的字符至少出现 n 次,最多出现 m 次
3 扩展正则表达式
熟悉正则表达式的童鞋应该很疑惑,在正则表达式中应该还可以支持一些元字符,比如“+”“?”“|”“()”。
熟悉正则表达式的童鞋应该很疑惑,在正则表达式中应该还可以支持一些元字符,比如“+”“?”
“|”“()”。其实 Linux 是支持这些元字符的,只是 grep 命令默认不支持而已。如果要想支持这
些元字符,必须使用 egrep 命令或 grep -E 选项,所以我们又把这些元字符称作扩展元字符。
如果查询 grep 的帮助,对 egrep 的说明就是和 grep -E 选项一样的命令,所以我们可以把两个命令当做别名来对待。
扩展元字符
+
前一个字符匹配 1 次或任意多次。
如“go+gle”会匹配“gogle”、“google”或“gooogle”,当然如
果“o”有更多个,也能匹配。
?
前一个字符匹配 0 次或 1 次。
如“colou?r”可以匹配“colour”或“color”。
|
匹配两个或多个分支选择。
如“was|his”会匹配既包含“was”的行,也匹配包含“his”的行。
()
匹配其整体为一个字符,即模式单元。<br>可以理解为由多个单个字符组成的大字符。
如“(dog)+”会匹配“dog”、“dogdog”、“dogdogdog”等,因为
被()包含的字符会当成一个整体。但“hello (world|earth)”
会匹配“hello world”及“hello earth”。
匹配邮箱
匹配IP
二、 字符截取和替换命令
1 cut 列提取命令
[root@localhost ~]# cut [选项] 文件名<br>选项:<br>-f 列号: 提取第几列<br>-d 分隔符: 按照指定分隔符分割列<br>-c 字符范围: 不依赖分隔符来区分列,而是通过字符范围(行首为 0)来进行字段<br>提取。“n-”表示从第 n 个字符到行尾;“n-m”从第 n 个字符到第 m<br>个字符;“-m”表示从第 1 个字符到第 m 个字符。
cut不能使用空格进行分隔
cut 命令的默认分隔符是制表符,也就是“tab”键,不过对空格符可是支持的不怎么好啊。我们<br>先建立一个测试文件,然后看看 cut 命令的作用吧:
[root@localhost ~]# vi student.txt<br>ID Name gender Mark<br>1 Liming M 86<br>2 Sc M 90<br>3 Tg M 83
[root@localhost ~]# cut -f 2 student.txt<br>#提取第二列内容
那如果想要提取多列呢?只要列号直接用“,”分开,命令如下:
[root@localhost ~]# cut -f 2,3 student.txt
cut 可以按照字符进行提取,需要注意“8-”代表的是提取所有行的第十个字符开始到行尾,而<br>“10-20”代表提取所有行的第十个字符到第二十个字符,而“-8”代表提取所有行从行首到第八个<br>字符:
[root@localhost ~]# cut -c 8- student.txt
#提取第八个字符开始到行尾,好像很乱啊,那是因为每行的字符个数不相等啊
[root@localhost ~]# cut -d ":" -f 1,3 /etc/passwd <br>#以“:”作为分隔符,提取/etc/passwd 文件的第一列和第三列
2 awk编程
1)、 概述
2)、 printf格式化输出
[root@localhost ~]# printf ‘输出类型输出格式’ 输出内容<br>输出类型:<br>%ns: 输出字符串。n是数字指代输出几个字符<br>%ni: 输出整数。n是数字指代输出几个数字<br>%m.nf: 输出浮点数。m和n是数字,指代输出的整数位数和小数位数。如%8.2f<br>代表共输出8位数,其中2位是小数,6位是整数。<br>输出格式:<br>\a: 输出警告声音<br>\b: 输出退格键,也就是Backspace键<br>\f: 清除屏幕<br>\n: 换行<br>\r: 回车,也就是Enter键<br>\t: 水平输出退格键,也就是Tab键<br>\v: 垂直输出退格键,也就是Tab键
为了演示printf命令,我们需要修改下刚刚cut命令<br>使用的student.txt文件,文件内容如下:
[root@localhost ~]# vi student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
我们使用printf命令输出下这个文件的内容:
[root@localhost ~]# printf '%s' $(cat student.txt)
IDNamegenderPHPLinuxMySQLAverage1LimingM82958687.662ScM74968785.663TgM99839391.66[root@localhost ~]#
如果不指定输出格式,则会把所有输出内容连在一起输出。其实文本的输出本身就是这样的,<br>cat等文本输出命令之所以可以按照格式漂亮的输出,那是因为cat命令已经设定了输出格式。
[root@localhost ~]# printf '%s\t %s\t %s\t %s\t %s\t %s\t \n' $(cat student.txt)<br>#注意在printf命令的单引号中,只能识别格式输出符号,而手工输入的空格是无效的<br>ID Name PHP Linux MySQL Average<br>1 Liming 82 95 86 87.66<br>2 Sc 74 96 87 85.66<br>3 Tg 99 83 93 91.66
如果不想把成绩当成字符串输出,而是按照整型和浮点型输出,则要这样:
[root@localhost ~]# printf '%i\t %s\t %i\t %i\t %i\t %8.2f\t \n' \
$(cat student.txt | grep -v Name)
printf 使用单引号,在awk中使用双引号
3)、 awk基本使用
awk支持
print
不加\n
自动换行
printf
换行需要加\n
[root@localhost ~]# awk ‘条件1{动作1} 条件2{动作2}…’ 文件名
条件(Pattern):
一般使用关系表达式作为条件。这些关系表达式非常多,具体参考表12-3所示,例如:
x > 10 判断变量 x是否大于10
x == y 判断变量 x是否等于变量y
A ~ B 判断字符串A中是否包含能匹配B 表达式的子字符串
A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串
动作(Action):
格式化输出
流程控制语句
[root@localhost ~]# awk '{printf $2 "\t" $6 "\n"}' student.txt<br>#输出第二列和第六列
比如刚刚截取df命令的结果时,cut命令已经力不从心了,我们来看看awk命令:
[root@localhost ~]# df -h | awk '{print $1 "\t" $3}'
4)、 awk的条件
条件的类型
awk保留字
BEGIN
在awk程序一开始时,尚未读取任何数据之前执行。BEGIN后的动作只在程序开始时执行一次
END
在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次
关系运算符
>
大于
<
小于
>=
大于等于
<=
小于等于
==
等于。用于判断两个值是否相等,如果是给变量赋值,请使用“=”号
!=
不等于
A~B
判断字符串A中是否包含能匹配B 表达式的子字符串
A!~B
判断字符串A中是否不包含能匹配B表达式的子字符串
正则表达式
/正则/
如果在“//”中可以写入字符,也可以支持正则表达式
BEGIN
BEGIN是awk的保留字,是一种特殊的条件类型。BEGIN的执行时机是“在awk程序一开始时,尚未读取任何数据之前执行”。一旦BEGIN后的动作执行一次,当awk开始从文件中读入数据,BEGIN的条件就不再成立,所以BEGIN定义的动作只能被执行一次。
[root@localhost ~]# awk 'BEGIN{printf "This is a transcript \n" }<br>{printf $2 "\t" $6 "\n"}' student.txt<br>#awk命令只要检测不到完整的单引号不会执行,所以这个命令的换行不用加入“\”,就是一行命令<br>#这里定义了两个动作<br>#第一个动作使用BEGIN条件,所以会在读入文件数据前打印“这是一张成绩单”(只会执行一次)<br>#第二个动作会打印文件的第二字段和第六字段<br>
END
END也是awk保留字,不过刚好和BEGIN相反。END是在awk程序处理完所有数据,即将结束时执行。END后的动作只在程序结束时执行一次。例如:
[root@localhost ~]# awk 'END{printf "The End \n" }
{printf $2 "\t" $6 "\n"}' student.txt
#在输出结尾输入“The End”,这并不是文档本身的内容,而且只会执行一次
关系运算符
例子1:<br>[root@localhost ~]# cat student.txt | grep -v Name | \<br>awk '$6 >= 87 {printf $2 "\n" }'<br>#使用cat输出文件内容,用grep取反包含“Name”的行<br>#判断第六字段(平均成绩)大于等于87分的行,如果判断式成立,则打第六列(学员名)
加入了条件之后,只有条件成立动作才会执行,如果条件不满足,则动作则不运行。通过这个实验,大家可以发现,<br>虽然awk是列提取命令,但是也要按行来读入的。这个命令的执行过程是这样的:
1) 如果有BEGIN条件,则先执行BEGIN定义的动作
2) 如果没有BEGIN条件,则读入第一行,把第一行的数据依次赋予$0、$1、$2等变量。其中$0代表此行的整体数据,$1代表第一字段,$2代表第二字段。
3) 依据条件类型判断动作是否执行。如果条件符合,则执行动作,否则读入下一行数据。如果没有条件,则每行都执行动作。
4) 读入下一行数据,重复执行以上步骤。
再举个例子,如果我想看看Sc用户的平均成绩呢
例子2:
[root@localhost ~]# awk '$2 ~ /Sc/ {printf $6 "\n"}' student.txt
#如果第二字段中输入包含有“Sc”字符,则打印第六字段数据
85.66
这里要注意在awk中,使用“//”包含的字符串,awk命令才会查找。也就是说字符串必须用“//”包含,awk命令才能正确识别。
正则表达式
如果要想让awk识别字符串,必须使用“//”包含,例如:
例子1:
[root@localhost ~]# awk '/Liming/ {print}' student.txt
#打印Liming的成绩
当使用df命令查看分区使用情况是,如果我只想查看真正的系统分区的使用状况,而不想查看光盘和临时分区的使用状况,则可以:
例子2:
[root@localhost ~]# df -h | awk '/sda[0-9]/ {printf $1 "\t" $5 "\n"} '
#查询包含有sda数字的行,并打印第一字段和第五字段
5)、 awk内置变量
$0
代表目前awk所读入的整行数据。我们已知awk是一行一行读入数据的,$0就代表当前读入行的整行数据。
$n
代表目前读入行的第n个字段。
NF
当前行拥有的字段(列)总数。
NR
当前awk所处理的行,是总数据的第几行。
FS
用户定义分隔符。awk的默认分隔符是任何空格或Tab,如果想要使用其他分隔符(如“:”),就需要FS变量定义。
ARGC
命令行参数个数。
ARGV
命令行参数数组。
FNR
当前文件中的当前记录数(对输入文件起始为1)。
OFMT
数值的输出格式(默认为%.6g)。
OFS
输出字段的分隔符(默认为空格)。
ORS
输出记录分隔符(默认为换行符)。
RS
输入记录分隔符(默认为换行符)。
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \<br>awk '{FS=":"} {printf $1 "\t" $3 "\n"}'<br>#查询可以登录的用户的用户名和UID
这里“:”分隔符生效了,可是第一行却没有起作用,原来我们忘记了“BEGIN”条件,那么再来试试:
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \
awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\n"}'
加begin可以提前执行命令
原因是,awk提前把第一行数据读到了,并且没有进行用“:”分隔,所以打印时直接打印出了连续的数据
[root@localhost ~]# cat /etc/passwd | grep "/bin/bash" | \<br>awk 'BEGIN {FS=":"} {printf $1 "\t" $3 "\t 行号:" NR "\t 字段数:" NF "\n"}'<br>#解释下awk命令<br>#开始执行{分隔符是“:”} {输出第一字段和第三字段 输出行号(NR值) 字段数(NF值)}<br>root 0 行号:1 字段数:7<br>user1 501 行号:2 字段数:7
printf中变量不用双引号包括,字符串用双引号包括
如果我只想看看sshd这个伪用户的相关信息,则可以这样使用:
[root@localhost ~]# cat /etc/passwd | \<br>awk 'BEGIN {FS=":"} $1=="sshd" {printf $1 "\t" $3 "\t 行号:"NR "\t 字 段数:"F "\n"}'<br>#可以看到sshd伪用户的UID是74,是/etc/passwd文件的第28行,此行有7个字段
6)、 awk流程控制
我们再来利用下 student.txt 文件做个练习,后面的使用比较复杂,我们再看看这个文件的内容:
[root@localhost ~]# cat student.txt <br>ID Name PHP Linux MySQL Average<br>1 Liming 82 95 86 87.66<br>[root@localhost ~]# cat student.txt <br>ID Name PHP Linux MySQL Average<br>1 Liming 82 95 86 87.66<br>
统计 PHP 成绩的总分,那么就<br>应该这样:
[root@localhost ~]# awk 'NR==2{php1=$3}
NR==3{php2=$3}
NR==4{php3=$3;totle=php1+php2+php3;print "totle php is " totle}' student.txt
#统计 PHP 成绩的总分
“NR==2{php1=$3}”(条件是 NR==2,动作是 php1=$3)这句话是指如果输入数据是第二行(第一行是标题行),就把第二行的第三字段的值赋予变量“php1”。“NR==3{php2=$3}”这句话是指如果输入数据是第三行,就把第三行的第三字段的值赋予变量“php2。“NR==4{php3=$3;totle=php1+php2+php3;print "totle php is " totle}”(“NR==4”是条件,后面{}中的都是动作)这句话是指如果输入数据是第四行,就把第四行的第三字段的值赋予变量“php3”;然后定义变量 totle 的值是“php1+php2+php3”;然后输出“totle php is”关键字,后面加变量 totle的值。
在 awk 编程中,因为命令语句非常长,在输入格式时需要注意以下内容:
多个条件{动作}可以用空格分割,也可以用回车分割。<br> 在一个动作中,如果需要执行多个命令,需要用“;”分割,或用回车分割。<br> 在 awk 中,变量的赋值与调用都不需要加入“$”符。<br> 条件中判断两个值是否相同,请使用“==”,以便和变量赋值进行区分。
如果 Linux 成绩大于 90,就是一个好男人(学 PHP 的表示压力很大!)
[root@localhost ~]# awk '{if (NR>=2) <br>{if ($4>90) printf $2 " is a good man!\n"}}' student.txt <br>#程序中有两个 if 判断,第一个判断行号大于 2,第二个判断 Linux 成绩大于 90 分<br>Liming is a good man!<br>Sc is a good man!
完全可以直接利用 awk 自带的条件来取代,刚刚的脚本可以改写成这样:
[root@localhost ~]# awk ' NR>=2 {test=$4} <br>test>90 {printf $2 " is a good man!\n"}' student.txt <br>#先判断行号如果大于 2,就把第四字段赋予变量 test<br>#在判断如果 test 的值大于 90 分,就打印好男人<br>Liming is a good man!<br>Sc is a good man!
7)、 awk函数
awk 编程也允许在编程时使用函数,awk 函数的定义方法如下:
function 函数名(参数列表){<br>函数体<br>}
[root@localhost ~]# awk 'function test(a,b) { printf a "\t" b "\n" } <br>#定义函数 test,包含两个参数,函数体的内容是输出这两个参数的值<br>{ test($2,$6) } ' student.txt <br>#调用函数 test,并向两个参数传递值。<br>Name Average<br>Liming 87.66<br>Sc 85.66<br>Tg 91.66
8)、 awk中调用脚本
对于小的单行程序来说,将脚本作为命令行自变量传递给 awk 是非常简单的,而对于多行程序就比较难处理。当程序是多行的时候,使用外部脚本是很适合的。首先在外部文件中写好脚本,然后可以使用 awk 的-f 选项,使其读入脚本并且执行。
先编写一个 awk 脚本:
[root@localhost ~]# vi pass.awk
BEGIN {FS=":"}
{ print $1 "\t" $3}
然后可以使用“-f”选项来调用这个脚本:
[root@localhost ~]# awk -f pass.awk /etc/passwd
root 0
bin 1
daemon 2
…省略部分输出…
3 sed命令
sed 主要是用来将数据进行选取、替换、删除、新增的命令,我们看看命令的语法:
语法
[root@localhost ~]# sed [选项] ‘[动作]’ 文件名<br>选项:<br>-n: 一般 sed 命令会把所有数据都输出到屏幕,如果加入此选择,则只会<br>把经过 sed 命令处理的行输出到屏幕。<br>-e: 允许对输入数据应用多条 sed 命令编辑。<br>-f 脚本文件名: 从 sed 脚本中读入 sed 操作。和 awk 命令的-f 非常类似。<br>-r: 在 sed 中支持扩展正则表达式。<br>-i: 用 sed 的修改结果直接修改读取数据的文件,而不是由屏幕输出<br>动作:<br>a \: 追加,在当前行后添加一行或多行。添加多行时,除最后 一行外,<br>每行末尾需要用“\”代表数据未完结。<br>c \: 行替换,用 c 后面的字符串替换原数据行,替换多行时,除最后一行<br>外,每行末尾需用“\”代表数据未完结。<br>i \: 插入,在当期行前插入一行或多行。插入多行时,除最后 一行外,<br>每行末尾需要用“\”代表数据未完结。<br>d: 删除,删除指定的行。<br>p: 打印,输出指定的行。<br>s: 字串替换,用一个字符串替换另外一个字符串。格式为“行范围 s/<br>旧字串/新字串/g”(和 vim 中的替换格式类似)。<br>
对 sed 命令大家要注意,sed 所做的修改并不会直接改变文件的内容(如果是用管道符接收的命<br>令的输出,这种情况连文件都没有),而是把修改结果只显示到屏幕上,除非使用“-i”选项才会直<br>接修改文件。
行数据操作
可以利用“p”动作
[root@localhost ~]# sed '2p' student.txt
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
好像看着不怎么顺眼啊!“p”命令确实输出了第二行数据,但是sed命令还会把所有数据都输出一次,这时就会看到这个比较奇怪的结果。那如果我想指定输出某行数据,就需要“-n”选项的帮助了:
[root@localhost ~]# sed -n '2p' student.txt
1 Liming 82 95 86 87.66
再来看看如何删除文件的数据:
[root@localhost ~]# sed '2,4d' student.txt
#删除第二行到第四行的数据
ID Name PHP Linux MySQL Average
[root@localhost ~]# cat student.txt
#但是文件本身并没有修改
ID Name PHP Linux MySQL Average
1 Liming 82 95 86 87.66
2 Sc 74 96 87 85.66
3 Tg 99 83 93 91.66
再来看看如何追加和插入行数据:
[root@localhost ~]# sed '2a hello' student.txt
#在第二行后加入hello
“a”会在指定行后面追加入数据,如果想要在指定行前面插入数据,则需要使用“i”动作:
[root@localhost ~]# sed '2i hello \
> world' student.txt
#在第二行前插入两行数据
如果是想追加或插入多行数据,除最后一行外,每行的末尾都要加入“\”代表数据未完结。再来看看“-n”选项的作用:
[root@localhost ~]# sed -n '2i hello \<br>#只查看sed命令操作的数据<br>> world' student.txt<br>
“-n”只查看sed命令操作的数据,而不是查看所有数据。
可以这样:
[root@localhost ~]# cat student.txt | sed '2c No such person'
[root@localhost ~]# sed -i '2c No such person' student.txt
字符串替换
“c”动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用“s”动作了。s动作的格式是:
[root@localhost ~]# sed ‘s/旧字串/新字串/g’ 文件名
替换的格式和vim非常类似
[root@localhost ~]# sed '3s/74/99/g' student.txt
#在第三行中,把74换成99
把Tg老师的成绩注释掉
[root@localhost ~]# sed '4s/^/#/g' student.txt
#这里使用正则表达式,“^”代表行首
删除字符串
[root@localhost ~]# sed -e 's/Liming//g ; s/Tg//g' student.txt<br>#同时把“Liming”和“Tg”替换为空
-e”选项可以同时执行多个sed动作,当然如果只是执行一个动作也可以使用“-e”选项,但是这时没有什么意义。还要注意,多个动作之间要<font color="#c41230">用“;”号或回车分割</font>,例如上一个命令也可以这样写:
[root@localhost ~]# sed -e 's/Liming//g
> s/Tg//g' student.txt
三、字符处理命令
1 排序命令sort
[root@localhost ~]# sort [选项] 文件名<br>选项:<br>-f: 忽略大小写<br>-b: 忽略每行前面的空白部分<br>-n: 以数值型进行排序,默认使用字符串型排序<br>-r: 反向排序<br>-u: 删除重复行。就是uniq命令<br>-t: 指定分隔符,默认是分隔符是制表符<br>-k n[,m]: 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾)<br>
sort命令默认是用每行开头第一个字符来进行排序的,比如:
[root@localhost ~]# sort /etc/passwd
#排序用户信息文件
[root@localhost ~]# sort -r /etc/passwd<br>#反向排序
如果想要指定排序的字段,需要使用“-t”选项指定分隔符,并使用“-k”选项指定字段号。<br>加入我想要按照UID字段排序/etc/passwd文件:
[root@localhost ~]# sort -t ":" -k 3,3 /etc/passwd
#指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序
看起来好像很美,可是如果仔细看看,怎么daemon用户的UID是2,反而排在了下面?<br>这是因为sort默认是按照字符排序,前面用户的UID的第一个字符都是1,所以这么排序。要想按照数字排序,请使用“-n”选项:
[root@localhost ~]# sort -n -t ":" -k 3,3 /etc/passwd
当然“-k”选项可以直接使用“-k 3”,代表从第三字段到行尾都排序(第一个字符先排序,如果一致,第二个字符再排序,知道行尾)。
2 uniq
uniq命令是用来取消重复行的命令,其实和“sort -u”选项是一样的。命令格式如下:
[root@localhost ~]# uniq [选项] 文件名
选项:
-i: 忽略大小写
3 统计命令wc
[root@localhost ~]# wc [选项] 文件名
选项:
-l: 只统计行数
-w: 只统计单词数
-m: 只统计字符数
四、 条件判断
1 按照文件类型进行判断
来看看test可以进行哪些文件类型的判断:
-b 文件
判断该文件是否存在,并且是否为块设备文件(是块设备文件为真)
-c文件
判断该文件是否存在,并且是否为字符设备文件(是字符设备文件为真)
-d 文件
判断该文件是否存在,并且是否为目录文件(是目录为真)
-e 文件
判断该文件是否存在(存在为真)
-f 文件
判断该文件是否存在,并且是否为普通文件(是普通文件为真)
-L 文件
判断该文件是否存在,并且是否为符号链接文件(是符号链接文件为真)
-p 文件
判断该文件是否存在,并且是否为管道文件(是管道文件为真)
-s 文件
判断该文件是否存在,并且是否为非空(非空为真)
-S 文件
判断该文件是否存在,并且是否为套接字文件(是套接字文件为真)
[root@localhost ~]# [ -e /root/sh/ ]<br>[root@localhost ~]# echo $?<br>0<br>#判断结果为0,/root/sh/目录是存在的<br>[root@localhost ~]# [ -e /root/test ]<br>[root@localhost ~]# echo $?<br>1<br>#在/root/下并没有test文件或目录,所以“$?”的返回值为非零
还记得多命令顺序执行的“&&”和“||”吗?我们可以再判断一下/root/sh/是否是目录:
[root@localhost ~]# [ -d /root/sh ] && echo "yes" || echo "no"
#第一个判断命令如果正确执行,则打印“yes”,否则打印“no”
test命令 等同于[ ]
2 按照文件权限进行判断
test是非常完善的判断命令,还可以判断文件的权限,我们通过表12-6来看看:
-r 文件
判断该文件是否存在,并且是否该文件拥有读权限(有读权限为真)
-w文件
判断该文件是否存在,并且是否该文件拥有写权限(有写权限为真)
-x 文件
判断该文件是否存在,并且是否该文件拥有执行权限(有执行权限为真)
-u 文件
判断该文件是否存在,并且是否该文件拥有SUID权限(有SUID权限为真)
-g 文件
判断该文件是否存在,并且是否该文件拥有SGID权限(有SGID权限为真)
-k 文件
判断该文件是否存在,并且是否该文件拥有SBit权限(有SBit权限为真)
比如:
[root@localhost ~]# ll student.txt
-rw-r--r--. 1 root root 97 6月 7 07:34 student.txt
[root@localhost ~]# [ -w student.txt ] && echo "yes" || echo "no"
yes
#判断文件是拥有写权限的
只能判断整个文件是否有写权限,不能区分拥有者,如果要区分需要截取出来再进行判断
3 两个文件之间进行比较
文件1 -nt 文件2
判断文件1的修改时间是否比文件2的新(如果新则为真)
文件1 -ot 文件2
判断文件1的修改时间是否比文件2的旧(如果旧则为真)
文件1 -ef 文件2
判断文件1是否和文件2的Inode号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法
如何判断两个文件是否是硬链接呢?这时test就派上用场了:
[root@localhost ~]# ln /root/student.txt /tmp/stu.txt<br>#创建个硬链接吧<br>[root@localhost ~]# [ /root/student.txt -ef /tmp/stu.txt ] && echo "yes" || echo "no"<br>yes<br>#用test测试下,果然很有用<br>
4 两个整数之间比较
“==”在shell中用来比较字符串是否相等
整数1 -eq 整数2
判断整数1是否和整数2相等(相等为真)
整数1 -ne 整数2
判断整数1是否和整数2不相等(不相等位置)
整数1 -gt 整数2
判断整数1是否大于整数2(大于为真)
整数1 -lt 整数2
判断整数1是否小于整数2(小于位置)
整数1 -ge 整数2
判断整数1是否大于等于整数2(大于等于为真)
整数1 -le 整数2
判断整数1是否小于等于整数2(小于等于为真)
举个例子:
[root@localhost ~]# [ 23 -ge 22 ] && echo "yes" || echo "no"
yes
#判断23是否大于等于22,当然是了
[root@localhost ~]# [ 23 -le 22 ] && echo "yes" || echo "no"
no
#判断23是否小于等于22,当然不是了
5 字符串的判断
-z 字符串
判断字符串是否为空(为空返回真)
-n 字符串
判断字符串是否为非空(非空返回真)
字串1 ==字串2
判断字符串1是否和字符串2相等(相等返回真)
字串1 != 字串2
判断字符串1是否和字符串2不相等(不相等返回真)
举个例子:
[root@localhost ~]# name=sc
#给name变量赋值
[root@localhost ~]# [ -z "$name" ] && echo "yes" || echo "no"
no
#判断name变量是否为空,因为不为空,所以返回no
再来看看如何判断两个字符串相等:
[root@localhost ~]# aa=11
[root@localhost ~]# bb=22
#给变量aa和变量bb赋值
[root@localhost ~]# [ "$aa" == "bb" ] && echo "yes" || echo "no"
no
#判断两个变量的值是否相等,明显不相等,所以返回no
6 多重条件判断
测试选项
判断1 -a 判断2
逻辑与,判断1和判断2都成立,最终的结果才为真
判断1 -o 判断2
逻辑或,判断1和判断2有一个成立,最终的结果就为真
!判断
逻辑非,使原始的判断式取反
举个例子:
[root@localhost ~]# aa=11
#给变量aa赋值
[root@localhost ~]# [ -n "$aa" -a "$aa" -gt 23 ] && echo "yes" || echo "no"
no
#判断变量aa是否有值,同时判断变量aa的是否大于23
#因为变量aa的值不大于23,所以虽然第一个判断值为真,返回的结果也是假
[root@localhost ~]# aa=24
[root@localhost ~]# [ -n "$aa" -a "$aa" -gt 23 ] && echo "yes" || echo "no"
yes
逻辑非
[root@localhost ~]# [ ! -n "$aa" ] && echo "yes" || echo "no"
no
#本来“-n”选项是变量aa不为空,返回值就是真。
#加入!之后,判断值就会取反,所以当变量aa有值时,返回值是假
注意:“!”和“-n”之间必须加入空格,否则会报错的。
4 两个整数之间比较
五、 流程控制
1 if条件判断
1)、 单分支if条件语句
单分支条件语句最为简单,就是只有一个判断条件,如果符合条件则执行某个程序,否则什么事情都不做。语法如下:
if [ 条件判断式 ];then
程序
fi
if [ 条件判断式 ]
then
程序
fi
例子
[root@localhost ~]# vi sh/if1.sh<br>#!/bin/bash<br>#统计根分区使用率<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>rate=$(df -h | grep "/dev/sda3" | awk '{print $5}' | cut -d "%" -f1)<br>#把根分区使用率作为变量值赋予变量rate<br>if [ $rate -ge 80 ]<br>#判断rate的值如果大于等于80,则执行then程序<br>then<br>echo "Warning! /dev/sda3 is full!!"<br>#打印警告信息。在实际工作中,也可以向管理员发送邮件。<br>fi<br>
2)、 双分支if条件语句
if [ 条件判断式 ]
then
条件成立时,执行的程序
else
条件不成立时,执行的另一个程序
fi
例子1:
写一个数据备份的例子,来看看双分支if条件语句。
例子1:备份mysql数据库<br>[root@localhost ~]# vi sh/bakmysql.sh<br>#!/bin/bash<br>#备份mysql数据库。<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>ntpdate asia.pool.ntp.org &>/dev/null<br>#同步系统时间<br>date=$(date +%y%m%d)<br>#把当前系统时间按照“年月日”格式赋予变量date<br>size=$(du -sh /var/lib/mysql)<br>#统计mysql数据库的大小,并把大小赋予size变量<br>if [ -d /tmp/dbbak ]<br>#判断备份目录是否存在,是否为目录<br>then<br>#如果判断为真,执行以下脚本<br>echo "Date : $date!" > /tmp/dbbak/dbinfo.txt<br>#把当前日期写入临时文件<br>echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt<br>#把数据库大小写入临时文件<br>cd /tmp/dbbak<br>#进入备份目录<br>tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &>/dev/null<br>#打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出)<br>rm -rf /tmp/dbbak/dbinfo.txt<br>#删除临时文件<br>else<br>mkdir /tmp/dbbak<br>#如果判断为假,则建立备份目录<br>echo "Date : $date!" > /tmp/dbbak/dbinfo.txt<br>echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt<br>#把日期和数据库大小保存如临时文件<br>cd /tmp/dbbak<br>tar -zcf mysql-lib-$date.tar.gz dbinfo.txt /var/lib/mysql &>/dev/null<br>#压缩备份数据库与临时文件<br>rm -rf /tmp/dbbak/dbinfo.txt<br>#删除临时文件<br>fi<br>
例子2:
再举个例子,在工作当中,服务器上的服务经常会宕机。如果我们对服务器监控不好,就会造成服务器中服务宕机了,而管理员却不知道的情况,这时我们可以写一个脚本来监听本机的服务,如果服务停止或宕机了,可以自动重启这些服务。我们拿apache服务来举例:
例子2:判断apache是否启动,如果没有启动则自动启动<br>[root@localhost ~]# vi sh/autostart.sh<br>#!/bin/bash<br>#判断apache是否启动,如果没有启动则自动启动<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}')<br>#使用nmap命令扫描服务器,并截取apache服务的状态,赋予变量port<br>if [ "$port" == "open" ]<br>#如果变量port的值是“open”<br>then<br>echo “$(date) httpd is ok!” >> /tmp/autostart-acc.log<br>#则证明apache正常启动,在正常日志中写入一句话即可<br>else<br>/etc/rc.d/init.d/httpd start &>/dev/null<br>#否则证明apache没有启动,自动启动apache<br>echo "$(date) restart httpd !!" >> /tmp/autostart-err.log<br>#并在错误日志中记录自动启动apche的时间<br>fi
以我们使用nmap端口扫描命令,nmap命令格式如下:
[root@localhost ~]# nmap -sT 域名或IP<br>选项:<br>-s 扫描<br>-T 扫描所有开启的TCP端口<br>
[root@localhost ~]# nmap -sT 192.168.4.210<br>#可以看到这台服务器开启了如下的服务<br>Starting Nmap 5.51 ( http://nmap.org ) at 2018-11-25 15:11 CST<br>Nmap scan report for 192.168.4.210<br>Host is up (0.0010s latency).<br>Not shown: 994 closed ports<br>PORT STATE SERVICE<br>22/tcp open ssh<br>80/tcp open http apache的状态是open<br>111/tcp open rpcbind<br>139/tcp open netbios-ssn<br>445/tcp open microsoft-ds<br>3306/tcp open mysql<br>Nmap done: 1 IP address (1 host up) scanned in 0.49 seconds
知道了nmap命令的用法,我们在脚本中使用的命令就是为了截取http的状态,<br>只要状态是“open”就证明apache启动正常,否则证明apache启动错误。来看看脚本中命令的结果:
[root@localhost ~]# nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}'
#扫描指定计算机,提取包含tcp的行,在提取包含httpd的行,截取第二列
open
#把截取的值赋予变量port
3)、 多分支if条件语句
if [ 条件判断式1 ]
then
当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]
then
当条件判断式2成立时,执行程序2
…省略更多条件…
else
当所有条件都不成立时,最后执行此程序
fi
那我们再写一个例子,用if多分支条件语句来判断<br>一下用户输入的是一个文件,还是一个目录:
例子:判断用户输入的是什么文件<br>[root@localhost ~]# vi sh/if-elif.sh<br>#!/bin/bash<br>#判断用户输入的是什么文件<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>read -p "Please input a filename: " file<br>#接收键盘的输入,并赋予变量file<br>if [ -z "$file" ]<br>#判断file变量是否为空<br>then<br>echo "Error,please input a filename"<br>#如果为空,执行程序1,也就是输出报错信息<br>exit 1<br>#退出程序,并返回值为1(把返回值赋予变量$?)<br>elif [ ! -e "$file" ]<br>#判断file的值是否存在<br>then<br>echo "Your input is not a file!"<br>#如果不存在,则执行程序2<br>exit 2<br>#退出程序,把并定义返回值为2<br>elif [ -f "$file" ]<br>#判断file的值是否为普通文件<br>then<br>echo "$file is a regulare file!"<br>#如果是普通文件,则执行程序3<br>elif [ -d "$file" ]<br>#判断file的值是否为目录文件<br>then<br>echo "$file is a directory!"<br>#如果是目录文件,则执行程序4<br>else<br>echo "$file is an other file!"<br>#如果以上判断都不是,则执行程序5<br>fi<br>
2 多分支case条件语句
case语句和if…elif…else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系。case语句语法如下:
case $变量名 in<br>"值1")<br>如果变量的值等于值1,则执行程序1<br>;;<br>"值2")<br>如果变量的值等于值2,则执行程序2<br>::<br>…省略其他分支…<br>*)<br>如果变量的值都不是以上的值,则执行此程序<br>;;<br>esac<br>
这个语句需要注意以下内容:
case语句,会取出变量中的值,然后与语句体中的值逐一比较。如果数值符合,则执行对应的程序,如果数值不符,则依次比较下一个值。如果所有的值都不符合,则执行“*)”(“*”代表所有其他值)中的程序。
case语句以“case”开头,以“esac”结尾。
每一个分支程序之后要通过“;;”双分号结尾,代表该程序段结束(千万不要忘记,超哥每次写case语句,都会忘记双分号,有点“囧”)。
写一个判断是“yes/no”的例子:
[root@localhost ~]# vi sh/case.sh
#!/bin/bash
#判断用户输入
# Author: shenchao (E-mail: shenchao@atguigu.com)
read -p "Please choose yes/no: " -t 30 cho
#在屏幕上输出“请选择yes/no”,然后把用户选择赋予变量cho
case $cho in
#判断变量cho的值
"yes")
#如果是yes
echo "Your choose is yes!"
#执行程序1
;;
"no")
#如果是no
echo "Your choose is no!"
#执行程序2
;;
*)
#如果既不是yes,也不是no
echo "Your choose is error!"
#则执行此程序
;;
esac
3 for循环
for循环是固定循环,也就是在循环时已经知道需要进行几次的循环,有时也把for循环称为计数循环。for的语法有如下两种:
语法一:
for 变量 in 值1 值2 值3…
do
程序
done
这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。
语法一举例:
例子1:打印时间
[root@localhost ~]# vi sh/for.sh
#!/bin/bash
#打印时间
# Author: shenchao (E-mail: shenchao@atguigu.com)
for time in morning noon afternoon evening
do
echo "This time is $time!"
done
批量解压缩脚本就应该这样写:
例子2:批量解压缩<br>[root@localhost ~]# vi sh/auto-tar.sh<br>#!/bin/bash<br>#批量解压缩脚本<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>cd /lamp<br>#进入压缩包目录<br>ls *.tar.gz > ls.log<br>#把所有.tar.gz结尾的文件的文件覆盖到ls.log临时文件中<br>for i in $(cat ls.log)<br>#读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i<br>do<br>tar -zxf $i &>/dev/null<br>#加压缩,并把所有输出都丢弃<br>done<br>rm -rf /lamp/ls.log<br>#删除临时文件ls.log<br>
语法二:
for (( 初始值;循环控制条件;变量变化 ))
do
程序
done
语法二中需要注意:
初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;
循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;
变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1。
语法二举例
那语法二就和其他语言中的for循环更加类似了,也就是事先决定循环次数的固定循环了。先举个简单的例子:
例子1
例子1:从1加到100
#!/bin/bash
#从1加到100
# Author: shenchao (E-mail: shenchao@atguigu.com)
s=0
for (( i=1;i<=100;i=i+1 ))
#定义循环100次
do
s=$(( $s+$i ))
每次循环给变量s赋值
done
echo "The sum of 1+2+...+100 is : $s"
#输出1加到100的和
例子2
例子2:批量添加指定数量的用户<br>[root@localhost ~]# vi useradd.sh<br>#!/bin/bash<br>#批量添加指定数量的用户<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>read -p "Please input user name: " -t 30 name<br>#让用户输入用户名,把输入保存入变量name<br>read -p "Please input the number of users: " -t 30 num<br>#让用户输入添加用户的数量,把输入保存入变量num<br>read -p "Please input the password of users: " -t 30 pass<br>#让用户输入初始密码,把输入保存如变量pass<br>if [ ! -z "$name" -a ! -z "$num" -a ! -z "$pass" ]<br>#判断三个变量不为空<br>then<br>y=$(echo $num | sed 's/[0-9]//g')<br>#定义变量的值为后续命令的结果<br>#后续命令作用是,把变量num的值替换为空。如果能替换为空,证明num的值为数字<br>#如果不能替换为空,证明num的值为非数字。我们使用这种方法判断变量num的值为数字<br>if [ -z "$y" ]<br>#如果变量y的值为空,证明num变量是数字<br>then<br>for (( i=1;i<=$num;i=i+1 ))<br>#循环num变量指定的次数<br>do<br>/usr/sbin/useradd $name$i &>/dev/null<br>#添加用户,用户名为变量name的值加变量i的数字<br>echo $pass | /usr/bin/passwd --stdin $name$i &>/dev/null<br>#给用户设定初始密码为变量pass的值<br>done<br>fi<br>fi<br>
例子3
批量删除用户
[root@localhost ~]# vi sh/userdel.sh<br>#!/bin/bash<br>#批量删除用户<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>user=$(cat /etc/passwd | grep "/bin/bash"|grep -v "root"|cut -d ":" -f 1)<br>#读取用户信息文件,提取可以登录用户,取消root用户,截取第一列用户名<br>for i in $user<br>#循环,有多少个普通用户,循环多少次<br>do<br>userdel -r $i<br>#每次循环,删除指定普通用户<br>done
4 while循环
while [ 条件判断式 ]
do
程序
done
对while循环来讲,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。
例子
例子:1加到100<br>#!/bin/bash<br>#从1加到100<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>i=1<br>s=0<br>#给变量i和变量s赋值<br>while [ $i -le 100 ]<br>#如果变量i的值小于等于100,则执行循环<br>do<br>s=$(( $s+$i ))<br>i=$(( $i+1 ))<br>done<br>echo "The sum is: $s"<br>
5 until循环
再来看看until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序。一旦循环条件成立,则终止循环。语法如下:
until [ 条件判断式 ]
do
程序
done
还是写从1加到100这个例子,注意和while循环的区别:
例子:从1加到100
[root@localhost ~]# vi sh/until.sh
#!/bin/bash
#从1加到100
# Author: shenchao (E-mail: shenchao@atguigu.com)
i=1
s=0
#给变量i和变量s赋值
until [ $i -gt 100 ]
#循环直到变量i的值大于100,就停止循环
do
s=$(( $s+$i ))
i=$(( $i+1 ))
done
echo "The sum is: $s"
6 函数
function 函数名 () {
程序
}
1加到100了,我们让用户自己来决定加到多少吧:
例子:
[root@localhost ~]# vi sh/function.sh
#!/bin/bash
#接收用户输入的数字,然后从1加到这个数字
# Author: shenchao (E-mail: shenchao@atguigu.com)
function sum () {
#定义函数sum
s=0
for (( i=0;i<=$1;i=i+1 ))
#循环直到i大于$1为止。$1是函数sum的第一个参数
#在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数
do
s=$(( $i+$s ))
done
echo "The sum of 1+2+3...+$1 is : $s"
#输出1加到$1的和
}
read -p "Please input a number: " -t 30 num
#接收用户输入的数字,并把值赋予变量num
y=$(echo $num | sed 's/[0-9]//g')
#把变量num的值替换为空,并赋予变量y
if [ -z "$y" ]
#判断变量y是否为空,以确定变量num中是否为数字
then
sum $num
#调用sum函数,并把变量num的值作为第一个参数传递给sum函数
else
echo "Error!! Please input a number!"
#如果变量num的值不是数字,则输出报错信息
fi
7 特殊流程控制语句
1、 exit语句
系统是有exit命令的,用于退出当前用户的登录状态。可是在Shell脚本中,exit语句是用来退出当前脚本的。也就是说,在Shell脚本中,只要碰到了exit语句,后续的程序就不再执行,而直接退出脚本。
exit的语法如下:
exit [返回值]
如果exit命令之后定义了返回值,那么这个脚本执行之后的返回值就是我们自己定义的返回值。可以通过查询$?这个变量,来查看返回值。
如果exit之后没有定义返回值,脚本执行之后的返回值是执行exit语句之前,最后执行的一条命令的返回值。
例子:
[root@localhost ~]# vi sh/exit.sh<br>#!/bin/bash<br>#演示exit的作用<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>read -p "Please input a number: " -t 30 num<br>#接收用户的输入,并把输入赋予变量num<br>y=$(echo $num | sed 's/[0-9]//g')<br>#如果变量num的值是数字,则把num的值替换为空,否则不替换<br>#把替换之后的值赋予变量y<br>[ -n "$y" ] && echo "Error! Please input a number!" && exit 18<br>#判断变量y的值如果不为空,输出报错信息,退出脚本,退出返回值为18<br>echo "The number is: $num"<br>#如果没有退出加班,则打印变量num中的数字<br>
这个脚本中,大家需要思考,如果我输入的不是数字,那么“echo "The number is: $num"”这个脚本是否会执行?当然不会,因为如果输入的不是数字,“[ -n "$y" ] && echo "Error! Please input a number!" && exit 18”这句脚本会执行,exit一旦执行脚本就会终止。执行下这个脚本:
[root@localhost ~]# chmod 755 sh/exit.sh
#给脚本服务执行权限
[root@localhost ~]# sh/exit.sh 执行脚本
Please input a number: test 输入值不是数字,而是test
Error! Please input a number! 输出报错信息,而不会输出test
[root@localhost ~]# echo $? 查看下返回值
18 返回值居然真是18啊
[root@localhost ~]# sh/exit.sh
Please input a number: 10 输入数字10
The number is: 10 输出数字10
2、 break语句
再来看看特殊流程控制语句break的作用,当程序执行到break语句时,会结束整个当前循环。而continue语句也是结束循环的语句,不过continue语句单次当前循环,而下次循环会继续。
举个例子:
[root@localhost ~]# vi sh/break.sh
#!/bin/bash
#演示break跳出循环
# Author: shenchao (E-mail: shenchao@atguigu.com)
for (( i=1;i<=10;i=i+1 ))
#循环十次
do
if [ "$i" -eq 4 ]
#如果变量i的值等于4
then
break
#退出整个循环
fi
echo $i
#输出变量i的值
done
执行下这个脚本,因为一旦变量i的值等于4,<br>整个循环都会跳出,所以应该只能循环三次:
[root@localhost ~]# chmod 755 sh/break.sh
[root@localhost ~]# sh/break.sh
1
2
3
3、 continue语句
再来看看continue语句,continue也是结束流程控制的语句。如果在循环中,continue语句只会结束单次当前循环,也画个示意图来说明下continue语句,如图12-2所示:
还是用刚刚的脚本,不过退出语句换成continue语句
[root@localhost ~]# vi sh/continue.sh<br>#!/bin/bash<br>#演示continue语句<br># Author: shenchao (E-mail: shenchao@atguigu.com)<br>for (( i=1;i<=10;i=i+1 ))<br>do<br>if [ "$i" -eq 4 ]<br>then<br>continue<br>#退出语句换成continue<br>fi<br>echo $i<br>done<br>
运行下这个脚本:
[root@localhost ~]# chmod 755 sh/continue.sh
#赋予执行权限
[root@localhost ~]# sh/continue.sh
1
2
3
5 少了4这个输出
6
7
8
9
10
continue只会退出单次循环,所以并不影响后续的循环,所以只会少4的输出。这个例子和break的例子做个比较,应该可以更清楚的说明break和continue的区别。
0 条评论
下一页
为你推荐
查看更多