LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 25631|回复: 14

<<Linux与unix shell编程>>一书学习体会(根据书和其他

[复制链接]
发表于 2005-3-14 17:52:01 | 显示全部楼层 |阅读模式
NOTICE:
下面是我上周学习<<linux与unix shell编程>>这本书和阅读其他相关帖子后整理的内容,写给新手看,就当我练练手,如果你是新手,而且想学shell,这里你可以收获很多,并且我建议有<<linux与unix shell编程>>这本书的初学者不要看,因为里面错误不少!如果你是高手,我想这里一定不适合你的口味

初学者一定要多练习,多实践,另外多思考,相信苦学之后必有正果!

如有错误也请大家指正
(其中部分引用其他人的帖子,我稍加总结,希望能对学习shell有些帮助)
 楼主| 发表于 2005-3-14 18:01:33 | 显示全部楼层
一.linux文件的安全与权限
Linux文件的属性:
zhyfly: ~/sixiang$ ls -all
total 54
  1. drwxr-xr-x   2 zhyfly zhyfly   192 2005-03-03 11:31 .
  2. drwxr-xr-x  36 zhyfly zhyfly  1976 2005-03-08 16:20 ..
  3. -rw-r--r--   1 zhyfly zhyfly  5186 2005-02-28 13:03 1
  4. -rw-r--r--   1 zhyfly zhyfly  4819 2005-02-28 13:04 2
  5. -rw-r--r--   1 zhyfly zhyfly  5096 2005-02-28 13:05 3
  6. -rw-r--r--   1 zhyfly zhyfly 18923 2005-02-28 13:41 4
  7. -rw-r--r--   1 zhyfly zhyfly  3120 2005-02-28 13:43 5
  8. -rw-r--r--   1 zhyfly zhyfly  1411 2005-02-28 13:43 6
复制代码

其中:
  1. total 54
复制代码

总用量 54:是ls所列出的入口占用空间的字节数(以K为单位)。
  1. -rw-r--r--
复制代码

该文件的权限位
第一个横杠:指定文件类型,表示该文件是一个普通文件。(所创建的文件绝大多数都是普通文件或符号链接文件)。
补充:
  1. -rwxrw-r-- 1 root admin 34890 10月 19 20:17 httpd.conf
复制代码


1.文件类型:
文件类型有七种,它可以从ls -l命令所列出的结果的第一位看出.
d 目录。
l 符号链接(指向另一个文件)。
s 套接字文件。
b 块设备文件。
c 字符设备文件。
p 命名管道文件。
- 普通文件,或者更准确地说,不属于以上几种类型的文件。

2.文件权限:
前三位:rwx:它的所有者是root,即就是文件所有者,它对httpd.conf这个文件的权限是读写执行;
中间三位:rw-:表示admin组对这个文件的权限是读写
后面位:r--:就是即不是所有者,也不是属组的对文件的权限是读

3.umask及文件权限:
先讓我們看看 x,r,w 的 bit 位元表示方式﹕

x: 001
w: 010
r: 100

如果將全部位元做 OR 運算就是最終 permission﹕rwx 的位置都是 111 ﹐換成十進位就是 7 了。

下面您如果輸入﹕
chmod 750 test.ls

您會發現權限會變成﹕rwxr-x---﹐至於為什麼會如此﹐不妨算算看﹕

1) 將 750 換成二進位﹐就是﹕111,101,000
2) 分別將 rwx 的位置代入得﹕rwx,r-x,---
(方法﹕1 對應著的權限就打開﹐0 對應的就關閉)

就這麼簡單 ^_^

好了﹐我們剛纔已經會得計算自己對一個檔案擁有什麼樣的權限﹐也知道如何改變檔案的權限設定。但是﹐檔案被建立的時候﹐預設會獲得怎樣的權限呢﹖這個值就要靠 umask ?頉Q定了。如果您現在輸入 umask﹐會得到 022 的值﹐這個值決定了檔案在建立的時候﹐要拿掉什麼樣的權限。在沒有使用 umask 的時候﹐所建立的檔案都是 777 這個值的﹐但當運用了 umask 之後呢﹐目錄會變為 755﹐而檔案則為 644 。那是怎麼得出?淼哪丞t還是回到二進位吧﹐然後將完整權限和 umask 進行 NOT 和 AND 運算﹐就是最後的權限值。

在建立目錄的時候﹕
1) 首先﹐我們先對 unmask 進行 NOT 運算﹕
000,010,010 的 NOT 運算結果是﹕111,101,101 ﹔
2) 然後再和 777﹐也就是﹕111,111,111 進行 AND 運算﹔
3) 最後﹐我們會得到這樣的結果﹕111,101,101 ﹔
換成十進位就是 755。

而至於檔案的建立﹕
預設會把它的 x (001) 拿掉﹐其?也是先進行 NOT + AND 運算﹕
1) 先對 x 做 NOT 運算﹕001 NOT ==> 110
2) 然後和 111 做 AND 運算﹕
111,111,111
110,110,110 AND
-----------------
110,110,110 ===> 也就是 666﹐ 然後再和 umask 022 做 NOT 跟 AND 運算﹕
111,101,101 AND
-----------------
110,100,100 ===> 換成十進位是 644 ﹐這才是真正的結果。

我們的 umask 值是登入的時候﹐由 shell 的 profile 分配的(一般規則是:user 與 group 同名為 022﹐否則是 002)﹐我們可以在任何時候改變這個值﹕用 umask 命令以新的值作參數就可以了﹐如﹕umask 002 ﹐然後新建立的檔案就變成 664 的權限了。
tips﹕如果您不習慣用二進位進行計算﹐只能用十進位?硭愕脑挬o可以簡單的這樣算﹕
目錄是用 777 扣掉 umask 的值﹔
檔案是用 666 扣掉 umask 的值。

不過﹐電腦上面真正的運算是二進位啦﹐這個和我們計算 IP subnet 的時候是一樣的原理。
查看umask的值:
  1. zhyfly: ~/1$ umask
  2. 0022
复制代码

修改umask的值:
  1. zhyfly: ~/1$ umask 0002
  2. zhyfly: ~/1$ umask
  3. 0002
复制代码

彻底修改umask的值应修改.profile文件(bash)或.login文件(c shell)

4.修改文件权限chmod:
使用chmod来改变权限位

这一命令有符号模式和绝对模式。
符号模式

chmod命令的一般格式为:

chmod [who] operator [permission] filename

w h o的含义是:
  1. u 文件属主权限。
  2. g 属组用户权限。
  3. o 其他用户权限。
  4. a 所有用户(文件属主、属组用户及其他用户)。
复制代码

o p e r a t o r的含义:
  1. + 增加权限。
  2. - 取消权限。
  3. = 设定权限。
复制代码

p e r m i s s i o n的含义:
  1. r 读权限。
  2. w 写权限。
  3. x 执行权限。
  4. s 文件属主和组set-ID。
  5. t 粘性位*。
  6. l 给文件加锁,使其他用户无法访问。
  7. u,g,o 针对文件属主、属组用户及其他用户的操作。

  8. *在列文件或目录时,有时会遇到“ t”位。“t”代表了粘性位。如果在一个目录上出现“t”位,这就意味着该目录中的文件只有其属主才可以删除,即使某个属组用户具有和属主同等的权限。不过有的系统在这一规则上并不十分严格。

  9. "t"权限用在文件上面是没有意义的,不是什么在交换区的概念,它跟文件的执行没有关系,而主要是为了文件共享设置的。
复制代码

  1. chmod a-x temp //rw- rw- rw- 收回所有用户的执行权限
  2. chmod og-w temp //rw- r-- r- - 收回属组用户和其他用户的写权限
  3. chmod g+w temp //rw- rw- r- - 赋予属组用户写权限
  4. chmod u+x temp //rwx rw- r- - 赋予文件属主执行权限
  5. chmod go+x temp //rwx rwx r- x 赋予属组用户和其他用户执行权限
复制代码



绝对模式

chmod命令绝对模式的一般形式为:

chmod [mode] file

其中m o d e是一个八进制数。
在绝对模式中,权限部分有着不同的含义。每一个权限位用一个八进制数来代表,如
  1. 0 4 0 0 文件属主可读
  2. 0 2 0 0 文件属主可写
  3. 0 1 0 0 文件属主可执行

  4. 0 0 4 0 属组用户可读
  5. 0 0 2 0 属组用户可写
  6. 0 0 1 0 属组用户可执行

  7. 0 0 0 4 其他用户可读
  8. 0 0 0 2 其他用户可写
  9. 0 0 0 1 其他用户可执行
复制代码

在设定权限的时候,只需按照上面查出与文件属主、属组用户和其他用户所具有的权限相对应的数字,并把它们加起来,就是相应的权限表示。
可以看出,文件属主、属组用户和其他用户分别所能够具有的最大权限值就是7。
e.g
  1. -rwxr--r--  1   root            0 10月 19 20:16 temp
复制代码

相应的权限:
  1. rwx-:0400 + 0200 +0100 (文件属主可读、写、执行) = 0 7 0 0
  2. r--:0 0 4 0 (属组用户可读) = 0 0 4 0
  3. r--:0 0 4 0 (属组用户可读) = 0 0 4 0
  4. 0 7 4 4
复制代码

有一个计算八进制权限表示的更好办法,如下:


  1. 文件属主:r w x:4 + 2 + 1
  2. 属组用户:r w x:4 + 2 + 1
  3. 其他用户:r w x:4 + 2 + 1
复制代码



这上面这相,更容易地计算出相应的权限值,只要分别针对文件属主、属组用户和其他用户把相应权限下面的数字加在一起就可以了。

temp文件具有这样的权限:


  1. r w x     r - - r - -
复制代码

4+2+1  4     4


把相应权限位所对应的值加在一起,就是7 4 4。

如:


  1. chmod 666 rw- rw- rw- 赋予所有用户读和写的权限
  2. chmod 644 rw- r-- r- - 赋予所有文件属主读和写的权限,所有其他用户读权限
  3. chmod 744 rwx r-- r- - 赋予文件属主读、写和执行的权限,所有其他用户读的权限
  4. chmod 664 rw- rw- r- - 赋予文件属主和属组用户读和写的权限,其他用户读权限
  5. chmod 700 rwx --- --- 赋予文件属主读、写和执行的权限
  6. chmod 444 r-- r-- r- - 赋予所有用户读权限
复制代码



下面举一个例子,假定有一个名为temp的文件,具有如下权限:

  1. -rw-rw-r--  1   root            0 10月 19 20:16 test1
复制代码



现在希望对该文件可读、写和执行, root组用户对该文件只读,可以键入:


  1. $chmod 740 test1
  2. $ls -l
  3. -rwxr-----  1   root            0 10月 19 20:16 test1
复制代码



如果文件可读、写和执行,对其他所有用户只读,用:

  1. $chmod 744 test1
  2. $ls -l
  3. -rwxr--r--  1   root            0 10月 19 20:16 test1
复制代码



如果希望一次设置目录下所有文件的权限,可以用:

  1. $chmod 664*
  2. $ls -l
  3. -rw-r--r--  1   root            0 10月 19 20:16 test1
复制代码



这将使文件属主和属组用户都具有读和写的权限,其他用户只具有读权限。

