目录
2BASH Shell(Bourne shell 简称bash)
1 什么是shell编程
● 用户与Linux的接口
● 命令解释器
● 支持多用户
● 支持复杂的编程语言
● Shell有很多种,如:csh,tcsh,pdksh,ash,sash,zsh,bash等。Linux的缺省Shell为bash(Bourne Again Shell)。
Shell是用户和操作系统之间最主要接口。通过Shell,可以同时在后台运行多个应用程序,并且在把需要与用户交互的程序放在前台运行。
通过在多条命令的序列中使用变量和流程控制,Shell可以作为一名复杂的编程语言。
学习shell的目的是为了更深入地学好Linux及更好地管理好主机。Shell在文字界面下工作,在远程联机时,文字界面的传输速度较快,而且不容易出现断线或者信息外泄的问题。Shell是很有必要学习的一个工具,它可以让您更进步了解Linux,而不是只会按按鼠标。特别是在维护主机显得尤为重要。此外,要管理好主机,良好的shell编程知识是必要的。
2BASH Shell(Bourne shell 简称bash)
BASH目前是GNU操作系统中标准的shell,它主要兼容于sh,并依据一些用户需求而加强,可以说目前几乎所有的Linux版本都是使用bash作为管理核心的主要shell。BASH主要优点有:
- 命令编辑能力
bash中相当棒的一个功能是它能记忆使用过的指令,只要在命令行中按上下键就可以找到输入的前一个指令。RedHat默认的指令记忆功能可以达到1000个,记录在用户的根目录的.bash_history中。不过,需要留意的是,~/.bash_history记录的是上一次登录以前执行过的指令,至于这一次登录执行指令都被暂存在内存中,当用户成功注销系统后,该指令记忆才会记录到.bash_history中。
- 补全功能(比对数据正确性)
这个功能也相当棒,主要分为指令补全与文件名称补全。
指令补全:用在执行命令时不想按太多的按键的时候。例如指令passwd,在输入pass之后按下Tab键,bash马上会自动将后面的wd接上来。如果有重复的指令呢?按下两次Tab键会把所有重复的指令都列出来。如按下ch之后按下两次Tab键,再按下两次Tab键后会发现以ch开头的命令都显示出来了。另外直接在提示符后连按两次Tab键,系统会将所有可以使用的指令都列出来。
文件名称补全:如果你用vi读取某个文件,例如/etc/man.config,你可以在输入vi /etc/man.之后直接按下Tab键,那么该文件名称会被自动补全。
- 命令别名(alias)设定功能
要实现自定义命令可以使用alias,在命令行输入alias就可以知道当前的命令别名都有哪些。也可以直接输入下列命令来设定别名,如用lm来代替ls -al 命令为:alias lm=’ls -al’。
- 作业控制、前台后台控制
使用前台、后台控制可以让工作更顺利。作业控制的用途则更广,可以让我们随时将作业放到后台中执行,而不用怕不小心使用了Ctrl+C关掉该程序。此处,还可以在单一登录的环境中达到多任务的目的。
- Shell scripts的强大功能
DOS中有将一堆指令写在一起的所谓的“批处理文档”,Linux下的shell scripts则具有更强大的功能,可以将你需要频繁输入的连续指令写成一个文件,该文件通过交互方式进行主机的检测工作。也可以藉由shell提供的环境变量及相关指令编写一个小型的程序语言,这样,以前在DOS下只有程序语言才能写的东西,在Linux下使用简单的shell scripts就可以完成。
在了解了BASH的优点之后,来谈谈如何在Shell环境中输入指令。其实很简单,输入指令的方式为:
# command [-options] parameter1 parameter2 …
指令 选项 参数1 参数2 等等
- command为指令的名称,例如变换路径的指令为cd
- 中括号[]并不存在于实际的指令中,在加入参数时,通常为“-”符号,有时候完整名称会输入“--”符号。例如:ls –help
- parameter1 parameter2为依附在option之后的参数,或者是command的参数
- command,-options,parameter1这几项之间以空格分隔,不论空几格shell都视为一格
- 指令太长的时候,可以使用\符号来跳转Enter,使指令连续到下一行
实例:#ls –al /tmp 以ls列出/tmp目录中的隐藏文件与相关的属性参数
#ls –al /tmp\
>/xc 这两行实际上是一行指令,但是加上\跳转符号后,指令可以连续到下一行。请注意,\字符之后直接按下Enter就可以,不可在后面接空格符
3 变量与变量的设定
包含一些环境变量PATH,HOME,MAIL,SHELL等。(系统预设变量一般大写)
- echo显示变量的值,如#echo $PATH。Linux在系统预设变量名称前会加一个$符号,所以写成echo $PATH的形式。再如#echo $HOME 及#echo $ MAIL (说明:#为命令提示符)
- env显示系统主要环境变量(是environment的简写),如#env 将列出Linux系统中的预设变量。其中比较重要的变量如下:
ENV=/root/.bashrc //用户自己的环境变量的配置文件
HISTSIZE=1000 //当前的指令记忆数量
HOME=/root //登录用户的根目录(主目录)
HOSTNAME=linuxserver.xag.com.cn //这台主机的名字
HOSTTYPE=i386 //这台主机的硬件等级大致状态(i386,i686…)
INPUTRC=/etc/inputrc //一些shell加载的数据文件设定处
LANGUAGE=C //默认语言的资料
LANG=en_US.UTF-8 //默认语言的编码
LOGNAME=root //登录者的账号,这里会有一堆user和USERNAME等,是因为如果你以一般身份使用远程登录,再以su换成root时,你的很多设定不太一样,所以会有这么多的user数据
MAIL=/var/spool/mail/root //你的邮件文件
OSTYPE=linux-gnu //操作系统的形式
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin
PS1=[\u@\h \w]\$ //提示符的样式
PWD=/root //当前所在的路径
TERM=xterm //终端机形式
USERNAME=root //和LOGNAME类似
USER=root //用户账号
- set除了显示当前的环境变量,也会显示用户自己定义的变量,有哪些用户常用的自定义变量呢?下面仅列出部分常见的变量值:
BASH=/bin/bash //BASH的主程序放置路径
COLORS=/etc/DIR_COLORS //使用颜色
COLUMNS=100 //当前终端使用的字段有几个字符的宽度
HISTFILE=/home/2041011061/.bash_history //当前用来保存历史指令的文件,为一个隐藏文件
HISTFILESIZE=1000 //历史指令文件中指令的最大数
IFS=$’ \t\n’ //默认的分隔符
LINES=40 //当前光标所在的位置为第几行
MAILCHECK=60 //每隔多久检查一次有无新邮件(以秒为单位)
PPID=24572 //当前bash程序的ID号
UID=500 //登录者的用户ID(UID)
$ //当前shell的PID
? //最后一个命令的回传值。若之前的命令被正确执行,则传回0,否则传回1
说明:上面的变量中比较有趣的是$和?,尤其是?变量,如果你上一个命令执行过程中没有发生错误,那么这个变量会被设为0,如果上一个命令出现错误信息,那么这个变量会变成1。可以用echo $? 来显示?的值。
注意:set可以显示自定义变量,若系统有多个用户在线,你的变量只能自己使用,而不会干扰到别人
4 变量设定规则
- 变量与变量内容以等号“=”连结;
- 等号两边不能直接接空格符;
- 变量名称只能是英文字母与数字,其中数字不能是开头字符;
- 若有空格符,可以使用双引号或单引号将变量内容结合起来,但要特别留意,双引号内的特殊字符可以保留变量特性,单引号内的特殊字符则仅为一般字符;
- 必要时以跳转字符“\”将特殊符号(如Enter,$,\,空格符,’等)变成一般符号;
- 在一串指令中,还需要借助其他指令提供的信息,这时可以使用quote“’command’”;
- 若该变量为扩增变量内容时,则需以双引号及$变量名称(如”$PATH”:/home)继续累加内容;
- 若该变量需要在其他子程序执行,则以export使变量可以动作,如export PATH;
- 通常大写字符为系统预设变量,自定义变量可以使用小写字符,方便判断;
- 取消变量的方法为:unset 变量名称
举例如下:
一般变量设定:
name = xag //错误。因为等号两边不能直接接空格符
name=xag //正确。echo $name显示为xag
name=xag name //错误。需要加双引号(字符串中间有空格符)
name=”xag name” //正确
name=”xag’s name” //正确
name=’xag’s name’ //错误,因为有3个单引号
name=xag\’s\ name //正确
变量累加设定:
name=$nameisme //错误。需要用双引号将原变量圈起来
name=”$name”isme //正确
PATH=”$PATH”:/home/2041011061 //正确
PATH=”$PATH:/home/2041011061” //正确。这个形式对于PATH来说也是正确的格式
变量延伸到下一个子程序:
name=”xag’s name” //设定name变量
echo $name //显示name变量的指令
xag’s name //显示结果在此行
/bin/bash //另开一个子bash子程序
echo $name //显示name变量,但结果为空字符串,因为name这个变量不能用在子程序
exit //退出子程序bash shell
export name //正确。如此则$name可以用于下一个子程序中
指令中的指令:
cd /lib/modules/`uname –r`/kernel //会先执行`uname –r`这个内嵌的指令,然后输出结果附加在/lib/module…中,所以执行这个指令可以完成几个附指令程序(说明:“`”与“~”同在一个键上)
取消变量设定:
unset name
说明:这部分的内容很重要,要勤加练习。
注:’与”的区别:
name=xag
echo $name //显示为xag
myname=”$name its me”
echo $myname //显示为xag its me
myname=’$name its me’
echo $myname //显示为 $name its me 这说明$name在’中为一般字符,不再表示变量
-
-
-
-
-
- export使用它来设置变量,此变量将被继承到子程序中,即子程序中可以继承父程序中被export的变量
-
-
-
-
如:export 变量
-
-
-
-
-
- unset直接取消该变量内容。如:unset 变量
- 其他注意事项
-
-
-
-
变量可以让我们的系统管理变得简单,如为了安全起见,可以将HISTSIZE改小些。此外,关于路径的设定,当你以一般用户登录系统,再以su转换成root身份时,基本上,一堆环境变量仍是以当初一般用户身份为主,因此,你常会发现root使用的指令找不到,那就是环境变量的错误设定所致。这时,如果能够将该一般用户的路径设定为root能用的指令,转换身份时,将可以免除很多困扰。
5 命令别名与历史命令
-
alias与unalias
如:将ls –al的结果到more中显示,命令为:ls –al | more,这要输入好几个单词。可以用alias来定义一个别,以便以后使用:alias lm=’ls –al | more’ 。之后就可以用lm来代替使用。
单个alias命令用来显示已有的别名;
取消别名:unalias [别名]
-
history历史命令
格式:《1》history [n] //有n则显示最近n条命令,无n则显示所有的历史命令
《2》[!number] [!command] [!!] //number第几个指令;command指令的开头几个字母;!上一个指令
范例:
#history //列出历史指令的编号及指令内容如下:
-
-
-
-
-
-
- man rm
- alias
- man history
- history
-
-
-
-
-
# !66 //执行第66条历史指令
# !! //执行上一个指令(本例中为!66指令)
# !al //执行最近一次以al开头的指令内容,就是第67个指令
6 bash shell 的配置文件
在命令行输入的变量也好、命令别名也罢,都只是针对该次登录的设定,所以只要你注销,那么上次的设定值就会不见了。因此,我们需要几个文件帮助我们,在每次登录时帮我们完成环境的设定。
-
系统设定值
就是每个用户进入到bash shell之后先读取的配置文件。
-
- /etc/profile:这个文件设定了几个重要变量,如PATH、USER、MAIL、HOSTNAME、HISTSIZE、umask等,这些内容都可以修改
- /etc/bashrc:这个文件用于规划umask,同时规划提示符的内容(就是里面那个PS1)。
- /etc/man.config:这个文件内容规范了使用man时man page的路径到哪里寻找,说得简单点,这个文件指定了输入man时查看数据的路径设定。
-
个人设定值
就是在个人根目录下的几个隐藏文件中,分别会用到下面的几个文件(注意,下面的文件都是隐藏文件,需要使用ls –al方能显示出来),另外,注意,下面的“~”代表的是用户根目录。
-
- ~/.bash_profile:里面定义了个人路径(PATH)与环境变量的文件名称。你可以在这里修改个人路径,当然也可以在~/.bashrc这个个人设定的变量中修改。有的时候可以用~/.profile或~/.bash_login等文件来取代这个文件!
- ~/.bashrc:这个文件对于个人喜好的bash设定是最重要的,如设定命令别名,路径的重新定义等
- ~/.bash_history:这个文件用于将你曾经用过的命令记录下来,而当你再次以上下键搜寻或者直接以history搜寻时,就可以找到曾经用过的指令。需要注意的是:
-
- 在这一次执行过程中用到的指令,将在你退出shell之后才会被记录到这个文件中,否则将先被写到高速缓存中;
- 可以通过history指令将其中的记录搜寻出来;
- 这个文件的指令记录笔数与HISTSIZE有关,你可以自行在~/.bashrc中设定,或者直接由root在/etc/profile中统一设定
-
- ~/.bash_logout:这个文件则是在你注销shell的时候BASH为你所做的事情。通常默认是只有清除屏幕这件事,不过,你也可以将一些备份或其他你认为重要的工作写在这个文件中,那么当你退出linux时,就可以解决一些烦人的事情。
- source
前面提过,如果需要将当前的配置文件内容读入一次,需要重新注销再登录,那么有没有办法不注销而直接读入变量配置文件呢?当然可以,使用source即可!
格式:#source 变量配置文件
通配符与特殊符号
符号 | 内容 |
* | 通配符,代表任意个字符 |
? | 通配符,代表一个字符 |
# | 注释,这个最常用在脚本中,视为说明 |
\ | 跳转符号,将特殊字符可通配符还原成一般字符 |
| | 分隔两个管线命令的界定 |
; | 连续性命令的界定(注意,与管线命令不同) |
~ | 用户的根目录 |
$ | 即变量前需要加的变量值 |
& | 将指令变成在后台(背景下)工作 |
! | 逻辑运算中的“非”(not) |
/ | 路径分隔符号 |
>,>> | 输出导向,分别为“取代”与“累加” |
‘ | 单引号,不具有变量置换功能 |
“ | 具有变量置换功能 |
` ` | 两个“`”中间为可以先执行的指令(说明:“`”与“~”同在一个键上) |
() | 中间为子shell的起始与结束 |
[] | 中间为字符组合 |
{} | 中间为命令区块组合 |
组合键 | 执行结果 |
Ctrl + C | 终止当前命令 |
Ctrl + D | 输入结束(EOF),例如邮件结束的时候 |
Ctrl + M | 就是Enter |
Ctrl + S | 暂停屏幕的输出 |
Ctrl + Q | 恢复屏幕的输出 |
Ctrl + U | 在提示符下,将整行命令删除 |
Ctrl + Z | 暂停当前命令 |
上面的通配符中,最常用的是*,?,[]和`,我们提几简单的例子。举例如下:
#ls test* //代表后面不论接几个字符都予以接受(没有字符也接受)
#ls test? //?代表后面一定要接一个字符
#ls test??? //???代表一定要接3个字符
#cp test[1-5] /tmp //test1,test2,test3,test4,test5若存在,就将其复制到/tmp下
#cd /lib/modules/`uname -r`/kernel/drivers //被` `括起来的内容会先执行
上面几个例子中,注[]中代表只有一个字符,但是范围可以是1~5,这样,我们如果允许大写字母,就可以交文件复制出来,可以这样做:#cp [A-Z] /tmp
连续输入指令的方式:
两个指令先后写在一起,可以这样写:#command1;command2
两个指令间用分号“;”分隔,这个分号的意思,不论command1执行结果为何,command2都会被执行。那么如果是两个相关的指令,第一个command1的执行结果如果有错误,第二个就不被执行,可以这样做吗?当然可以,使用下面两个格式即可:
#command1 && command2
#command1 | | command2
“?”变量代表前一个执行的指令内容有没有错误,如果有错误就返回1,没有错误就返回0,你可以使用#echo $?来查询。&&则是代表当command1执行结果返回值为0时,也就是没有错误信息时,command2才会开始执行。| |恰恰相反,当command1有错误信息时,command2才会执行。举个例子,我的系统中并没有/xag这个目录,所以执行ls /xag会产生错误信息,所以:
# ls /xag ;ls /
# ls /xag && ls /
# ls /xag | | ls /
上面3行指令的返回结果是什么?请自己试验!
7 绝对路径与相对路径
绝对路径:从根目录开始写到文件的一种命令书写方法
相当路径:相对于从某个目录开始写的文件的一种命令书写方法
. //代表当前目录,代表这一层,如执行当前目录下的xxx.x文件,则:#./xxx.x
.. //代表上一层目录
8命令重定向
什么是重定向:就是将你目前所得数据转到其他地方。例如我们常用的将当前屏幕输出数据转到文件中,就可以写成:ls -l / >test,大于号“>”就是将当前输出结果导向到test文件的意思。
除了这个>符号,在bash命令执行过程中,还有3种输出输入状况,分别是:
-
-
-
-
-
-
-
- 标准输入:代码为0,或称为stdin,使用方式为<
- 标准输出:代码为1,或称为stdout,使用方式为1>
- 错误输出:代码为2,或称为stderr,使用方式为2>
-
-
-
-
-
-
基本的指令书写方式为:
指令 | > | 设备或文件 |
2> | ||
>> | ||
< |
左边一定是指令,右边则可能是设备或文件。
注意:,1>与2>之间没有空格符。相关使用说明举例如下:
# ls -al > list.txt //将显示结果输出到list.txt文件中,若该文件已存在则予以取代
# ls -al >> list.txt //将显示结果累加到list.txt文件中,该文件为累加的,旧数据保留!
# ls -al 1> list.txt 2> list.err //将显示数据正确输出到list.txt,错误的数据输出到list.err
# ls -al 1> list.txt 2>&1 //将显示数据不论正确或错误均输出到list.txt中。注意,错误与正确信息输出到同一个文件中,则必须以此种方法来写,不能写成其他格式
# ls -al 1> list.txt 2> /dev/null //将显示的数据,正确的输出到list.txt,错误的数据予以丢弃!(/dev/null表示垃圾桶设备,存入的数据就会被丢弃)
说明命令重定向里几个常用的符号与设备:
-
-
-
-
-
-
-
-
- <:由<的右边读入参数文件;
- >:将原本由屏幕输出的正确数据输出到>右边的file(文件名称)或device(设备,如printer);
- >>:将原本由屏幕输出的正确数据输出到>>右边,与>不同的是,该文件将不会被覆盖,而新的数据将以累加方式添加到文件的最后面;
- 2>:将原本应该由屏幕输出的错误数据输出到2>的右边;
- /dev/null:可以视为垃圾设备。
-
-
-
-
-
-
-
说明:一般用户在使用find时,有些目录无权访问这时就会有错误输出信息,而有些又可以访问又会有正确输出信息。例如:
#find / -name testing
find:/home/test1: Permission denied //这是错误的输出
find:/home/root: Permission denied //这是错误的输出
find:/home/masda: Permission denied //这是错误的输出
/home/test/testing //这是正确的输出
现在假如我们想将数据输出到list这个文件中,执行find / -name testing > list会有什么结果?你会发现list里保存了刚刚那个正确的输出数据,而屏幕上还是会出现错误的信息。如果想将正确的与错误的数据分别存入不同的文件,需要怎么做?其实在数据的重定向方面,正确的写法应该是1>与2>,但是如果只有>,则默认是以1>进行数据的输出。1>是输出正确数据,2>则是输出错误数据。也就是说:
1>:是将正确的数据输出到指定的地方
2>:是将错误的数据输出到指定的地方
正确地说,1>被称为标准输出,而2>被称为标准错误输出,不过,这里为帮助大家记忆,直接以所谓的正确与错误输出来说明。上面的例如可以这样写:
# find / -name testing 1>list_right 2> list_error
这样一来,刚刚执行结果中,有Permission的那几行错误信息都会写入list_error文件中,正确的输出数据则会存到list_right文件中。
如果要正确的数据,错误的信息不要,这个时候/dev/null这个垃圾设备就很重要了。如下:
# find / -name testing 1>list_right 2> /dev/null
如果要将数据定到同一个文件中,这时需要用到特殊写法,请注意下面的写法:
# find / -name testing 1>list 2> list //错误写法
# find / -name testing 1>list 2> &1 //正确写法
请特别留意,同时写入同一个文件需要使用2>&1
“<”指将原本需要由键盘输入的数据由文件文件读入,最明显的例子就是mail这个东西。以root身份寄信给root,可以这样做:
- 完全由键盘输入数据:
#mail -s “test” root //-s表示邮件标题,root为收件人
I am root! //以下的数据都是由键盘输入的
That’s OK
. //“.”表示结束输入,需要在一行的最前面加上“.”
- //是否需要有密件副本?不需要的话,直接按下Enter
EOF //表示送出
- 由文件代替输入
#mail -s “test” root < /root/.bashrc //将.bashrc内容寄给root
这样就可以将信寄出去了。熟悉命令重导向,对自己可是相当有帮助的。(可以用#mail命令查看自己的邮件)
9管线命令
如同前面据说的,bash命令执行时会输出数据,那么如果这些数据必须经过几道手续之后才能得到我们想要的格式,应该如何设定?这就涉及到管线命令的问题(pipe)。管线命令使用的是“|”界定符号,另外,管线命令与连续输入是不一样的,下面举例说明:
假设要读取last指令中root登录的次数,应该怎么做?注意,只要次数。步骤如下:
- 执行last,将这个月的所有人的所有登录数据取出来;
- 使用grep将上面的输出数据(stdout)中的root撷取出来,其他的不要;
- 使用wc这个可以计算行数的指令对上一步取到的数据计算行数。
命令如下:
# last
# last | grep root
# last | grep root | wc -l
管线命令用图可表示如下:
常用管线命令如下:
-
cut 取出每一行数据的第几个区块或那几列字符
格式:cut -d “分隔字符” [-cf] fields //-d后面接的是分隔字符,默认是空格符;-c后面接的是第几个字符;-f后面接的是第几个区块
范例:
#cat /etc/passwd | cut -d “:” -f 1 //将passwd文件中每一行里的“:”用作分隔符,列出第一个区块,也就是用户名所在
# last | cut –d “ “ –f 1 //以空格符作为分隔,并列出第一个区块
# last | cut -c 1-20 // 将last之后的数据,每一行的1~20个字符取出来
-
sort 对输出信息进行排序
格式:sort [-t 分隔符] [(+起始) (-结束)] [-nru] //-t 分隔符:使用分隔符隔开不同区块,默认是tab;+start -end:由第start区块排序到end区块;-n:使用纯数字排序(否则会以字母方式排序);-r:反向排序;-u:相同出现的一行,只列出一次
范例:
#cat /etc/passwd | sort //将列出来的个人账号排序
#cat /etc/passwd | sort –t : +2n //将个人账号以用户ID排序(以:作分隔符,第三个为ID,但第一个代号为0)
#cat /etc/passwd | sort –t : +2nr //反相排序
-
wc计算文件内容的一个工具组
格式:wc [-lmw] //-l:多少行;-m:多少字符;-w:多少字
范例:
# cat /etc/passwd | wc -l //这个文件里有多少行
# cat /etc/passwd | wc -w //这个文件里有多少字
-
uniq 只显示一个
#last | cut -d " " -fl | sort | uniq
这个例子用来删除重复的行从而只显示一个,例如,想知道这个月登录主机的用户有哪些人,而不在乎他们的登录次数,那么就可以用上面的例子(1)先列出所有的数据;(2)将用户名独立列出来;(3)排序;(4)只显示一个。
-
tee重定向后也可在屏幕上显示
#last | tee last.list | cut -d " " -fl
last可以查看到这个月的登录数据,而使用tee之后,会将数据同时传给下一个命令执行,也会将数据写入last.list文件中
-
tr 删除或取代重复字符串
格式:#tr [-ds] SET1
说明:-d 删除SET1这个字符串
-s 取代重复的字符
例如:#last | tr '[a-z]' '[A-Z]' //将小写改成大写
#cat /etc/passwd | -d : //这个“:”在/etc/passwd中不显示了
-
split分割文件
格式:split [-bl] 输入文件 输出文件前导字符
说明:-b以文件size来分
-l以行数来分
例如:#split -l 5 /etc/passwd test //会产生testaa,testab,testac等文件