一、Bash Shell基础
1.Bash Shell简单介绍
管理整个硬件的其实是核心(kernel),通常用户(user)都是以shell来与核心沟通。
什么是Shell?
我们通过shell与核心沟通,shell相当于用户和核心之间的桥梁。系统当前有多少Shells?可以检查一下/etc/shells文件。我们一般使用linux支持最广泛的bash。
Bash的主要优点:
- 命令记忆能力:可以记忆使用过的命令。只要在命令行按“上下键”,就可以找到前一个输入的命令。
- 命令与文件补全功能:
- 热键[Tab]可以帮助我们少打很多字,并且确定输入的数据是正确的。
- [Tab]接在一串命令的第一个字的后面,则为命令补全;
- [Tab]接在一串命令的第二个字以后,则为“文件补齐”。
- 想知道环境中可执行的命令有几个,直接在bash的提示符后输入两个[Tab][Tab],就能输出所有可执行的命令了。
- 如果想要知道系统中所有以c开头的命令,则按下c[tab][tab]即可。
- 命令别名(alias)设置功能:alias lm=’ls -al’ 自定义lm替换ls -al 命令。
- Shell scripts的强大功能
Bash Shell的内置命令:type
- 为了方便shell的操作,bash已经“内置”了很多命令,如cd和umask等,都是内置在bash中的。如何知道一个命令是外部命令(其他非bash套件所提供的命令)还是内置在bash命令中的?下面介绍命令type:
1
2
3
4
5
6
7
8
type [-tpa] name
不加任何参数:type会显示出name是外部命令还是bash内置的命令。
-t:加入该参数,type会将name通过下面这些文字显示出它的意义:
file:表示为外部命令
alias:表示该命令为命令别名所设置的名称
builtin:表示该命令为bash内置的命令功能
-p:如果后面接的name为命令,会显示完整的文件名(外部命令)或显示为内置命令
-a:在PATH变量定义的路径中,列出所有含有name的命令,包含alias。
- 执行命令
1
2
3
4
5
command [-options] parameter1 parameter2
命令 选项 参数(1) 参数(2)
command为命令的名称,如改变路径的命令为cd
-options为加入参数的设置,如-h等
parameter1 parameter2为跟在option后面的参数,或者是command的参数。
2.Shell的变量功能
前面我们提到,在任何地方执行ls,系统就是通过PATH变量里的内容所记录的路径顺序来搜索该命令。这是系统默认的变量的作用,如果是个人设置方面的应用,比如路径,使用变量,并将该变量定义写在最前面,后面相关的路径名称都以变量来替换,只需修改一行,就修改了整个脚本,很方便。
变量的获取与设置:echo、变量设置规则、unset
使用echo命令可以获取变量,获取变量时,前面必须加上$。1
2echo $PATH
echo ${PATH}设置或修改某个变量的内容
规定:1.变量与变量内容以等号“=”来连接
2.等号两边不能直接接空格符
3.变量名称只能是英文字母与数字,但数字不能是开头字符
4.若有空格符,可以使用双引号或单引号将变量内容结合起来。需要注意的是:双引号内的特殊字符可以保持变量特性,但单引号内的特殊字符会变成一般符号。
5.必要时使用转义字符“\”将特殊字符(Enter,$,,空格等)转义为一般字符。
6.在一串命令中,还需要通过其他命令提供的信息,可以使用这样的方式 ‘commond’,注意不是单引号,是数字键1左边的那个按键。
7.若变量为扩展变量内容,需要以双引号及 $变量名称如 “$PATH”:/home继续累加内容。
8.若该变量需要在其他子程序中执行,则需要用export 使用变量变成环境变量,如“export PATH”。
9.通常大写字母为系统默认变量,自行设置变量可以使用小写字母,便于判断。
10.取消变量的方法为:“unset 变量名称”
1
2
3echo $myname 这条命令执行完,没有任何数据,因为这个变量还未设置,是空的
myname=VBird 这条命令执行完,会将变量myname的内容设置为VBird
echo $myname 这条命令执行完,显示VBird。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
例一:设置变量name,内容为VBird
name = VBird 错误,因为有空白
name=VBird OK
例二:设置变量的内容为VBird's name
name="VBird's name" OK
name=VBird\'s\ name OK 采用反斜线(\)转义特殊字符,如单引号和空格键
例三:在PATH变量中“累加”:/home/dmtsai/bin目录
PATH=$PATH:/home/dmtsai/bin OK
PATH="$PATH":/home/dmtsai/bin OK
例四:要将name的内容多出“yes”
name=$nameyes NOT OK name的内容成了$nameyes这个变量
正确的写法:
name="$name"yes
name=${name}yes
例五:如何让刚刚设置的name=VBird可以用在下一个Shell程序中
name=VBird
bash 进入到子程序
echo $name 并没有刚设置的内容
exit 离开刚才设置的子程序
export name
bash 进入到子程序
echo $name 出现设置的值了
exit 离开刚刚的子程序
“子程序”:就是说在当前这个Shell的情况下,去启用另一个新的Shell,那个新的Shell就是子程序。
一般状态下,父程序的自定义变量是无法在子程序内使用的。但是通过export将变量变成环境变量后,就能够在子程序下应用了。
例六:取消刚刚设置的name这个变量的内容
unset name
3.环境变量的功能
环境变量可以帮助我们实现很多功能,包括家目录的修改、提示符显示、执行文件搜索的路径,等等。
一些环境变量的说明
1
2例一:列出当前Shell环境下所有环境变量及内容
env环境变量LANG表示主语言环境,如我们要使用utf-8,可以LANG=utf-8
set命令:在bash环境下,其实还有一些很重要的变量,这些变量是在“shell环境下有效”,如果是在“子程序”中,这些变量值就会不同了。用set命令除了会将环境变量列出来之外,其他的自定义变量,以及所有的变量,都会列出来。
一般来说,不论是否为环境变量,只要与当前Shell的操作接口有关的变量,通常都会被设置为大写字母。
set$(关于本地shell的PID):这个变量表示“当前这个shell的进程号”。要想知道shell的PID(process ID),使用echo $$即可。
?(上一个执行命令的回传码): 当我们执行某些命令时,这些命令都会回传一个执行后的代码。一般来说,执行成功,则回传一个0值,如果执行过程错误,则返回错误代码(一个非零值)。
自定义变量转成环境变量:export
自定义变量与环境变量区别:主要在于是否可以被子程序所引用。
取得一个bash后,即得到了一个子程序,若再执行一次bash,将进入“子程序”。由于您已经进入该子程序,所有在父程序中自定义的变量将不存在,存在于子程序的,仅有“环境变量”。1
2
3
4export 变量
将自定义的变量转换成环境变量
export 后面没有跟变量,会把所有环境变量显示出来4.其他一些变量知识
语系文件的变量(locale)
1
2locale -a
会显示出linux支持的语系变量的有效范围
- 启动一个shell时,操作系统分配一块内存给shell使用,这个区域的变量可以让子程序访问;
- 利用export功能,可以让变量内容写到上述内存中(环境变量);
- 当加载另一个shell时(即启动子程序,离开原来的父程序),子shell可以将父shell的环境变量所在的内存导入自己的环境变量区块中。
变量键盘读取、数组与声明:read、array、declare
- read(读取来自键盘输入的变量)
1
2
3
4
read [-pt] variable
参数:
-p:后面可以接提示符
-t:后面可以接等待的“秒数”。

