Friday, January 23, 2009

rather than, other than, instead of

rather than : 比起什么更什么,一般都是更喜欢什么.
other than : 除了这个之外
instead of : 以什么什么代替什么什么

Examples :
1 I prefer milk rather than soymilk.
2 As for lunch, other than Tuesday, anyday works for me.
3 He stormed right into my room instead of knocking the door.

Wednesday, January 21, 2009

Writing Makefiles

The information that tells make how to recompile a system comes from reading a data base called the makefile.

  1. Makefile Contents: What makefiles contain.
  2. Makefile Names: How to name your makefile.
  3. Include: How one makefile can use another makefile.
  4. MAKEFILES Variable: The environment can specify extra makefiles.
  5. MAKEFILE_LIST Variable: Discover which makefiles have been read.
  6. Special Variables: Other special variables.
  7. Remaking Makefiles: How makefiles get remade.
  8. Overriding Makefiles: How to override part of one makefile with another makefile.
  9. Reading Makefiles: How makefiles are parsed.
  10. Secondary Expansion: How and when secondary expansion is performed.

1 What makefiles containtents

Makefiles contain five kinds of things:
  • An explicit rule targets, prerequisites, commands
  • An implicit rule It describes how a target may depend on a file with a name similar to the target and gives commands to create or update such a target.
  • A variable definition
  • A directive
    1. Reading another makefile (include)
    2. Deciding (based on the values of variables) whether to use or ignore a part of the makefile
  • '#' starts comments

2 What Name to Give Your Makefile

By default, when make looks for the makefile, it tries the following names, in order: GNUmakefile, makefile and Makefile. Other make programs look for makefile and Makefile, but not GNUmakefile.

If you want to use a nonstandard name for your makefile, you can specify the makefile name with the `-f' or `--file' option. If you use more than one `-f' or `--file' option, you can specify several makefiles. All the makefiles are effectively concatenated in the order specified. The default makefile names GNUmakefile, makefile and Makefile are not checked automatically if you specify `-f' or `--file'.

3 Including Other Makefiles