还可以通过使用- R选项连同子目录下的文件一起设置:

  1. chmod -R 664 /temp/*
复制代码



这样就可以一次将/ temp目录下的所有文件连同各个子目录下的文件的权限全部设置为文件属主和属组用户可读和写,其他用户只读。使用- R选项一定要谨慎,只有在需要改变目录树下全部文件权限时才可以使用。

5.修改文件属主chown:
c h o w n命令的一般形式为:


  1. chmod -R -h owner file
复制代码



- R选项意味着对所有子目录下的文件也都进行同样的操作。
- h选项意味着在改变符号链接文件的属主时不影响该链接所指向的目标文件。


chown举例

如:
  1. # ls -l
  2. drwxrwxr-x    2 sam      sam          4096 10月 26 19:48 sam
  3. # chown gem sam
  4. # ls -l
  5. drwxrwxr-x    2 gem      sam          4096 10月 26 19:48 sam
复制代码



文件sam的所有权现在由用户sam交给了用户gem。

6.修改文件属组chgrp:

chgrp举例

c h g r p命令和c h o w n命令的格式差不多,下面给出一个例子。

  1. # ls -l
  2. drwxrwxr-x    2 gem      sam          4096 10月 26 19:48 sam
  3. # chgrp group sam
  4. # ls -l
  5. drwxrwxr-x    2 gem      group        4096 10月 26 19:48 sam
复制代码



现在把该文件sam所属的组由sam变为group。


找出你所属于的用户组

如果你希望知道自己属于哪些用户组,可以用ID这个命令:

  1. # su sam
  2. $ id
  3. uid=506(sam) gid=4(adm) groups=4(adm)
复制代码




找出其他用户所属于的组


  1. # id
  2. uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
复制代码

查看当前用户所属组

  1. # id gem
  2. uid=507(gem) gid=507(group) groups=507(group),0(root),4(adm)
复制代码

查看其它用户所用组:#id 用户名

  1. # su sam
  2. $ id gem
  3. uid=507(gem) gid=507(group) groups=507(group),0(root),4(adm)
复制代码

查看其它用户所属组

7.suid/sgid
为什么要使用这种类型的脚本?

例如有几个着几个大型的数据库系统,对它们进行备份需要有系统管理权限。可以写几个脚本,并设置了它们的g u i d,这样就可以指定的一些用户来执行这些脚本就能够完成相应的工作,而无须以数据库管理员的身份登录,以免不小心破坏了数据库服务器。通过执行这些脚本,他们可以完成数据库备份及其他管理任务,但是在这些脚本运行结束之后,他们就又回复到他们作为普通用户的权限。


查找suid/sgid命令

有相当一些U N I X命令也设置了s u i d和s g i d。如果想找出这些命令,可以进入/ b i n或/ s b i n目录,执行下面的命令:

  1. $ ls -l | grep '^...s'
复制代码


上面的命令是用来查找s u i d文件的;

  1. $ ls -l | grep '^...s..s'
复制代码


上面的命令是用来查找s u i d和gs g i d的。


设置UID

  1. 设置s u i d:将相应的权限位之前的那一位设置为4;
  2. 设置g u i d:将相应的权限位之前的那一位设置为2;
  3. 两者都置位:将相应的权限位之前的那一位设置为4+2=6。
复制代码



设置了这一位后x的位置将由s代替。
记住:在设置s u i d或s g i d的同时,相应的执行权限位必须要被设置。
例如,如果希望设置s g i d,那么必须要让该用户组具有执行权限。

如果想要对文件l o g i n[它当前所具有的权限为rwx rw- r-- (741)]设置s u i d,,可在使用c h m o d命令时在该权限数字的前面加上一个4,即chmod 4741,这将使该文件的权限变为r w s rw- r - -。

  1. $ chmod 4741 login
复制代码



设置suid/sgid的例子


  1.       命令          结果        含义
  2. chmod 4755   rws r-x r- x   文件被设置了s u i d,文件属主具有读、写和执行的权限,其他用户具有读和执行的权限
  3. chmod 6711   rws --s --x    文件被设置了s u i d和g u i d,文件属主具有读、写和执行的权限,其他用户具有执行的权限
  4. chmod 4764   rws rw- r- -   文件被设置了s u i d,文件属主具有读、写和执行的权限,属组用户具有读和执行的权限,用户具有读权限
复制代码




还可以使用符号方式来设置s u i d / s g i d。如果某个文件具有这样的权限: rwx r-x r- x,那么可以这样设置其s u i d:


  1. chmod u+s <filename>
复制代码



于是该文件的权限将变为: rws r-x r-x

在查找设置了s u i d的文件时,没准会看到具有这样权限的文件:rwS r-x r- x,其中S为大写。
它表示相应的执行权限位并未被设置,这是一种没有什么用处的s u i d设置,可以忽略它的存在。

注意,c h m o d命令不进行必要的完整性检查,可以给某一个没用的文件赋予任何权限,但chmod 命令并不会对所设置的权限组合做什么检查。因此,不要看到一个文件具有执行权限,就认为它一定是一个程序或脚本.

8.符号链接

存在两种不同类型的链接,软链接和硬链接。修改其中一个,硬连接指向的是节点(inode),而软连接指向的是路径(path)
软链接文件

  软链接又叫符号链接,这个文件包含了另一个文件的路径名。可以是任意文件或目录,可以链接不同文件系统的文件。和win下的快捷方式差不多。链接文件甚至可以链接不存在的文件,这就产生一般称之为"断链"的问题(或曰“现象"),链接文件甚至可以循环链接自己。类似于编程语言中的递归。

命令格式:
  1. ln -s source_path target_path
复制代码



硬链接文件

  info ln 命令告诉您,硬链接是已存在文件的另一个名字,硬连接的命令是

  1. ln [-d] existfile newfile
复制代码



硬链接文件有两个限制

  1、不允许给目录创建硬链接;
  2、只有在同一文件系统中的文件之间才能创建链接。


  对硬链接文件进行读写和删除操作时候,结果和软链接相同。但如果我们删除硬链接文件的源文件,硬链接文件仍然存在,而且保留了愿有的内容。这时,系统就“忘记”了它曾经是硬链接文件。而把他当成一个普通文件。修改其中一个,与其连接的文件同时被修改.

补充inux的用户和用户组管理
 Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪,并控制他们对系统资源的访问;另一方面也可以帮助用户组织文件,并为用户提供安全性保护。每个用户账号都拥有一个惟一的用户名和各自的口令。用户在登录时键入正确的用户名和口令后,就能够进入系统和自己的主目录。

  实现用户账号的管理,要完成的工作主要有如下几个方面:
  · 用户账号的添加、删除与修改。
  · 用户口令的管理。
  · 用户组的管理。

9.Linux系统用户账号的管理
用户账号的管理工作主要涉及到用户账号的添加、修改和删除。

  添加用户账号就是在系统中创建一个新账号,然后为新账号分配用户号、用户组、主目录和登录Shell等资源。刚添加的账号是被锁定的,无法使用。
a.添加新的用户账号使用useradd命令,其语法如下:

  1. useradd 选项 用户名
复制代码



  其中各选项含义如下:


  1.   -c comment 指定一段注释性描述。
  2.   -d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。
  3.   -g 用户组 指定用户所属的用户组。
  4.   -G 用户组,用户组 指定用户所属的附加组。
  5.   -s Shell文件 指定用户的登录Shell。
  6.   -u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。
复制代码


  用户名 指定新账号的登录名。
例1:


  1. # useradd –d /usr/sam -m sam
复制代码



  此命令创建了一个用户sam,
  其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam(/usr为默认的用户主目录所在的父目录)。


  例2:


  1. # useradd -s /bin/sh -g group –G adm,root gem
复制代码



  此命令新建了一个用户gem,该用户的登录Shell是/bin/sh,它属于group用户组,同时又属于adm和root用户组,其中group用户组是其主组。
  这里可能新建组:#groupadd group及groupadd adm 
 
  增加用户账号就是在/etc/passwd文件中为新用户增加一条记录,同时更新其他系统文件如/etc/shadow, /etc/group等。

  Linux提供了集成的系统管理工具userconf,它可以用来对用户账号进行统一管理。
b.删除帐号

  如果一个用户的账号不再使用,可以从系统中删除。删除用户账号就是要将/etc/passwd等系统文件中的该用户记录删除,必要时还删除用户的主目录。删除一个已有的用户账号使用userdel命令,其格式如下:


  1. userdel 选项 用户名
复制代码



  常用的选项是-r,它的作用是把用户的主目录一起删除。

  例如:

  

  1. # userdel sam
复制代码



  此命令删除用户sam在系统文件中(主要是/etc/passwd, /etc/shadow, /etc/group等)的记录,同时删除用户的主目录。
c.修改帐号

  修改用户账号就是根据实际情况更改用户的有关属性,如用户号、主目录、用户组、登录Shell等。

  修改已有用户的信息使用usermod命令,其格式如下:


  1. usermod 选项 用户名
复制代码



  常用的选项包括-c, -d, -m, -g, -G, -s, -u以及-o等,这些选项的意义与useradd命令中的选项一样,可以为用户指定新的资源值。另外,有些系统可以使用如下选项:


  1. -l 新用户名
复制代码



  这个选项指定一个新的账号,即将原来的用户名改为新的用户名。

  例如:


  1. # usermod -s /bin/ksh -d /home/z –g developer sam
复制代码



  此命令将用户sam的登录Shell修改为ksh,主目录改为/home/z,用户组改为developer。
d.用户口令的管理

  用户管理的一项重要内容是用户口令的管理。用户账号刚创建时没有口令,但是被系统锁定,无法使用,必须为其指定口令后才可以使用,即使是指定空口令。

  指定和修改用户口令的Shell命令是passwd。超级用户可以为自己和其他用户指定口令,普通用户只能用它修改自己的口令。命令的格式为:


  1. passwd 选项 用户名
复制代码



  可使用的选项:


  1.   -l 锁定口令,即禁用账号。
  2.   -u 口令解锁。
  3.   -d 使账号无口令。
  4.   -f 强迫用户下次登录时修改口令。
  5.   如果默认用户名,则修改当前用户的口令。
复制代码



  例如,假设当前用户是sam,则下面的命令修改该用户自己的口令:


  1.   $ passwd
  2.   Old password:******
  3.   New password:*******
  4.   Re-enter new password:*******
复制代码



  如果是超级用户,可以用下列形式指定任何用户的口令:


  1.   # passwd sam
  2.   New password:*******
  3.   Re-enter new password:*******
复制代码



  普通用户修改自己的口令时,passwd命令会先询问原口令,验证后再要求用户输入两遍新口令,如果两次输入的口令一致,则将这个口令指定给用户;而超级用户为用户指定口令时,就不需要知道原口令。

  为了系统安全起见,用户应该选择比较复杂的口令,例如最好使用8位长的口令,口令中包含有大写、小写字母和数字,并且应该与姓名、生日等不相同。

  为用户指定空口令时,执行下列形式的命令:

  1. # passwd -d sam
复制代码



  此命令将用户sam的口令删除,这样用户sam下一次登录时,系统就不再询问口令。

  passwd命令还可以用-l(lock)选项锁定某一用户,使其不能登录,例如:


  1. # passwd -l sam
复制代码


10.Linux系统用户组的管理

  每个用户都有一个用户组,系统可以对一个用户组中的所有用户进行集中管理。不同Linux 系统对用户组的规定有所不同,如Linux下的用户属于与它同名的用户组,这个用户组在创建用户时同时创建。
  用户组的管理涉及用户组的添加、删除和修改。组的增加、删除和修改实际上就是对/etc/group文件的更新。

a.增加一个新的用户组使用groupadd命令。其格式如下:
  

  1. groupadd 选项 用户组
复制代码


  可以使用的选项有:

  1.   -g GID 指定新用户组的组标识号(GID)。
  2.   -o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。
复制代码


  例1:
  

  1. # groupadd group1
复制代码


  此命令向系统中增加了一个新组group1,新组的组标识号是在当前已有的最大组标识号的基础上加1。

  例2:
  

  1. #groupadd -g 101 group2
复制代码


  此命令向系统中增加了一个新组group2,同时指定新组的组标识号是101。

b.如果要删除一个已有的用户组,使用groupdel命令,其格式如下:
  

  1. groupdel 用户组
复制代码


  例如:
  

  1. #groupdel group1
复制代码


  此命令从系统中删除组group1。

c.修改用户组的属性使用groupmod命令。其语法如下:
  

  1. groupmod 选项 用户组
复制代码


  常用的选项有:

  1.   -g GID 为用户组指定新的组标识号。
  2.   -o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。
  3.   -n新用户组 将用户组的名字改为新名字
复制代码



  例1:
  

  1. # groupmod -g 102 group2
复制代码


  此命令将组group2的组标识号修改为102。

  例2:
  

  1. # groupmod –g 10000 -n group3 group2
复制代码


  此命令将组group2的标识号改为10000,组名修改为group3。

d.如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。用户可以在登录后,使用命令newgrp切换到其他用户组,这个命令的参数就是目的用户组。例如:
  

  1. $ newgrp root
复制代码


  这条命令将当前用户切换到root用户组,前提条件是root用户组确实是该用户的主组或附加组。类似于用户账号的管理,用户组的管理也可以通过集成的系统管理工具来完成。

11.与用户账号有关的系统文件

  完成用户管理的工作有许多种方法,但是每一种方法实际上都是对有关的系统文件进行修改。与用户和用户组相关的信息都存放在一些系统文件中,这些文件包括/etc/passwd, /etc/shadow, /etc/group等。下面分别介绍这些文件的内容。
a./etc/passwd文件是用户管理工作涉及的最重要的一个文件。Linux系统中的每个用户都在/etc/passwd文件中有一个对应的记录行,它记录了这个用户的一些基本属性。这个文件对所有用户都是可读的。它的内容类似下面的例子:


  1.   # cat /etc/passwd
  2.       root:x:0:0:Superuser:/:
  3.   daemon:x:1:1:System daemons:/etc:
  4.   bin:x:2:2:Owner of system commands:/bin:
  5.   sys:x:3:3:Owner of system files:/usr/sys:
  6.   adm:x:4:4:System accounting:/usr/adm:
  7.   uucp:x:5:5:UUCP administrator:/usr/lib/uucp:
  8.   auth:x:7:21:Authentication administrator:/tcb/files/auth:
  9.   cron:x:9:16:Cron daemon:/usr/spool/cron:
  10.   listen:x:37:4:Network daemon:/usr/net/nls:
  11.   lp:x:71:18:Printer administrator:/usr/spool/lp:
  12.   sam:x:200:50:Sam san:/usr/sam:/bin/sh
复制代码



  从上面的例子我们可以看到,/etc/passwd中一行记录对应着一个用户,每行记录又被冒号(分隔为7个字段,其格式和具体含义如下:


  1. 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell
复制代码



  1)“用户名”是代表用户账号的字符串。通常长度不超过8个字符,并且由大小写字母和/或数字组成。登录名中不能有冒号(,因为冒号在这里是分隔符。为了兼容起见,登录名中最好不要包含点字符(.),并且不使用连字符(-)和加号(+)打头。

  2)“口令”一些系统中,存放着加密后的用户口令字。。虽然这个字段存放的只是用户口令的加密串,不是 明文,但是由于/etc/passwd文件对所有用户都可读,所以这仍是一个安全隐患。因此,现在许多 Linux 系统(如SVR4)都使用了shadow技术,把真正的加密后的用户口令字存放到/etc/shadow文件中,而在/etc/passwd文件的口令字段中只存放一个特殊的字符,例如“x”或者“*”。

  3)“用户标识号”是一个整数,系统内部用它来标识用户。一般情况下它与用户名是一一对应的。如果几个用户名对应的用户标识号是一样的,系统内部将把它们视为同一个用户,但是它们可以有不同的口令、不同的主目录以及不同的登录Shell等。

  通常用户标识号的取值范围是0~65 535。0是超级用户root的标识号,1~99由系统保留,作为管理账号,普通用户的标识号从100开始。在Linux系统中,这个界限是500。

  4)“组标识号”字段记录的是用户所属的用户组。它对应着/etc/group文件中的一条记录。

  5)“注释性描述”字段记录着用户的一些个人情况,例如用户的真实姓名、电话、地址等,这个字段并没有什么实际的用途。在不同的Linux 系统中,这个字段的格式并没有统一。在许多Linux系统中,这个字段存放的是一段任意的注释性描述文字,用做finger命令的输出。

  6)“主目录”,也就是用户的起始工作目录,它是用户在登录到系统之后所处的目录。在大多数系统中,各用户的主目录都被组织在同一个特定的目录下,而用户主目录的名称就是该用户的登录名。各用户对自己的主目录有读、写、执行(搜索)权限,其他用户对此目录的访问权限则根据具体情况设置。

  7)用户登录后,要启动一个进程,负责将用户的操作传给内核,这个进程是用户登录到系统后运行的命令解释器或某个特定的程序,即Shell。Shell 是用户与Linux系统之间的接口。Linux的Shell有许多种,每种都有不同的特点。常用的有sh(Bourne Shell), csh(C Shell), ksh(Korn Shell), tcsh(TENEX/TOPS-20 type C Shell), bash(Bourne Again Shell)等。系统管理员可以根据系统情况和用户习惯为用户指定某个Shell。如果不指定Shell,那么系统使用sh为默认的登录Shell,即这个字段的值为/bin/sh。

  用户的登录Shell也可以指定为某个特定的程序(此程序不是一个命令解释器)。利用这一特点,我们可以限制用户只能运行指定的应用程序,在该应用程序运行结束后,用户就自动退出了系统。有些Linux 系统要求只有那些在系统中登记了的程序才能出现在这个字段中。


  系统中有一类用户称为伪用户(psuedo users),这些用户在/etc/passwd文件中也占有一条记录,但是不能登录,因为它们的登录Shell为空。它们的存在主要是方便系统管理,满足相应的系统进程对文件属主的要求。常见的伪用户如下所示。


  1. 伪  用  户         含     义
  2. bin                        拥有可执行的用户命令文件
  3. sys                       拥有系统文件
  4. adm                     拥有帐户文件
  5. uucp                    UUCP使用
  6. lp                         lp或lpd子系统使用
  7. nobody                 NFS使用
复制代码


拥有帐户文件

  除了上面列出的伪用户外,还有许多标准的伪用户,例如:audit, cron, mail, usenet等,它们也都各自为相关的进程和文件所需要。

  由于/etc/passwd文件是所有用户都可读的,如果用户的密码太简单或规律比较明显的话,一台普通的计算机就能够很容易地将它破解,因此对安全性要求较高的Linux系统都把加密后的口令字分离出来,单独存放在一个文件中,这个文件是/etc/shadow文件。只有超级用户才拥有该文件读权限,这就保证了用户密码的安全性。


b./etc/shadow中的记录行与/etc/passwd中的一一对应,它由pwconv命令根据/etc/passwd中的数据自动产生。它的文件格式与/etc/passwd类似,由若干个字段组成,字段之间用“:”隔开。这些字段是:


  1. 登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志
复制代码



  1)“登录名”是与/etc/passwd文件中的登录名相一致的用户账号
  2)“口令”字段存放的是加密后的用户口令字,长度为13个字符。如果为空,则对应用户没有口令,登录时不需要口令;如果含有不属于集合 { ./0-9A-Za-z }中的字符,则对应的用户不能登录。
  3)“最后一次修改时间”表示的是从某个时刻起,到用户最后一次修改口令时的天数。时间起点对不同的系统可能不一样。例如在SCO Linux 中,这个时间起点是1970年1月1日。
  4)“最小时间间隔”指的是两次修改口令之间所需的最小天数。
  5)“最大时间间隔”指的是口令保持有效的最大天数。
  6)“警告时间”字段表示的是从系统开始警告用户到用户密码正式失效之间的天数。
  7)“不活动时间”表示的是用户没有登录活动但账号仍能保持有效的最大天数。
  8)“失效时间”字段给出的是一个绝对的天数,如果使用了这个字段,那么就给出相应账号的生存期。期满后,该账号就不再是一个合法的账号,也就不能再用来登录了。

  下面是/etc/shadow的一个例子:

  

  1. # cat /etc/shadow
  2.     root:Dnakfw28zf38w:8764:0:168:7:::
  3.   daemon:*::0:0::::
  4.   bin:*::0:0::::
  5.   sys:*::0:0::::
  6.   adm:*::0:0::::
  7.   uucp:*::0:0::::
  8.   nuucp:*::0:0::::
  9.   auth:*::0:0::::
  10.   cron:*::0:0::::
  11.   listen:*::0:0::::
  12.   lp:*::0:0::::
  13.   sam:EkdiSECLWPdSa:9740:0:0::::
复制代码




c.用户组的所有信息都存放在/etc/group文件中。

将用户分组是Linux 系统中对用户进行管理及控制访问权限的一种手段。每个用户都属于某个用户组;一个组中可以有多个用户,一个用户也可以属于不同的组。当一个用户同时是多个组中的成员时,在/etc/passwd文件中记录的是用户所属的主组,也就是登录时所属的默认组,而其他组称为附加组。用户要访问属于附加组的文件时,必须首先使用newgrp命令使自己成为所要访问的组中的成员。用户组的所有信息都存放在/etc/group文件中。此文件的格式也类似于 /etc/passwd文件,由冒号(隔开若干个字段,这些字段有:

  

  1. 组名:口令:组标识号:组内用户列表
复制代码



  1)“组名”是用户组的名称,由字母或数字构成。与/etc/passwd中的登录名一样,组名不应重复。
  2)“口令”字段存放的是用户组加密后的口令字。一般Linux 系统的用户组都没有口令,即这个字段一般为空,或者是*。
  3)“组标识号”与用户标识号类似,也是一个整数,被系统内部用来标识组。
  4)“组内用户列表”是属于这个组的所有用户的列表/b],不同用户之间用逗号(,)分隔。这个用户组可能是用户的主组,也可能是附加组。

  /etc/group文件的一个例子如下:

  1. # cat /etc/group
  2.   root::0:root
  3.   bin::2:root,bin
  4.   sys::3:root,uucp
  5.   adm::4:root,adm
  6.   daemon::5:root,daemon
  7.   lp::7:root,lp
  8.   users::20:root,sam
复制代码


12.添加量用户批
添加和删除用户对每位Linux系统管理员都是轻而易举的事,比较棘手的是如果要添加几十个、上百个甚至上千个用户时,我们不太可能还使用useradd一个一个地添加,必然要找一种简便的创建大量用户的方法。Linux系统提供了创建大量用户的工具,可以让您立即创建大量用户,方法如下:

  (1)先编辑一个文本用户文件,每一列按照/etc/passwd密码文件的格式书写,要注意每个用户的用户名、UID、宿主目录都不可以相同,其中密码栏可以留做空白或输入x号。一个范例文件user.txt内容如下:


  1.   user001::600:100:user:/home/user001:/bin/bash
  2.   user002::601:100:user:/home/user002:/bin/bash
  3.   user003::602:100:user:/home/user003:/bin/bash
  4.   user004::603:100:user:/home/user004:/bin/bash
  5.   user005::604:100:user:/home/user005:/bin/bash
  6.   user006::605:100:user:/home/user006:/bin/bash
复制代码


  (2)以root身份执行命令/usr/sbin/newusers,从刚创建的用户文件user.txt中导入数据,创建用户:


  1. # newusers < user.txt
复制代码



  然后可以执行命令vipw或vi /etc/passwd检查/etc/passwd文件是否已经出现这些用户的数据,并且用户的宿主目录是否已经创建。

  (3)执行命令/usr/sbin/pwunconv,将/etc/shadow产生的shadow密码解码,然后回写到/etc/passwd中,并将/etc/shadow的shadow密码栏删掉。这是为了方便下一步的密码转换工作,即先取消shadow password功能。


  1. # pwunconv
复制代码



  (4)编辑每个用户的密码对照文件,范例文件passwd.txt内容如下:


  1.   user001:密码
  2.   user002:密码
  3.   user003:密码
  4.   user004:密码
  5.   user005:密码
  6.   user006:密码
复制代码


  (5)以root身份执行命令/usr/sbin/chpasswd,创建用户密码,chpasswd会将经过/usr/bin/passwd命令编码过的密码写入/etc/passwd的密码栏。

  

  1. # chpasswd < passwd.txt
复制代码



  (6)确定密码经编码写入/etc/passwd的密码栏后,执行命令/usr/sbin/pwconv将密码编码为shadow password,并将结果写入/etc/shadow。


  1. # pwconv
复制代码



  这样就完成了大量用户的创建了,之后您可以到/home下检查这些用户宿主目录的权限设置是否都正确,并登录验证用户密码是否正确。

13.赋予普通用户特殊权限

  在Linux系统中,管理员往往不止一人,若每位管理员都用root身份进行管理工作,根本无法弄清楚谁该做什么。所以最好的方式是:管理员创建一些普通用户,分配一部分系统管理工作给他们。

  我们不可以使用su让他们直接变成root,因为这些用户都必须知道root的密码,这种方法很不安全,而且也不符合我们的分工需求。一般的做法是利用权限的设置,依工作性质分类,让特殊身份的用户成为同一个工作组,并设置工作组权限。例如:要wwwadm这位用户负责管理网站数据,一般Apache Web Server的进程httpd的所有者是www,您可以设置用户wwwadm与www为同一工作组,并设置Apache默认存放网页目录 /usr/local/httpd/htdocs的工作组权限为可读、可写、可执行,这样属于此工作组的每位用户就可以进行网页的管理了。

  但这并不是最好的解决办法,例如管理员想授予一个普通用户关机的权限,这时使用上述的办法就不是很理想。这时您也许会想,我只让这个用户可以以root身份执行shutdown命令就行了。完全没错,可惜在通常的Linux系统中无法实现这一功能,不过已经有了工具可以实现这样的功能——sudo。

  sudo通过维护一个特权到用户名映射的数据库将特权分配给不同的用户,这些特权可由数据库中所列的一些不同的命令来识别。为了获得某一特权项,有资格的用户只需简单地在命令行输入sudo与命令名之后,按照提示再次输入口令(用户自己的口令,不是root用户口令)。例如,sudo允许普通用户格式化磁盘,但是却没有赋予其他的root用户特权。

  sudo工具由文件/etc/sudoers进行配置,该文件包含所有可以访问sudo工具的用户列表并定义了他们的特权。一个典型的/etc/sudoers条目如下:


  1. liming ALL=(ALL) ALL
复制代码



  这个条目使得用户liming作为超级用户访问所有应用程序,如用户liming需要作为超级用户运行命令,他只需简单地在命令前加上前缀sudo。因此,要以root用户的身份执行命令format,liming可以输入如下命令:


  1. # sudo /usr/sbin/useradd sam
复制代码



  注意:命令要写绝对路径,/usr/sbin默认不在普通用户的搜索路径中,或者加入此路径:PATH=$PATH:/usr/sbin;export PATH。另外,不同系统命令的路径不尽相同,可以使用命令“whereis 命令名”来查找其路径。

  这时会显示下面的输出结果:


  1.   We trust you have received the usual lecture from the local System
  2.   Administrator. It usually boils down to these two things:
  3.   #1) Respect the privacy of others.
  4.   #2) Think before you type.
  5.   Password:

复制代码

  如果liming正确地输入了口令,命令useradd将会以root用户身份执行。

  注意:配置文件/etc/sudoers必须使用命令 Visudo来编辑。

  只要把相应的用户名、主机名和许可的命令列表以标准的格式加入到文件/etc/sudoers,并保存就可以生效,再看一个例子。

例子:管理员需要允许gem用户在主机sun上执行reboot和shutdown命令,在/etc/sudoers中加入:


  1. gem sun=/usr/sbin/reboot,/usr/sbin/shutdown
复制代码


  注意:命令一定要使用绝对路径,以避免其他目录的同名命令被执行,从而造成安全隐患。

  然后保存退出,gem用户想执行reboot命令时,只要在提示符下运行下列命令:


  1. $ sudo /usr/sbin/reboot
复制代码


  输入正确的密码,就可以重启服务器了。

  如果您想对一组用户进行定义,可以在组名前加上%,对其进行设置,如:


  1. %cuug ALL=(ALL) ALL
复制代码


  另外,还可以利用别名来简化配置文件。别名类似组的概念,有用户别名、主机别名和命令别名。多个用户可以首先用一个别名来定义,然后在规定他们可以执行什么命令的时候使用别名就可以了,这个配置对所有用户都生效。主机别名和命令别名也是如此。注意使用前先要在/etc/sudoers中定义:User_Alias, Host_Alias, Cmnd_Alias项,在其后面加入相应的名称,也以逗号分隔开就可以了,举例如下:


  1.   Host_Alias SERVER=no1
  2.   User_Alias ADMINS=liming,gem
  3.   Cmnd_Alias SHUTDOWN=/usr/sbin/halt,/usr/sbin/shutdown,/usr/sbin/reboot
  4.   ADMINS SERVER=SHUTDOWN
复制代码


  再看这个例子:


  1. ADMINS ALL=(ALL) NOPASSWD: ALL
复制代码


  表示允许ADMINS不用口令执行一切操作,其中“NOPASSWD:”项定义了用户执行操作时不需要输入口令。

  sudo命令还可以加上一些参数,完成一些辅助的功能,如


  1. $ sudo –l
复制代码


  会显示出类似这样的信息:


  1.   User liming may run the following commands on this host:
  2.   (root) /usr/sbin/reboot
复制代码


  说明root允许用户liming执行/usr/sbin/reboot命令。这个参数可以使用户查看自己目前可以在sudo中执行哪些命令。

  在命令提示符下键入sudo命令会列出所有参数,其他一些参数如下:


  1.   -V 显示版本编号。
  2.   -h 显示sudo命令的使用参数。
  3.   -v 因为sudo在第一次执行时或是在N分钟内没有执行(N预设为5)会询问密码。这个参数是重新做一次确认,如果超过N分钟,也会问密码。
  4.   -k 将会强迫使用者在下一次执行sudo时询问密码(不论有没有超过N分钟)。
  5.   -b 将要执行的命令放在背景执行。
  6.   -p prompt 可以更改问密码的提示语,其中%u会替换为使用者的账号名称,%h会显示主机名称。
  7.   -u username/#uid 不加此参数,代表要以root的身份执行命令,而加了此参数,可以以username的身份执行命令(#uid为该username的UID)。
  8.   -s 执行环境变量中的 SHELL 所指定的 Shell ,或是 /etc/passwd 里所指定的 Shell。
  9.   -H 将环境变量中的HOME(宿主目录)指定为要变更身份的使用者的宿主目录。(如不加-u参数就是系统管理者root。)
复制代码


   要以系统管理者身份(或以-u更改为其他人)执行的命令。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-14 18:13:21 | 显示全部楼层
二.Linux文件查找命令find,xargs详述
1.find
由于f i n d具有强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时间来了解一下。即使系统中含有网络文件系统( N F S ),f i n d命令在该文件系统中同样有效,只你具有相应的权限。
在运行一个非常消耗资源的f i n d命令时,很多人都倾向于把它放在后台执行,因为遍历一个大的文件系统可能会花费很长的时间(这里是指3 0 G字节以上的文件系统)。

F i n d命令的一般形式为:

  1. find pathname -options [-print -exec -ok ...]
复制代码


让我们来看看该命令的参数:


  1. pathname: find命令所查找的目录路径。例如用.来表示当前目录,用/来表示系统根目录。
  2. -print: find命令将匹配的文件输出到标准输出。
  3. -exec: find命令对匹配的文件执行该参数所给出的s h e l l命令。相应命令的形式为' command' {} \;,注意{ }和\;之间的空格。
  4. -ok: 和- e x e c的作用相同,只不过以一种更为安全的模式来执行该参数所给出的s h e l l命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行。
复制代码


find命令选项

  1. -name:按照文件名查找文件。
  2. -perm:按照文件权限来查找文件。
  3. -prune:使用这一选项可以使f i n d命令不在当前指定的目录中查找,如果同时使用-depth选项,那么-prune将被f i n d命令忽略。
  4. -user: 按照文件属主来查找文件。
  5. -group:按照文件所属的组来查找文件。
  6. -mtime -n +n:按照文件的更改时间来查找文件, - n表示文件更改时间距现在n天以内,+ n表示文件更改时间距现在n天以前。F i n d命令还有- a t i m e和- c t i m e选项,但它们都和- m t i m e选项。
  7. -nogroup:查找无有效所属组的文件,即该文件所属的组在/ e t c / g r o u p s中不存在。
  8. -nouser:查找无有效属主的文件,即该文件的属主在/ e t c / p a s s w d中不存在。
  9. -newer file1 ! file2:查找更改时间比文件f i l e 1新但比文件f i l e 2旧的文件。
  10. -type 查找某一类型的文件,诸如:

  11. b - 块设备文件。
  12. d - 目录。
  13. c - 字符设备文件。
  14. p - 管道文件。
  15. l - 符号链接文件。
  16. f - 普通文件。

  17. -size n:[c] 查找文件长度为n块的文件,带有c时表示文件长度以字节计。
  18. -depth:在查找文件时,首先查找当前目录中的文件,然后再在其子目录中查找。
  19. -fstype:查找位于某一类型文件系统中的文件,这些文件系统类型通常可以在配置文件/ e t c / f s t a b中找到,该配置文件中包含了本系统中有关文件系统的信息。

  20. -mount:在查找文件时不跨越文件系统m o u n t点。
  21. -follow:如果f i n d命令遇到符号链接文件,就跟踪至链接所指向的文件。
  22. -cpio:对匹配的文件使用c p i o命令,将这些文件备份到磁带设备中。
复制代码

另外,下面三个的区别:


  1.      -amin n

  2.   查找系统中最后N分钟访问的文件

  3.   -atime n

  4.   查找系统中最后n*24小时访问的文件

  5.   -cmin n

  6.   查找系统中最后N分钟被改变文件状态的文件

  7.   -ctime n

  8.   查找系统中最后n*24小时被改变文件状态的文件

  9.     -mmin n

  10.   查找系统中最后N分钟被改变文件数据的文件

  11.   -mtime n

  12.   查找系统中最后n*24小时被改变文件数据的文件
复制代码


使用exec或ok来执行shell命令
使用find时,只要把想要的操作写在一个文件里,就可以用exec来配合find查找,很方便的

(在有些操作系统中只允许- e x e c选项执行诸如l s或ls -l这样的命令)。大多数用户使用这一选项是为了查找旧文件并删除它们。建议在真正执行r m命令删除文件之前,最好先用l s命令看一下,确认它们是所要删除的文件。

e x e c选项后面跟随着所要执行的命令或脚本,然后是一对儿{ },一个空格和一个\,最后是一个分号。

为了使用e x e c选项,必须要同时使用p r i n t选项。如果验证一下f i n d命令,会发现该命令只输出从当前路径起的相对路径及文件名。


例如:为了用ls -l命令列出所匹配到的文件,可以把ls -l命令放在f i n d命令的- e x e c选项中

  1. # find . -type f -exec ls -l {} \;
  2. -rw-r--r--    1 root     root        34928 2003-02-25  ./conf/httpd.conf
  3. -rw-r--r--    1 root     root        12959 2003-02-25  ./conf/magic
  4. -rw-r--r--    1 root     root          180 2003-02-25  ./conf.d/README
复制代码

上面的例子中,f i n d命令匹配到了当前目录下的所有普通文件,并在- e x e c选项中使用ls -l命令将它们列出。

在/ l o g s目录中查找更改时间在5日以前的文件并删除它们:

  1. $ find logs -type f -mtime +5 -exec rm {} \;
复制代码

记住,在s h e l l中用任何方式删除文件之前,应当先查看相应的文件,一定要小心!当使用诸如m v或r m命令时,可以使用- e x e c选项的安全模式。它将在对每个匹配到的文件进行操作之前提示你。

在下面的例子中, f i n d命令在当前目录中查找所有文件名以. L O G结尾、更改时间在5日以上的文件,并删除它们,只不过在删除之前先给出提示。

  1. $ find . -name "*.conf"  -mtime +5 -ok rm {} \;
  2. < rm ... ./conf/httpd.conf > ? n
复制代码

按y键删除文件,按n键不删除。


任何形式的命令都可以在- e x e c选项中使用。

在下面的例子中我们使用g r e p命令。f i n d命令首先匹配所有文件名为“ passwd*”的文件,例如passwd、passwd.old、passwd.bak,然后执
行grep命令看看在这些文件中是否存在一个sam用户。

  1. # find /etc -name "passwd*" -exec grep "sam" {} \;
  2. sam:x:501:501::/usr/sam:/bin/bash
复制代码



find命令的例子

查找当前用户主目录下的所有文件,下面两种方法都可以使用:

  1. $ find $HOME -print
  2. $ find ~ -print
复制代码


为了在当前目录中文件属主具有读、写权限,并且文件所属组的用户和其他用户具有读权限的文件,可以用:

  1. $ find . -type f -perm 644 -exec ls -l {} \;
复制代码


为了查找系统中所有文件长度为0的普通文件,并列出它们的完整路径,可以用:

  1. $ find / -type f -size 0 -exec ls -l {} \;
复制代码


查找/var/logs目录中更改时间在7日以前的普通文件,并在删除之前询问它们:

  1. $ find /var/logs -type f -mtime +7 -ok rm {} \;
复制代码


为了查找系统中所有属于root组的文件,可以用:

  1. $find . -group root -exec ls -l {} \;
  2. -rw-r--r--    1 root     root          595 10月 31 01:09 ./fie1
复制代码



下面的find命令将删除当目录中访问时间在7日以来、含有数字后缀的admin.log文件。该命令只检查三位数字,所以相应文件的后缀不要超过999。
先建几个admin.log*的文件 ,才能使用下面这个命令

  1. $ find . -name "admin.log[0-9][0-9][0-9]" -atime -7  -ok
  2. rm {} \;
  3. < rm ... ./admin.log001 > ? n
  4. < rm ... ./admin.log002 > ? n
  5. < rm ... ./admin.log042 > ? n
  6. < rm ... ./admin.log942 > ? n
复制代码


为了查找当前文件系统中的所有目录并排序,可以用:

  1. $ find . -type d  |sort
复制代码


为了查找系统中所有的r m t磁带设备,可以用:

  1. $ find /dev/rmt -print
复制代码


2.xargs
在使用f i n d命令的- e x e c选项处理匹配到的文件时, f i n d命令将所有匹配到的文件一起传递给e x e c执行。但有些系统对能够传递给e x e c的命令长度有限制,这样在f i n d命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是x a rg s命令的用处所在,特别是与f i n d命令一起使用。

F i n d命令把匹配到的文件传递给x a rg s命令,而x a rg s命令每次只获取一部分文件而不是全部,不像- e x e c选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。

在有些系统中,使用- e x e c选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;
而使用x a rg s命令则只有一个进程。另外,在使用x a rg s命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。

来看看x a rg s命令是如何同f i n d命令一起使用的,并给出一些例子。

下面的例子查找系统中的每一个普通文件,然后使用x a rg s命令来测试它们分别属于哪类文件

  1. #find . -type f -print | xargs file
  2. ./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text
  3. ./.kde/Autostart/.directory:      ISO-8859 text\
  4. ......
复制代码


在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到/tmp/core.log 文件中:

  1. $ find / -name "core" -print | xargs echo "" >/tmp/core.log
复制代码

上面这个执行太慢,我改成在当前目录下查找

  1. #find . -name "file*" -print | xargs echo "" > /temp/core.log
  2. # cat /temp/core.log
  3. ./file6
复制代码


在当前目录下查找所有用户具有读、写和执行权限的文件,并收回相应的写权限:


  1. # ls -l
  2. drwxrwxrwx    2 sam      adm          4096 10月 30 20:14 file6
  3. -rwxrwxrwx    2 sam      adm             0 10月 31 01:01 http3.conf
  4. -rwxrwxrwx    2 sam      adm             0 10月 31 01:01 httpd.conf

  5. # find . -perm -7 -print | xargs chmod o-w
  6. # ls -l
  7. drwxrwxr-x    2 sam      adm          4096 10月 30 20:14 file6
  8. -rwxrwxr-x    2 sam      adm             0 10月 31 01:01 http3.conf
  9. -rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf

复制代码

用g r e p命令在所有的普通文件中搜索hostname这个词:

  1. # find . -type f -print | xargs grep "hostname"
  2. ./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
  3. ./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
  4. on your
复制代码


用g r e p命令在当前目录下的所有普通文件中搜索hostnames这个词:

  1. # find . -name \* -type f -print | xargs grep "hostnames"
  2. ./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
  3. ./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
  4. on your
复制代码

注意,在上面的例子中, \用来取消f i n d命令中的*在s h e l l中的特殊含义。

3.f i n d命令配合使用e x e c和x a rg s可以使用户对所匹配到的文件执行几乎所有的命令。

下面是find一些常用参数的例子,有用到的时候查查就行了,像上面前几个贴子,都用到了其中的的一些参数,也可以用man或查看论坛里其它贴子有find的命令手册

a.使用name选项

文件名选项是f i n d命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用。
可以使用某种文件名模式来匹配文件,记住要用引号将文件名模式引起来。
不管当前路径是什么,如果想要在自己的根目录$ H O M E中查找文件名符合* . t x t的文件,使用~作为' p a t h n a m e参数,波浪号~代表了你的$ H O M E目录。

  1. $ find ~ -name "*.txt" -print
复制代码


想要在当前目录及子目录中查找所有的‘ * . t x t’文件,可以用:

  1. $ find . -name "*.txt" -print
复制代码


想要的当前目录及子目录中查找文件名以一个大写字母开头的文件,可以用:

  1. $ find . -name "[A-Z]*" -print
复制代码


想要在/ e t c目录中查找文件名以h o s t开头的文件,可以用:

  1. $ find /etc -name "host*" -print
复制代码


想要查找$ H O M E目录中的文件,可以用:

  1. $ find ~ -name "*" -print 或find . -print
复制代码


要想让系统高负荷运行,就从根目录开始查找所有的文件。

  1. $ find / -name "*" -print
复制代码


如果想在当前目录查找文件名以两个小写字母开头,跟着是两个数字,最后是. t x t的文件,下面的命令就能够返回名为a x 3 7 . t x t的文件:

  1. $find . -name "[a-z][a-z][0--9][0--9].txt" -print
复制代码




b.用perm选项

按照文件权限模式用- p e r m选项。

按文件权限模式来查找文件的话。最好使用八进制的权限表示法。

如在当前目录下查找文件权限位为7 5 5的文件,即文件属主可以读、写、执行,其他用户可以读、执行的文件,可以用:

  1. $ find . -perm 755 -print
复制代码


还有一种表达方法:在八进制数字前面要加一个横杠-,表示都匹配,如-007就相当于777,-006相当于666

  1. # ls -l
  2. -rwxrwxr-x    2 sam      adm             0 10月 31 01:01 http3.conf
  3. -rw-rw-rw-    1 sam      adm         34890 10月 31 00:57 httpd1.conf
  4. -rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf
  5. drw-rw-rw-    2 gem      group        4096 10月 26 19:48 sam
  6. -rw-rw-rw-    1 root     root         2792 10月 31 20:19 temp

  7. # find . -perm 006
  8. # find . -perm -006
  9. ./sam
  10. ./httpd1.conf
  11. ./temp
复制代码

-perm mode:文件许可正好符合mode
-perm +mode:文件许可部分符合mode
-perm -mode: 文件许可完全符合mode

c.忽略某个目录

如果在查找文件时希望忽略某个目录,因为你知道那个目录中没有你所要查找的文件,那么可以使用- p r u n e选项来指出需要忽略的目录。在使用- p r u n e选项时要当心,因为如果你同时使用了- d e p t h选项,那么- p r u n e选项就会被f i n d命令忽略。

如果希望在/ a p p s目录下查找文件,但不希望在/ a p p s / b i n目录下查找,可以用:

  1. $ find /apps -path "/apps/bin" -prune -o -print
复制代码


补充:
使用find查找文件的时候怎么避开某个文件目录

比如要在/usr/sam目录下查找不在dir1子目录之内的所有文件

  1. find /usr/sam -path "/usr/sam/dir1" -prune -o -print
复制代码



find [-path ..] [expression] 在路径列表的后面的是表达式
-path "/usr/sam" -prune -o -print 是 -path "/usr/sam" -a -prune -o -print 的简写表达式按顺序求值, -a 和 -o 都是短路求值,与 shell 的 && 和 || 类似如果 -path "/usr/sam" 为真,则求值 -prune , -prune 返回真,与逻辑表达式为真;否则不求值 -prune ,与逻辑表达式为假。如果 -path "/usr/sam" -a -prune 为假,则求值 -print ,-print 返回真,或逻辑表达式为真;否则不求值 -print,或逻辑表达式为真。


这个表达式组合特例可以用伪码写为


  1. if -path "/usr/sam"  then
  2.           -prune
  3. else
  4.           -print

复制代码


避开多个文件夹

  1. find /usr/sam \( -path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -print
复制代码


圆括号表示表达式的结合。
\ 表示引用,即指示 shell 不对后面的字符作特殊解释,而留给 find 命令去解释其意义。


查找某一确定文件,-name等选项加在-o 之后

  1. #find /usr/sam  \(-path /usr/sam/dir1 -o -path /usr/sam/file1 \) -prune -o -name "temp" -print
复制代码


4.使用user和nouser选项

按文件属主查找文件,如在$ H O M E目录中查找文件属主为sam的文件,可以用:

  1. $ find ~ -user sam -print
复制代码


在/ e t c目录下查找文件属主为u u c p的文件:

  1. $ find /etc -user uucp -print
复制代码


为了查找属主帐户已经被删除的文件,可以使用- n o u s e r选项。这样就能够找到那些属主在/ e t c / p a s s w d文件中没有有效帐户的文件。在使用- n o u s e r选项时,不必给出用户名; f i n d命令能够为你完成相应的工作。
例如,希望在/ h o m e目录下查找所有的这类文件,可以用:

  1. $ find /home -nouser -print
复制代码


5、使用group和nogroup选项

就像u s e r和n o u s e r选项一样,针对文件所属于的用户组, f i n d命令也具有同样的选项,为了在/ a p p s目录下查找属于gem用户组的文件,可以用:

  1. $ find /apps -group gem -print
复制代码


要查找没有有效所属用户组的所有文件,可以使用n o g r o u p选项。下面的f i n d命令从文件系统的根目录处查找这样的文件

  1. $ find / -nogroup-print
复制代码


6、按照更改时间或访问时间等查找文件

如果希望按照更改时间来查找文件,可以使用m t i m e,atime或ctime选项。如果系统突然没有可用空间了,很有可能某一个文件的长度在此期间增长迅速,这时就可以用m t i m e选项来查找这样的文件。
用减号-来限定更改时间在距今n日以内的文件,而用加号+来限定更改时间在距今n日以前的文件。
希望在系统根目录下查找更改时间在5日以内的文件,可以用:

  1. $ find / -mtime -5 -print
复制代码


为了在/ v a r / a d m目录下查找更改时间在3日以前的文件,可以用:

  1. $ find /var/adm -mtime +3 -print
复制代码


7、查找比某个文件新或旧的文件

如果希望查找更改时间比某个文件新但比另一个文件旧的所有文件,可以使用- n e w e r选项。它的一般形式为:

  1. newest_file_name ! oldest_file_name
复制代码

其中,!是逻辑非符号。

查找更改时间比文件sam新但比文件temp旧的文件:

例:有两个文件

  1. -rw-r--r--    1 sam      adm             0 10月 31 01:07 fiel
  2. -rw-rw-rw-    1 sam      adm         34890 10月 31 00:57 httpd1.conf
  3. -rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf
  4. drw-rw-rw-    2 gem      group        4096 10月 26 19:48 sam
  5. -rw-rw-rw-    1 root     root         2792 10月 31 20:19 temp

  6. # find -newer httpd1.conf  ! -newer temp -ls
  7. 1077669    0 -rwxrwxr-x   2 sam      adm             0 10月 31 01:01 ./httpd.conf
  8. 1077671    4 -rw-rw-rw-   1 root     root         2792 10月 31 20:19 ./temp
  9. 1077673    0 -rw-r--r--   1 sam      adm             0 10月 31 01:07 ./fiel
复制代码



查找更改时间在比temp文件新的文件:

  1. $ find . -newer temp -print
复制代码


8、使用type选项

在/ e t c目录下查找所有的目录,可以用:

  1. $ find /etc -type d -print
复制代码


在当前目录下查找除目录以外的所有类型的文件,可以用:

  1. $ find . ! -type d -print
复制代码


在/ e t c目录下查找所有的符号链接文件,可以用:

  1. $ find /etc -type l -print
复制代码


9、使用size选项

可以按照文件长度来查找文件,这里所指的文件长度既可以用块( b l o c k)来计量,也可以用字节来计量。以字节计量文件长度的表达形式为N c;以块计量文件长度只用数字表示即可。
在按照文件长度查找文件时,一般使用这种以字节表示的文件长度,在查看文件系统的大小,因为这时使用块来计量更容易转换。

在当前目录下查找文件长度大于1 M字节的文件:

  1. $ find . -size +1000000c -print
复制代码


在/ h o m e / a p a c h e目录下查找文件长度恰好为1 0 0字节的文件:

  1. $ find /home/apache -size 100c -print
复制代码


在当前目录下查找长度超过1 0块的文件(一块等于5 1 2字节):

  1. $ find . -size +10 -print
复制代码


10、使用depth选项

在使用f i n d命令时,可能希望先匹配所有的文件,再在子目录中查找。使用d e p t h选项就可以使f i n d命令这样做。这样做的一个原因就是,当在使用f i n d命令向磁带上备份文件系统时,希望首先备份所有的文件,其次再备份子目录中的文件。

在下面的例子中, f i n d命令从文件系统的根目录开始,查找一个名为C O N . F I L E的文件。
它将首先匹配所有的文件然后再进入子目录中查找。

  1. $ find / -name "CON.FILE" -depth -print
复制代码


11、使用mount选项
在当前的文件系统中查找文件(不进入其他文件系统),可以使用f i n d命令的m o u n t选项。
从当前目录开始查找位于本文件系统中文件名以X C结尾的文件:

  1. $ find . -name "*.XC" -mount -print
复制代码


Linux其他查找文件的相关命令如下:

 more [文件名]

  分页显示一个文件或任何输出结果

  其实more不是用来寻找文件的,但是一般人却十有八九是在找文件时把它派上用场。

  因为 more 主要的作用是把输出结果显示在屏幕上,一页停止一次,所以例如当我们用 ls 命令去找一个 x字母开头的文件,而下达了 ls x* 却仍然列出太多文件,一个屏幕看不完时,就可以配合管道符号和 more 命令:

  ls x* | more

  它会一屏停止一下,等待您按空白键才继续往上卷。于是 more 俨然犹如 DOS 的 DIR 命令 /P 选项的地位了。而 more 当主角的时候,是用做一页一次显示文章,例如我们想要看 /etc 里面的 XF86Config 文件,可以下如下命令:

  more /etc/XF86Config

  这样,我们就可以不断按空白键把这个文件慢慢看完。但是,因为more 先天的设计,如果您看完了这页,想要回头看上一页,很抱歉,是不行的,您必须从头再来!于是大家在使用中就干脆摒弃这个命令,而代之以 vi 或者pico, joe等文书编辑器来看文字文件了!

  pico /etc/XF86Config

  到最后,很少人再用more了。所以more经常配合 ls在找文件的场合出现,每天都可以上场十几次。

  所以,相信把失去主要舞台的 more归类为找寻文件的相关命令虽不合法,但合情合理也合于现状。

  练习:

  请您用 more 去看一个文字文件,与用 pico 去看一个文字文件相比较,哪一个比较方便?

 less [文件名]

  分页显示一个文件并且可以回头

  less命令很好笑,取名时就故意与more 命令打对台,你叫“更多”,我就叫“更少”,就好像你叫黑人牙膏我就叫白人牙膏一样。事实上与什么“更多”、“更少”都没有关系。它最主要只是为了改进一点:more 不能回头看的问题!

  less 的优点就是可以随时回头,最简单的用【PgUp】键就可以向上翻。

  可是依我们的孤陋之见,还是用文书编辑器去阅读文件就好了嘛,更何况 less 本身还有高达 42 个选项,何必那么麻烦!

  所以,为了您好,选项我们也不介绍了。这个命令目前只在 Linux 系统可以使用,其他 UNIX 家族尚无。

  练习:

  (1)请您用 less 去看一个文字文件,与用 pico 去看一个文字文件相比,哪一个比较方便?

  (2)less命令能像more命令一样配合管道符号,让ls的列示文件能够回头翻看吗?

  whereis 文件名

  寻找文件工具

  whereis 是一个小巧好用的文件寻找工具,它专门用来寻找可执行的程序、原始程序和使用手册。

  例如执行命令:

  whereis bzip2

  它就会告诉您,bzip2 放在 /usr/bin 。通常,如果您确定某个东西是程序,而用 whereis 找不到的话,那就表示本系统没有安装该程序了,例如:

  whereis cjoe

  表示这套系统中没有装 cjoe,否则应该会找到才对。

  练习:

  (1)找找您的系统上有没有xpaint这个程序?因为这个程序在 KDE 和 GNOME 的默认主菜单上都没有列出来,但并不表示一定没有。

  (2)能用 whereis 命令去找到XF86Config设置文件在哪里吗?为什么?

  locate 文件名

  寻找文件工具

  locate 也是一个寻找文件的工具,但是它不像 whereis 只能找程序文件等几种文件,也不像find那么复杂,可以算是“中庸之道”!

  中庸之道,往往就是大部分人最佳的选择。


  whereis找不到的文件,find要一大串命令,还花了很久的时间才找到的XF86Config设置文件,而用locate一下子就简单找到了!

  练习:

  (1)用 locate 找找有没有一个inittab文件?

  (2)用 locate 找找有哪些 .pcf 字形文件。如果找不到的话,找找看已经压缩过的字形文件 .pcf.Z 或者 .pcf.gz。

  grep [-选项] [字串] [文件名]

  寻找某字串内容工具

  有些时候,我们存储文件时随手乱取了一个文件名,事后自己都忘了那个文件名叫什么,连开头第一个字母都想不起来。那么,如果您还记得该文件一点特殊的词语,应该可以用 grep 命令找到。

  例如,我们想在一个目录的200个文件里面,找出哪一个文件提到“排版”这个词语:

  grep 排版 *.txt

  找到了两个文件有此字样。然后您再自行调出看看到底哪一篇才是您所要的即可。寻找时注意尽量寻找特殊词语,如果您输入“然后”、“这样”、“不是”、“电脑”……这类太普遍的词语,可能几百篇文章中都有,那就失去了用 grep 找文件的意义了。

  因为例子中第二篇文章出现了两次,所以列出了3行,其实只有两个文件符合。如果您想要让它凡是符合条件的只出现一次的话,加上 -l 选项就可以了.

  其他常用的选项还有:

  -n 同时列出该词语出现在文章的第几行。

  -c 计算一下该字串出现的次数。

  -i 比对的时候,不计较大小写的不同。

  练习:

  当我们要为系统增加一个用户时,往往担心添加一个已经存在的用户名字。如果用 grep 命令,可不可以避免这个问题?如何避免?

 env

  查看环境设置

  env 命令可以设置很多环境变量,例如终端机的类型、使用的浏览器、用户主目录(Home Directory)的所在等等。

  但是我们现在把焦点集中在其中一项:PATH环境变量。

  因为 PATH 这个环境变量掌管了您下达命令的时候,它会到下面几个目录去找您命令的程序:

  /usr/bin

  /sbin

  /usr/sbin

  /usr/X11R6/bin

  /root/bin

  那么您用 env 命令,让它把所有的环境变量列出来的时候,就可以看到有一行PATH 设置,把上述几个目录用分号分开,然后串成一串。这就是它所谓寻找命令的“路径”。DOS 系统也有一个同名而且意义非常相似的路径设置。

  如果您想在它查找命令的路径上增加一个 /usr/bin/he,而把 /root/bin 去掉,那么您可以重新下达:

  env PATH=/usr/bin;/sbin;/usr/sbin;/usr/X11R6/bin;/usr/bin/he

  当然env 其他的变量也可以这样改变。但是因为涉及面太广,我们就只把焦点集中在改变查找路径上,其他暂时不谈。

  练习:

  用env命令改变您默认的浏览器。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-14 21:06:30 | 显示全部楼层
Post by 寂寞烈火
不知道楼主要说什么?!!!

ok,我stop,呵呵
原来看不出来!就当我写的废话吧哈哈哈哈
回复 支持 反对

使用道具 举报

发表于 2005-3-14 22:24:17 | 显示全部楼层
zhy2111314 兄休怒!
我粗略看一下写得不错,是合适新手兄弟的入门教程,我给加精~~,多谢兄弟的劳动。
另外补充一下setuid这节,脚本的setuid在linux下是无效的,在一些unix下可行,但有许多潜在的危险,这在论坛里有过讨论。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-17 08:26:12 | 显示全部楼层
Post by kiron
zhy2111314 兄休怒!
我粗略看一下写得不错,是合适新手兄弟的入门教程,我给加精~~,多谢兄弟的劳动。
另外补充一下setuid这节,脚本的setuid在linux下是无效的,在一些unix下可行,但有许多潜在的危险,这在论坛里有过讨论。

多谢版主,我对linuxsir很有感情很喜欢这里和这里的兄弟!
这两天病了所以没过来,我把剩下的补完吧!如果有错误敬请指正!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-17 08:36:59 | 显示全部楼层
三.Linux后台运行命令详述
本次主要讲述后台运行命令(crontab,at,&,nohup)及(*,?,[])等

&#8226; 设置c r o n t a b文件,并用它来提交作业。
&#8226; 使用a t命令来提交作业。
&#8226; 在后台提交作业。
&#8226; 使用n o h u p命令提交作业。
名词解释:
cron:系统调度进程。可以使用它在每天的非高峰负荷时间段运行作业,或在一周或一月中的不同时段运行。
at命令:使用它在一个特定的时间运行一些特殊的作业,或在晚一些的非负荷高峰时间段或高峰负荷时间段运行。
&:使用它在后台运行一个占用时间不长的进程。
nohup:用它在后台运行一个命令,即使在用户退出时也不受影响

1.cron and crontab

c r o n是系统主要的调度进程,可以在无需人工干预的情况下运行作业。c r o n t a b命令允许用户提交、编辑或删除相应的作业。每一个用户都可以有一个c r o n t a b文件来保存调度信息。可以使用它运行任意一个s h e l l脚本或某个命令,每小时运行一次,或一周三次,这完全取决于你。每一个用户都可以有自己的c r o n t a b文件,但在一个较大的系统中,系统管理员一般会禁止这些文件,而只在整个系统保留一个这样的文件。系统管理员是通过c r o n . d e n y和c r o n . a l l o w这两个文件来禁止或允许用户拥有自己的c r o n t a b文件。

crontab的域

为了能够在特定的时间运行作业,需要了解c r o n t a b文件每个条目中各个域的意义和格式。

下面就是这些域:

  1. 第1列分钟1~5 9
  2. 第2列小时1~2 3(0表示子夜)
  3. 第3列日1~3 1
  4. 第4列月1~1 2
  5. 第5列星期0~6(0表示星期天)
  6. 第6列要运行的命令
复制代码


下面是c r o n t a b的格式:

  1. 分< >时< >日< >月< >星期< >要运行的命令
复制代码


其中< >表示空格。

c r o n t a b文件的一个条目是从左边读起的,第一列是分,最后一列是要运行的命令,它位于星期的后面。

可以用横杠-来表示一个时间范围,例如你希望星期一至星期五运行某个作业,那么可以在星期域使用1 - 5来表示。
还可以在这些域中使用逗号“,”,例如你希望星期一和星期四运行某个作业,只需要使用1 , 4来表示。
可以用星号*来表示连续的时间段。如果你对某个表示时间的域没有特别的限定,也应该在该域填入*。该文件的每一个条目必须含有5个时间域,而且每个域之间要用空格分隔。
该文件中所有的注释行要在行首用#来表示。



c r o n t a b文件例子:

  1. 30 21* * * /apps/bin/cleanup.sh
复制代码

上面的例子表示每晚的2 1 : 3 0运行/ a p p s / b i n目录下的c l e a n u p . s h。


  1. 45 4 1,10,22 * * /apps/bin/backup.sh
复制代码

上面的例子表示每月1、1 0、2 2日的4 : 4 5运行/ a p p s / b i n目录下的b a c k u p . s h。


  1. 10 1 * * 6,0 /bin/find -name "core" -exec rm {} \;
复制代码

上面的例子表示每周六、周日的1 : 1 0运行一个f i n d命令。


  1. 0,30 18-23 * * * /apps/bin/dbcheck.sh
复制代码

上面的例子表示在每天1 8 : 0 0至2 3 : 0 0之间每隔3 0分钟运行/ a p p s / b i n目录下的d b c h e c k . s h。


  1. 0 23 * * 6 /apps/bin/qtrend.sh
复制代码

上面的例子表示每星期六的11 : 0 0 p m运行/ a p p s / b i n目录下的q t r e n d . s h。

你可能已经注意到上面的例子中,每个命令都给出了绝对路径。当使用c r o n t a b运行s h e l l脚本时,要由用户来给出脚本的绝对路径,设置相应的环境变量。记住,既然是用户向c r o n提交了这些作业,就要向c r o n提供所需的全部环境。不要假定c r o n知道所需要的特殊环境,它其实并不知道。所以你要保证在s h e l l脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量。
如果c r o n不能运行相应的脚本,用户将会收到一个邮件说明其中的原因。

c r o n t a b命令的一般形式为:

  1. crontab [-u user] -e -l -r
复制代码


其中:
-u 用户名。
-e 编辑c r o n t a b文件。
-l 列出c r o n t a b文件中的内容。
-r 删除c r o n t a b文件。


如果使用自己的名字登录,就不用使用- u选项,因为在执行c r o n t a b命令时,该命令能够知道当前的用户。


创建一个新的crontab文件

在向c r o n进程提交一个c r o n t a b文件之前,要先设置环境变量E D I TO R.c r o n进程根据它来确定使用哪个编辑器编辑c r o n t a b文件。大部份的U N I X和L I N U X用户都使用v i,如果你也是这样,那么你就编辑$ H O M E目录下的. p r o f i l e文件,在其中加入这样一行:

  1. EDITOR=vi; export EDITOR
复制代码

然后保存并退出。

创建一个名为< u s e r > c r o n的文件,其中< u s e r >是用户名,例如, samcron。在该文件中加入如下的内容。

  1. #(put your own initials here) echo the date to the console every
  2. #15 minutes between 6pm and 6am
  3. 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console
复制代码


保存并退出。确信前面5个域用空格分隔。
在上面的例子中,系统将每隔1 5分钟向控制台输出一次当前时间。如果系统崩溃或挂起,从最后所显示的时间就可以一眼看出系统是什么时间停止工作的。在有些系统中,用t t y 1来表示控制台,可以根据实际情况对上面的例子进行相应的修改。
为了提交你刚刚创建的c r o n t a b文件,可以把这个新创建的文件作为c r o n命令的参数:

  1. $su sam
  2. crontab samcron
复制代码


为了方便演示,切换到sam用户环境下,然后用crontab samcron提交给c r o n进程,它将每隔1 5分钟运行一次。

同时,新创建文件的一个副本已经被放在/ v a r / s p o o l / c r o n目录中,文件名就是用户名(即sam)。


  1. #su
  2. # cat /var/spool/cron/sam
  3. # DO NOT EDIT THIS FILE - edit the master and reinstall.
  4. # (samcron installed on Wed Nov 10 21:41:55 2004)
  5. # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
  6. #(put your own initials here) echo the date to the console every
  7. #15 minutes between 6pm and 6am
  8. 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console
复制代码


回到root下,查看/var/spool/cron/sam

列出crontab文件

为了列出c r o n t a b文件,可以用:

  1. $ crontab -l
  2. # DO NOT EDIT THIS FILE - edit the master and reinstall.
  3. # (samcron installed on Wed Nov 10 21:41:55 2004)
  4. # (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
  5. #(put your own initials here) echo the date to the console every
  6. #15 minutes between 6pm and 6am
  7. 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console
复制代码


你将会看到和上面类似的内容。可以使用这种方法在$ H O M E目录中对c r o n t a b文件做一备份:

  1. $ crontab -l > $HOME/mycron
复制代码

这样,一旦不小心误删了c r o n t a b文件,可以用上一节所讲述的方法迅速恢复。


编辑crontab文件

如果希望添加、删除或编辑c r o n t a b文件中的条目,而E D I TO R环境变量又设置为v i,那么就可以用v i来编辑c r o n t a b文件,相应的命令为:

  1. $ crontab -e
复制代码


可以像使用v i编辑其他任何文件那样修改c r o n t a b文件并退出。如果修改了某些条目或添加了新的条目,那么在保存该文件时, c r o n会对其进行必要的完整性检查。如果其中的某个域出现了超出允许范围的值,它会提示你。
例如,加入下面的一条:

  1. #DT:delete core files,at 3:30am on 1,7,14,21,26 days of each month
  2. 30 3 1,7,14,21,26 * * /bin/find -name "core" -exec rm {} \;
复制代码


现在保存并退出。最好在c r o n t a b文件的每一个条目之上加入一条注释,这样就可以知道它的功能、运行时间,更为重要的是,知道这是哪位用户的作业。
现在让我们使用前面讲过的crontab -l命令列出它的全部信息:

  1. #(put your own initials here) echo the date to the console every
  2. #15 minutes between 6pm and 6am
  3. 0,15,30,45 18-06 * * * /bin/echo 'date' > /dev/console

  4. #DT:delete core files,at 3:30am on 1,7,14,21,26 days of each month
  5. 30 3 1,7,14,21,26 * * /bin/find -name "core" -exec rm {} \;
复制代码



删除crontab文件

为了删除c r o n t a b文件,可以用:

  1. $ crontab -r
复制代码


恢复丢失的crontab文件
如果不小心误删了c r o n t a b文件,假设你在自己的$ H O M E目录下还有一个备份,那么可以将其拷贝到/ v a r / s p o o l / c r o n / < u s e r n a m e >,其中< u s e r n a m e >是用户名。如果由于权限问题无法完成拷贝,可以用:

  1. $ crontab <filename>
复制代码


其中,< f i l e n a m e >是你在$ H O M E目录中副本的文件名。
建议在自己的$ H O M E目录中保存一个该文件的副本。编辑副本,然后重新提交新的文件。
有些c r o n t a b的变体有些怪异,所以在使用c r o n t a b命令时要格外小心。如果遗漏了任何选项,c r o n t a b可能会打开一个空文件,或者看起来像是个空文件。这时敲d e l e t e键退出,不要按< C t r l - D >,否则你将丢失c r o n t a b文件。

2.at

a t命令允许用户向c r o n守护进程提交作业,使其在稍后的时间运行。一旦一个作业被提交, a t命令将会保留所有当前的环境变量,包括路径,不象c r o n t a b,只提供缺省的环境。该作业的所有输出都将以电子邮件的形式发送给用户,除非你对其输出进行了重定向,绝大多数情况下是重定向到某个文件中。
和c r o n t a b一样,根用户可以通过/ e t c目录下的a t . a l l o w和a t . d e n y文件来控制哪些用户可以使用a t命令,哪些用户不行。不过一般来说,对a t命令的使用不如对c r o n t a b的使用限制那么严格。


a t命令的基本形式为:

  1. at [-f script] [-m -l -r] [time] [date]
复制代码


其中,
-f:script 是所要提交的脚本或命令。

-l:列出当前所有等待运行的作业。a t q命令具有相同的作用。
-r:清除作业。为了清除某个作业,还要提供相应的作业标识( I D);有些U N I X变体只接受a t r m作为清除命令。
-m:作业完成后给用户发邮件。

time:at命令的时间格式非常灵活;可以是H、H H . H H M M、H H : M M或H : M,其中H和M分别是小时和分钟。还可以使用a . m .或p . m .。
date:日期格式可以是月份数或日期数,而且a t命令还能够识别诸如t o d a y、t o m o r r o w这样的词。

  使用at命令提交命令或脚本

使用a t命令提交作业有几种不同的形式,可以通过命令行方式,也可以使用a t命令提示符。一般来说在提交若干行的系统命令时,使用a t命令提示符方式,在提交s h e l l脚本时,使用命令行方式。

提示符方式:

  1. 以在a t命令后面跟上日期/时间并回车。然后就进入了a t命令提示符,这时只需逐条输入相应的命令,然后按‘ < C T R L - D >’退出。
复制代码


命令行方式:

  1. at [-f script] [-m -l -r] [time] [date]
复制代码



例一:提示符方式

  1. # su sam
  2. $ at 10:40
  3. warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh
  4. at> find /etc -name "passwd" -print
  5. at> <EOT>
  6. job 1 at 2004-11-02 10:40
复制代码


其中, < E O T >就是< C T R L - D >。在10:40系统将执行一个简单的f i n d命令。提交的作业被分配了一个唯一标识job 1。该命令在完成以后会将全部结果以邮件的形式发送给我。

下面这些日期/时间格式都是a t命令可以接受的:

  1. at 5.00am May23
  2. at 11.20pm
  3. at now +2 hour
  4. at 9am tomorrow
  5. at 15:00 May24
  6. at now + 10 minutes
复制代码


  例二:命令行方式
如果希望向a t命令提交一个s h e l l脚本,使用其命令行方式即可。在提交脚本时使用- f选项。
如:

  1. $ touch db_table.sh
  2. $ at 3:00pm tomorrow -f db_table.sh
  3. warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh
  4. job 3 at 2004-11-02 15:00
复制代码


在上面的例子中,一个叫做d b _ t a b l e . s h的脚本将在2004-11-02 15:00运行。

  还可以使用e c h o命令向a t命令提交作业:

  1. $ echo find /etc -name "passwd" -print | at now +1 minute
  2. warning: commands will be executed using (in order) a) $SHELL b) login shell c) /bin/sh
  3. job 4 at 2004-11-01 19:07
复制代码



列出所提交的作业
一个作业被提交后,可以使用at -l命令来列出所有的作业:

  1. $ at -l
  2. 1       2004-11-02 10:40 a sam
  3. 3       2004-11-02 15:00 a sam
  4. 4       2004-11-01 19:07 a sam
复制代码


其中,第一行是作业标识,后面是作业运行的日期/时间。最后一列a代表a t。
还可以使用a t q命令来完成同样的功能,它是a t命令的一个链接。
直接>atq,相当于>at -l

当提交一个作业后,它就被拷贝到/ v a r / s p o o l / a t目录中,准备在要求的时间运行。

  1. # pwd
  2. /var/spool/at
  3. # ls -l
复制代码


清除一个作业

清除作业的命令格式为:

  1. atrm [job no] 或at -r [job no]
复制代码

要清除某个作业,首先要执行at -l命令,以获取相应的作业标识,然后对该作业标识使用at -r 命令,清除该作业。

  1. $ at -l
  2. 1       2004-11-02 10:40 a sam
  3. 3       2004-11-02 15:00 a sam
  4. 4       2004-11-01 19:07 a sam
  5. $at -r 3
  6. $at -l
  7. 1       2004-11-02 10:40 a sam
  8. 4       2004-11-01 19:07 a sam
复制代码



有些系统使用at-r [job no]命令清除作业。

3.&

当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。可以使用&命令把作业放到后台执行。

该命令的一般形式为:
  1. 命令&
复制代码



在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。
不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:

  1. command >out.file 2>&1 &
复制代码


在上面的例子中,2>&1表示所有的标准输出和错误输出都将被重定向到一个叫做out.file 的文件中。
当你成功地提交进程以后,就会显示出一个进程号,可以用它来监控该进程,或杀死它。

例一:

查找名为“httpd.conf”的文件,并把所有标准输出和错误输出重定向到f i n d . d t的文件中:

  1. # find /etc/httpd/ -name "httpd.conf" -print >find.dt 2>&1 &
  2. [2] 7832
  3. [1]   Done                    find /etc/ -name "httpd.conf" -print >find.dt 2>&1 &
复制代码

成功提交该命令之后,系统给出了它的进程号7832。

  1. # cat find.dt
  2. /etc/httpd/conf/httpd.conf
  3. [2]+  Done                    find /etc/httpd/ -name "httpd.conf" -print >find.dt 2>&1 &
复制代码

查看find.dt,可以看到执行结果


例二:
在后台执行脚本,如:有一个叫psl的脚本

  1. $ps psl &
  2. [7878]
复制代码

用ps命令查看进程
用提交命令时所得到的进程号来监控它的运行。用p s命令和g r e p命令列出这个进程:

  1. # ps -x |grep 7832
  2. 7868 pts/0    S      0:00 grep 7832
复制代码


如果系统不支持ps x命令,可以用:

  1. # ps -ef |grep 7832
  2. root      7866  7790  0 23:40 pts/0    00:00:00 grep 7832
复制代码

在用p s命令列出进程时,它无法确定该进程是运行在前台还是后台。


杀死后台进程

杀死后台进程可以使用k i l l命令。当一个进程被放到后台运行时, s h e l l会给出一个进程号,我们可以根据这个进程号,用k i l l命令杀死该进程。该命令的基本形式为:

  1. kill -signal [process_number]
复制代码


现在暂且不要考虑其中的各种不同信号。
在杀进程的时候,执行下面的命令(你的进程号可能会不同)并按回车键。系统将会给出相应的信息告诉用户进程已经被杀死。

  1. $kill 7832
复制代码


如果系统没有给出任何信息,告诉你进程已经被杀死,那么不妨等一会儿,也许系统正在杀该进程,如果还没有回应,就再执行另外一个k i l l命令,这次带上一个信号选项:

  1. $kill - 9 7868
复制代码

如果用上述方法提交了一个后台进程,那么在退出时该进程将会被终止。为了使后台进程能够在退出后继续运行,可以使用n o h u p命令。

4.nohug

如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用n o h u p命令。该命令可以在你退出帐户之后继续运行相应的进程。n o h u p就是不挂起的意思( no hang up)。
该命令的一般形式为:

  1. nohup command &
复制代码


使用nohup命令提交作业
如果使用n o h u p命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为n o h u p . o u t的文件中,除非另外指定了输出文件:

  1. nohup command > myout.file 2>&1
复制代码

在上面的例子中,输出被重定向到m y o u t . f i l e文件中。

让我们来看一个例子,验证一下在退出帐户后相应的作业是否能够继续运行。我们先提交一个名为p s 1的日志清除进程:

  1. $nobup ps1 &
复制代码

现在退出该s h e l l,再重新登录,然后执行下面的命令:

  1. $ps x |grep ps1
复制代码


我们看到,该脚本还在运行。如果系统不支持ps x命令,使用ps -ef|grep ps1命令。

5.一次提交几个作业

如果希望一次提交几个命令,最好能够把它们写入到一个s h e l l脚本文件中,并用n o h u p命令来执行它。
例如,下面的所有命令都用管道符号连接在一起;我们可以把这些命令存入一个文件,并使该文件可执行。

  1. cat /home/accounts/qrt_0499 | /apps/bin/trials.awk | sort | lp
  2. $cat > quarterend
  3. cat /home/accounts/qtr_0499 | /apps/bin/trials.awk | sort | lp
  4. <ctrl-D>
复制代码


现在让它可执行:

  1. $ chmod 744 quarterend
复制代码


我们还将该脚本的所有输出都重定向到一个名为q t r. o u t的文件中。

  1. nobup ./quarterend > qtr.out 2>
复制代码


后台运行作业的:
有时我们必须要对大文件进行大量更改,或执行一些复杂的查找,这些工作最好能够在系统负荷较低时执行。
创建一个定时清理日志文件或完成其他特殊工作的脚本,这样只要提交一次,就可以每天晚上运行,而且无需你干预,只要看看相应的脚本日志就可以了。c r o n和其他工具可以使系统管理任务变得更轻松。

6.*,?,[...],[!...]等


&#8226; 匹配文件名中的任何字符串。
&#8226; 匹配文件名中的单个字符。
&#8226; 匹配文件名中的字母或数字字符。


下面就是这些特殊字符:

* 匹配文件名中的任何字符串,包括空字符串。
? 匹配文件名中的任何单个字符。
[...] 匹配[ ]中所包含的任何字符。
[!...] 匹配[ ]中非感叹号!之后的字符。


当s h e l l遇到上述字符时,就会把它们当作特殊字符,而不是文件名中的普通字符,这样用户就可以用它们来匹配相应的文件名。

a、*:使用星号*可以匹配文件名中的任何字符串。就不用多说了,和win下差不多
b、?:使用可以匹配文件名中的任何单个字符。和win差不多
c、[]:使用[ . . . ]可以用来匹配方括号[ ]中的任何字符。可以使用一个横杠-来连接两个字母或数字,以此来表示一个范围。

1)列出以i或o开头的文件名:

  1. #ls [io]*
复制代码


2)列出log.开头、后面跟随一个数字、然后可以是任意字符串的文件名:

  1. #ls log.[0-9]*
复制代码


3)与例二相反,列出log.开头、后面不跟随一个数字、然后可以是任意字符串的文件名

  1. #ls log.[!0-9]*
复制代码


4)列出所有以LPS开头、中间可以是任何两个字符,最后以1结尾的文件名:

  1. #ls LPS??1
复制代码


5)列出所有以大写字母开头的文件名:

  1. $ ls [A-Z]*
复制代码


6)列出所有以小写字母开头的文件名:

  1. $ ls [a-z]*
复制代码


7)为了列出所有以数字开头的文件名:

  1. $ ls [0-9]*
复制代码


8)列出所有以. 开头的文件名(隐含文件,例如. p r o f i l e、. r h o s t s、. h i s t o r y等):

  1. $ ls .*
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-3-17 08:44:47 | 显示全部楼层
四.Linux输入和输出命令详述

1.echo
首先,在LINUX中,要使转义符生效,需加参数-e
从echo的变量开始说起
如:e c h o命令输出转义符以及变量。

  1. # echo -e "\007your home is $HOME , you are connected on `tty`"
  2. your home is /root , you are connected on /dev/pts/1
  3. # echo -e "\ayour home is $HOME , you are connected on `tty`"
  4. your home is /root , you are connected on /dev/pts/1
  5. #
复制代码


本例中
\007或\a你可以让终端铃响一声
显示出$ H O M E目录,
并且可以让系统执行t t y命令(注意,该命令用键盘左上角的符号,法语中的抑音符引起来,不是单引号 )。

在e c h o命令输出之后附加换行,可以使用\ n选项:


  1. $ cat echod
  2. #!/bin/sh
  3. echo -e "this echo's 3 new lines\n\n\n"
  4. echo "OK"
复制代码

编辑一个新echod,如上内容,然后运行输出如下:

  1. $ ./echod
  2. this echo's 3 new lines



  3. OK
  4. $
复制代码


在e c h o语句中使用跳格符,记住别忘了加反斜杠\:


  1. $ echo -e "here is a tab\there are two tabs\t\tok"
  2. here is a tab   here are two tabs               ok
  3. $
复制代码


把一个字符串输出到文件中,使用重定向符号>。
在下面的例子中一个字符串被重定向到一个名为m y f i l e的文件中:


  1. $ echo "The log files have all been done"> myfile
复制代码


或者可以追加到一个文件的末尾,这意味着不覆盖原有的内容:

  1. $ echo "$LOGNAME carried them out at `date`">>myfile
复制代码


现在让我们看一下m y f i l e文件中的内容:

  1. The log files have all been done
  2. sam carried them out at 六 11月 13 12:54:32 CST 2004
复制代码


引号是一个特殊字符,所以必须要使用反斜杠\来使s h e l l忽略它的特殊含义。
假设你希望使用e c h o命令输出这样的字符串:“/ d e v / r m t 0”,那么我们只要在引号前面加上反斜杠\即可:

  1. $ echo ""/dev/rmt0""
  2. "/dev/rmt0"
  3. $
复制代码


上面说过,

  1. 在LINUX中,要使转义符生效,需加参数-e
复制代码

那么echo可以用的转义字符有下面这些:

  1. \a 鸣叫beep
  2. \n 换行
  3. \t 水平制表
  4. \v 垂直制表
  5. \b 退格
  6. \r 回车
  7. \f 换页
  8. \\ \
  9. \' '
  10. " "
  11. \ddd 一到三位八进制数ddd所代表的字符
  12. \xhh 一到二位十六进制数hh所代表的字符
  13. (这两个都是ASCII码)
  14. \c 取消末行换行符,等价于echo -n ...
复制代码

ps:注意其中\n和\r的区别,可以自己实验......

补充:
echo的特殊用法
a.把字符串输出到标准显示的指定位置:

  1. r= #指定行
  2. c= #指定列
  3. echo -e "\033[${r};${c}H我在这里"
复制代码


b.隐藏光标:

  1. echo -ne "\033[?25l"
复制代码

其中25后面是字母l

c.ANSI控制码
例如:

  1. echo -ne "\033[32m"  #可以将字符的显示颜色改为绿色
  2. echo -ne "\033[3;1H" #可以将光标移到第3行第1列处
复制代码

具体的摘抄一些如下:

  1. \033[0m #关闭所有属性
  2. \033[1m #设置高亮度
  3. \033[4m #下划线
  4. \033[5m #闪烁
  5. \033[7m #反显
  6. \033[8m #消隐
  7. \033[30m -- \33[37m #设置前景色
  8. \033[40m -- \33[47m #设置背景色
  9. \033[nA #光标上移n行
  10. \033[nB #光标下移n行
  11. \033[nC #光标右移n行
  12. \033[nD #光标左移n行
  13. \033[y;xH #设置光标位置
  14. \033[2J #清屏
  15. \033[K #清除从光标到行尾的内容
  16. \033[s #保存光标位置
  17. \033[u #恢复光标位置
  18. \033[?25l #隐藏光标
  19. \033[?25h #显示光标
复制代码


下面是摘抄的shell中俄罗斯方块程序,我也没有具体研究,先放到这里,有兴趣可以研究一下
运行于GNU bash, version 2.05a.0(1)-release (i686-pc-linux-gnu)


  1. #!/bin/bash
  2. # Tetris Game
  3. # 10.21.2003 xhchen<xhchen@winbond.com.tw>

  4. #颜色定义
  5. cRed=1
  6. cGreen=2
  7. cYellow=3
  8. cBlue=4
  9. cFuchsia=5
  10. cCyan=6
  11. cWhite=7
  12. colorTable=($cRed $cGreen $cYellow $cBlue $cFuchsia $cCyan $cWhite)

  13. #位置和大小
  14. iLeft=3
  15. iTop=2
  16. ((iTrayLeft = iLeft + 2))
  17. ((iTrayTop = iTop + 1))
  18. ((iTrayWidth = 10))
  19. ((iTrayHeight = 15))

  20. #颜色设置
  21. cBorder=$cGreen
  22. cScore=$cFuchsia
  23. cScoreValue=$cCyan

  24. #控制信号
  25. #改游戏使用两个进程,一个用于接收输入,一个用于游戏流程和显示界面;
  26. #当前者接收到上下左右等按键时,通过向后者发送signal的方式通知后者。
  27. sigRotate=25
  28. sigLeft=26
  29. sigRight=27
  30. sigDown=28
  31. sigAllDown=29
  32. sigExit=30

  33. #七中不同的方块的定义
  34. #通过旋转,每种方块的显示的样式可能有几种
  35. box0=(0 0 0 1 1 0 1 1)
  36. box1=(0 2 1 2 2 2 3 2 1 0 1 1 1 2 1 3)
  37. box2=(0 0 0 1 1 1 1 2 0 1 1 0 1 1 2 0)
  38. box3=(0 1 0 2 1 0 1 1 0 0 1 0 1 1 2 1)
  39. box4=(0 1 0 2 1 1 2 1 1 0 1 1 1 2 2 2 0 1 1 1 2 0 2 1 0 0 1 0 1 1 1 2)
  40. box5=(0 1 1 1 2 1 2 2 1 0 1 1 1 2 2 0 0 0 0 1 1 1 2 1 0 2 1 0 1 1 1 2)
  41. box6=(0 1 1 1 1 2 2 1 1 0 1 1 1 2 2 1 0 1 1 0 1 1 2 1 0 1 1 0 1 1 1 2)
  42. #所有其中方块的定义都放到box变量中
  43. box=(${box0[@]} ${box1[@]} ${box2[@]} ${box3[@]} ${box4[@]} ${box5[@]} ${box6[@]})
  44. #各种方块旋转后可能的样式数目
  45. countBox=(1 2 2 2 4 4 4)
  46. #各种方块再box数组中的偏移
  47. offsetBox=(0 1 3 5 7 11 15)

  48. #每提高一个速度级需要积累的分数
  49. iScoreEachLevel=50   #be greater than 7

  50. #运行时数据
  51. sig=0      #接收到的signal
  52. iScore=0   #总分
  53. iLevel=0   #速度级
  54. boxNew=()   #新下落的方块的位置定义
  55. cBoxNew=0   #新下落的方块的颜色
  56. iBoxNewType=0   #新下落的方块的种类
  57. iBoxNewRotate=0   #新下落的方块的旋转角度
  58. boxCur=()   #当前方块的位置定义
  59. cBoxCur=0   #当前方块的颜色
  60. iBoxCurType=0   #当前方块的种类
  61. iBoxCurRotate=0   #当前方块的旋转角度
  62. boxCurX=-1   #当前方块的x坐标位置
  63. boxCurY=-1   #当前方块的y坐标位置
  64. iMap=()      #背景方块图表

  65. #初始化所有背景方块为-1, 表示没有方块
  66. for ((i = 0; i < iTrayHeight * iTrayWidth; i++)); do iMap[$i]=-1; done


  67. #接收输入的进程的主函数
  68. function RunAsKeyReceiver()
  69. {
  70.    local pidDisplayer key aKey sig cESC sTTY

  71.    pidDisplayer=$1
  72.    aKey=(0 0 0)

  73.    cESC=`echo -ne "\33"`
  74.    cSpace=`echo -ne "\40"`

  75.    #保存终端属性。在read -s读取终端键时,终端的属性会被暂时改变。
  76.    #如果在read -s时程序被不幸杀掉,可能会导致终端混乱,
  77.    #需要在程序退出时恢复终端属性。
  78.    sTTY=`stty -g`
  79.    
  80.    #捕捉退出信号
  81.    trap "MyExit;" INT TERM
  82.    trap "MyExitNoSub;" $sigExit
  83.    
  84.    #隐藏光标
  85.    echo -ne "\33[?25l"

  86.    
  87.    while (( 1 ))
  88.    do
  89.       #读取输入。注-s不回显,-n读到一个字符立即返回
  90.       read -s -n 1 key
  91.       
  92.       aKey[0]=${aKey[1]}
  93.       aKey[1]=${aKey[2]}
  94.       aKey[2]=$key
  95.       sig=0

  96.       #判断输入了何种键
  97.       if [[ $key == $cESC && ${aKey[1]} == $cESC ]]
  98.       then
  99.          #ESC键
  100.          MyExit
  101.       elif [[ ${aKey[0]} == $cESC && ${aKey[1]} == "[" ]]
  102.       then
  103.          if [[ $key == "A" ]]; then sig=$sigRotate   #<向上键>
  104.          elif [[ $key == "B" ]]; then sig=$sigDown   #<向下键>
  105.          elif [[ $key == "D" ]]; then sig=$sigLeft   #<向左键>
  106.          elif [[ $key == "C" ]]; then sig=$sigRight   #<向右键>
  107.          fi
  108.       elif [[ $key == "W" || $key == "w" ]]; then sig=$sigRotate   #W, w
  109.       elif [[ $key == "S" || $key == "s" ]]; then sig=$sigDown   #S, s
  110.       elif [[ $key == "A" || $key == "a" ]]; then sig=$sigLeft   #A, a
  111.       elif [[ $key == "D" || $key == "d" ]]; then sig=$sigRight   #D, d
  112.       elif [[ "[$key]" == "[]" ]]; then sig=$sigAllDown   #空格键
  113.       elif [[ $key == "Q" || $key == "q" ]]         #Q, q
  114.       then
  115.          MyExit
  116.       fi

  117.       if [[ $sig != 0 ]]
  118.       then
  119.          #向另一进程发送消息
  120.          kill -$sig $pidDisplayer
  121.       fi
  122.    done
  123. }

  124. #退出前的恢复
  125. function MyExitNoSub()
  126. {
  127.    local y
  128.    
  129.    #恢复终端属性
  130.    stty $sTTY
  131.    ((y = iTop + iTrayHeight + 4))

  132.    #显示光标
  133.    echo -e "\33[?25h\33[${y};0H"
  134.    exit
  135. }


  136. function MyExit()
  137. {
  138.    #通知显示进程需要退出
  139.    kill -$sigExit $pidDisplayer
  140.    
  141.    MyExitNoSub
  142. }


  143. #处理显示和游戏流程的主函数
  144. function RunAsDisplayer()
  145. {
  146.    local sigThis
  147.    InitDraw

  148.    #挂载各种信号的处理函数
  149.    trap "sig=$sigRotate;" $sigRotate
  150.    trap "sig=$sigLeft;" $sigLeft
  151.    trap "sig=$sigRight;" $sigRight
  152.    trap "sig=$sigDown;" $sigDown
  153.    trap "sig=$sigAllDown;" $sigAllDown
  154.    trap "ShowExit;" $sigExit

  155.    while (( 1 ))
  156.    do
  157.       #根据当前的速度级iLevel不同,设定相应的循环的次数
  158.       for ((i = 0; i < 21 - iLevel; i++))
  159.       do
  160.          sleep 0.02
  161.          sigThis=$sig
  162.          sig=0

  163.          #根据sig变量判断是否接受到相应的信号
  164.          if ((sigThis == sigRotate)); then BoxRotate;   #旋转
  165.          elif ((sigThis == sigLeft)); then BoxLeft;   #左移一列
  166.          elif ((sigThis == sigRight)); then BoxRight;   #右移一列
  167.          elif ((sigThis == sigDown)); then BoxDown;   #下落一行
  168.          elif ((sigThis == sigAllDown)); then BoxAllDown;   #下落到底
  169.          fi
  170.       done
  171.       #kill -$sigDown $$
  172.       BoxDown   #下落一行
  173.    done
  174. }


  175. #BoxMove(y, x), 测试是否可以把移动中的方块移到(x, y)的位置, 返回0则可以, 1不可以
  176. function BoxMove()
  177. {
  178.    local j i x y xTest yTest
  179.    yTest=$1
  180.    xTest=$2
  181.    for ((j = 0; j < 8; j += 2))
  182.    do
  183.       ((i = j + 1))
  184.       ((y = ${boxCur[$j]} + yTest))
  185.       ((x = ${boxCur[$i]} + xTest))
  186.       if (( y < 0 || y >= iTrayHeight || x < 0 || x >= iTrayWidth))
  187.       then
  188.          #撞到墙壁了
  189.          return 1
  190.       fi
  191.       if ((${iMap[y * iTrayWidth + x]} != -1 ))
  192.       then
  193.          #撞到其他已经存在的方块了
  194.          return 1
  195.       fi
  196.    done
  197.    return 0;
  198. }


  199. #将当前移动中的方块放到背景方块中去,
  200. #并计算新的分数和速度级。(即一次方块落到底部)
  201. function Box2Map()
  202. {
  203.    local j i x y xp yp line

  204.    #将当前移动中的方块放到背景方块中去
  205.    for ((j = 0; j < 8; j += 2))
  206.    do
  207.       ((i = j + 1))
  208.       ((y = ${boxCur[$j]} + boxCurY))
  209.       ((x = ${boxCur[$i]} + boxCurX))
  210.       ((i = y * iTrayWidth + x))
  211.       iMap[$i]=$cBoxCur
  212.    done
  213.    
  214.    #消去可被消去的行
  215.    line=0
  216.    for ((j = 0; j < iTrayWidth * iTrayHeight; j += iTrayWidth))
  217.    do
  218.       for ((i = j + iTrayWidth - 1; i >= j; i--))
  219.       do
  220.          if ((${iMap[$i]} == -1)); then break; fi
  221.       done
  222.       if ((i >= j)); then continue; fi
  223.    
  224.       ((line++))   
  225.       for ((i = j - 1; i >= 0; i--))
  226.       do
  227.          ((x = i + iTrayWidth))
  228.          iMap[$x]=${iMap[$i]}
  229.       done
  230.       for ((i = 0; i < iTrayWidth; i++))
  231.       do
  232.          iMap[$i]=-1
  233.       done
  234.    done
  235.    
  236.    if ((line == 0)); then return; fi

  237.    #根据消去的行数line计算分数和速度级
  238.    ((x = iLeft + iTrayWidth * 2 + 7))
  239.    ((y = iTop + 11))
  240.    ((iScore += line * 2 - 1))
  241.    #显示新的分数
  242.    echo -ne "\33[1m\33[3${cScoreValue}m\33[${y};${x}H${iScore}         "
  243.    if ((iScore % iScoreEachLevel < line * 2 - 1))
  244.    then
  245.       if ((iLevel < 20))
  246.       then
  247.          ((iLevel++))
  248.          ((y = iTop + 14))
  249.          #显示新的速度级
  250.          echo -ne "\33[3${cScoreValue}m\33[${y};${x}H${iLevel}        "
  251.       fi
  252.    fi
  253.    echo -ne "\33[0m"


  254.    #重新显示背景方块
  255.    for ((y = 0; y < iTrayHeight; y++))
  256.    do
  257.       ((yp = y + iTrayTop + 1))
  258.       ((xp = iTrayLeft + 1))
  259.       ((i = y * iTrayWidth))
  260.       echo -ne "\33[${yp};${xp}H"
  261.       for ((x = 0; x < iTrayWidth; x++))
  262.       do
  263.          ((j = i + x))
  264.          if ((${iMap[$j]} == -1))
  265.          then
  266.             echo -ne "  "
  267.          else
  268.             echo -ne "\33[1m\33[7m\33[3${iMap[$j]}m\33[4${iMap[$j]}m[]\33[0m"
  269.          fi
  270.       done
  271.    done
  272. }


  273. #下落一行
  274. function BoxDown()
  275. {
  276.    local y s
  277.    ((y = boxCurY + 1))   #新的y坐标
  278.    if BoxMove $y $boxCurX   #测试是否可以下落一行
  279.    then
  280.       s="`DrawCurBox 0`"   #将旧的方块抹去
  281.       ((boxCurY = y))
  282.       s="$s`DrawCurBox 1`"   #显示新的下落后方块
  283.       echo -ne $s
  284.    else
  285.       #走到这儿, 如果不能下落了
  286.       Box2Map      #将当前移动中的方块贴到背景方块中
  287.       RandomBox   #产生新的方块
  288.    fi
  289. }

  290. #左移一列
  291. function BoxLeft()
  292. {
  293.    local x s
  294.    ((x = boxCurX - 1))
  295.    if BoxMove $boxCurY $x
  296.    then
  297.       s=`DrawCurBox 0`
  298.       ((boxCurX = x))
  299.       s=$s`DrawCurBox 1`
  300.       echo -ne $s
  301.    fi
  302. }

  303. #右移一列
  304. function BoxRight()
  305. {
  306.    local x s
  307.    ((x = boxCurX + 1))
  308.    if BoxMove $boxCurY $x
  309.    then
  310.       s=`DrawCurBox 0`
  311.       ((boxCurX = x))
  312.       s=$s`DrawCurBox 1`
  313.       echo -ne $s
  314.    fi
  315. }


  316. #下落到底
  317. function BoxAllDown()
  318. {
  319.    local k j i x y iDown s
  320.    iDown=$iTrayHeight

  321.    #计算一共需要下落多少行
  322.    for ((j = 0; j < 8; j += 2))
  323.    do
  324.       ((i = j + 1))
  325.       ((y = ${boxCur[$j]} + boxCurY))
  326.       ((x = ${boxCur[$i]} + boxCurX))
  327.       for ((k = y + 1; k < iTrayHeight; k++))
  328.       do
  329.          ((i = k * iTrayWidth + x))
  330.          if (( ${iMap[$i]} != -1)); then break; fi
  331.       done
  332.       ((k -= y + 1))
  333.       if (( $iDown > $k )); then iDown=$k; fi
  334.    done
  335.    
  336.    s=`DrawCurBox 0`   #将旧的方块抹去
  337.    ((boxCurY += iDown))   
  338.    s=$s`DrawCurBox 1`   #显示新的下落后的方块
  339.    echo -ne $s
  340.    Box2Map      #将当前移动中的方块贴到背景方块中
  341.    RandomBox   #产生新的方块
  342. }


  343. #旋转方块
  344. function BoxRotate()
  345. {
  346.    local iCount iTestRotate boxTest j i s
  347.    iCount=${countBox[$iBoxCurType]}   #当前的方块经旋转可以产生的样式的数目

  348.    #计算旋转后的新的样式
  349.    ((iTestRotate = iBoxCurRotate + 1))
  350.    if ((iTestRotate >= iCount))
  351.    then
  352.       ((iTestRotate = 0))
  353.    fi

  354.    #更新到新的样式, 保存老的样式(但不显示)
  355.    for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  356.    do
  357.       boxTest[$j]=${boxCur[$j]}
  358.       boxCur[$j]=${box[$i]}
  359.    done

  360.    if BoxMove $boxCurY $boxCurX   #测试旋转后是否有空间放的下
  361.    then
  362.       #抹去旧的方块
  363.       for ((j = 0; j < 8; j++))
  364.       do
  365.          boxCur[$j]=${boxTest[$j]}
  366.       done
  367.       s=`DrawCurBox 0`

  368.       #画上新的方块
  369.       for ((j = 0, i = (${offsetBox[$iBoxCurType]} + $iTestRotate) * 8; j < 8; j++, i++))
  370.       do
  371.          boxCur[$j]=${box[$i]}
  372.       done
  373.       s=$s`DrawCurBox 1`
  374.       echo -ne $s
  375.       iBoxCurRotate=$iTestRotate
  376.    else
  377.       #不能旋转,还是继续使用老的样式
  378.       for ((j = 0; j < 8; j++))
  379.       do
  380.          boxCur[$j]=${boxTest[$j]}
  381.       done
  382.    fi
  383. }


  384. #DrawCurBox(bDraw), 绘制当前移动中的方块, bDraw为1, 画上, bDraw为0, 抹去方块。
  385. function DrawCurBox()
  386. {
  387.    local i j t bDraw sBox s
  388.    bDraw=$1

  389.    s=""
  390.    if (( bDraw == 0 ))
  391.    then
  392.       sBox="\40\40"
  393.    else
  394.       sBox="[]"
  395.       s=$s"\33[1m\33[7m\33[3${cBoxCur}m\33[4${cBoxCur}m"      
  396.    fi
  397.    
  398.    for ((j = 0; j < 8; j += 2))
  399.    do
  400.       ((i = iTrayTop + 1 + ${boxCur[$j]} + boxCurY))
  401.       ((t = iTrayLeft + 1 + 2 * (boxCurX + ${boxCur[$j + 1]})))
  402.       #\33[y;xH, 光标到(x, y)处
  403.       s=$s"\33[${i};${t}H${sBox}"
  404.    done
  405.    s=$s"\33[0m"
  406.    echo -n $s
  407. }


  408. #更新新的方块
  409. function RandomBox()
  410. {
  411.    local i j t

  412.    #更新当前移动的方块
  413.    iBoxCurType=${iBoxNewType}
  414.    iBoxCurRotate=${iBoxNewRotate}
  415.    cBoxCur=${cBoxNew}
  416.    for ((j = 0; j < ${#boxNew[@]}; j++))
  417.    do
  418.       boxCur[$j]=${boxNew[$j]}
  419.    done
  420.    

  421.    #显示当前移动的方块
  422.    if (( ${#boxCur[@]} == 8 ))
  423.    then
  424.       #计算当前方块该从顶端哪一行"冒"出来
  425.       for ((j = 0, t = 4; j < 8; j += 2))
  426.       do
  427.          if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  428.       done
  429.       ((boxCurY = -t))
  430.       for ((j = 1, i = -4, t = 20; j < 8; j += 2))
  431.       do
  432.          if ((${boxCur[$j]} > i)); then i=${boxCur[$j]}; fi
  433.          if ((${boxCur[$j]} < t)); then t=${boxCur[$j]}; fi
  434.       done
  435.       ((boxCurX = (iTrayWidth - 1 - i - t) / 2))

  436.       #显示当前移动的方块
  437.       echo -ne `DrawCurBox 1`

  438.       #如果方块一出来就没处放,Game over!
  439.       if ! BoxMove $boxCurY $boxCurX
  440.       then
  441.          kill -$sigExit ${PPID}
  442.          ShowExit
  443.       fi
  444.    fi
  445.    
  446.    

  447.    #清除右边预显示的方块
  448.    for ((j = 0; j < 4; j++))
  449.    do
  450.       ((i = iTop + 1 + j))
  451.       ((t = iLeft + 2 * iTrayWidth + 7))
  452.       echo -ne "\33[${i};${t}H        "
  453.    done

  454.    #随机产生新的方块
  455.    ((iBoxNewType = RANDOM % ${#offsetBox[@]}))
  456.    ((iBoxNewRotate = RANDOM % ${countBox[$iBoxNewType]}))
  457.    for ((j = 0, i = (${offsetBox[$iBoxNewType]} + $iBoxNewRotate) * 8; j < 8; j++, i++))
  458.    do
  459.       boxNew[$j]=${box[$i]};
  460.    done

  461.    ((cBoxNew = ${colorTable[RANDOM % ${#colorTable[@]}]}))
  462.    
  463.    #显示右边预显示的方块
  464.    echo -ne "\33[1m\33[7m\33[3${cBoxNew}m\33[4${cBoxNew}m"
  465.    for ((j = 0; j < 8; j += 2))
  466.    do
  467.       ((i = iTop + 1 + ${boxNew[$j]}))
  468.       ((t = iLeft + 2 * iTrayWidth + 7 + 2 * ${boxNew[$j + 1]}))
  469.       echo -ne "\33[${i};${t}H[]"
  470.    done
  471.    echo -ne "\33[0m"
  472. }


  473. #初始绘制
  474. function InitDraw()
  475. {
  476.    clear
  477.    RandomBox   #随机产生方块,这时右边预显示窗口中有方快了
  478.    RandomBox   #再随机产生方块,右边预显示窗口中的方块被更新,原先的方块将开始下落
  479.    local i t1 t2 t3

  480.    #显示边框
  481.    echo -ne "\33[1m"
  482.    echo -ne "\33[3${cBorder}m\33[4${cBorder}m"
  483.    
  484.    ((t2 = iLeft + 1))
  485.    ((t3 = iLeft + iTrayWidth * 2 + 3))
  486.    for ((i = 0; i < iTrayHeight; i++))
  487.    do
  488.       ((t1 = i + iTop + 2))
  489.       echo -ne "\33[${t1};${t2}H||"
  490.       echo -ne "\33[${t1};${t3}H||"
  491.    done
  492.    
  493.    ((t2 = iTop + iTrayHeight + 2))
  494.    for ((i = 0; i < iTrayWidth + 2; i++))
  495.    do
  496.       ((t1 = i * 2 + iLeft + 1))
  497.       echo -ne "\33[${iTrayTop};${t1}H=="
  498.       echo -ne "\33[${t2};${t1}H=="
  499.    done
  500.    echo -ne "\33[0m"

  501.    
  502.    #显示"Score"和"Level"字样
  503.    echo -ne "\33[1m"
  504.    ((t1 = iLeft + iTrayWidth * 2 + 7))
  505.    ((t2 = iTop + 10))
  506.    echo -ne "\33[3${cScore}m\33[${t2};${t1}HScore"
  507.    ((t2 = iTop + 11))
  508.    echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iScore}"
  509.    ((t2 = iTop + 13))
  510.    echo -ne "\33[3${cScore}m\33[${t2};${t1}HLevel"
  511.    ((t2 = iTop + 14))
  512.    echo -ne "\33[3${cScoreValue}m\33[${t2};${t1}H${iLevel}"
  513.    echo -ne "\33[0m"
  514. }


  515. #退出时显示GameOVer!
  516. function ShowExit()
  517. {
  518.    local y
  519.    ((y = iTrayHeight + iTrayTop + 3))
  520.    echo -e "\33[${y};0HGameOver!\33[0m"
  521.    exit
  522. }



  523. #游戏主程序在这儿开始.
  524. if [[ $1 != "--show" ]]
  525. then
  526.    bash $0 --show&   #以参数--show将本程序再运行一遍
  527.    RunAsKeyReceiver $!   #以上一行产生的进程的进程号作为参数
  528.    exit
  529. else
  530.    #当发现具有参数--show时,运行显示函数
  531.    RunAsDisplayer   
  532.    exit
  533. fi
复制代码


2.read
格式:

  1. read [-ers] [-u fd] [-t timeout] [-a aname] [-p prompt] [-n nchars] [-d delim] [name ...]
复制代码

当read命令没有参数[name ...]时,直接设在REPLY上。
可以用$REPLY引用.
其他命令参数看下面运行实例:

  1. zhyfly: ~/1$ read
  2. what
  3. zhyfly: ~/1$ echo $REPLY
  4. what

  5. zhyfly: ~/1$ read name
  6. zhyfly
  7. zhyfly: ~/1$ echo $name
  8. zhyfly

  9. zhyfly: ~/1$ read name veb name1
  10. i love you
  11. zhyfly: ~/1$ echo $name $veb $name1
  12. i love you

  13. zhyfly: ~/1$ read name veb name1
  14. i love
  15. zhyfly: ~/1$ echo "$name,$veb,$name1"
  16. i,love,

  17. zhyfly: ~/1$ read name veb
  18. i love you
  19. zhyfly: ~/1$ echo "$name,$veb"
  20. i,love you

  21. zhyfly: ~/1$ read -p "how old r u?" age
  22. how old r u?23
  23. zhyfly: ~/1$ echo $age
  24. 23

  25. zhyfly: ~/1$ read -p "some words?" -a words
  26. some words?i love u!
  27. zhyfly: ~/1$ echo ${words[*]}
  28. i love u!

  29. zhyfly: ~/1$ read -p "passwd:" -s passwd
  30. passwd:#输入密码zhyfly: ~/1$ echo $passwd
  31. zhyfly

  32. zhyfly: ~/1$ read -p "type the string end with n characters:" -n 5
  33. type the string end with n characters:34352zhyfly: ~/1$

  34. zhyfly: ~/1$ read -p "type the string end with the letter you set:" -dp
  35. type the string end with the letter you set:hahaw^?^?pzhyfly: ~/1$

  36. zhyfly: ~/1$ read -p "type the string in the seconds you set:" -t 5 test
  37. type the string in the seconds you set:zhyfly: ~/1$
  38. #5秒钟不反应自动退出

  39. zhyfly: ~/1$ read -p 'the \ will work as a metacharater!' a
  40. the \ will work as a metacharater!bc\$d
  41. zhyfly: ~/1$ echo $a
  42. bc$d
  43. zhyfly: ~/1$ read -r -p 'the \ will not work as a metacharater!' A
  44. the \ will not work as a metacharater!bc\$d
  45. zhyfly: ~/1$ echo $A
  46. bc\$d
复制代码

一个echo和read的例子:

  1. zhyfly: ~/1$ cat test
  2. #!/bin/bash
  3. echo -n "First name:"
  4. read firstname
  5. echo -n "Middle name:"
  6. read middlename
  7. echo -e "Last name:\c"
  8. read lastname
  9. echo "$firstname,$middlename,$lastname"
  10. zhyfly: ~/1$ sudo chmod +x test
  11. zhyfly: ~/1$ ./test
  12. First name:zhy
  13. Middle name:2
  14. Last name:fly
  15. zhy,2,fly
复制代码


3.cat
cat:显示文件内容,创建文件,还可以用它来显示控制字符。

注意:在文件分页符处不会停下来;会一下显示完整个文件。因此,可以使用m o r e命令或把c a t命令的输出通过管道传递到另外一个具有分页功能的命令中,使用命令less file可实现相同的功能。

如下形式

  1. $cat myfile | more
复制代码


  1. $cat myfile | pg
复制代码


  1. $cat myfile | less
复制代码


c a t命令的一般形式为:

  1. cat [options] filename1 ... filename2 ...
复制代码


a、显示名为m y f i l e的文件:

  1. $ cat myfile
复制代码


b、显示m y f i l e 1、m y f i l e 2、m y f i l e 3这三个文件,可以用:

  1. $ cat myfile1 myfile2 myfile3
复制代码


c、创建一个包含上述三个文件的内容,名为b i g f i l e的文件,可以用输出重定向到新文件中:

  1. $ cat myfile1 myfile2 myfile3 > bigfile
复制代码


d、如果cat的命令行中没有参数,输入的每一行都立刻被cat命令输出到屏幕上,输入完毕后按< C T R L - D >结束

  1. $ cat
  2. Hello world
  3. Hello world
  4. <ctrl+d>
  5. $
复制代码


e、新建文件

  1. $cat >myfile
  2. This is great
  3. <ctrl-d>
  4. $cat myfile
  5. This is great
复制代码



cat:参数选项

使用方式:

  1. cat [-AbeEnstTuv] [--help] [--version] fileName
复制代码

说明:把档案串连接后传到基本输出(萤幕或加 > fileName 到另一个档案)

参数:
-n 或 --number 由 1 开始对所有输出的行数编号
-b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号
-s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行
-v 或 --show-nonprinting 显示非打印字符

例:
显示时加上行号

  1. $cp /etc/httpd/conf/httpd /usr/sam
  2. $ cat -n httpd.conf
复制代码


把 httpd.conf 的内容加上行号后输入 httpd1.conf 这个文件里

  1. $cat -n httpd.conf > httpd1.conf
复制代码


对文件httpd.conf加上行号(空白不加)后显示

  1. $ cat -b httpd.conf
复制代码


把 textfile1 和 textfile2 的档案内容加上行号(空白行不加)之后将内容附加到 textfile3 里。

  1. $ cat -b textfile1 textfile2 >> textfile3
复制代码


清空/etc/test.txt档案内容

  1. $cat /dev/null > /etc/test.txt
复制代码


使用 sed 与 cat 除去空白行

  1. $ cat -s /etc/X11/XF86Config | sed '/^[[:space:]]*$/d'
复制代码


cat 还可以在您查看包含如制表符这样的非打印字符的文件时起帮助作用。您可以用以下选项来显示制表符:
* -T 将制表符显示为 ^I

* -v 显示非打印字符,除了换行符和制表符,它们使用各自效果相当的“控制序列”。例如,当您处理一个在 Windows 系统中生成的文件时,这个文件将使用 Control-M(^M)来标记行的结束。对于代码大于 127 的字符,它们的前面将会被加上 M-(表示“meta”),这与其它系统中在字符前面加上 Alt- 相当。

* -E 在每一行的结束处添加美元符($)。


显示非打印字符

  1. $ cat -t /etc/X11/XF86Config
  2. ...
  3. # Multiple FontPath entries are allowed (they are concatenated together)
  4. # By default, Red Hat 6.0 and later now use a font server independent of
  5. # the X server to render fonts.
  6. ^IFontPath^I"/usr/X11R6/lib/X11/fonts/TrueType"
  7. ^IFontPath^I"unix/:7100"
  8. EndSection
  9. ...
复制代码


  1. $ cat -E /etc/X11/XF86Config
  2. ...
  3. # Multiple FontPath entries are allowed (they are concatenated together)$
  4. # By default, Red Hat 6.0 and later now use a font server independent of$
  5. # the X server to render fonts.$
  6. $
  7. FontPath "/usr/X11R6/lib/X11/fonts/TrueType"$
  8. FontPath "unix/:7100"$
  9. $
  10. EndSection$
  11. ...
复制代码


  1. $ cat -v /etc/X11/XF86Config
  2. ...
  3. ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-|M-8^X^@^@^@
  4. P^@^O"M-X^O M-@^M^@^@^@M-^@^O"M-@M-k^@M-8*^@
  5. @M-^H$M-@M-9|A(M-@)M-yM-|M-sM-*M-hW^A^@^@j^@
  6. M-|M-sM-%1M-@M-9^@^B^@^@M-sM-+fM-^A= ^@ ^@
  7. F^@^@ ^@M-9^@^H^@^@M-sM-$M-G^E(l!M-@M-^?
  8. ^IM-A5^@^@^D^@PM-^]M-^\X1M-H%^@^@^D^@tyM-G
  9. ...
复制代码


4.tee
tee:读取标准输入的数据,并将其内容输出成文件。

  语   法:tee [-ai][--help][--version][文件…]
  补充说明:tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。我们可利用tee把管道导入的数据存成文件,甚至一次保存数份文件。

  参   数:-a 附加到既有文件的面,而非覆盖它。如果给予tee指令的文件名称已经存在,预设会覆盖该文件的内容。加上此参数,数据会新增在该文件内容的最面,而不会删除原先之内容。
       -i 忽略中断信号
       --help 在线帮助
       --version 显示版本信息

  例一:
  列出文本文件slayers.story的内容,同时复制3份副本,文件名称分别为ss-copy1、ss-copy2、ss-copy3:

  1. $ cat slayers.story |tee ss-copy1 ss-copy2 ss-copy3
复制代码


  例二: 把列出当前目录,并把结果结到myfile里

  1. $ls -l |tee myfile
复制代码



管道:可以通过管道把一个命令的输出传递给另一个命令作为输入。管道用竖杠|表示。它的一般形式为:

命令1 |命令2
其中|是管道符号。

5.标准输入、输出和错误
当我们在s h e l l中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件。由于文件描述符不容易记忆, s h e l l同时也给出了相应的文件名。
下面就是这些文件描述符及它们通常所对应的文件名:

文件文件描述符
输入文件—标准输入stdin 0:它是命令的输入,缺省是键盘,也可以是文件或其他命令的输出。
输出文件—标准输出stdout 1:它是命令的输出,缺省是屏幕,也可以是文件。
错误输出文件—标准错误stderr 2:这是命令错误的输出,缺省是屏幕,同样也可以是文件。

如果没有特别指定文件说明符,命令将使用缺省的文件说明符(你的屏幕,更确切地说是你的终端)。

系统中实际上有1 2个文件描述符,但是正如我们在上表中所看到的, 0、1、2是标准输入、输出和错误。可以任意使用文件描述符3到9。

在执行命令时,可以指定命令的标准输入、输出和错误,要实现这一点就需要使用文件重定向。下面列出了最常用的重定向组合,并给出了相应的文件描述符。
在对标准错误进行重定向时,必须要使用文件描述符,但是对于标准输入和输出来说,这不是必需的。

常用文件重定向命令
command > filename 把标准输出重定向到一个新文件中
command >> filename 把标准输出重定向到一个文件中(追加)
command 1 > fielname 把标准输出重定向到一个文件中
command > filename 2>&1 把标准输出和标准错误一起重定向到一个文件中
command 2> filename 把标准错误重定向到一个文件中
command 2>> filename 把标准输出重定向到一个文件中(追加)
command >> filename 2>&1 把标准输出和标准错误一起重定向到一个文件中(追加)
command < filename >filename2 把c o m m a n d命令以f i l e n a m e文件作为标准输入,以f i l e n a m e 2文件作为标准输出
command < filename 把c o m m a n d命令以f i l e n a m e文件作为标准输入
command << delimiter 把从标准输入中读入,直至遇到d e l i m i t e r分界符
command <&m 把文件描述符m作为标准输入
command >&m 把标准输出重定向到文件描述符m中
command <&- 关闭标准输入

补充
File Descriptor (FD)

程式的運算,在大部份情況下都是進行數據(data)的處理,
這些數據?哪淖x進?又,送出到哪裡呢?
這就是 file descriptor (FD) 的功用了。

在 shell 程式中,最常使用的 FD 大概有三個,分別為:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)

在標準情況下,這些 FD 分別跟如下設備(device)關聯:
stdin(0): keyboard
stdout(1): monitor
stderr(2): monitor

我們可以用如下下命令測試一下:
  1. $ mail -s test root
  2. this is a test mail.
  3. please skip.
  4. ^d (同時按 crtl 跟 d 鍵)
复制代码

很明顯,mail 程式所讀進的數據,就是? stdin 也就是 keyboard 讀進的。
不過,不見得每個程式的 stdin 都跟 mail 一樣? keyboard 讀進,
因為程式作者可以?臋n案參數讀進 stdin ,如:

  1. $ cat /etc/passwd
复制代码

但,要是 cat 之後沒有檔案參數則又如何呢?
哦,請您自己玩玩看囉.... ^_^
  1. $ cat
复制代码


(請留意數據輸出到哪裡去了,最後別忘了按 ^d 離開...)
事?上,stderr 沒甚麼難理解的:說穿了就是"錯誤信息"要往哪邊送而已...
比方說,若讀進的檔案參數是不存在的,那我們在 monitor 上就看到了:

  1. $ ls no.such.file
  2. ls: no.such.file: No such file or directory
复制代码

若,一個命令同時產生 stdout 與 stderr 呢?
那還不簡單,都送到 monitor ?砭秃昧耍篬code]
$ touch my.file
$ ls my.file no.such.file
ls: no.such.file: No such file or directory
my.file
[/code]
okay,至此,關於 FD 及其名稱、還有相關聯的設備,相信你已經沒問題了吧?
那好,接下?碜屛覀兛纯慈绾胃淖冞@些 FD 的預設數據通道,
我們可用 < ?砀淖冏x進的數據通道(stdin),使之?闹付ǖ臋n案讀進。
我們可用 > ?砀淖兯统龅臄祿通道(stdout, stderr),使之輸出到指定的檔案。

比方說:

  1. $ cat < my.file
复制代码

就是? my.file 讀進數據

  1. $ mail -s test root < /etc/passwd
复制代码

則是? /etc/passwd 讀進...
這樣一?恚瑂tdin 將不再是? keyboard 讀進,而是?臋n案讀進了...
嚴格?碚f,< 符號之前需要指定一個 FD 的(之間不能有空白),
但因為 0 是 < 的預設值,因此 < 與 0< 是一樣的﹗

那,要是用兩個 << 又是啥呢?
這是所謂的 HERE Document ,它可以讓我們輸入一段文本,直到讀到 << 後指定的字串。
比方說:
  1. $ cat <<FINISH
  2. first line here
  3. second line there
  4. third line nowhere
  5. FINISH
复制代码

這樣的話,cat 會讀進 3 行句子,而無需? keyboard 讀進數據且要等 ^d 結束輸入。

當你搞懂了 0< 原?砭褪歉淖 stdin 的數據輸入通道之後,相信要理解如下兩個 redirection 就不難了:
* 1>
* 2>
前者是改變 stdout 的數據輸出通道,後者是改變 stderr 的數據輸出通道。
兩者都是將原本要送出到 monitor 的數據轉向輸出到指定檔案去。
由於 1 是 > 的預設值,因此,1> 與 > 是相同的,都是改 stdout 。

用上面的 ls 例子?碚f明一下好了:

  1. $ ls my.file no.such.file 1>file.out
  2. ls: no.such.file: No such file or directory
复制代码

這樣 monitor 就只剩下 stderr 而已。因為 stdout 給寫進 file.out 去了。

  1. $ ls my.file no.such.file 2>file.err
  2. my.file
复制代码

這樣 monitor 就只剩下 stdout ,因為 stderr 寫進了 file.err 。

  1. $ ls my.file no.such.file 1>file.out 2>file.err
复制代码

這樣 monitor 就啥也沒有,因為 stdout 與 stderr 都給轉到檔案去了...

不過,有些地方還是要注意一下.
首先,是 file locking 的問題。比方如下這個例子:

  1. $ ls my.file no.such.file 1>file.both 2>file.both
复制代码

? file system 的角度?碚f,單一檔案在單一時間內,只能被單一的 FD 作寫入。
假如 stdout(1) 與 stderr(2) 都同時在寫入 file.both 的話,
則要看它們在寫入時否碰到同時競爭的情形了,基本上是"先搶先贏"的原則。
讓我們用"慢鏡頭"?砜匆幌 stdout 與 stderr 同時寫入 file.out 的情形好了:
* 第 1, 2, 3 秒為 stdout 寫入
* 第 3, 4, 5 秒為 stderr 寫入
那麼,這時候 stderr 的第 3 秒所寫的數據就?G失掉了﹗
要是我們能控制 stderr 必須等 stdout 寫完再寫,或倒過?恚瑂tdout 等 stderr 寫完再寫,那問題就能解決。
但?募夹g上,較難掌控的,尤其是 FD 在作"長期性"的寫入時...

那,如何解決呢?所謂山不轉路轉、路不轉人轉嘛,
我們可以換一個思維:將 stderr 導進 stdout 或將 stdout 導進 sterr ,而不是大家在搶同一份檔案,不就行了﹗
bingo﹗就是這樣啦:
* 2>&1 就是將 stderr ?氵M stdout 作輸出
* 1>&2 或 >&2 就是將 stdout ?氵M stderr 作輸出
於是,前面的錯誤操作可以改為:
  1. $ ls my.file no.such.file 1>file.both 2>&1
复制代码


  1. $ ls my.file no.such.file 2>file.both >&2
复制代码


在 Linux 檔案系統裡,有個設備檔位於 /dev/null 。
許多人都問過我那是甚麼玩意兒?那就是"空"啦﹗
沒錯﹗空空如也的空就是 null 了....

這個 null 在 I/O Redirection 中可有用得很呢:
* 若將 FD1 跟 FD2 轉到 /dev/null 去,就可將 stdout 與 stderr 弄不見掉。
* 若將 FD0 接到 /dev/null ?恚蔷褪亲x進 nothing 。
比方說,當我們在執行一個程式時,畫面會同時送出 stdout 跟 stderr ,
假如你不想看到 stderr (也不想存到檔案去),那可以:
  1. $ ls my.file no.such.file 2>/dev/null
  2. my.file
复制代码

若要相反:只想看到 stderr 呢?還不簡單﹗將 stdout 弄到 null 就行:
  1. $ ls my.file no.such.file >/dev/null
  2. ls: no.such.file: No such file or directory
复制代码

那接下?恚偃鐔渭冎慌艹淌剑幌肟吹饺魏屋敵鼋Y果呢?
除了用 >/dev/null 2>&1 之外,你還可以如此:
  1. $ ls my.file no.such.file &>/dev/null
复制代码

(提示:將 &> 換成 >& 也行啦~~! )

另外,看下面:

  1. $ echo "1" > file.out
  2. $ cat file.out
  3. 1
  4. $ echo "2" > file.out
  5. $ cat file.out
  6. 2
复制代码

看?恚覀冊谥貙 stdout 或 stderr 進一份檔案時,似乎永遠只獲得最後一次導入的結果。
那,之前的內容呢?
呵~~~ 要解決這個問提很簡單啦,將 > 換成 >> 就好:
$ echo "3" >> file.out
$ cat file.out
2
3
如此一?恚恢貙У哪繕藱n案之內容?K不會失去,而新的內容則一直增加在最後面去。

但,只要你再一次用回單一的 > ?碇貙У脑挘屈N,舊的內容還是會被"洗"掉的﹗
這時,你要如何避免呢?
----備份﹗ yes ,我聽到了﹗不過.... 還有更好的嗎?

  1. $ set -o noclobber
  2. $ echo "4" > file.out
  3. -bash: file: cannot overwrite existing file
复制代码

那,要如何取消這個"限制"呢?
哦,將 set -o 換成 set +o 就行:

  1. $ set +o noclobber
  2. $ echo "5" > file.out
  3. $ cat file.out
  4. 5
复制代码

再問:那... 有辦法不取消而又"臨時"蓋寫目標檔案嗎

  1. $ set -o noclobber
  2. $ echo "6" >| file.out
  3. $ cat file.out
  4. 6
复制代码

留意到沒有:在 > 後面再加個" | "就好(注意: > 與 | 之間不能有空白哦)....

再?磉有一個難題要你去參透的呢:
  1. $ echo "some text here" > file
  2. $ cat < file
  3. some text here
  4. $ cat < file > file.bak
  5. $ cat < file.bak
  6. some text here
  7. $ cat < file > file
  8. $ cat < file

复制代码

嗯?﹗注意到沒有?﹗﹗
---- 怎麼最後那個 cat 命令看到的 file 竟是空的?﹗
why? why? why?
要理解這一現像其?不難,這只是 priority 的問題而已:
* 在 IO Redirection 中,stdout 與 stderr 的管道會先準備好,才會? stdin 讀進資料。
也就是說,在上例中,> file 會先將 file 清空,然後才讀進 < file ,
但這時候檔案已經被清空了,因此就變成讀不進任何資料了...

哦~~~ 原?砣绱藒~~~ ^_^
那... 如下兩例又如何呢?

  1. $ cat <> file
  2. $ cat < file >> file
复制代码


pipe line
談到 pipe line ,我相信不少人都不會陌生:
我們在很多 command line 上常看到的" | "符號就是 pipe line 了。
不過,究竟 pipe line 是甚麼東東呢?
別急別急... 先查一下英?h字典,看看 pipe 是甚麼意思?
沒錯﹗它就是"水管"的意思...
那麼,你能想像一下水管是怎麼一根接著一根的嗎?
又,每根水管之間的 input 跟 output 又如何呢?
嗯??
靈光一閃:原? pipe line 的 I/O 跟水管的 I/O 是一模一樣的:
* 上一個命令的 stdout 接到下一個命令的 stdin 去了﹗
的確如此... 不管在 command line 上你使用了多少個 pipe line ,
前後兩個 command 的 I/O 都是彼此連接的﹗(恭喜:你終於開竅了﹗ ^_^ )

不過... 然而... 但是... ... stderr 呢?
好問題﹗不過也容易理解:
* 若水管漏水怎麼辦?
也就是說:在 pipe line 之間,前一個命令的 stderr 是不會接進下一命令的 stdin 的,
其輸出,若不用 2> 導到 file 去的話,它還是送到監視器上面?愆u
這點請你在 pipe line 運用上務必要注意的。

那,或許你又會問:
* 有辦法將 stderr 也餵進下一個命令的 stdin 去嗎?
方法當然是有,而且你早已學過了﹗ ^_^
我提示一下就好:
* 請問你如何將 stderr 合?氵M stdout 一同輸出呢?
或許,你仍意尤未盡﹗或許,你曾經碰到過下面的問題:
* 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結果存到某一檔案呢?

若你寫成 cm1 | cm2 > file | cm3 的話,
那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦,你都將水管接到別的水池了﹗)
聰明的你或許會如此解決:
cm1 | cm2 > file ; cm3 < file
是的,你的確可以這樣做,但最大的壞處是:這樣一?恚琭ile I/O 會變雙倍﹗
在 command 執行的整個過程中,file I/O 是最常見的最大效能殺手。
凡是有經驗的 shell 操作者,都會盡量避免或降低 file I/O 的頻率。

那,上面問題還有更好方法嗎?
有的,那就是 tee 命令了。
* 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。
因此,上面的命令行可以如此打:
cm1 | cm2 | tee file | cm3
在預設上,tee 會改寫目標檔案,若你要改為增加內容的話,那可用 -a 參數達成。

基本上,pipe line 的應用在 shell 操作上是非常?V泛的,尤其是在 text filtering 方面,
凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具,
搭配起 pipe line ?硎褂茫銜驚覺 command line 原?硎腔畹萌绱司实末u

6.exec
e x e c命令可以用来替代当前s h e l l;换句话说,并没有启动子s h e l l。使用这一命令时任何现有环境都将会被清除,并重新启动一个s h e l l。它的一般形式为:
  1. exec command
复制代码

其中的c o m m a n d通常是一个s h e l l脚本。

我所能够想像得出的描述e x e c命令最贴切的说法就是:当这个脚本结束时,相应的会话可能就结束了。e x e c命令的一个常见用法就是在用户的. p r o f i l e最后执行时,用它来执行一些用于增强安全性的脚本。如果用户的输入无效,该s h e l l将被关闭,然后重新回到登录提示符。e x e c还常常被用来通过文件描述符打开文件。文件描述符:file descriptor(FD)
e x e c在对文件描述符进行操作的时候(也只有在这时),它不会覆盖你当前的s h e l l。
source和exec的区别
1,他们带的参数不一样
source通常是shell脚本,而exec不但可以把一个脚本当成参数,而且还可以把一个系统命令当参数,例如: exec ls
2,另外一个不同就是,exec任务执行完毕后,会执行类似logout的操作,而source执行完一个任务后返回当前的shell.
回复 支持 反对

使用道具 举报

发表于 2005-4-6 23:48:27 | 显示全部楼层
好人啊!!!多谢了好心人,在linuxsir好心人,高人就是多,以后常来。
回复 支持 反对

使用道具 举报

发表于 2006-3-30 08:56:04 | 显示全部楼层
Post by ghbspecial
好人啊!!!多谢了好心人,在linuxsir好心人,高人就是多,以后常来。

既学知识,又修思想。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表