- declare/typeset(声明变量的属性)
如果declare后面没有任何参数,那么会将所有的变量名与内容都调出来,就像使用set一样。
1
2
3
4
5
6
7
8
9
10
11
12
declare [-aixr] variable
参数:
-a:将后面的variable定义为数组(array)
-i:将后面的variable定义为整数数字(integer)
-x:用法与export一样,将后面的variable变成环境变量
-r:将一个variable变量设置成只读(readonly)该变量不可更改内容,也不能取消设置(unset)
例:
sum=100+300+500
echo $sum 显示结果为100+300+500,因为这是文字类型的变量属性,不会求和
declare -i sum=100+300+500
echo $sum 显示结果为900
- 数组属性array的说明
1
2
3
4
var[index]=content
有一个数组名为var,index为数字角标,这个数组的内容为var[1]=小明,var[2]=大明,...
读取数组,以${数组}的方式来读取。
与文件系统及程序的限制关系:ulimit
假如有10个人同时登陆了一台Linux主机,这10个人同时打开了100个文件,每个文件的大小约10MB,Linux的内存需要1010010=10000MB,这样显然会造成系统死机。为了预防这种情况的发生,我们可以通过ulimit限制可打开的文件数量、可以使用的CPU情况、可以使用的内存总量。1
2
3
4
5
6
7ulimit [-SHacdflmnpstuv] [配额]
参数:
-H:hard limit,严格设置,必定不能超过设置的值
-S:soft limit,警告设置,可以超过这个设置值,但会有警告,
-a:列出所有的限制额度
-f:此shell可以建立的最大文件容量,单位为KB
其他参数用到时再查附加的变量设置功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24echo $HOME
echo ${HOME} 这种方法中,我们可以修改变量。只要加上一些字符标志,后面再接着使用比较字符串,就能修改变量的内容了。
例:以vbird="/home/vbird/testing/testing.x.sh"为例
1. 在vbird变量中,从最前面开始比较,若开头为/,则删除两个/之间所有的数据,即/*/
echo ${vbird##/*/}
结果:testing.x.sh
echo ${vbird#/*/}
结果:vbird/testing/testing.x.sh ##表示去后面字符串最长的那一段;#表示取最小的那一段
2.从后面开始删除,删除/*?
echo ${vbird%%/*/}
结果:删除失败
echo ${vbird%%/*}
结果:/home/vbird/testing
%%/*表示删除最长的那一段,%/*表示删除最短的一段
3.将vbird变量中的testing替换为TEST
echo ${vbird/testing/TEST}
结果:/home/vbird/TEST/testing.x.sh
echo ${vbird//testing/TEST}
结果:/home/vbird/TEST/TEST.x.sh
如果后面接/,表示后面进行的是替换工作,而且仅替换第一个
如果是//,则表示替换全部字符串。
5.命名别名与历史命令
命令别名设置:alias、unalias
1
2
3
4
5
6如果要查询隐藏文件,并且需要列出很长的信息,要一页一页地翻看,则需要执行“ls -al|more”
命令,可以使用lm简化
alias lm='ls -l | more'
取消别名的设置
unalias lm历史命令:history
1
2
3
4
5
6
7
8
9
10
11history [n]
history [-c]
history [-raw] histfiles
参数:
什么都不接,默认输出当前内存的所有历史记忆
n:数字,表示列出最近的n行命令
-c:将当前shell中所有的history内容全部清除
-a:将当前新增的history命令加入到histfiles中,若没有加入histfiles,则默认写入~/.bash_history
-r:将histfiles的内容读取到当前shell的历史记忆
-w:将当前的历史记忆内容写入histfiles中6.管道命令(pipe)
bash 命令执行时,会出现数据。如果这组数据必须经过几道手续之后才能得到所想要的格式,如何设置?这可以通过管道命令完成,管道命令使用 | 这个分隔符。
管道命令”|”仅能处理通过前面一个命令传来的正确信息,也就是标准输出(STDOUT)的信息,对于标准错误,并没有直接处理的能力。下面介绍一下基本的管道命令。选取命令:cut、grep
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16这个命令可以将一段消息的某段“切”出来。处理的消息是以“行”为单位。
cut -d '分隔符' -f fields
参数:
-d:后面接分隔符。与-f一起使用。
-f:根据-d的分隔符将一段消息分为数段,用 -f 取出第几段的意思。
例:
echo $PATH | cut -d ':' -f 5 将PATH变量用“:”切割成几段,取出第5段
echo $PATH | cut -d ':' -f 3,5 将PATH变量用“:”切割成几段,取出第3段和第5段
cut -c 字符范围
参数:
-c:以字符为单位(characters)为单位取出固定的字符范围
例:
export | cut -c 12- 将export输出的消息,取得第12个字符以后的所有字符串1
2
3
4
5
6
7
8
9
10
11
12cut是将一行消息中取出我们想要的部分,grep则分析一行消息,若其中有所需要的信息,就将该行取出。
grep [-acinv] '搜索字符串' filename
参数:
-a:将二进制文件以文本的方式搜索数据
-c:计算找到‘搜索字符串’的次数
-i:忽略大小写的不同,所有大小写视为相同
-n:顺便输出行号
-v:反向选择,即显示出没有 '搜索字符串' 内容的那一行
last | grep 'root' 将last中出现root的一行取出来
last | grep -v 'root' 只要没有root的就取出
last | grep 'root' | cut -d ' ' -f 1 在last的输出消息中,只要有root就取出,并且仅取出第一栏其他命令(命令较多,就不做具体介绍了)
- 排序命令:sort、wc、uniq
- 双向重导向:tee
- 字符转换命令:tr,col,join,paste,expand
- 拆分命令:split
二、正则表达式(请参考鸟哥的Linux私房菜)
三、Shell脚本
1.shell脚本基本介绍
什么是shell脚本?
shell脚本是利用shell功能所编写的“程序(program)”,这个程序使用纯文本文件,将一些shell的语法与命令写在里面,与正则表达式、管道命令与数据流重导向一起实现我们的目的。为什么学习shell脚本?
shell脚本其实就是纯文本文件(ASCII),我们可以编辑这个文件,然后让这个文件帮我们一次执行多个命令。编写shell脚本时需要注意的事项:
- 命令与参数间的多个空白会被忽略掉。
- 如果读到一个ENTER符,就开始执行该命令。
- 如果一行内容太多,则可以使用[Enter]来扩展至下一行。
- 此外,#可作为注释。任何加在#后面的内容,将会被视为注释文字而被忽略。
假设我们编写好的文件名是shell.sh,如何执行该文件?
- 将shell.sh加上可读与执行(rx)权限,然后就能用./shell.sh来执行了。
- 或者使用sh shell.sh的方式来直接执行即可。
编写第一个脚本
1.!#bin/bash在声明这个脚本使用的shell名称:因为使用的是bash,所有必须要用“!#bin/bash”来声明这个文件内的语法使用bash的语法。当执行这个程序时,它就能加载bash的相关环境设置文件,并且执行bash来使下面的命令执行。这一行很重要,因为如果没有的话系统很可能无法判断要使用声明shell来执行。
2.#后面的是注释
3.主要环境变量的声明:建议将重要的环境变量设置好。PATH变量比较重要,设置好PATH,可让程序在进行时,直接执行命令,而不必写绝对路径。
4.主要程序部分:将主要的程序写好。
5.告知执行结果:可以使用exit命令让程序中断,并给系统回传一个数值0。
执行结果:
3.简单的Shell脚本练习
- 变量内容由用户决定
1
2
3
4
5
6
7
8
编写一个脚本,让用户输入名字,然后在屏幕上打印用户名字
#!/bin/bash
#Program: this is a practice
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input your name:" name
echo -e "your name is : $name"
利用date建立文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#!/bin/bash
#Program: this is a practice
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
1.让用户输入文件名,并获取fileuser变量
read -p "Please input the filename what you want:"fileuser
2.为了避免用户随意按ENTER,使用变量功能分析是否设置了文件名?
filename=${fileuser:-"filename"}
3.开始使用date命令来获取所需要的文件名
date=`date --date='2 days ago' +%Y%m%d'
file="$filename""$date"
4.建立文件名
touch $file数值运算的方法
1
2
3
4
5
6
7
8
9
10#!/bin/bash
#Program: this is a practice
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "first number:" firstnum
read -p "second number:" secondnum
total=$(($firstnum*$secondnum))
echo -e "the total is: $total"数学运算可以使用“declare -i total=$firstNum*$secondNum”,但建议使用上面的方式进行。
var=$((运算内容)),不仅容易记忆,而且更加方便。
4.善用判断条件
使用test命令的测试功能
1
2
3
4test -e /dmtsai 检查/dmtsai是否存在
执行结果不会显示任何消息,但最后可通过$?或&&及||来展现整个结果
test -e /dmtsai && echo "exist" || echo "Not exist" 最后结果可以告诉我们是“exist”还是“Not exist”。测试标志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
151.某个文件名的“类型”检测,如test -e filename
-e:该“文件名” 是否存在
-f:该“文件名” 是否为文件(file)
-d:该“文件名” 是否为目录(directory)
2.文件的权限检测,如test -r filename
-r:检测该文件名是否具有“可读”属性
-w:检测该文件名是否具有“可写”属性
-x:检测该文件名是否具有“可执行”属性
3.多重条件判断,如test -r filename -a -x filename
-a:(and)两个条件同时成立
-o:(or)两个条件任何一个成立
还有比较两个文件、两个整数之间的判断、判断字符串的数据等,用到时再查询使用判断符号[ ]
除了使用test外,还可以使用判断符号[]来进行数据的判断。
1
2
3
4
5
6
7
8
9
10
11
12
判断$HOME变量是否为空
[ -z "$HOME" ] 需要注意的是:上述每个组件中间,都要用空格符分隔
判断两个字符串$HOME和$MAIL是否相同
[ "$HOME" == "$MAIL" ]
中括号内的变量,最好使用双引号来设置
name="VBird Tsai"
[ $name == "VBird" ]
结果会报错,因为$name没有用双引号括起来,那么上面的判断条件会变成:
[ VBird Tsai == "VBird" ] 而不是我们要的 [ "VBird Tsai" == "VBird" ]
- Shell脚本的默认变量($0,$1…)
5.条件判断
if then
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23语法:
if [ 条件判断表达式 ]; then
当条件表达式成立时,可以执行的命令
fi
当有多个条件需要判断时,除了将多个条件写入一个中括号,
还可以使用多个中括号来隔开,而括号与括号之间,则以&&或||来隔开,他们的含义是:
&&表示AND
||表示or
if [ 条件判断表达式 ]; then
当条件表达式成立时,可以执行的命令
else
当条件表达式不成立时,可以执行的命令
fi
if [ 条件判断表达式一]; then
当条件表达式成立时,可以执行的命令
elif [ 条件判断表达式二]; then
当条件表达式二成立时,可以执行的命令
else
当条件表达式一与二都不成立时,可以执行的命令
finetstat
该命令可以查询当前主机是否有打开的网络服务端口(service ports)
部分网络服务
常见的端口与相关网络服务的关系是:- 80:WWW
- 22:ssh
- 21:ftp
- 25:mail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
#Program: Using netstat and grep to detect WWW,SSH,FTP and Mail services
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
testing=`netstat -tuln | grep ":80 "`
if [ "$testing" != "" ]; then
echo "WWW is running in your system."
fi
testing=`netstat -tuln | grep ":22 "`
if [ "$testing" != "" ]; then
echo "SSH is running in your system."
fi
testing=`netstat -tuln | grep ":21 "`
if [ "$testing" != "" ]; then
echo "FTP is running in your system."
fi
testing=`netstat -tuln | grep ":25 "`
if [ "$testing" != "" ]; then
echo "Mail is running in your system."
fi
使用case esac判断
1
2
3
4
5
6
7
8
9
10
11
12case $变量名称 in
"第一个变量内容")
程序段
;;
"第二个变量内容")
程序段
;;
*)
不包含第一个变量内容与第二个变量内容的其他程序执行段
exit 1
;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
#Program: Show "Hello" from $1 ... by using case ...esac
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case $1 in
"hello")
echo "Hello,how are you?"
;;
"")
echo "You MUST input parameters,ex>$0 some word"
;;
*)
echo "Usage $0 {hello}
;;
esac
假设上面的脚本文件名字为sh08.sh
如果输入sh sh08.sh test执行,那么屏幕上就会出现“Usage sh08.sh{hello}”
6.循环
while do done、until do done
1
2
3
4
5
6
7
8
9
10while [ condition ]
do
程序段落
done
until [ condition ]
do
程序段落
done
这种方式与while相反,它说的是当condition条件成立时,就终止循环,否则就持续执行循环的程序段。for…do…done
相对于while、until的循环方式是必须要符合某个条件的状态,for这种语法是已经知道要进行几次循环的状态。1
2
3
4for(( 初始值; 限制值; 执行步长))
do
程序段
done