The directive is a line in the makefile that looks like this:
     include filenames... [comment starting with `#' is allowed at the end of the line. ]
For example, if you have three .mk files, a.mk, b.mk, and c.mk, and $(bar) expands to bish bash, then the following expression
     include foo *.mk $(bar)
is equivalent to
     include foo a.mk b.mk c.mk bish bash
One occasion for using include directives is when several programs, handled by individual makefiles in various directories, need to use a common set of variable definitions (see Setting Variables) or pattern rules (see Defining and Redefining Pattern Rules).
Another such occasion is when you want to generate prerequisites from source files automatically; the prerequisites can be put in a file that is included by the main makefile. This practice is generally cleaner than that of somehow appending the prerequisites to the end of the main makefile as has been traditionally done with other versions of make. See Automatic Prerequisites.
If the specified name does not start with a slash, and the file is not found in the current directory, several other directories are searched. First, any directories you have specified with the `-I' or `--include-dir' option are searched (see Summary of Options). Then the following directories (if they exist) are searched, in this order: prefix/include (normally /usr/local/include 1), /usr/gnu/include, /usr/local/include, /usr/include.
If an included makefile cannot be found in any of these directories, a warning message is generated, but it is not an immediately fatal error; processing of the makefile containing the include continues. Once it has finished reading makefiles, make will try to remake any that are out of date or don't exist. See How Makefiles Are Remade. Only after it has tried to find a way to remake a makefile and failed, will make diagnose the missing makefile as a fatal error.
If you want make to simply ignore a makefile which does not exist and cannot be remade, with no error message, use the -include directive instead of include, like this:
  -include filenames...
This acts like include in every way except that there is no error (not even a warning) if any of the filenames do not exist. For compatibility with some other make implementations, sinclude is another name for -include.

4 The Variable MAKEFILES

If the environment variable MAKEFILES is defined, make considers its value as a list of names (separated by whitespace) of additional makefiles to be read before the others.

5 The Variable MAKEFILE_LIST

As make reads various makefiles, including any obtained from the MAKEFILES variable, the command line, the default files, or from include directives, their names will be automatically appended to the MAKEFILE_LIST variable. They are added right before make begins to parse them.
This means that if the first thing a makefile does is to examine the last word in this variable, it will be the name of the current makefile. Once the current makefile has used include, however, the last word will be the just-included makefile.
If a makefile named Makefile has this content:
     name1 := $(lastword $(MAKEFILE_LIST))



    include inc.mk



    name2 := $(lastword $(MAKEFILE_LIST))



    all:

          @echo name1 = $(name1)

          @echo name2 = $(name2)
then you would expect to see this output:
     name1 = Makefile
     name2 = inc.mk

6 Other Special Variables

DEFAULT_GOAL MAKE_RESTARTS VARIABLES FEATURES INCLUDE_DIRS




Tuesday, January 20, 2009

A sample Makefile

edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

main.o : main.c defs.h
cc -c main.c

kbd.o : kbd.c defs.h command.h
cc -c kbd.c

command.o : command.c defs.h command.h
cc -c command.c

display.o : display.c defs.h buffer.h
cc -c display.c

insert.o : insert.c defs.h buffer.h
cc -c insert.c

search.o : search.c defs.h buffer.h

cc -c search.c

files.o : files.c defs.h buffer.h command.h

cc -c files.c

utils.o : utils.c defs.h

cc -c utils.c

clean :

rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

Above, a backslash-newline is used to split a long line into two lines.
To use this Makefile to create the executable file called edit, type : make.
If typing make clean, the executable file edit and all the object files in the directory will be deleted.

The targets include the executable file edit and the object files *.o.
The prerequisites include *.c and *.h.
In fact, each *.o file is both a target and a prerequisite.
commands include cc -c main.c and cc -c kbd.c.
The target clean is not a file, but merely the name of an action, called Phony Targets. clean is not a prerequisite of any other rule. Consequently, make never does anything with it unless you tell it specifically. So the purpose of the rule is to run the specified commands.

Rules

A Makefile consists of rules with the following shape:

target ... : prerequisites ...
command
...
...
A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out.

A prerequisite is a file that is used as input to create the target. A target depends on several files.

A command is an action that make carries out. A rule may have more than one command, each on its own line. Note: you need to put a tab character at the beginning of every command line.

Monday, January 19, 2009

延伸正规表示法

from linux-vbird
事实上,一般读者只要了解基础型的正规表示法大概就已经相当足够了,不过,某些时刻, 为了要简化整个指令操作,了解一下使用范围更广的延伸型正规表示法的表示式,会更方便呢! 举个简单的例子好了,在上节的例题三的最后一个例子中,我们要去除空白行与行首为 # 的行列, 使用的是
    grep -v '^$' regular_express.txt | grep -v '^#'
需要使用到管线命令来搜寻两次! 那么如果使用延伸型的正规表示法,我们可以简化为:
    egrep -v '^$|^#' regular_express.txt
利用支持延伸型正规表示法的 egrep 与特殊字符 | 来区隔两组字符串,如此一来,是否方便很多呢?

这里必须要特别强调, grep 支持的是基础型的正规表示法,而 egrep 支持延伸正规表示法。 事实上, egrep 是 grep -E 的命令别名,为了方便使用,我们还是以 egrep 来跟 grep 区分吧!

熟悉了正规表示法之后,到这个延伸型的正规表示法,您应该也会想到, 不就是多几个重要的特殊符号吗? ^_^y 是的~所以,我们就直接来说明一下,延伸型正规表示法有哪几个特殊符号?

RE 字符意义与范例
+重复『一个或一个以上』的前一个 RE 字符
范例:egrep -n 'go+d' regular_express.txt
搜寻 (god) (good) (goood)... 等等的字符串。 那个 o+ 代表『一个以上的 o 』所以,上面的执行成果会将第 1, 9, 13 行列出来。
?『零个或一个』的前一个 RE 字符
范例:egrep -n 'go?d' regular_express.txt
搜寻 (gd) (god) 这两个字符串。 那个 o? 代表『空的或 1 个 o 』所以,上面的执行成果会将第 13, 14 行列出来。
有没有发现到,这两个案例( 'go+d' 与 'go?d' )的结果集合与 'go*d' 相同? 想想看,这是为什么喔! ^_^
|用或( or )的方式找出数个字符串
范例:egrep -n 'gd|good' regular_express.txt
搜寻 gd good 这两个字符串,注意,是『或』! 所以,第 1,9,14 这三行都可以被打印出来喔!那如果还想要找出 dog 呢?就这样啊:
egrep -n 'gd|good|dog' regular_express.txt
( )找出『群组』字符串
范例:egrep -n 'g(la|oo)d' regular_express.txt
搜寻 (glad) 或 (good) 这两个字符串,因为 g 与 d 是重复的,所以, 我就可以将 la 与 oo 列于 ( ) 当中,并以 | 来分隔开来,就可以啦!
此外,这个功能还可以用来作为『多个重复群组』的判别喔!举例来说:
echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C'
上面的例子当中,意思是说,我要找开头是 A 结尾是 C ,中间有一个以上的 "xyz" 字符串的意思~


以上这些就是延伸型的正规表示法的特殊字符。另外,要特别强调的是,那个 ! 在正规表示法当中并不是特殊字符, 所以,如果您想要查出来档案中含有 ! 与 > 的字行时,可以这样:
    grep -n '[!>]' regular_express.txt
这样可以了解了吗?!常常看到有陷阱的题目写:『反向选择这样对否? '[!a-z]'?』, 呵呵!是错的呦~要 '[^a-z] 才是对的!


大标题的图示格式化打印: printf
在很多时候,我们可能需要将输出的数据给他格式化输出的~ 举例来说,考试卷分数的输出,姓名与科目及分数之间,总是可以稍微作个比较漂亮的版面配置吧? 例如我想要输出底下的样式:
Name     Chinese   English   Math    Average

DmTsai        80        60     92      77.33

VBird         75        55     80      70.00

Ken           60        90     70      73.33
分成五个字段,各个字段分配到正确的位置去!但是因为每个字段的原始数据其实并非是如此固定的, 而我就是想要如此表示出这些数据,此时,就得需要打印格式管理员 printf 的帮忙了! printf 可以帮我们将资料输出的结果格式化,而且而支持一些特殊的字符~底下我们就来看看!
[root@linux ~]# printf '打印格式' 实际内容

参数:

关于格式方面的几个特殊样式:

     \a    警告声音输出

     \b    退格键(backspace)

     \f    清除屏幕 (form feed)

     \n    输出新的一行

     \r    亦即 Enter 按键

     \t    水平的 [tab] 按键

     \v    垂直的 [tabl] 按键

     \xNN  NN 为两位数的数字,可以转换数字成为字符。

关于 C 程序语言内,常见的变数格式

     %ns   那个 n 是数字, s 代表 string ,亦即多少个字符;

     %ni   那个 n 是数字, i 代表 integer ,亦即多少整数字数;

     %N.nf 那个 n 与 N 都是数字, f 代表 floating (浮点),如果有小数字数,

           假设我共要十个位数,但小数点有两位,即为 %10.2f 啰!

范例:



范例一:将刚刚上头的数据变成档案,仅列出姓名与成绩:(用 [tab] 分隔

[root@linux ~]# printf '%s\t %s\t %s\t %s\t %s\t \n' `cat printf.txt`

Name     Chinese         English         Math    Average

DmTsai   80      60      92      77.33

VBird    75      55      80      70.00

Ken      60      90      70      73.33

# 假设我将上面的档案存成 printf.txt 档案档名,则可利用上面的案例,

# 将每个单字中间以 [tab] 按键隔开。由上面的输出来看,虽然第二行以后是 OK 的,

# 但是第一行则因为某些单字长度较长,所以就无法对齐了!而 %s 表示以字符串 (string)

# 的方式来展现该内容。而每个内容则以 \t 即 [tab] 来隔开啊!



范例二:将上述资料关于第二行以后,分别以字符串、整数、小数点来显示:

[root@linux ~]# printf '%10s %5i %5i %5i %8.2f \n' `cat printf.txt |\

> grep -v Name`

  DmTsai    80    60    92    77.33

   VBird    75    55    80    70.00

     Ken    60    90    70    73.33

# 这个时候的输出可就有趣了!我将几个内容分成不同的数据格式来输出,

# 最有趣的应该是 %8.2f 这个项目了!我可以针对不同的小数字数来进行格式输出,

# 例如变成底下的样子时,您自己试看看,会是输出什么结果喔!

# printf '%10s %5i %5i %5i %8.1f \n' `cat printf.txt | grep -v Name`



范例三:列出数值 45 代表的字符为何?

[root@linux ~]# printf '\x45\n'

E

# 这东西也很好玩~他可以将数值转换成为字符,如果您会写 script 的话,

# 可以自行测试一下,由 20~80 之间的数值代表的字符是啥喔! ^_^ 
printf 的使用相当的广泛喔!包括等一下后面会提到的 awk 以及在 C 程序语言当中使用的屏幕输出, 都是利用 printf 呢!鸟哥这里也只是列出一些可能会用到的格式而已, 有兴趣的话,可以自行多作一些测试与练习喔! ^_^
Tips:
打印格式化这个 printf 指令,乍看之下好像也没有什么很重要的~ 不过,如果您需要自行撰写一些软件,需要将一些数据在屏幕上头漂漂亮亮的输出的话, 那么 printf 可也是一个很棒的工具喔!

大标题的图示sed 工具简介
在了解了一些正规表示法的基础应用之后,再来呢?呵呵~两个东西可以玩一玩的,那就是 sed 跟 awk 了! 这两个家伙可是相当的有用的啊!举例来说,鸟哥写的 logfile.sh 分析登录文件的小程序, 绝大部分分析关键词的取用、统计等等,就是用这两个宝贝蛋来帮我完成的! 那么你说,要不要玩一玩啊?! ^_^

我们先来谈一谈 sed 好了,基本上, sed 可以分析 Standard Input (STDIN) 的数据, 然后将数据经过处理后,再将他输出到 standrad out (STDOUT) 的一个工具。 至于处理呢?可以进行取代、删除、新增、撷取特定行等等的功能呢!很不错吧~ 我们先来了解一下 sed 的用法,再来聊他的用途好了!
[root@linux ~]# sed [-nefr] [动作]

参数:

-n  :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN

    的数据一般都会被列出到屏幕上。但如果加上 -n 参数后,则只有经过

    sed 特殊处理的那一行(或者动作)才会被列出来。

-e  :直接在指令列模式上进行 sed 的动作编辑;

-f  :直接将 sed 的动作写在一个档案内, -f filename 则可以执行 filename 内的

    sed 动作;

-r  :sed 的动作支持的是延伸型正规表示法的语法。(预设是基础正规表示法语法)

-i  :直接修改读取的档案内容,而不是由屏幕输出。



动作说明:  [n1[,n2]]function

n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作

       是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』



function 有底下这些咚咚:

a   :新增, a 的后面可以接字符串,而这些字符串会在新的一行出现(目前的下一行)~

c   :取代, c 的后面可以接字符串,这些字符串可以取代 n1,n2 之间的行!

d   :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;

i   :插入, i 的后面可以接字符串,而这些字符串会在新的一行出现(目前的上一行);

p   :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运作~

s   :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配

    正规表示法!例如 1,20s/old/new/g 就是啦!

范例:



范例一:将 /etc/passwd 的内容列出,并且我需要打印行号,同时,请将第 2~5 行删除!

[root@linux ~]# nl /etc/passwd | sed '2,5d'

   1  root:x:0:0:root:/root:/bin/bash

   6  sync:x:5:0:sync:/sbin:/bin/sync

   7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

.....(后面省略).....

# 看到了吧?因为 2-5 行给他删除了,所以显示的数据中,就没有 2-5 行啰~

# 另外,注意一下,原本应该是要下达 sed -e 才对,没有 -e 也行啦!

# 同时也要注意的是, sed 后面接的动作,请务必以 '' 两个单引号括住喔!

# 而,如果只要删除第 2 行,可以使用 nl /etc/passwd | sed '2d' 来达成,

# 至于第 3 到最后一行,则是 nl /etc/passwd | sed '3,$d' 的啦! 



范例二:承上题,在第二行后(亦即是加在第三行)加上『drink tea?』字样!

[root@linux ~]# nl /etc/passwd | sed '2a drink tea'

   1  root:x:0:0:root:/root:/bin/bash

   2  bin:x:1:1:bin:/bin:/sbin/nologin

drink tea

   3  daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 嘿嘿!在 a 后面加上的字符串就已将出现在第二行后面啰!那如果是要在第二行前呢?

# nl /etc/passwd | sed '2i drink tea' 就对啦!



范例三:在第二行后面加入两行字,例如『Drink tea or .....』『drink beer?』

[root@linux ~]# nl /etc/passwd | sed '2a Drink tea or ......\

> drink beer ?'

   1  root:x:0:0:root:/root:/bin/bash

   2  bin:x:1:1:bin:/bin:/sbin/nologin

Drink tea or ......

drink beer ?

   3  daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 这个范例的重点是,我们可以新增不只一行喔!可以新增好几行~

# 但是每一行之间都必须要以反斜线 \ 来进行新行的增加喔!所以,上面的例子中,

# 我们可以发现在第一行的最后面就有 \ 存在啦!那是一定要的喔!



范例四:我想将第2-5行的内容取代成为『No 2-5 number』呢?

[root@linux ~]# nl /etc/passwd | sed '2,5c No 2-5 number'

   1  root:x:0:0:root:/root:/bin/bash

No 2-5 number

   6  sync:x:5:0:sync:/sbin:/bin/sync

# 没有了 2-5 行,嘿嘿嘿嘿!我们要的数据就出现啦!



范例五:仅列出第 5-7 行

[root@linux ~]# nl /etc/passwd | sed -n '5,7p'

   5  lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

   6  sync:x:5:0:sync:/sbin:/bin/sync

   7  shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

# 为什么要加 -n 的参数呢?您可以自行下达 sed '5,7p' 就知道了!(5-7行会重复输出)

# 有没有加上 -n 的参数时,输出的数据可是差很多的喔!



范例六:我们可以使用 ifconfig 来列出 IP ,若仅要 eth0 的 IP 时?

[root@linux ~]# ifconfig eth0

eth0      Link encap:Ethernet  HWaddr 00:51:FD:52:9A:CA

          inet addr:192.168.1.12  Bcast:192.168.1.255  Mask:255.255.255.0

        inet6 addr: fe80::250:fcff:fe22:9acb/64 Scope:Link

        UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

.....(以下省略).....

# 其实,我们要的只是那个 inet addr:..那一行而已,所以啰,利用 grep 与 sed 来捉

[root@linux ~]# ifconfig eth0 | grep 'inet ' | sed 's/^.*addr://g' | \

> sed 's/Bcast.*$//g'

# 您可以将每个管线 (|) 的过程都分开来执行,就会晓得原因啰!

# 去头去尾之后,就会得到我们所需要的 IP 亦即是 192.168.1.12 啰~



范例七:将 /etc/man.config 档案的内容中,有 MAN 的设定就取出来,但不要说明内容。

[root@linux ~]# cat /etc/man.config | grep 'MAN'| sed 's/#.*$//g' | \

> sed '/^$/d'

# 每一行当中,若有 # 表示该行为批注,但是要注意的是,有时候,

# 批注并不是写在第一个字符,亦即是写在某个指令后方,如底下的模样:

# 『shutdown -h now # 这个是关机的指令』,批注 # 就在指令的后方了。

# 因此,我们才会使用到将 #.*$ 这个正规表示法!



范例八:利用 sed 直接在 ~/.bashrc 最后一行加入『# This is a test』

[root@linux ~]# sed -i '$a # This is a test'  ~/.bashrc

# 上头的 -i 参数可以让你的 sed 直接去修改后面接的档案内容喔!而不是由屏幕输出。

# 至于那个 $a  则代表最后一行才新增的意思。 
总之,这个 sed 不错用啦!而且很多的 shell script 都会使用到这个指令的功能~ sed 可以帮助系统管理员管理好日常的工作喔!要仔细的学习呢!


大标题的图示awk 工具简介
相较于 sed 常常作用于一整个行的处理, awk 则比较倾向于一行当中分成数个『字段』来处理。 因此,awk 相当的适合处理小型的数据数据处理呢!awk 通常运作的模式是这样的:
[root@linux ~]# awk '条件类型1{动作1} 条件类型2{动作2} ...' filename 
awk 可以处理后续接的档案,也可以读取来自前个指令的 standard output 。 但如前面说的, awk 主要是处理『每一行的字段内的数据』,而预设的『字段的分隔符为 "空格键" 或 "[tab]键" 』!举例来说,我们用 last 可以将登入者的数据取出来, 结果如下所示:
[root@linux ~]# last

dmtsai   pts/0        192.168.1.12     Mon Aug 22 09:40   still logged in

root     tty1                          Mon Aug 15 11:38 - 11:39  (00:01)

reboot   system boot  2.6.11           Sun Aug 14 18:18         (7+15:41)

dmtsai   pts/0        192.168.1.12     Fri Aug 12 12:07 - 12:08  (00:01)
若我想要取出账号与登入者的 IP ,且账号与 IP 之间以 [tab] 隔开,则会变成这样:
[root@linux ~]# last | awk '{print $1 "\t" $3}'

dmtsai  192.168.1.12

root    Mon

reboot  boot

dmtsai  192.168.1.12
因为不论哪一行我都要处理,因此,就不需要有 "条件类型" 的限制!我所想要的是第一栏以及第三栏, 但是,第二行及第三行的内容怪怪的~这是因为数据格式的问题啊!所以啰~使用 awk 的时候,请先确认一下您的数据当中,如果是连续性的数据,请不要有空格或 [tab] 在内,否则,就会像这个例子这样,会发生误判喔!

另外,由上面这个例子您也会知道,在每一行的每个字段都是有变量名称的,那就是 $1, $2... 等变量名称,以上面的例子来说, dmtsai 是 $1 ,因为他是第一栏嘛!至于 192.168.1.12 是第三栏, 所以他就是 $3 啦!后面以此类推~呵呵!还有个变数喔!那就是 $0 ,$0 代表『一整列资料』的意思~ 以上面的例子来说,第一行的 $0 代表的就是『dmtsai pts/0.... 』那一行啊! 由此可知,刚刚上面四行当中,整个 awk 的处理流程是:
  1. 读入第一行,并将第一行的资料填入 $0, $1, $2.... 等变数当中;
  2. 依据 "条件类型" 的限制,判断是否需要进行后面的 "动作";
  3. 做完所有的动作与条件类型;
  4. 若还有后续的『行』的数据,则重复上面 1~3 的步骤,直到所有的数据都读完为止。
经过这样的步骤,您会晓得, awk 是『以行为一次处理的单位』, 而『以字段为最小的处理单位』。好了,那么 awk 怎么知道我到底这个数据有几行?有几栏呢?这就需要 awk 的内建变量的帮忙啦~

变量名称代表意义
NF每一行 ($0) 拥有的字段总数
NR目前 awk 所处理的是『第几行』数据
FS目前的分隔字符,预设是空格键

我们继续以上面例子来做说明,如果我想要列出每一行的账号,并且列出目前处理的行数, 并且说明,该行有多少字段,则可以这样 (注意, awk 后续的所有动作以 ' 括住, 所以,内容如果想要以 print 打印时,记得,非变量的文字部分,包含上一小节 printf 提到的格式中,都需要使用双引号来定义出来喔!)
[root@linux ~]# last | awk '{print $1 "\t lines: " NR "\t columes: " NF}'

dmtsai   lines: 1        columes: 10

root     lines: 2        columes: 9

reboot   lines: 3        columes: 9

dmtsai   lines: 4        columes: 10
这样可以了解 NR 与 NF 的差别了吧?好了,底下来谈一谈所谓的 "条件类型" 了吧!


小标题的图示awk 的逻辑运算字符
既然有需要用到 "条件" 的类别,自然就需要一些逻辑运算啰~例如底下这些:

运算单元代表意义
> 大于
< 小于
>= 大于或等于
<=小于或等于
== 等于
!= 不等于

值得注意的是那个 == 的符号,因为在『逻辑运算』上面, 就是所谓的大于、小于、等于等等的判断式上面,我们习惯上是以 == 来表示,而如果是直接给予一个值,例如变量设定时,就直接使用 = 而已。 好了,我们实际来运用一下逻辑判断吧!举例来说,在 /etc/passwd 当中是以冒号 ":" 来作为字段的分隔,那假设我要查阅,第三栏小于 10 以下的数据,并且仅列出账号与第三栏, 那么可以这样做:
[root@linux ~]# cat /etc/passwd | \

> awk '{FS=":"} $3 <>

root:x:0:0:root:/root:/bin/bash

bin      1

daemon   2

......(以下省略)...... 
有趣吧!不过,怎么第一行没有正确的显示出来呢?这是因为我们读入第一行的时候, 那些变数 $1, $2... 预设还是以空格键为分隔的,所以虽然我们定义了 FS=":" 了, 但是却仅能在第二行后才开始生效。那么怎么办呢?我们可以预先设定 awk 的变量啊! 利用 BEGIN 这个关键词喔!这样做:
[root@linux ~]# cat /etc/passwd | \

> awk 'BEGIN {FS=":"} $3 <>

root     0

bin      1

daemon   2

......(以下省略)...... 
很有趣吧!而除了 BEGIN 之外,我们还有 END 呢!另外,如果要用 awk 来进行『计算功能』呢?以底下的例子来看, 假设我有一个薪资数据表,内容是这样的:
Name    1st     2nd     3th

VBird   23000   24000   25000

DMTsai  21000   20000   23000

Bird2   43000   42000   41000
如何帮我计算每个人的总额呢?而且我还想要格式化输出喔! 你可以将上面的数据储存成一个名称为 pay.txt 的档案,则:
[root@linux ~]# cat pay.txt | \

> awk 'NR==1{printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total" }

NR>=2{total = $2 + $3 + $4

printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}'

    Name        1st        2nd        3th      Total

   VBird      23000      24000      25000   72000.00

  DMTsai      21000      20000      23000   64000.00

   Bird2      43000      42000      41000  126000.00
上面的例子有几个重要事项应该要先说明的:
  • 所有的动作,亦即在 {} 内的动作,如果有需要多个指令辅助时,可利用分号『;』间隔, 或者直接以 [Enter] 按键来隔开每个指令,例如上面的 NR>=2 后面接的动作, 利用 total = ... 那个指令来指定加总,而后续则以 printf 来格式化输出!
  • 逻辑运算当中,如果是『等于』的情况,则务必使用两个等号『==』!
  • 格式化输出时,在 printf 的格式设定当中,务必加上 \n ,才能进行分行!
  • 与 bash shell 的变量不同,在 awk 当中,变量可以直接使用,不需加上 $ 符号。
利用 awk 这个玩意儿,就可以帮我们处理很多日常工作了呢!真是好用的很~ 此外, awk 的输出格式当中,常常会以 printf 来辅助,所以, 最好您对 printf 也稍微熟悉一下比较好啦!另外, awk 的动作内 {} 也是支持 if (条件) 的喔! 举例来说,上面的指令可以修订成为这样:
[root@linux ~]# cat pay.txt | \

> awk '{if(NR==1) printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"}

NR>=2{total = $2 + $3 + $4

printf "%10s %10d %10d %10d %10.2f\n", $1, $2, $3, $4, total}' 
你可以仔细的比对一下上面两个输入有啥不同~从中去了解两种语法吧! 我个人是比较倾向于使用第一种语法,因为会比较有统一性啊! ^_^

除此之外, awk 还可以帮我们进行循环计算喔!真是相当的好用! 不过,那属于比较进阶的单独课程了,我们这里就不再多加介绍。如果您有兴趣的话, 可以到中研院的网站查询喔:http://phi.sinica.edu.tw/aspac/reports/94/94011/,鸟哥这里也有一份 pdf 档的备份: http://linux.vbird.org/linux_basic/0330regularex/awk.pdf。 您可以自行参阅一下该文章的内容,里头可以好好的查阅一下关于数组与循环方面的介绍, 我认为该文章写的很棒喔!该介绍的都介绍了!很好~我喜欢~ ^_^

大标题的图示文件数据比对与打印的相关功能
正规表示法是相当有用的工具,当然,那个 sed 还有 awk 也是很棒的工具程序, 不过,除此之外,我们其实还有很多可以使用的工作来处理文件数据喔! 举例来说,假如我有两个档案,一个档案是原始档,一个则是经过一些时间累积处理后的档案, 我想要知道这两个档案之间的差别,该如何运用正规表示法?呼呼~可能要透过所谓的循环来一行一行比对检查呢~ 但是,我们可以透过 Linux 提供的 diff 及 cmp 指令来进行比对即可喔!很棒的啊!


小标题的图示档案比对
什么时候会用到档案的比对啊?通常是『同一个软件包的不同版本之间,比较设定档与原始档的差异』, 所以啰,很多时候所谓的档案比对,通常是用在 ASCII 纯文字文件的比对上的! 那么比对档案的指令有哪些?最常见的就是 diff 啰!



  • diff

  • diff 就是用在比对两个档案之间的差异的,一般是用在 ASCII 纯文字文件的比对上。 我们先预处理一下一个档案好了。假设我要将 /etc/passwd 的内容,将第四行删除, 第六行则取代成为『no six line』,新的档案放置到 /tmp/test 里面, 那么应该怎么做?
    [root@linux ~]# mkdir -p /tmp/test
    
    [root@linux ~]# cat /etc/passwd | \
    
    > sed -e '4d' -e '6c no six line' > /tmp/test/passwd
    
    # 注意一下, sed 后面如果要接超过两个以上的动作时,每个动作前面得加 -e 才行! 
    接下来讨论一下关于 diff 的用法吧!
    [root@linux ~]# diff [-bBi] from-file to-file
    
    参数:
    
    from-file :一个档名,作为原始比对档案的档名;
    
    to-file   :一个档名,作为目的比对档案的档名;
    
    注意,from-file 或 to-file 可以 - 取代,那个 - 代表『Standard input』之意。
    
    
    
    -b  :忽略一行当中,仅有多个空白的差异(例如 "about me" 与 "about     me" 视为相同
    
    -B  :忽略空白行的差异。
    
    -i  :忽略大小写的不同。
    
    范例:
    
    
    
    范例一:比对 /tmp/test/passwd 与 /etc/passwd 的差异:
    
    [root@linux ~]# diff /etc/passwd /tmp/test/passwd
    
    4d3    <==这里是说,左边档案(/etc/passwd)第四行被删除 (d)
    
    < class="term_note"><==这里是说,左边档案的第六行被取代成右边档案(/tmp/test/passwd)的第五行

    <> no six line
    # 很聪明吧!用 diff 就把我们刚刚的处理给比对完毕了! 用 diff 比对档案真的是很简单喔!另外, diff 也可以比对整个目录下的差异喔! 举例来说,我们将两个目录比对一下:
    [root@linux ~]# diff /etc /tmp/test
    
    ......(前面省略).....
    
    Only in /etc: paper.config
    
    diff /etc/passwd /tmp/test/passwd
    
    4d3
    
    <> no six line
    
    Only in /etc: passwd-
    
    ......(后面省略)..... 
    我们的 diff 很聪明吧!还可以比对不同目录下的相同文件名的内容, 这样真的很方便喔~



  • cmp

  • 相对于 diff 的广泛用途, cmp 似乎就用的没有这么多了~ cmp 主要也是在比对两个档案,他主要利用『位』单位去比对,因此, 当然也可以比对 binary file 啰~(还是要再提醒喔, diff 主要是以『行』为单位比对, cmp 则是以『位』为单位去比对,这并不相同!)
    [root@linux ~]# cmp [-s] file1 file2
    
    参数:
    
    -s  :将所有的不同点的位处都列出来。因为 cmp 预设仅会输出第一个发现的不同点。
    
    范例:
    
    
    
    范例一:用 cmp 比较一下 /etc/passwd 与 /tmp/test/passwd 
    
    [root@linux ~]# cmp /etc/passwd /tmp/test/passwd
    
    /etc/passwd /tmp/test/passwd differ: byte 106, line 4
    
    看到了吗?第一个发现的不同点在第四行,而且位数是在第 106 个位处! 这个 cmp 也可以用来比对 binary 啦! ^_^



  • patch

  • patch 这个指令与 diff 可是有密不可分的关系啊!我们前面提到, diff 可以用来分辨两个版本之间的差异,举例来说,刚刚我们所建立的 /tmp/test/passwd 与 /etc/passwd 就是两个不同版本之间的档案。那么,如果要『升级』呢? 就是『将旧的档案升级成为新的档案』时,应该要怎么做呢? 举例来说,我们可以这样做测试:
    [root@linux ~]# mkdir /tmp/old; cp /etc/passwd /tmp/old
    
    [root@linux ~]# mkdir /tmp/new; cp /tmp/test/passwd /tmp/new
    
    [root@linux ~]# cd /tmp ; diff -Naur old/ new/ > test.patch 
    此时,在 /tmp/test.patch 档案之中,就记录了新旧的档案之间的差异, 对了!您必须要了解的是,用 diff 制作这个档案时,旧的档案必须是在前面,亦即是 diff oldfile newfile 才行喔!此外,新旧档案的『相对目录位置』最好也是一样比较好喔! OK!那么如何将旧的内容 (/tmp/old/passwd) 更新到新版 (/tmp/new/passwd) 的内容呢? 简单的说,可以用这样:
    [root@linux ~]# patch -pN <>
    
    参数:
    
    -p  :后面可以接『取消几层目录』的意思。
    
    范例:
    
    
    
    范例一:将刚刚制作出来的 patch file 用来更新旧版数据
    
    [root@linux ~]# cd /tmp/old
    
    [root@linux ~]# patch -p1 < /tmp/test.patch
    
    patching file passwd
    
    # 为什么这里会使用 -p1 呢?因为我们在比对新旧版的数据时,是在 /tmp 底下,
    
    # 而实际的数据是在 /tmp/old 里面,因此,当我们进入到 /tmp/old 时,
    
    # 再查阅 /tmp/test.patch 的第一行如下:
    
    # diff -Naur old/passwd new/passwd (用 head -n 1 /tmp/test.patch)
    
    # 发现到,我们所在的目录其实是 old 里面,所以,就必须要减去一层目录。 
    更详细的 patch 用法我们会在后续的第五章跟大家介绍, 这里仅是介绍给您,呵呵!我们可以利用 diff 来比对两个档案之间的差异, 更可进一步利用这个功能来制作修补档案 (patch file) ,让大家更容易进行比对与升级呢! 很不赖吧! ^_^

    小标题的图示档案打印准备: pr
    如果您曾经使用过一些图形接口的文字处理软件的话,那么很容易发现, 当我们在打印的时候,可以同时选择与设定每一页打印时的标头吧! 也可以设定页码呢!那么,如果我是在 Linux 底下打印纯文字文件呢 可不可以具有标题啊?可不可以加入页码啊?呵呵!当然可以啊! 使用 pr 就能够达到这个功能了。不过, pr 的参数实在太多了, 我也说不完,一般来说,我都仅使用最简单的方式来处理而已。 举例来说,如果想要打印 /etc/man.config 呢?
    [root@linux ~]# pr /etc/man.config
    
    
    
    
    
    2003-02-10 23:20                 /etc/man.config                  Page 1
    
    
    
    
    
    #
    
    # Generated automatically from man.conf.in by the
    
    # configure script.
    
    .....以下省略...... 
    上面特殊字体那一行呢,其实就是使用 pr 处理后所造成的标题啦~ 标题中会有『档案时间』、『档案档名』及『页码』三大项目。 更多的 pr 使用,请参考 pr 的说明啊! ^_^

    大标题的图示重点回顾
    • 使用 grep 或其它工具进行正规表示法的字符串比对时,因为编码的问题会有不同的状态,因此, 您最好将 LANG 及 LANGUAGE 等变量设定为 C 或者是 en 等英文语系!
    • 正规表示法 ( Regular Expression ) 的用途主要是用来做为『搜寻』字符串之用,还可以用来过滤特殊讯息等用途;
    • 由于严谨度的不同,正规表示法之上还有更严谨的延伸正规表示法;
    • 正规表示法的处理方式,经常是以『整行』或称为『整段』来进行处理的;
    • grep 与 egrep 在正规表示法里面是很常见的两支程序,其中, egrep 支持更严谨的正规表示法的语法;

    正规表示法 Regular Expression

    from linux-vbird

    基础正规表示法
    既然正规表示法是处理字符串的一个标准表示方式,他需要支持的工具程序来辅助, 所以,我们这里就先介绍一个最简单的字符串撷取功能的工具程序,那就是 grep 啰! 在介绍完 grep 的基本功能之后,就进入正规表示法的特殊字符的处理能力了。


    小标题的图示以 grep 撷取字符串
    既然要使用 grep 当然就得要先了解一下 grep 的语法啰~
    [root@test root]# grep [-acinv] '搜寻字符串' filename
    
    参数说明:
    
    -a :将 binary 档案以 text 档案的方式搜寻数据
    
    -c :计算找到 '搜寻字符串' 的次数
    
    -i :忽略大小写的不同,所以大小写视为相同
    
    -n :顺便输出行号
    
    -v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!
    
    范例:
    
    [root@test root]# grep 'root' /var/log/secure
    
    将 /var/log/secure 这个档案中有 root 的那一行秀出来
    
    
    
    [root@test root]# grep -v 'root' /var/log/secure
    
    若该行没有 root 才将数据秀出来到屏幕上!
    
    
    
    [root@test root]# last | grep root
    
    若该行有 root 才将数据秀出来到屏幕上! 
    grep 是一个很常见也很常用的指令,他最重要的功能就是进行字符串数据的比对, 然后将符合使用者需求的字符串打印出来。 需要说明的是『grep 在资料中查寻一个字符串时,是以 "整行" 为单位来进行数据的撷取的!』也就是说,假如一个档案内有 10 行,其中有两行具有你所搜寻的字符串,则将那两行显示在屏幕上,其它的就丢弃了!

    而 grep 除了可以进行档案的资料搜寻之外,也常常被应用在 input/output 的数据处理当中,例如常见的 管线命令 ( pipe ) 就可以常常见到他的踪影! 以上面表格中的例子来看,我们可以发现前两个例子是查寻档案的内容,有没有加上 -v 所显示出来的结果是『相反的!』,而第三个例子则是以 pipe 的功能进行数据的处理的喔!

    好了,我们就开始以 grep 来进行正规表示法的简易说明吧!我们先以底下这个档案来作为范例:
    [root@test root]# vi regular_express.txt
    "Open Source" is a good mechanism to develop programs.
    apple is my favorite food.
    Football game is not use feet only.
    this dress doesn't fit me.
    However, this dress is about $ 3183 dollars.
    GNU is free air not free beer.
    Her hair is very beauty.
    I can’t finish the test.
    Oh! The soup taste good.
    motorcycle is cheap than car.
    This window is clear.
    the symbol '*' is represented as start.
    Oh! My god!
    The gd software is a library for drafting programs.
    You are the best is mean you are the no. 1.
    The world is the same with "glad".
    I like dog.
    google is the best tools for search keyword.
    goooooogle yes!
    go! go! Let's go.
    # I am VBird
    需要特别注意的是,上面这个档案鸟哥是在 Windows 的环境下编辑的, 并且经过特殊处理过,因此,他虽然是纯文字文件,但是内含一些 Windows 环境下的软件常常自行加入的一些特殊字符,例如断行字符(^M)就是一例! 所以,您可以直接将上面的文字以 vi 储存成 regular_express.txt 这个档案, 不过,比较建议直接点底下的连结下载:
    此外,因为不同的语系编码是不一样的,所以,您必须要将语系改成英文语系, 才能够进行底下的测试,否则,可能会有显示的内容与底下的输出不符的状况喔! 修改语系的方法为:
    [root@test root]# LANG=en
    
    [root@test root]# export LANG 
    好了,现在开始我们一个案例一个案例的来介绍吧!
    • 例题一、搜寻特定字符串:
      搜寻特定字符串很简单吧?假设我们要从刚刚的档案当中取得 the 这个特定字符串, 最简单的方式就是这样:
      [root@test root]# grep -n 'the' regular_express.txt
      
      8:I can't finish the test.
      
      12:the symbol '*' is represented as start.
      
      15:You are the best is mean you are the no. 1.
      
      16:The world  is the same with "glad".
      
      18:google is the best tools for search keyword.
      
      那如果想要『反向选择』呢?也就是说,当该行没有 'the' 这个字符串时,才显示在屏幕上,那就直接使用:
      [root@test root]# grep -vn 'the' regular_express.txt 
      您会发现,屏幕上出现的行列为除了 8,12,15,16,18 五行之外的其它行列! 接下来,如果您想要取得不论大小写的 the 这个字符串,则:
      [root@test root]# grep -in 'the' regular_express.txt
      
      8:I can't finish the test.
      
      9:Oh! The soup taste good.
      
      12:the symbol '*' is represented as start.
      
      14:The gd software is a library for drafting programs.
      
      15:You are the best is mean you are the no. 1.
      
      16:The world  is the same with "glad".
      
      18:google is the best tools for search keyword.
      

    • 例题二、利用 [] 来搜寻集合字符
      如果我想要搜寻 test 或 taste 这两个单字时,可以发现到,其实她们有共通的 't?st' 存在~这个时候,我可以这样来搜寻:
      [root@test root]# grep -n 't[ae]st' regular_express.txt
      
      8:I can't finish the test.
      
      9:Oh! The soup taste good.
      
      了解了吧?其实 [] 里面不论有几个字符,他都谨代表某『一个』字符, 所以,上面的例子说明了,我需要的字符串是『tast』或『test』两个字符串而已! 而如果想要搜寻到有 oo 的字符时,则使用:
      [root@test root]# grep -n 'oo' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      2:apple is my favorite food.
      
      3:Football game is not use feet only.
      
      9:Oh! The soup taste good.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      但是,如果我不想要 oo 前面有 g 的话呢?此时,可以利用在集合字符的反向选择 [^] 来达成
      [root@test root]# grep -n '[^g]oo' regular_express.txt
      
      2:apple is my favorite food.
      
      3:Football game is not use feet only.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      意思就是说,我需要的是 oo ,但是 oo 前面不能是 g 就是了! 仔细比较上面两个表格,妳会发现,第 1,9 行不见了,因为 oo 前面出现了 g 所致! 第 2,3 行没有疑问,因为 foo 与 Foo 均可被接受!但是第 18 行明明有 google 的 goo 啊~ 别忘记了,因为该行后面出现了 tool 的 too 啊!所以该行也被列出来~ 也就是说, 18 行里面虽然出现了我们所不要的项目 (goo) 但是由于有需要的项目 (too) , 因此,是符合字符串搜寻的喔!

      至于第 19 行,同样的,因为 goooooogle 里面的 oo 前面可能是 o ,例如: go(ooo)oogle ,所以,这一行也是符合需求的!

      再来,假设我 oo 前面不想要有小写字符,所以,我可以这样写 [^abcd....z]oo , 但是这样似乎不怎么方便,由于小写字符的 ASCII 上编码的顺序是连续的, 因此,我们可以将之简化为底下这样:
      [root@test root]# grep -n '[^a-z]oo' regular_express.txt
      
      3:Football game is not use feet only.
      
      也就是说,当我们在一组集合字符中,如果该字符组是连续的,例如大写英文/小写英文/数字等等, 就可以使用[a-z],[A-Z],[0-9]等方式来书写,那么如果我们的要求字符串是数字与英文呢? 呵呵!就将他全部写在一起,变成:[a-zA-Z0-9]

      例如,我们要取得有数字的那一行,就这样:
      [root@test root]# grep -n '[0-9]' regular_express.txt
      
      5:However, this dress is about $ 3183 dollars.
      
      15:You are the best is mean you are the no. 1.
      
      这样对于 [] 以及 [^] 以及 [] 当中的 - 有了解了吗?! ^_^y

    • 例题三、行首与行尾字符 ^ $:
      我们在例题一当中,可以查询到一行字符串里面有 the 的,那如果我想要让 the 只在行首列出呢? 这个时候就得要使用定位字符了!我们可以这样做:
      [root@test root]# grep -n '^the' regular_express.txt
      
      12:the symbol '*' is represented as start.
      
      此时,就只剩下第 12 行,因为只有第 12 行的行首是 the 开头啊~此外, 如果我想要开头是小写字符的那一行就列出呢?可以这样:
      [root@test root]# grep -n '^[a-z]' regular_express.txt
      
      2:apple is my favorite food.
      
      4:this dress doesn't fit me.
      
      10:motorcycle is cheap than car.
      
      12:the symbol '*' is represented as start.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      如果我不想要开头是英文字母,则可以是这样:
      [root@test root]# grep -n '^[^a-zA-Z]' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      20:# I am VBird
      
      注意到了吧?那个 ^ 符号,在字符集合符号(括号[])之内与之外是不同的! 在 [] 内代表『反向选择』,在 [] 之外则代表定位在行首的意义!要分清楚喔!

      那如果我想要找出来,行尾结束为小数点 (.) 的那一行,该如何处理:
      [root@test root]# grep -n '\.$' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      2:apple is my favorite food.
      
      3:Football game is not use feet only.
      
      4:this dress doesn't fit me.
      
      10:motorcycle is cheap than car.
      
      11:This window is clear.
      
      12:the symbol '*' is represented as start.
      
      15:You are the best is mean you are the no. 1.
      
      16:The world  is the same with "glad".
      
      17:I like dog.
      
      18:google is the best tools for search keyword. 
      特别注意到,因为小数点具有其它意义(底下会介绍),所以必须要使用跳脱字符(\)来加以解除其特殊意义! 不过,您或许会觉得奇怪,但是第 5~9 行最后面也是 . 啊~怎么无法打印出来?? 这里就牵涉到 Windows 平台的软件对于断行字符的判断问题了!我们使用 cat -A 将第五行拿出来看, 您会发现:
      [root@test root]# cat -A regular_express.txt
      
      However, this dress is about $ 3183 dollars.^M$
      
      注意到了没?最后面的断行字符应该是 $ 才对,但是,因为 Windows 的 nodepad 会主动加上 ^M 作为断行的判断,因此,那个 . 自然就不是紧接在 $ 之前喔!这样可以了解 ^ 与 $ 的意义吗? 好了,先不要看底下的解答,自己想一想,那么如果我想要找出来,哪一行是『空白行』, 也就是说,该行并没有输入任何数据,该如何搜寻??
      [root@test root]# grep -n '^$' regular_express.txt
      
      21:
      
      因为只有行首跟行尾( ^$ ),所以,这样就可以找出空白行啦!再来, 假设您已经知道在一个批次脚本 (shell script) 或者是设定档当中, 空白行与开头为 # 的那一行是批注,因此如果您要将数据列出给别人参考时, 可以将这些数据省略掉,以节省保贵的纸张,那么,您可以怎么作呢? 我们以 /etc/syslog.conf 这个档案来作范例,您可以自行参考一下输出的结果:
      [root@test root]# cat /etc/syslog.conf
      
      [root@test root]# grep -v '^$' /etc/syslog.conf | grep -v '^#' 
      是否节省很多版面啊??

    • 例题四、任意一个字符 . 与重复字符 *
      在 bash 的章节当中,我们知道万用字符 * 可以用来代表任意(0或多个)字符, 但是正规表示法并不是万用字符,两者之间是不相同的! 至于正规表示法当中的『 . 』则代表『绝对有一个任意字符』的意思!这样讲不好懂, 我们直接做个练习吧!假设我需要找出 g??d 的字符串,亦即共有四个字符, 起头是 g 而结束是 d ,我可以这样做:
      [root@test root]# grep -n 'g..d' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      9:Oh! The soup taste good.
      
      16:The world  is the same with "glad".
      
      因为强调 g 与 d 之间一定要存在两个字符,因此,第 13 行的 god 与第 14 行的 gd 就不会被列出来啦!再来,如果我想要列出有 oo, ooo, oooo 等等的资料, 也就是说,至少要有两个 o 以上,该如何是好??是 o* 还是 oo* 还是 ooo* 呢? 虽然您可以试看看结果, 不过结果太占版面了 @_@ ,所以,我这里就直接说明。

      因为 * 代表的是『重复 0 个或多个前面的 RE 字符』的意义, 因此,『o*』代表的是:『拥有空字符或一个 o 以上的字符』, 特别注意,因为允许空字符(就是有没有字符都可以的意思),因此, grep -n 'o*' regular_express.txt 将会把所有的数据都打印出来屏幕上!

      那如果是『oo*』呢?则第一个 o 肯定必须要存在,第二个 o 则是可有可无的多个 o , 所以,凡是含有 o, oo, ooo, oooo 等等,都可以被列出来~

      同理,当我们需要『至少两个 o 以上的字符串』时,就需要 ooo* ,亦即是:
      [root@test root]# grep -n 'ooo*' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      2:apple is my favorite food.
      
      3:Football game is not use feet only.
      
      9:Oh! The soup taste good.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      这样理解 * 的意义了吗?!好了,现在出个练习,如果我想要字符串开头与结尾都是 g, 但是两个 g 之间仅能存在至少一个 o ,亦即是 gog, goog, gooog.... 等等, 那该如何?
      [root@test root]# grep -n 'goo*g' regular_express.txt
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      如此了解了吗?好,再来一题,如果我想要找出 g 开头与 g 结尾的字符串, 当中的字符可有可无,那该如何是好?是『g*g』吗?
      [root@test root]# grep -n 'g*g' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      3:Football game is not use feet only.
      
      9:Oh! The soup taste good.
      
      13:Oh!  My god!
      
      14:The gd software is a library for drafting programs.
      
      16:The world  is the same with "glad".
      
      17:I like dog.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      但测试的结果竟然出现这么多行??太诡异了吧? 其实一点也不诡异,因为 g*g 里面的 g* 代表『空字符或一个以上的 g』 在加上后面的 g ,因此,整个 RE 的内容就是 g, gg, ggg, gggg , 因此,只要该行当中拥有一个以上的 g 就符合所需了!

      那该如何得到我们的 g....g 的需求呢?呵呵!就利用任意一个字符『.』啊! 亦即是:『g.*g』的作法,因为 * 可以是 0 或多个重复前面的字符,而 . 是任意字符,所以: 『.* 就代表零个或多个任意字符』的意思啦!
      [root@test root]# grep -n 'g.*g' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      14:The gd software is a library for drafting programs.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      因为是代表 g 开头与 g 结尾,中间任意字符均可接受,所以,第 1 与第 14 行是可接受的喔! 这个 .* 的 RE 表示任意字符是很常见的,希望大家能够理解并且熟悉!

      再出一题,如果我想要找出『任意数字』的行列呢?因为仅有数字,所以就成为:
      [root@test root]# grep -n '[0-9][0-9]*' regular_express.txt
      
      5:However, this dress is about $ 3183 dollars.
      
      15:You are the best is mean you are the no. 1.
      
      虽然使用 grep -n '[0-9]' regular_express.txt 也可以得到相同的结果, 但鸟哥希望大家能够理解上面指令当中 RE 表示法的意义才好!


    • 例题五、限定连续 RE 字符范围 {}
      在上个例题当中,我们可以利用 . 与 RE 字符及 * 来设定 0 个到无线多个重复字符, 那如果我想要限制一个范围区间内的重复字符数呢?举例来说,我想要找出两个到五个 o 的连续字符串,该如何作?这时候就得要使用到限定范围的字符 {} 了。 但因为 { 与 } 的符号在 shell 是有特殊意义的,因此, 我们必须要使用跳脱字符 \ 来让他失去特殊意义才行。

      至于 {} 的语法是这样的,假设我要找到两个 o 的字符串,可以是:
      [root@test root]# grep -n 'o\{2\}' regular_express.txt
      
      1:"Open Source" is a good mechanism to develop programs.
      
      2:apple is my favorite food.
      
      3:Football game is not use feet only.
      
      9:Oh! The soup taste good.
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      这样看似乎与 ooo* 的字符没有什么差异啊?因为第 19 行有多个 o 依旧也出现了! 好,那么换个搜寻的字符串,假设我们要找出 g 后面接 2 到 5 个 o ,然后再接一个 g 的字符串, 他会是这样:
      [root@test root]# grep -n 'go\{2,5\}g' regular_express.txt
      
      18:google is the best tools for search keyword.
      
      嗯!很好!第 19 行终于没有被取用了(因为 19 行有 6 个 o 啊!)。 那么,如果我想要的是 2 个 o 以上的 goooo....g 呢?除了可以是 gooo*g ,也可以是:
      [root@test root]# grep -n 'go\{2,\}g' regular_express.txt
      
      18:google is the best tools for search keyword.
      
      19:goooooogle yes!
      
      呵呵!就可以找出来啦~

    小标题的图示重要特殊字符(characters)
    经过了上面的几个简单的范例,我们可以将基础的正规表示法特殊字符汇整如下:

    RE 字符意义与范例
    ^word待搜寻的字符串(word)在行首!
    范例:grep -n '^#' regular_express.txt
    搜寻行首为 # 开始的那一行!
    word$待搜寻的字符串(word)在行尾!
    范例:grep -n '!$' regular_express.txt
    将行尾为 ! 的那一行打印出来!
    .代表『任意一个』字符,一定是一个任意字符!
    范例:grep -n 'e.e' regular_express.txt
    搜寻的字符串可以是 (eve) (eae) (eee) (e e), 但不能仅有 (ee) !亦即 e 与 e 中间『一定』仅有一个字符,而空格符也是字符!
    \跳脱字符,将特殊符号的特殊意义去除!
    范例:grep -n \' regular_express.txt
    搜寻含有单引号 ' 的那一行!
    *重复零个或多个的前一个 RE 字符
    范例:grep -n 'ess*' regular_express.txt
    找出含有 (es) (ess) (esss) 等等的字符串,注意,因为 * 可以是 0 个,所以 es 也是符合带搜寻字符串。另外,因为 * 为重复『前一个 RE 字符』的符号, 因此,在 * 之前必须要紧接着一个 RE 字符喔!例如任意字符则为 『.*』 !
    \{n,m\}连续 n 到 m 个的『前一个 RE 字符』
    若为 \{n\} 则是连续 n 个的前一个 RE 字符,
    若是 \{n,\} 则是连续 n 个以上的前一个 RE 字符!
    范例:grep -n 'go\{2,3\}g' regular_express.txt
    在 g 与 g 之间有 2 个到 3 个的 o 存在的字符串,亦即 (goog)(gooog)
    []字符集合的 RE 特殊字符的符号
    [list]
    范例:grep -n 'g[ld]' regular_express.txt
    搜寻含有 (gl) 或 (gd) 的那一行~
    需要特别留意的是,在 [] 当中『谨代表一个待搜寻的字符』,
    例如: a[afl]y 代表搜寻的字符串可以是 aay, afy, aly
    亦即 [afl] 代表 a 或 f 或 l 的意思!


    [ch1-ch2]
    范例:grep -n '[0-9]' regular_express.txt
    搜寻含有任意数字的那一行!需特别留意,在字符集合 [] 中的减号 - 是有特殊意义的,他代表两个字符之间的所有连续字符!但这个连续与否与 ASCII 编码有关, 因此,您的编码需要设定正确(在 bash 当中,需要确定 LANG 与 LANGUAGE 的变量是否正确!) 例如所有大写字符则为 [A-Z]

    [^]
    范例:grep -n 'oo[^t]' regular_express.txt
    搜寻的字符串可以是 (oog) (ood) 但不能是 (oot) ,那个 ^ 在 [] 内时, 代表的意义是『反向选择』的意思~例如,我不要大写字符,则为 [^A-Z] ~ 但是,需要特别注意的是,如果以 grep -n [^A-Z] regular_express.txt 来搜寻, 却发现该档案内的所有行都被列出,为什么?因为这个 [^A-Z] 是『非大写字符』的意思, 因为每一行均有非大写字符,例如第一行的 "Open Source" 就有 p,e,n,o.... 等等的小写字符, 以及双引号 (") 等字符,所以当然符合 [^A-Z] 的搜寻!

    请特别留意的是,『正规表示法的特殊字符』 与一般在指令列输入指令的『万用字符』并不相同, 例如,在万用字符当中,* 代表的是 0 ~ 无限多个字符的意思,但是在正规表示法当中, * 则是重复 0 到多个的前一个 RE 字符的意思~使用的意义并不相同,不要搞混了! (鸟哥我一开始摸正规表示法时就很容易搞混!因为这里是新手最容易搞错的地方,特别小心啊!)

    举例来说,不支持正规表示法的 ls 这个工具中,若我们使用 『ls -l * 』 代表的是任意档名的档案,而 『ls -l a* 』代表的是以 a 为开头的任何档名的档案, 但在正规表示法中,我们要找到含有以 a 为开头的档案,则必须要这样:(需搭配支持正规表示法的工具)
      ls | grep -n '^a.*'
    另外,例如万用字符的反向选择,为 [!range] ,至于正规表示法则是 [^range] 。 这样是否了解正规表示法与万用字符的差异啦??

    管线命令 ( pipe )

      假设我们要读取 last 这个指令中,那个 root 登入的『次数』应该怎么作?注意呦!我们只需要『次数』。那么我所进行的步骤是:
      1. 执行 last ,将所有这个月的所有人登入数据取出来;
      2. 使用 grep 将上面的输出数据(stdout)当中的 root 撷取出来,其它的不要;
      3. 使用 wc 这个可以计算行数的指令将上一步的数据计算行数!
      由于 last 的输出是一行代表一次登入,所以只要计算几行就代表登入几次的意思,所以啰!经由上面三个步骤,将 last 数据逐步的筛选,就可以得到我们的数据了!整个命令可以写成如下:
       
      [test @test bin]# last
      [test @test bin]# last | grep root
      [test @test bin]# last | grep root | wc -l

      你可以分别执行『 last 』然后再逐步增加为『 last | grep root 』,最后到上面那一行,那么就马上可以清楚的知道为何会这么做啰!这个管线命令『 | 』仅能处理经由前面一个指令传来的正确信息,也就是 standard output ( STDOUT ) 的信息,对于 stdandard error 并没有直接处理的能力,请记得。那么整体的管线命令可以使用下图表示之:
       
    在每个管线的部分都是『指令』呢!而后一个指令的输入乃是由前一个指令的输出而来的!底下我们来谈一谈一些基本的管线命令指令介绍:
    cut
    语法
    [root @test /root ]# cut -d "分隔字符" [-cf] fields
    参数说明:
    -d :后面接的是用来分隔的字符,预设是『空格符』
    -c :后面接的是『第几个字符』
    -f :后面接的是第几个区块?
    范例:
    [root @test /root]# cat /etc/passwd | cut -d ":" -f 1
    将 passwd 这个档案里面,每一行里头的 : 用来作为分隔号,
    而列出第一个区块!也就是姓名所在啦!
    [root @test /root]# last | cut -d " " -f1
    以空格符为分隔,并列出第一个区间!
    [root @test /root]# last | cut -c1-20
    将 last 之后的数据,每一行的 1-20 个字符取出来!
    说明
    这个 cut 实在很好用!不过,说真的,除非你常常在分析 log 档案,否则使用到 cut 的机会并不多!好了! cut 主要的用途在于将『同一行里面的数据进行分解!』,最常使用在分析一些数据或文字数据的时候!这是因为有时候我们会以某些字符当作分割的参数,然后来将数据加以切割,以取得我们所需要的数据。我也很常使用这个功能呢!尤其是在分析 log 档案的时候!


  • sort

  • 语法
    [root @test /root ]# sort [-t 分隔符] [(+起始)(-结束)] [-nru]
    参数说明:
    -t 分隔符:使用分隔符来隔开不同区间,预设是 tab
    +start -end:由第 start 区间排序到 end 区间
    -n :使用『纯数字』排序(否则就会以文字型态来排序)
    -r :反向排序
    -u :相同出现的一行,只列出一次!
    范例:
    [root @test /root]# cat /etc/passwd | sort
    将列出来的个人账号排序!
    [root @test /root]# cat /etc/passwd | sort -t: +2n
    将个人账号中,以使用者 ID 来排序(以 : 来分隔,第三个为 ID ,
    但第一个代号为 0 之故)
    [root @test /root]# cat /etc/passwd | sort -t: +2nr
    反相排序啰!
    说明
    sort 同样是很常用的指令呢!因为我们常常需要比较一些信息啦!举个上面的第二个例子来说好了!今天假设你有很多的账号,而且你想要知道最大的使用者 ID 目前到哪一号了!呵呵!使用 sort 一下子就可以知道答案咯!当然其使用还不止此啦!有空的话不妨玩一玩!
    wc
    语法
    [root @test /root ]# wc [-lmw]
    参数说明:
    -l :多少行
    -m :多少字符
    -w :多少字?
    范例:
    [root @test /root]# cat /etc/passwd | wc -l
    这个档案里头有多少行?
    [root @test /root]# cat /etc/passwd | wc -w
    这个档案里头有多少字!?
    说明
    wc 也可以当作指令?呵呵!这可不是上洗手间的 WC 呢!这是相当有用的计算档案内容的一个工具组喔!举个例子来说,当你要知道目前你的账号档案中有多少个账号时,就使用上面的 wc -l 啦!因为 /etc/passwd 里头一行代表一个使用者呀!所以知道行数就晓得有多少的账号在里头了!而如果要计算一个档案里头有多少个字符时,呵呵!就使用 wc -w 这个参数吧!

    • uniq

    • 语法
      [root @test /root ]# uniq
      参数说明:
      范例:
      [root @test /root]# last | cut -d" " -f1 | sort | uniq
      说明
      这个指令用来将『重复的行删除掉只显示一个』,举个例子来说,你要知道这个月份登入你主机的使用者有谁,而不在乎他的登入次数,那么就使用上面的 范例,(1)先将所有的数据列出;(2)再将人名独立出来;(3)经过排序;(4)只显示一个!由于这个指令是在将重复的东西减少,所以当然需要『配合排 序过的档案』来处理啰!

    tee
    语法
    [root @test /root ]# last | tee last.list | cut -d " " -f1
    参数说明:
    范例:
    [root @test /root]# last | tee last.list | cut -d " " -f1
    说明
    有没有发现在命令重导向的时候,如果我们要将数据送出到档案的时候,屏幕上就不会出现任何的数据!那么如果我们需要将数据同时显示在屏幕上跟档案中呢?呵呵!这个时候就需要 tee 这个指令啰!使用 last 可以查看到这个月份的登入数据,而使用了 tee 之后,会将数据同时传给下一个命令去执行,也会将数据写入 last.list 这个档案中!也是个好帮手!

      • tr

      • 语法
        [root @test /root ]# tr [-ds] SET1
        参数说明:
        -d :删除 SET1 这个字符串
        -s :取代掉重复的字符!
        范例:
        [root @test /root]# last | tr '[a-z]' '[A-Z]' <==将小写改成大写
        [root @test /root]# cat /etc/passwd | tr -d : <==嘿嘿! : 这个符号在 /etc/passwd 中不见了!
        [root @test /root]# cat /home/test/dostxt | tr -d '\r' > dostxt-noM <==将 DOS 档案的字尾符号 ^M 的符号去除!
        说明
        其实这个指令也可以写在『正规表示法』里头!因为他也是由正规表示法的方式来取代数据的!以上面的例子来说,使用 [] 可以设定一串字呢!也常常用来取代档案中的怪异符号!例如上面第三个例子当中,可以去除 DOS 档案留下来的 ^M 这个断行的符号!这东西相当的有用!相信处理 Linux & Windows 系统中的人们最麻烦的一件事就是这个事情啦!亦即是 DOS 底下会自动的在每行行尾加入 ^M 这个断行符号!这个时候我们可以使用这个 tr 来将 ^M 去除! ^M 可以使用 \r 来代替之!
    split
    语法
    [root @test /root ]# split [-bl] 输入档案 输出档案前导字符
    参数说明:
    -b :以档案 size 来分
    -l :以行数来分
    范例:
    [root @test /root]# split -l 5 /etc/passwd test <==会产生 testaa, testab, testac... 等等的档案
    说明
    在 Windows 的情况下,你要将档案分割需要如何作?!伤脑筋吧!呵呵!在 Linux 底下就简单的多了!你要将档案分割的话,那么就使用 -b size 来将一个分割的档案限制其大小,如果是行数的话,那么就使用 -l line 来分割!好用的很!如此一来,你就可以轻易的将你的档案分割成 floppy 的大小,方便你 copy 啰!
      管线命令在 bash 的连续的处理程序中是相当重要的!另外,在 log file 的分析当中也是相当重要的一环,所以请特别留意!好嘛!?
    ----from linux-vbird
    bird


    命令重导向

      基本上,这个子题是 bash 相当重要的观念,这里可得花点心思才行呦!
      • 什么是『重导向, redirect ?』:简单的说,就是将你目前的所得资料转到其它地方去就是了!例如我们常用的,将目前的屏幕输出数据转到档案中去,就可以这么写:『ls -l / > test 』,那个大于的符号『 > 』就是将输出结果导向到 test 这个档案中的意思啰!这个时候:
        • 如果你执行『 ls -l / 』而已的话,屏幕会将根目录的档案与目录都列出在屏幕上;
        • 但是当使用 > 导向到 test 这个档案中时,则屏幕不会显示任何讯息,但是会将刚刚你执行的结果输出到 test 这个档案中。
        所以啰,你只要『 vi test 』一下,就会知道 test 这个档案中记录了刚刚我们执行的数据结果啰!不过,这里需要特别留意的是,当你使用 > 符号将数据由屏幕导向到档案中时,则:
        • 该档案(就是 test )若不存在,系统会自动的将他建立起来,但是,
        • 当这个档案存在的时候,那么系统就会先将这个档案内容清空,然后再将数据写入!
        • 也就是若以 > 输出到一个既存盘案中,呵呵,那个档案就会被覆盖掉啰
      除了这个 > 的符号之外,在 bash 命令执行的过程中,主要有三种输出入的状况,分别是:
      1. 标准输入;代码为 0 ;或称为 stdin ;使用的方式为 <
      2. 标准输出:代码为 1 ;或称为 stdout;使用的方式为 1>
      3. 错误输出:代码为 2 ;或称为 stderr;使用的方式为 2>
      基本的指令书写方式为:
      指令
      1>
      1>>
      2>
      2>>
      <
      装置或档案
      左边一定是指令,至于右边则可能是装置或者是档案!注意了!那个 1> 与 2> 之间并没有空格符!而相关的使用说明可以举例如下:
      [test @test test]# ls -al > list.txt
      将显示的结果输出到 list.txt 档案中,若该档案以存在则予以取代!
      [test @test test]# ls -al >> list.txt
      将显示的结果累加到 list.txt 档案中,该档案为累加的,旧数据保留!
      [test @test test]# ls -al 1> list.txt 2> list.err
      将显示的数据,正确的输出到 list.txt 错误的数据输出到 list.err
      [test @test test]# ls -al 1> list.txt 2>&1
      将显示的数据,不论正确或错误均输出到 list.txt 当中!
      [test @test test]# ls -al 1> list.txt 2> /dev/null
      将显示的数据,正确的输出到 list.txt 错误的数据则予以丢弃!
      注意!错误与正确档案输出到同一个档案中,则必须以上面的方法来写!
      不能写成其它格式!

      这个观念相当的重要,尤其是在 /etc/crontab 当中执行的时候,如果我们已经知道错误的讯息为何,又不想要让错误的讯息一直填满 root 的信箱,就必须以 2> 搭配 /dev/null 这个垃圾桶黑洞装置,来将数据丢弃!这个相当的重要!
      这里我们来说明一下命令重导向里面几个常用的符号与装置:
      • < :由 < 的右边读入参数档案;
      • > :将原本由屏幕输出的正确数据输出到 > 右边的 file ( 文件名称 ) 或 device ( 装置,如 printer )去;
      • >> :将原本由屏幕输出的正确数据输出到 >> 右边,与 > 不同的是,该档案将不会被覆盖,而新的数据将以『增加的方式』增加到该档案的最后面;
      • 2> :将原本应该由屏幕输出的错误数据输出到 2> 的右边去。
      • /dev/null :可以说成是黑洞装置!
    好了,对于『 > , >> 』这两个东西有一定的概念之后,我们来深入的谈一谈『命令输出重导向』的观念吧!如前所述,基本上, Linux 执行的结果中,可以约略的分成『正确输出』与『错误输出』两种方式。例如,当你以一般身份执行 find 这个指令时,例如执行『 find / -name testing 』时,由于你是一般身份,又有些数据夹是不允许一般身份者进入的,所以啰,当你使用 find 时,就会有错误讯息发生了!但同时如果有 testing 这个档案在你可以进入的资料夹当中,那么屏幕也会输出到给你看!因此,就具有正确的与错误的输出两种啰!(分别称为 Stdout 与 Stderror)例如下面为执行结果:里面的『 find: /home/root: Permission denied 』就告诉你该数据夹你没有权限进入,这就是错误的输出了,那么『 /home/test/tseting 』就是正确的输出了!
    [test @test test]# find / -name testing
    find: /home/test1: Permission denied <==这是错误的输出
    find: /home/root: Permission denied <==这是错误的输出
    find: /home/masda: Permission denied <==这是错误的输出
    /home/test/testing <==这是『正确』的输出
    [test @test test]#
    好了,那么假如我们想要将数据输出到 list 这个档案中呢?执行『 find / -name testing > list 』会有什么结果?呵呵,你会发现 list 里面存了刚刚那个『正确』的输出数据,至于屏幕上还是会有错误的讯息出现呢!伤脑筋!如果想要将正确的与错误的数据分别存入不同的档案中需要怎么做?!呵 呵!其实在数据的重导向方面,正确的写法应该是『 1> 』与『 2> 』才对!但是如果只有 > 则预设是以 1> 来进行数据的!那个 1> 是输出正确数据, 2> 则是错误数据输出项目。也就是说:
    • 1> :是将正确的数据输出到指定的地方去
    • 2> :是将错误的数据输出到指定的地方去
    好了,那么上面的例子中,我们如何将数据输出到不同的地方去呢?可以这么写:
    [test @test test]# find / -name testing 1> list_right 2> list_error
    这样一来,刚刚执行的结果中,有 Permission 的那几行错误信息都会跑到 list_error 这个档案中,至于正确的输出数据则会存到 list_right 这个档案中啰!这样可以了解了吗?如果有点混乱的话,去休息一下再来看看吧!!
      再来,如果我只要正确的数据,错误的信息我不要了呢?呵呵,这个时候 /dev/null 这个垃圾桶就很重要了!/dev/null 是什么呢?基本上,那就有点像是一个『黑洞』的垃圾桶功能!当你输入的任何东西导向到这个虚拟的垃圾桶装置时,『他就会凭空消失不见了~~』,这个东西有用的很!例如上面的例子中,我们可以这么做,来将错误的信息丢掉!
      [test @test test]# find / -name testing 1> list_right 2> /dev/null
      很神奇呦! error message 就会『不见了!』呵呵!真高兴!另外,如果我要将数据都写到同一个档案中呢?这个时候写法需要用到特殊写法,请注意底下的写法呦!
      [test @test test]# find / -name testing 1> list 2> list<==错误写法
      [test @test tset]# find / -name testing 1> list 2>&1 <==正确写法

      请特别留意这一点呢!同时写入同一个档案需要使用 2>&1 才对呦!
      OK!了解了 >, 2>, >> 与 /dev/null 之后,那么那个 < 又是什么呀!?呵呵!以最简单的说法来说,那就是『将原本需要由键盘输入的数据经由档案来读入』的意思,最明显的例子就是 mail 这个东西了!我们以 root 的身份来寄信给 root 好了,可以这样做:
      1. 完全由键盘输入数据:
      [root @test test]# mail -s "test" root <== -s 表示标题, root 为收件者
      I am root! <==以下的数据都是由键盘输入的
      That's OK
      .       <==要结束键盘的输入时,需要在一行的最前面加上 . 即可!
      CC.     <==是否需要有密件副本?不需要的话,直接按下 Enter !
      EOF     <==表示送出的提示字符而已!
      2. 由档案代替输入
      [test @test tset]# mail -s "test" root < /root/.bashrc <==将 .bashrc 内容寄给 root !
      很有趣吧! ^_^ 这样就可以将信寄出去啰!所以说,熟悉命令重导像的话,对您可是相当的有帮助的呦!
      好了,那么为何要使用命令输出重导向呢?这个问题一定会困扰你一下下的,如果你从来都没有写过 script 的话!好了,我们来说一说吧!
      • 当屏幕输出的信息很重要,而且我们需要将他存下来的时候;
      • 背景执行中的程序,不希望他干扰屏幕正常的输出结果时;
      • 一些系统的例行命令(例如写在 /etc/crontab 中的档案)的执行结果,希望他可以存下来时;
      • 一些执行命令,我们已经知道他可能的错误讯息,所以想以『 2> /dev/null 』将他丢掉时;
      • 错误讯息与正确讯息需要分别输出时。
      当然还有很多很多的功能的,最简单的就是网友们常常问到的:『为何我的 root 都会收到系统 crontab 寄来的错误讯息呢』这个咚咚是常见的错误,而如果我们已经知道这个错误讯息是可以忽略的时候,嗯!『 2> errorfile 』这个功能就很重要了吧!了解了吗?

      --from linux-vbird

    绝对路径与相对路径



      其实,在使用 bash 还有另一个困扰,就是当你的 PATH 没有设定完整的时候,下达指令都是要以『一长列的指令连带根目录都要列出来』,呵呵那就是绝对路径的设定法啦!基本上,这个『绝对路径』与『相对路径』的观念是很重要的!否则你将常常会找不到档案说!所谓的『绝对路径』就是以根目录开始写入到档案的一种命令写定方法,举例来说,我目前在 /home/test 这个 test 使用者的家目录中,我想要看看里面的 .bashrc 这个档案的数据,使用的是 more 这个指令,而这个指令在 /bin/more 当中,则正确的下达指令的方法为:
       
      [test @tset test]# /bin/more .bashrc<==我在的目录为 /home/test !这是绝对路径写法!

      我在的目录为 /home/test !这是绝对路径写法! 而如果你还记得我们在 档案系统指令 那一篇文章中提到的观念的话,那么应该记得使用 ls -al 时会出现两个一定存在的目录,分别是『.』与『..』,分别代表是『这个路径』,与『上一层路径』!
       
      [test @tset test]# ls -al
      total 728
      drwx------ 3 vbird vbird 4096 May 19 14:53 . <==这一层路径的属性说明
      drwxr-xr-x 3 root root 4096 May 5 16:50 .. <==上一层路径的属性说明
      以下略!

      所以说,要执行上一层目录中的命令,可以下达『../command 』那个 command 指的是存在的可执行档!那么我因为在 /home/test 里面,距离 /bin 有两层上层目录,所以我要使用 /bin/more 这个执行文件,并且使用相对路径的方法,就必须使用:
       
      [test @tset test]# ../../bin/more .bashrc <==一层一层回到根目录,在进入 /bin 的写法!相对路径

      这种相对路径的方法相当广泛的被运用于 script 当中,这是因为如前面提到的,每个人的安装预设的目录都不相同,则使用相对路径的话,很容易就可以找到套件之间相依软件或者是设定档案的相关性!
        关于路径搜寻的问题!为何不执行目前所在目录下的档案?
        咦!刚刚不是提到『.』与『..』吗?那么那个『 . 』是干嘛用的?!眼尖的朋友应该已经发现了,就是『我在执行档案的时候,基本上,并不会主动搜寻目前目录下的档案』举个例子来说,我安装的 squid 这个执行档在 /usr/local/squid/bin/squid 这个档案,然而我在 /usr/local/squid/bin 下达 squid 的时候,系统会告诉你『查不到这个档案!』真是见鬼了!明明有这个档案的呀!这是因为系统预设的 PATH (路径)并没有执行目前目录下的设定,也就是『.』这个路径!你可以使用『 echo $PATH 』看看,就可以知道为什么了!那么为何不要设定这个路径呢?这是因为『安全』的考虑。由于系统预设是允许任何人在 /tmp 底下写入任何档案的,那么万一有居心不良的使用者或者是 Cracker 入侵你的计算机,并在你的 /tmp 里头埋了一个小木马,并取名为 ls ,好了,改天你以 root 身份登入后,到 /tmp 底下,并执行 ls ,你看会有什么结果?!这个 /tmp/ls 由其它身份的人来执行或许没有问题,但是由 root 来执行却可能会导致 Cracker 所乐意见到的结果!那晓得为何了吧?!
        当然啰!您还是可以选择在 ~/.bashrc 当中设定你的 . 在你的 PATH 当中,不过并不这么建议就是了!
      好了,由于系统预设并不主动搜寻目前目录下的执行文件,那么你应该如何执行『目前目录下的执行文件』呢?很简单呀!就是以相对路径的观念,由于『 .. 』是上层,而『 . 』是这一层,所以要执行这一层目录的命令就使用『 ./command 』即可!例如你的 /usr/local/squid/bin 底下执行 squid 则可以写成:
       
      [test @tset bin]# ./squid <==以相对路径的观念来看!在本目录下达的指令写法!

      请特别留意这方面的问题!『新手特别容易犯这个错误呢』 ---from linux-vbird