本笔记按照鸟哥的Linux私房菜学习,另,有关Linux下的环境变量PATH方法设定请看该博客:在Linux里设置环境变量的方法(export PATH)

一、BASH Shell学习

1、什么是 Shell?

这应该是个蛮有趣的话题:『什么是 Shell ?』相信只要摸过计算机,对于操作系统 ( 不论是 Linux 、 Unix 或者是 Windows ) 有点概念的朋友们大多听过这个名词,因为只要有『操作系统』那么就离不开 Shell 这个东西。
这就是基本的一个输出声音的需要的步骤!那么也就是说,你必须要『输入』一个指令之后, 『硬件』才会透过你下达的指令来工作!嘿嘿!那么硬件如何知道你下达的指令呢?那就是 kernel (核心)的控制工作了!了解了吗?没错!也就是说,我们必须要透过『 Shell 』将我们输入的指令与 Kernel 沟通,好让 Kernel 可以控制硬件来正确无误的工作! 基本上,我们可以透过底下这两张图来说明一下:

clipboard.png

clipboard.png

基本上,替我们工作的是『硬件』,而控制硬件的是『核心』,再来,我们使用者乃是利用『Shell』控制一些 kernel 提供的 『工具 (Utility)』来操控硬件替我们正确的工作。再进一步来说,由于 kernel 听不懂人类的语言,而人类也没有办法直接记得 kernel 的语言,所以两者的沟通就得藉由 shell 来支援了!(其实早期的 DOS 的文字接口也是使用 shell 来沟通呀!那个 shell 的名称就叫做 command.com ,还记得吗? ^_^)

以字面上的意思来说, kernel 是『核心』的意思,而 Shell 是『壳』的意思,呵呵!也就是说, shell 是最外头的咚咚!而 kernel 乃是最内层的的咚咚啦!核心是操作系统的最底层的东西! 这个核心里头包括了各种的支持硬件的工具!当然啰,如果你的硬件太新,而你的 kernel 并没有支持的话,那么很抱歉,你的 Shell 能力再怎么强,也没有办法使硬件工作的! 这样可以了解了吗?呵呵!没错!使计算机主机工作的正是核心的任务,但是操作核心来替使用者工作的,却是 shell 喔!因此,有时候你的 shell 搞了老半天,硬件却不能工作的时候,请注意, 您的『核心』是否正确呢?阿!扯远了!这是 kernel 章节才要说的东西。

2.Shell scripts 的强大功能:

在 DOS 年代还记得将一堆指令写在一起的所谓的『批处理文件』吧?在 Linux 底下的 shell scripts 则发挥的更为强大的功能,可以将您日常生活当中常需要下达的连续指令写成一个档案, 该档案并且可以透过对谈交互式的方式来进行主机的侦测工作!也可以藉由 shell 提供的环境变量及相关指令来进行设计,哇!整个设计下来几乎就是一个小型的程序语言了!该 scripts 的功能真的是超乎我的想象之外!以前在 DOS 底下需要程序语言才能写的东西,在 Linux 底下使用简单的 shell scripts 就可以帮你达成了!真的厉害!!这部分我们在后续章节再来谈!

变量的取用与设定:echo, 变量设定规则, unset
说的口沫横飞的,也不知道『变量』与『变量代表的内容』有啥关系? 当然啦,那我们就将『变量』的『内容』拿出来给您瞧瞧就好了。利用 echo 这个指令来取用变量, 但是,变量在被取用时,前面必须要加上 $ 才行,举例来说,要知道 PATH 的内容,该如何是好?

[root@linux ~]# echo $variable
[root@linux ~]# echo $PATH
/bin:/sbin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin
[root@linux ~]# echo ${PATH}

变量的取用就如同上面的范例,利用 ehco 就能够读出,只是需要在变量名称前面加上 $ , 或者是以 ${variable} 的方式来取用都可以!当然啦,那个 echo 的功能可是很多的, 我们这里单纯是拿 echo 来读出变量的内容而已,更多的 echo 使用,请自行给他 man echo 吧! ^_^

范例一:设定一变量 name ,且内容为 VBird 。
[root@linux ~]# 12name=VBird
-bash: 12name=VBird: command not found  <==屏幕会显示错误!因为不能以数字开头!
[root@linux ~]# name = VBird  <==还是错误!因为有空白!
[root@linux ~]# name=VBird    <==OK 的啦!

范例二:承上题,若变量内容为 VBird's name 呢?
[root@linux ~]# name=VBird's name  
# 因为单引号可以将 Enter 这个特殊字符取消,所以,您可以继续在下一行输入内容~
# 不过,这与我们要达到的功能不同,所以,算是失败的啦!
[root@linux ~]# name="VBird's name"  <==OK 的啦!
[root@linux ~]# name=VBird\'s\ name
# 利用反斜杠 (\) 跳脱特殊字符,例如单引号与空格键,这也是 OK 的啦!

范例三:我要在 PATH 这个变量当中『累加』:/home/dmtsai/bin 这个目录
[root@linux ~]# PATH=$PATH:/home/dmtsai/bin
[root@linux ~]# PATH="$PATH":/home/dmtsai/bin
# 上面这两种格式在 PATH 里头的设定都是 OK 的!但是底下的例子就不见得啰!

范例四:呈范例三,我要将 name 的内容多出 "yes" 呢?
[root@linux ~]# name=$nameyes  
# 知道了吧?如果没有双引号,那么变量成了啥?name 的内容是 $nameyes 这个变量!
# 呵呵!我们可没有设定过 nameyes 这个变量吶!所以,应该是底下这样才对!
[root@linux ~]# name="$name"yes
[root@linux ~]# name=${name}yes

范例五:如何让我刚刚设定的 name=VBird 可以用在下个 shell 的程序?
[root@linux ~]# name=VBird
[root@linux ~]# bash        <==进入到所谓的子程序
[root@linux ~]# echo $name  <==嘿嘿!并没有刚刚设定的内容喔!
[root@linux ~]# exit        <==离开刚刚的子程序
[root@linux ~]# export name
[root@linux ~]# bash        <==进入到所谓的子程序
[root@linux ~]# echo $name  <==出现了设定值了!
[root@linux ~]# exit        <==离开刚刚的子程序
# 什么是『子程序』呢?就是说,在我目前这个 shell 的情况下,
# 去启用另一个新的 shell ,新的那个 shell 就是子程序啦!在一般的状态下,
# 父程序的自定义变量是无法在子程序内使用的。但是透过 export 将变量变成
# 环境变量后,就能够在子程序底下应用了!很不赖吧!至于程序的相关概念,
# 我们会在『程序与资源管理』章节当中提到的喔!

范例六:如何进入到您目前核心的模块目录?
[root@linux ~]# cd /lib/modules/`uname -r`/kernel
# 每个操作系统核心版本都不相同,以 FC4 为例,他的预设核心版本是 
# 2.6.11-1.1369_FC4 所以,他的模块目录在 /lib/modules/2.6.11-1.1369_FC4/kernel 。
# 因为每个 distributions 的这个值都不相同,但是我们却可以利用 uname -r 这个指令
# 先取得版本信息,所以啰,就可以透过上面指令当中的内含指令 `uname -r` 
# 先取得版本输出到 cd .. 那个指令当中,就能够顺利的进入目前核心的驱动程序所放置
# 的目录啰!很方便吧!

范例七:取消刚刚设定的 name 这个变量内容
[root@linux ~]# unset name

3.set

其他所有的变量说明: set
而除了这些环境变量之外,还有没有什么重要的变量呢?当然有啊! 我们在 bash 的环境下,其实还有一些挺重要的变量,这些变量是『在这个 shell 环境下有效』的, 如果是在『子程序』,这些变量值就不会相同了。 那么如何观察目前 shell 环境下的所有变量呢?很简单啊,就用 set 即可!set 这个指令除了会将环境变量列出来之外,其他我们的自定义变量,与所有的变量,都会被列出来喔!信息多好多。 底下仅列出几个重要的内容。

\d :代表日期,格式为 Weekday Month Date,例如 "Mon Aug 1"
\H :完整的主机名。举例来说,鸟哥的练习机 linux.dmtsai.tw ,那么这个主机名就是 linux.dmtsai.tw
\h :仅取主机名的第一个名字。以上述来讲,就是 linux 而已, .dmtsai.tw 被省略。
\t :显示时间,为 24 小时格式,如: HH:MM:SS
\T :显示时间,12 小时的时间格式!
\A :显示时间,24 小时格式, HH:MM
\u :目前使用者的账号名称;
\v :BASH 的版本信息;
\w :完整的工作目录名称。家目录会以 ~ 取代;
\W :利用 basename 取得工作目录名称,所以仅会列出最后一个目录名。
\# :下达的第几个指令。
\$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $ 啰~

OK!所以,由预设的 PS1 内容为: '[u@h W]&dollar; ' 就可以了解为何我们的提示字符会是: [root@linux ~]# 了吧!好了,那么假设我想要有类似底下的提示字符:

[root@linux /home/dmtsai 16:50 #12]#

,那个 # 代表第 12 次下达的指令。 那么应该如何设定 PS1 呢?可以这样啊:

[root@linux home]# PS1='[\u@\h \w \A #\#]\$ '
[root@linux /home 17:02 #85]# 
# 看到了吗?提示字符变了!变的很有趣吧!其中,那个 #85 比较有趣,
# 如果您按下 [Enter] 后,该数字就会增加喔!为啥?上面有说明ㄇㄟ!

$:(关于本 shell 的 PID)

其实这个咚咚代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。 更多的程序观念,我们会在第四章的时候提及。想要知道我们的 shell 的 PID ,就可以: echo $$ 即可!

?:(关于上个执行指令的回传码)

虾密?问号也是一个特殊的变数?没错!在 bash 里面这个变量可重要的很! 这个变数是:『上个执行的指令所回传的值』, 上面这句话的重点是『上一个指令』与『回传值』两个地方。当我们执行某些指令时, 这些指令都会回传一个执行后的代码。一般来说,如果成功的执行该指令, 则会回传一个 0 值,如果执行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。 我们以底下的例子来看看:

[root@linux ~]# echo $SHELL
/bin/bash
[root@linux ~]# echo $?
0
# 因为上个指令执行过程中,并没有错误,为成功的执行完毕,所以回传 0 。
[root@linux ~]# 12name=VBird
-bash: 12name=VBird: command not found
[root@linux ~]# echo $?
127
# 发生错误啦!所以 echo $? 时,就会出现错误的代码!
# 我们可以利用这个代码来搜寻错误的原因喔!
[root@linux ~]# echo $?
0
# 咦!怎么又变成正确了?这是因为 "?" 只与『上一个执行指令』有关,
# 所以,我们上一个指令是执行『 echo $? 』,当然没有错误,所以是 0 没错!

自定义变量转成环境变量: export
如您想要让该变量内容继续的在子程序中使用,那么就请执行:

export 变量

这个东西用在『引用他人的档案或者其他程序』时,相当的重要的! 尤其像鸟哥常常两三个档案互相引用来引用去的,如果忘记设定 export 的话,那么不同的档案中的相同变量值,将需要一再地重复设定才行!所以,我只要在头一个档案使用 export 的话,那么后续的档案引用时,将会把该变量内容读进来!好用的很,如果仅下达 export 而没有接变量时,那么此时将会把所有的『环境变量』秀出来喔!例如:

变量键盘读取、数组与宣告: read, array, declare

[root@linux ~]# export
declare -x ENV="/root/.bashrc"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="linux.dmtsai.tw"
declare -x INPUTRC="/etc/inputrc"
declare -x LANG="en_US.UTF-8"
declare -x MAIL="/var/spool/mail/root"
declare -x SHELL="/bin/bash"
# 很多都直接省略了!不然....重复性太高,浪费版面~ ^_^

额外的变量设定功能
刚刚我们提到了两种变量取用的方法,分别是这样:

[root@linux ~]# echo $HOME
[root@linux ~]# echo ${HOME}

那么,在那个 ${variable} 的使用方法中,其实,我们还可以将变量进行一些修订的工作喔! 只要加上一些字符标志,后面再接着使用比对字符串,就能够修改变量的内容了! 我们取底下的例子来说明:在底下的例子中,假设我的变量名称为 vbird ,且内容为 /home/vbird/testing/testing.x.sh。

二.常用命令

1、alias 别名

我们可以为命令设置别名,已达到好记易输入的效果,如清屏命令是clear,我们可以使其简化为clr, 查看目录命令 ’ls -al | more‘可以设置为 lm,是不是很方便?

// 设置别名
alias clr='clear'
alias lm='ls -al | more'

// 取消别名
unalias clr

查看设置的别名

// 查看设置的别名
alias

clipboard.png

2、history

history 获取输入的历史命令

// 查看所有的命令
history

// 查看最近输入的命令前N条
history 5

clipboard.png

更多使用方法请看鸟哥私房菜文档

三、通配符与特殊符号

在 bash 里头还支持一些通配符喔 (wild card) !多了这些通配符, 我们利用 bash 处理数据就更方便了!底下我们列出一些常用的通配符喔:

通配符
clipboard.png

组合键
clipboard.png

用法:

[root@linux ~]# ls test*      <==那个 * 代表后面不论接几个字符都予以接受
[root@linux ~]# ls test?      <==那个 ? 代表后面『一定』要接『一个』字符
[root@linux ~]# ls test???    <==那个 ??? 代表『一定要接三个』字符!
[root@linux ~]# cp test[1-5] /tmp
# 将 test1, test2, test3, test4, test5 若存在的话,就拷贝到 /tmp 
[root@linux ~]# cp test[!1-5] /tmp
# 只要不是 test1, test2, test3, test4, test5 之外的其他 test? ,
# 若存在的话,就拷贝到 /tmp 
[root@linux ~]# cd /lib/modules/`uname -r`/kernel/drivers
# 被 ` ` 括起来的内容『会先执行』

四、数据流重导向

数据流重导向 (redirect) 由字面上的意思来看,好像就是将『数据给他传导到其他地方去』的样子? 呵呵!是啊是啊!没错~数据流重导向就是将某个指令执行后应该要出现在屏幕上的数据, 给他传输到其他的地方,例如档案或者是装置 (例如打印机之类的!)!这玩意儿在 Linux 的文本模式底下可重要的! 尤其是如果我们想要将某些数据储存下来时,就更有用了!

1、什么是数据流重导向

好家伙!什么是数据流重导向啊?这得要由指令的执行结果谈起! 一般来说,如果你要执行一个指令,通常他会是这样的:
我们执行一个指令的时候,这个指令可能会由档案读入资料,经过处理之后,再将数据输出到屏幕上。 在图三当中, standard output 与 standard error 分别代表标准输出与标准错误输出, 这两个玩意儿默认都是输出到屏幕上面来的啊!举个简单例子来说, 我们下达『 cat /etc/crontab /etc/vbirdsay 』这个指令时,cat 会由 /etc/crontab 与 /etc/vbirdsay 读入资料, 然后再将数据输出到屏幕上,不过,因为系统本来就不存在 /etc/vbirdsay 这个档案, 所以就会显示错误讯息,这个错误讯息也会输出到屏幕上来喔!

clipboard.png

在这样的过程当中,我们可以将 standard error (简称 stderr) 与 standard output (简称 stdout) 给他传送到其他不同的地方,而不是屏幕上头!传送的目标处,通常是档案或者是装置! 而传送的指令则是如下所示:

标准输入(stdin) :代码为 0 ,使用 < 或 << ;
标准输出(stdout):代码为 1 ,使用 > 或 >> ;
标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;

举例来说,如果我想要将我目前根目录下所有的目录都记录下来的话,也就是说,将 ls -l / 这个指令的输出结果储存下来,就可以:

[root@linux ~]# ls -l /  >  ~/rootfile
# 本来 ls -l / 会将根目录的数据列出到屏幕上;
# 现在我使用了 > ~/rootfile 后,则本来应该在屏幕上出现的数据
# 就会被『重新导向』到 ~/rootfile 档案内了!就可以将该数据储存!

此时,原本应该在屏幕上面出现的数据通通不见去~因为那些资料都被写入到 ~/rootfile 去了! 当然,那个档案的档名随便你取啦~如果你下达:『 cat ~/rootfile 』就可以看到原本应该在屏幕上面的数据啰。 那么如果我再次下达:『 ls -l /home > ~/rootfile 』后,那么那个 ~/rootfile 档案的内容变成什么? 呵呵!变成『仅有 ls -l /home 的数据』而已!咦!原本的 ls -l / 数据就不见了吗?是的! 因为该档案的建立方式是:

  1. 该档案 (本例中是 ~/rootfile) 若不存在,系统会自动的将他建立起来,但是,
  2. 当这个档案存在的时候,那么系统就会先将这个档案内容清空,然后再将数据写入!
  3. 也就是若以 > 输出到一个既存盘案中,呵呵,那个档案就会被覆盖掉啰!

那如果我想要将数据累加,不想要将旧的数据删除,那该如何是好? 呵呵!就利用 >> 就好啦!例如上面的例子中,就变成『ls -l / >> ~/rootfile』 如此一来,当 ~/rootfile 不存在时,系统会主动建立这个档案,若该档案已存在, 则数据会在该档案的最下方累加进去!基本上,指令的下达方式:

    
               >
              1>
command       2>    装置或档案
              2>>
              <    

当然啦,一串指令的最左边一定是指令,而在 >,2>,< 右边的,必须是档案或装置才行! 此外,那个 > 会等于 1> ,因为 standard output 代码是 1 ,可以省略啦! 再者, 1 与 > 之间并没有空格喔!是紧接在一起的!注意注意!我们底下来玩几个东西好了:

范例一:将目前目录下的档案信息全部储存到 list.txt 档案中
[root@linux ~]# ls -al > list.txt

范例二:将根目录下的数据也储存到 list.txt 档案中
[root@linux ~]# ls -al / >> list.txt

好了,对于『 > , >> 』这两个东西有一定的概念之后,我们来深入的谈一谈『数据流重导向』的观念吧! 如前所述,基本上, Linux 执行的结果中,可以约略的分成『正确输出』与『错误输出』两种数据。 例如,当你以一般身份执行 find 这个指令时,例如执行『 find / -name testing 』时,由于你是一般身份,又有些文件夹是不允许一般身份者进入的, 所以啰,当你使用 find 时,就会有错误讯息发生了!但同时如果有 testing 这个档案在你可以进入的文件夹当中,那么屏幕也会输出到给你看!因此, 就具有正确的与错误的输出两种啰!(分别称为 Stdout 与 Stderror)例如下面为执行结果: 里面的『 find: /home/root: Permission denied 』就告诉你该文件夹你没有权限进入, 这就是错误的输出了,那么『 /home/dmtsai/tseting 』就是正确的输出了!

[dmtsai@linux ~]$ find /home -name testing
find: /home/test1: Permission denied   <== Starndard error
find: /home/root: Permission denied    <== Starndard error
find: /home/masda: Permission denied   <== Starndard error
/home/dmtsai/testing                   <== Starndard output

好了,那么假如我们想要将数据输出到 list 这个档案中呢?执行『 find / -name testing > list 』 会有什么结果?呵呵,你会发现 list 里面存了刚刚那个『正确』的输出数据, 至于屏幕上还是会有错误的讯息出现呢!伤脑筋!如果想要将正确的与错误的数据分别存入不同的档案中需要怎么做?! 呵呵!其实在数据的重导向方面,正确的写法应该是『 1> 』与『 2> 』才对!但是如果只有 > 则预设是以 1> 来进行数据的!那个 1> 是输出正确数据, 2> 则是错误数据输出项目。也就是说:

1> :是将正确的数据输出到指定的地方去
2> :是将错误的数据输出到指定的地方去

好了,那么上面的例子中,我们如何将数据输出到不同的地方去呢?可以这么写:


[dmtsai@linux ~]$ find /home -name testing > list_right 2> list_error

这样一来,刚刚执行的结果中,有 Permission 的那几行错误信息都会跑到 list_error 这个档案中,至于正确的输出数据则会存到 list_right 这个档案中啰!这样可以了解了吗? 如果有点混乱的话,去休息一下再来看看吧!!

再来,如果我只要正确的数据,错误的信息我不要了呢?呵呵,这个时候 /dev/null 这个垃圾桶就很重要了!/dev/null 是什么呢? 基本上,那就有点像是一个『黑洞』的垃圾桶功能!当你输入的任何东西导向到这个虚拟的垃圾桶装置时, 『他就会凭空消失不见了~~』,这个东西有用的很!例如上面的例子中,我们可以这么做,来将错误的信息丢掉!

[dmtsai@linux ~]$ find /home -name testing > list_right 2> /dev/null

很神奇呦! error message 就会『不见了!』呵呵!真高兴!另外, 如果我要将数据都写到同一个档案中呢?这个时候写法需要用到特殊写法,请注意底下的写法呦!

[dmtsai@linux ~]$ find /home -name testing > list 2> list  <==错误写法
[dmtsai@linux ~]$ find /home -name testing > list 2>&1     <==正确写法

请特别留意这一点呢!同时写入同一个档案需要使用 2>&1 才对呦!

OK!了解了 >, 2>, >> 与 /dev/null 之后,那么那个 < 又是什么呀!?呵呵!以最简单的说法来说, 那就是『将原本需要由键盘输入的数据,经由档案来读入』的意思。 举例来说,我们可以使用 cat 在键盘上面输入一些数据,然后写入一个档案内,例如:

[root@linux ~]# cat > catfile
testing
cat file test
<==这里按下 [ctrl]+d 结束输入来离开!

此时就会有 catfile 这个档案产生,而且该档案的内容就是刚刚输入的内容喔。 那么,我是否可以使用其他档案来取代键盘输入呢?可以啊!这样做!

[root@linux ~]# cat > catfile < somefile

我可以先编辑 somefile ,然后再以上述的指令来将数据输出到 catfile 去呢!这样可以理解了吗? 能够理解 < 之后,再来则是怪可怕一把的 << 这个连续两个小于的符号了~ 他代表的是『结束的输入字符』的意思!举例来讲:『我要用 cat 直接将输入的讯息输出到 catfile 中, 且当输入 eof 时,该次输入就结束』,那我可以这样做:

[root@linux ~]# cat > catfile <<eof
> This is a test testing
> OK now stop
> eof  <==输入这个玩意儿,嘿!立刻就结束了!

看到了吗?利用 << 右侧的控制字符,我们可以终止一次输入, 而不必输入 [crtl]+d 来结束哩!这对程序写作很有帮助喔!好了,那么为何要使用命令输出重导向呢? 这个问题一定会困扰你一下下的,如果你从来都没有写过 script 的话!好了,我们来说一说吧!

1.当屏幕输出的信息很重要,而且我们需要将他存下来的时候;
2.背景执行中的程序,不希望他干扰屏幕正常的输出结果时;
3.一些系统的例行命令(例如写在 /etc/crontab 中的档案)的执行结果,希望他可以存下来时;
4.一些执行命令,我们已经知道他可能的错误讯息,所以想以『 2> /dev/null 』将他丢掉时;
5.错误讯息与正确讯息需要分别输出时。

当然还有很多很多的功能的,最简单的就是网友们常常问到的:『 为何我的 root 都会收到系统 crontab 寄来的错误讯息呢』这个咚咚是常见的错误, 而如果我们已经知道这个错误讯息是可以忽略的时候,嗯!『 2> errorfile 』这个功能就很重要了吧! 了解了吗??

小结: > 表示覆盖 >>表示累加

五、命令执行的判断依据: ; , &&, ||

在某些时候,我们希望可以一次执行多个指令,例如关机时,希望我可以先执行两次 sync ,然后才 shutdown 计算机,那么可以怎么作呢?这样做呀:

[root@linux ~]# sync; sync; shutdown -h now

在指令与指令中间利用分号 (;) 来隔开,这样一来,分号前的指令执行完后, 就会立刻接着执行后面的指令了。

// 当前面的指令执行结果为正确 (例如:仅有 standard output 时),就可以接着执行后续的指令, 否则就予以略过
[root@linux ~]# ls /vbird && touch /vbird/test

// 如果我想要当某个档案不存在时,就去建立那个档案, 否则就略过呢?很简单啊~可以这样做:
[root@linux ~]# ls /tmp/vbirding || touch /tmp/vbirding

由于指令是一个接着一个去执行的,因此,如果真要使用判断, 那么这个 && 与 || 的顺序就不能搞错~一般来说,判断式最多会有三个,也就是:

command1 && command2 || command3

六、管线命令 (pipe)

就如同前面所说的, bash 命令执行的时候有输出的数据会出现! 那么如果这群数据必需要经过几道手续之后才能得到我们所想要的格式,应该如何来设定? 这就牵涉到管线命令的问题了 (pipe) ,管线命令使用的是『 | 』这个界定符号! 另外,管线命令与『连续下达命令』是不一样的呦! 这点底下我们会再说明。底下我们先举一个例子来说明一下简单的管线命令。

假设我们想要知道 /etc/ 底下有多少档案,那么可以利用 ls /etc 来查阅,不过, 因为 /etc 底下的档案太多,导致一口气就将屏幕塞满了~不知道前面输出的内容是啥?此时,我们可以透过 less 指令的协助,利用:

[root@linux ~]# ls -al /etc | less

七、Shell scripts

1、什么是Shell scripts

什么是 shell script (程序化脚本) 呢?就字面上的意义,我们将他分为两部份。 在『 shell 』部分,我们在 十一章的 BASH 当中已经提过了,那是一个文字介面底下让我们与系统沟通的一个工具介面。那么『 script 』是啥? 字面上的意义, script 是『脚本、剧本』的意思。整句话是说, shell script 是针对 shell 所写的『剧本!』。

Shell script 是利用 shell 的功能所写的一个『程序 (program)』,这个程序是使用纯文字档,将一些 shell 的语法与命令(含外部命令)写在里面, 搭配正规表示法、管线命令与数据流重导向等功能,以达到我们所想要的处理目的。

2、为什么学习Shell scripts

  1. 自动化管理的重要依据:
  2. 追踪与管理系统的重要工作:
  3. 简单入侵侦测功能:
  4. 连续命令单一化:
  5. 简易的数据处理:
  6. 跨平台支持与学习历程较短:

3、第一个script的编写及运行

script其实就是多个命令的批处理,要下达命令就要注意以下事项:

1.命令的运行是从上而下、从左而右的分析与运行;
2.命令的下达就如同第五章内提到的: 命令、选项与参数间的多个空白都会被忽略掉;
3.空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空白键;
4.如果读取到一个 Enter 符号 (CR) ,就尝试开始运行该行 (或该串) 命令;
5.至於如果一行的内容太多,则可以使用『 [Enter] 』来延伸至下一行;
6.『 # 』可做为注解!任何加在 # 后面的数据将全部被视为注解文字而被忽略!

运行script:

直接命令下达: shell.sh 文件必须要具备可读与可运行 (rx) 的权限,然后:
绝对路径:使用 /home/dmtsai/shell.sh 来下达命令;
相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来运行
变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/

以 bash 程序来运行:透过『 bash shell.sh 』或『 sh shell.sh 』来运行

4、撰写第一支 script

[root@www ~]# mkdir scripts; cd scripts
[root@www scripts]# vi sh01.sh
#!/bin/bash
# Program:
#       This program shows "Hello World!" in your screen.
# History:
# 2005/08/23    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0

clipboard.png
注:请将所有撰写的 script 放置到你家目录的 ~/scripts 这个目录内, 未来比较好管理啦!

脚本说明:

  1. 第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:
    因为我们使用的是 bash ,所以,必须要以『 #!/bin/bash 』来宣告这个文件内的语法使用 bash 的语法!那么当这个程序被运行时,他就能够加载 bash 的相关环境配置档 (一般来说就是 non-login shell 的 ~/.bashrc), 并且运行 bash 来使我们底下的命令能够运行!这很重要的!(在很多状况中,如果没有配置好这一行, 那么该程序很可能会无法运行,因为系统可能无法判断该程序需要使用什么 shell 来运行啊!)
  2. 程序内容的说明:
    整个 script 当中,除了第一行的『 #! 』是用来宣告 shell 的之外,其他的 # 都是『注解』用途! 所以上面的程序当中,第二行以下就是用来说明整个程序的基本数据。一般来说, 建议你一定要养成说明该 script 的:1. 内容与功能; 2. 版本资讯; 3. 作者与联络方式; 4. 建档日期;5. 历史纪录 等等。这将有助於未来程序的改写与 debug 呢!
  3. 主要环境变量的宣告:
    建议务必要将一些重要的环境变量配置好,鸟哥个人认为, PATH 与 LANG (如果有使用到输出相关的资讯时) 是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部命令,而不必写绝对路径呢!比较好啦!
  4. 主要程序部分
  5. 运行成果告知 (定义回传值)
    是否记得我们在第十一章里面要讨论一个命令的运行成功与否,可以使用 $? 这个变量来观察~ 那么我们也可以利用 exit 这个命令来让程序中断,并且回传一个数值给系统。 在我们这个例子当中,鸟哥使用 exit 0 ,这代表离开 script 并且回传一个 0 给系统, 所以我运行完这个 script 后,若接著下达 echo $? 则可得到 0 的值喔! 更聪明的读者应该也知道了,呵呵!利用这个 exit n (n 是数字) 的功能,我们还可以自订错误信息, 让这支程序变得更加的 smart 呢!

运行结果:

clipboard.png

或者你也可以利用:『chmod a+x sh01.sh; ./sh01.sh』来运行这个 script 的呢!

5、撰写 shell script 的良好习惯创建

建议你一定要养成良好的 script 撰写习惯,在每个 script 的档头处记录好:

  • script 的功能;
  • script 的版本资讯;
  • script 的作者与联络方式;
  • script 的版权宣告方式;
  • script 的 History (历史纪录);
  • script 内较特殊的命令,使用『绝对路径』的方式来下达;
  • script 运行时需要的环境变量预先宣告与配置。

并发测试
使用curl模拟并发请求网页

test.sh


#!/bin/bash

#betin time
begin=$(date +%s)

#批量处理
count=100
for(( i = 0; i < $count; i++ ))
do
{
  /usr/bin/curl http://172.18.82.21/xampps/web/login_test/index.php
}&
done

#结束时间
end=$(date +%s)
spend=$(expr $end - $begin)
echo "花费时间为$spend秒"

执行脚本

// 执行脚本
> bash test.sh

Corwien
6.3k 声望1.6k 粉丝

为者常成,行者常至。