Chinaunix

标题: 《UNIX编程环境》学习笔记 [打印本页]

作者: tanyear    时间: 2005-12-26 09:50
标题: 《UNIX编程环境》学习笔记
《UNIX编程环境》学习笔记

本笔记只记录个人比较感兴趣,或者不太明白的东西。
希望和大家一起讨论。有不对的地方也请指正

第1章:初学UNIX

主要是一些基本的Unix命令和shell的简单功能。

1.1 暂停输出
    指暂停屏幕上的标准输出。
   比如你要避免某些关键信息出现在屏幕上,可以按Ctrl+s。
   此时,你的输入和程序运行依然有效,只是所有的输出都看不见。
   按Ctrl+q可以继续暂停的输出。

   类似的应用在shell编程中,可以用stty -echo命令使得键盘输入不显示在标准输出上
   比如输入密码的之前,使用此命令。
   之后用stty echo恢复。

   这两者的不同:
   Ctrl+s是暂停所有的标准输出,包括键盘输入的回显,程序执行的输出
   Ctrl+q的时候,那些标准输出会出现。

   而stty -echo只是关闭了键盘输入引起的回显(echo)字符。
   stty echo停止的时候,之前键盘输入的字符不会出现。

   在以上两种情况下,键盘输入pwd的现象。
-----------------
Ctrl+s                           //先按Ctrl+s,再从键盘输入pwd,标准输出暂停,没有显示
#                                 
Ctrl+q                          //按Ctrl+q,之前暂停的键盘输入的回显和shell的信息都显示出来。
#pwd
/root                             
                                 
-----------------
# stty -echo
# /root                           //从键盘输入pwd,没有echo出来,但是pwd执行的信息还是会输出。
                         //然后摸黑输入stty echo。呵呵
#

1.2 用户间通信

   wall 给所有用户发送mesg。按Ctrl+D结束输入。

   write userb 给b用户发送mesg。如果b用户要回复a用户 write发过来的信息,
   输入write usera,就可以交互性的通信了。同样按Ctrl+D结束输入。

   可以用mesg命令关闭或打开用户间的通信
   mesg [ y|n ]

   mail usera 给用户a发送邮件,具体就不多说了。
   系统提示收到邮件,直接输入mail可查看。或者more /var/mail/usera

1.3 常用命令的一些选项

   ls -t 按照时间顺序排列,最后修改的排在前面。
   ls -l long模式,显示所有信息
   ls -rt -r是反转输出顺序,这里即是按时间顺序排列,但是最早修改的排在前面。

$ ls -lrt
total 24
drwxrwxr-x  2 rttw rttw 4096 Dec 17 12:10 downloads
drwxrwxr-x  6 rttw rttw 4096 Dec 17 12:31 source
-rw-------  1 rttw rttw  533 Dec 26 08:15 mbox

   wc统计数字(word-counting),
   wc -l        统计行数

   grep寻找匹配的行,
   grep -v      寻找不匹配的行。

   sort按首字母顺序逐行排序,默认排序依次是空白,大写字母,小写字母。
   sort -r  按正常排序的反序
   sort -n  按数字次序排序
   sort -nr 按逆向数字排序

   tail -n file file文件的最后n行
   tail -f file  动态的显示最后几行。即当有新的信息写入文件,立即显示。
             在查看一些log信息的时候比较有用

   cmp  比较两个文件,找出第一个有区别的地方。
   diff 比较两个文件,包括所有被修改、增加或减少的行。

[ 本帖最后由 tanyear 于 2005-12-30 08:48 编辑 ]
作者: tanyear    时间: 2005-12-27 00:07
1.4 shell  wildcard通配符

  常用的*?就不细说了。
  [...][匹配括号内的任何字符
#  rm [12356].txt 删除1.txt, 2.txt, 3.txt, 5.txt, 6.txt文件
#  rm [1-35-6].txt 同上
#  ls  [a-z].txt 列出所有的a.txt, ... , z.txt,等

  书上说通配符只能用于匹配已存在的文件,而不能用来创建文件。
#  mv ch.* chapter.*
   这样是不执行的。

但是在我的系统上
#  ls
1.txt  2.txt  3.txt  4.txt  5.txt  6.txt
# mv *.txt *.tx
mv: when moving multiple files, last argument must be a directory


1.5 I/O重定向(redirection)和管道(pipe)

  I/O重定向指 > , >> , < , <<
  重定向到一个文件时,会判断一个文件是否存在,不存在则创建它,然后再执行命令,写进去。
# ls
1.txt 2.txt
# ls >ls.out
# cat ls.out
1.txt
2.txt
ls.out

由于先判断文件是否存在的关系,ls.out本身也包括在里面了。

类似的情况,在管道中
# ps -ef|grep named
有时候你会grep到“grep named”这个命令。

# set -x
# ps -ef|grep named
+ grep named
+ ps -ef
root      4336  2148  0 18:47 pts/0    00:00:00 grep named

# ps -ef|grep named
+ ps -ef
+ grep named

这是因为管道的程序是“同时”运行的,而不是顺序执行。
管道中的程序是交互式的,由内核对其进行调度和同步处理,以使他们全部运行。

1.6 shell解释的情况

# sort<temp
# sort temp
这两者执行的结果是一样,但实质是有区别的。
sort<temp,是由shell解释<temp,然后作为标准输入传递给sort。
sort temp,是sort把temp看作一个变量,sort读入temp并排序。

#  rm [12356].txt  
同样,这些通配符也都是由shell来解释。而不是这些命令程序
首先shell解释[12356].txt 是1.txt , 2.txt ...
然后rm再读取1.txt , 2.txt ...
因此对于程序来说,通配符,重定向和管道都是透明的。

[ 本帖最后由 tanyear 于 2005-12-29 23:40 编辑 ]
作者: sakulagi    时间: 2005-12-27 07:34
不同的shell对命令的解释有很细微的差别。关于mv的通配符问题。可能提示的错误信息不一样,不过大体上和书上说的并没有冲突。

如果想看一下shell到底是怎么处理的。可以执行
set -x

然后再执行那个mv命令,可以看到shell是怎么展开通配符的。
作者: tanyear    时间: 2005-12-27 22:21
第2章 文件系统

2.1 od查看文件的字节

   文件就是字节的序列。可用od命令查看文件的字节序列

# cat  tmp                      //cat 显示文件内容
hello
123

# od -c tmp                    //-c 把字节解释为ASCII码字符
0000000   h   e   l   l   o  \n   1   2   3  \n
0000012

# od -cb tmp                 //-b 按八进制显示字节
0000000   h   e   l   l   o  \n   1   2   3  \n
        150 145 154 154 157 012 061 062 063 012
0000012

\n是换行符,在ASCII中,八进制值\n是012
左边的数字表示在文件中的位置。

2.2 文件结束

用换行来结束一行,\n,不对整行做记录或者统计。

而文件的结束没有任何标识。
内核跟踪文件的长度,当一个程序读完文件的所有字节时,就到文件尾结束了。

这个过程是,程序使用系统调用read来取得文件中的数据。
每一次调用read,它都返回文件中的下一部分,同时,还说明返回了文件中多少个字节,
当read说明“返回了零字节”,那么就到了文件尾。

用从终端输入的例子来看看
当从终端读入程序的时候,每次按下enter键,才把每一个输入行传给程序。
使用cat来观察这种情况。
#cat
123enter
123
Ctrl+d
#

然后我们看看Ctrl+d怎么结束文件的

#cat
123Ctrl+d123

Ctrl+d表示“立即把键盘输入的字符传给从终端读取程序”。
当第二次键入Ctrl+d的时候

#cat
123Ctrl+d123Ctrl+d#

shell响应一个提示符。因为cat这次没有读到任何字符。
上面说了,没有读到字符,则意味着文件结束。并且停止。
Ctrl+d 把你键入输出到终端的字符传给程序,
当你什么都没有键入时,程序就没有读入任何字符,就认为到了文件的末尾。
这就是我们键入Ctrl+d,会退出的原因。

2.3 文件结构

首先用file命令来看三种文件格式

# file /bin /bin/ls /root/install.log
/bin:              directory
/bin/ls:           ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.4.0, dynamically linked (uses shared libs), stripped
/root/install.log: ASCII English text

在Unix中,并不像windows靠扩展符,后缀来识别文件类型。
那么file是怎么识别各种不同的文件的呢?
这里用od命令来看看

#od -cb /bin/ls

0000000 177   E   L   F 002 001 001  \0  \0  \0  \0  \0  \0  \0  \0  \0
        177 105 114 106 002 001 001 000 000 000 000 000 000 000 000 000
0000020 002  \0   >  \0 001  \0  \0  \0   ?  '   @  \0  \0  \0  \0  \0
        002 000 076 000 001 000 000 000 360 047 100 000 000 000 000 000
......                    //省略一小段
*
0000760  \0  \0  \0  \0  \0  \0  \0  \0  \b  \0  \0  \0  \0  \0  \0  \0
        000 000 000 000 000 000 000 000 010 000 000 000 000 000 000 000
0001000   /   l   i   b   6   4   /   l   d   -   l   i   n   u   x   -
        057 154 151 142 066 064 057 154 144 055 154 151 156 165 170 055
0001020   x   8   6   -   6   4   .   s   o   .   2  \0 004  \0  \0  \0
        170 070 066 055 066 064 056 163 157 056 062 000 004 000 000 000
0001040 020  \0  \0  \0 001  \0  \0  \0   G   N   U  \0  \0  \0  \0  \0
        020 000 000 000 001 000 000 000 107 116 125 000 000 000 000 000
0001060 002  \0  \0  \0 004  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0

file通过读入文件的前几百个字节可以查看到文件类型的线索。

[ 本帖最后由 tanyear 于 2005-12-29 14:44 编辑 ]
作者: 公子月    时间: 2005-12-28 12:50
你把连接地址发这就可以拉
作者: 公子月    时间: 2005-12-28 12:52
要不就打包把
作者: tanyear    时间: 2005-12-28 14:49
我没有电子版的啊,呵呵
作者: tiemuzheng    时间: 2005-12-28 14:54
好教材,楼主继续啊
作者: tanyear    时间: 2005-12-29 14:38
文件系统这一章不想说太多了。今天把它结束

2.4 关于目录

在某些unix版本上,可以用od查看目录。
因为目录其实也都是文件。
在SunOS 5.9上
在test目录下创建了hello ok两个文件

bash-2.05# ls test
hello  ok

bash-2.05# od -bc test
0000000 000 010 257 320 000 014 000 001 056 000 000 000 000 000 000 002
          \0  \b 257 320  \0  \f  \0 001   .  \0  \0  \0  \0  \0  \0 002
0000020 000 014 000 002 056 056 000 000 000 010 257 322 000 020 000 005
          \0  \f  \0 002   .   .  \0  \0  \0  \b 257 322  \0 020  \0 005
0000040 150 145 154 154 157 000 000 000 000 010 257 323 001 330 000 002
           h   e   l   l   o  \0  \0  \0  \0  \b 257 323 001 330  \0 002
0000060 157 153 000 000 000 000 000 000 000 000 000 000 000 000 000 000
           o   k  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000100 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
          \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0001000

可以看到目录也是一个包含有目录下文件名的一个文件。

但是我在linux下面,centos 4.1是不能查看目录的
# od -bc intel
od: intel: read error: Is a directory
0000000

2.5 目录与文件权限

文件的权限只能由root或者文件的拥有者来改变。
其他人即使有文件的写权限,也不能改变文件的权限

但是这种情况,如果有写的权限,可以把文件copy一份作为你自己的文件
这样你就可以为所欲为了。

-rw-rw-rw-  1 test    test       0 Jan 13 20:21 hehe
比如这样一个文件
[wrxu@lc tmp]$ chmod 644 hehe
chmod: changing permissions of `hehe': Operation not permitted

目录的权限跟文件的权限有些不同,所以这里花一定的篇幅来说明
希望对大家理解目录的权限有帮助。

目录的r权限,表示目录可读,上面有说到目录也是一个包含目录下文件名的文件,
这时候只能够用ls来读此目录。

只有具有x的权限,才能进入到目录,
这里的x并不是可执行,而是搜索的意思。

没有x权限的例子
[snms@matrix root]$ ls -l
drwxrw-rw-  2 root  root       4096 Dec 30 08:59 tmp
[snms@matrix root]$ cd tmp
-bash: cd: tmp: Permission denied
[snms@matrix root]$ ls -l tmp
total 0
?---------  ? ? ? ?           ? a
?---------  ? ? ? ?           ? b
?---------  ? ? ? ?           ? c
虽然snms用户可以读到tmp目录的权限,但是不能搜索到信息。
也不能进去目录。

没有r权限的例子
[snms@matrix root]$ ls -l
drwx-wx-wx  2 root  root       4096 Dec 30 08:59 tmp
[snms@matrix root]$ cd tmp
[snms@matrix tmp]$ ls -l
ls: .: Permission denied
[snms@matrix tmp]$ rm a
rm: remove write-protected regular empty file `a'? n

具有了x权限,所以可以进入目录,具有了w权限,所以可以删除文件
但是没有r权限,读不了目录文件,就无法得到当前目录下的内容。

w权限就是可以创建或删除文件,
注意:删除一个文件的权限与文件本身无关
即使用户对某个目录下的文件没有写的权限,但他只要具有目录的w权限,
就可以轻易的删除此目录下的任何文件。

下面的例子:snms用户删除了root的tmp文件夹下面的写保护文件。
[snms@matrix root]$ ls -l
drwxr-xrwx  2 root  root       4096 Dec 30 08:52 tmp
[snms@matrix root]$ cd tmp
[snms@matrix tmp]$ ls -l
total 12
-rw-r--r--  1 root root 0 Dec 30 08:52 a
-rw-r--r--  1 root root 0 Dec 30 08:52 b
-rw-r--r--  1 root root 0 Dec 30 08:52 c
[snms@matrix tmp]$ rm *
rm: remove write-protected regular empty file `a'?y

所以目录的写权限不要轻易的给出去。

更改权限的命令chmod
chmod [u/g/o]+/-[x/w/r] file
u,g,o分别指user,group,others
+-就是加上或者去掉某种权限

或者chmod 777 file
777这个数的三位,从左到右分别表示用户,组和others
数字的意义,4表示r,2表示w,1表示x
需要什么要的权限把它加起来就可以了。
比如用户读写(r+w),组读(r),others读(r)
                   4+2         4              4
chmod 644 file
数字的记忆办法,都是2的*次方。权限越高,数字越小。

看一个权限的应用。
前面说了用户之间的通信,比如wall,write这类的命令
可以用mesg [ y|n ]来关闭或打开

mesg命令的实质就是对用户当前所在的终端设备的写权限的更改

首先查看test用户的终端设备
test$ who am i
test     pts/1        Dec 29 19:24 (192.168.1.2)

test$ mesg
is y

test$ ls -l /dev/pts/1
crw--w----  1 test tty 136, 1 Dec 29 19:26 /dev/pts/1
test$ mesg n
$ ls -l /dev/pts/1
crw-------  1 test tty 136, 1 Dec 29 19:26 /dev/pts/1

关掉了mesg,关掉了test用户当前所在终端设备的写的权限
这个时候用户tony就不能给test发信息了
tony$ write test
write: test has messages disabled

此时test用户自己都不能写了
test$ write tony
write: you have write permission turned off.

[ 本帖最后由 tanyear 于 2005-12-30 09:37 编辑 ]
作者: tanyear    时间: 2005-12-29 23:33
2.6 inode与ln

一个文件包括有名称、内容和管理信息如权限或修改时间。
管理信息就存放在Inode中,同时存放的还有文件的长度,文件放在磁盘的位置等

查看一个文件的Inode号码
# ls -i
654076 test1  

我们用ln使一个链指向一个已有的文件。
#ln test1 test2
#ls -i
654076 test1  654076 test2
这里test1和test2的Inode号码是一样的。
也就是说他们其实是同一个文件(Inode号码相同,也即是文件放在磁盘的位置一样),但是具有两个名称。
这样的好处是可以让一个文件存在不同的目录,或者存在不同的名称。
对test1的修改也即是对test2的修改。
# ls -l
-rw-r--r--  2 root root 29 Dec 29 17:32 test1
-rw-r--r--  2 root root 29 Dec 29 17:32 test2
               ~~ 这里的2,就表示指向同一个Inode的链接数。

删掉test2
#ls -l
-rw-r--r--  1 root root 29 Dec 29 17:32 test1

或许会有疑惑,既然test1和test2是同一个文件,为什么删除test2,test1还在呢?
在这种情况,删除test2只是删除了一条指向654076这个Inode的一个链接。
文件依然存在。只有当最后一条链被删除时,即指向Inode的链接数为0时,文件才被删除。

# ln /boot/grub/grub.conf /home/test/grub
ln: creating hard link `grub' to `/boot/grub/grub.conf': Invalid cross-device link

在这里,我的系统分了/,/boot,等分区
ln有一个问题就是不能跨设备(分区或者文件子系统)创建链接,一是因为分区可以被mount和umount,
跨分区的硬链接在分区umount会带来一些麻烦,另外每个分区的Inode值不相同。

昨天看见有个帖说,除了swap区,可以把所有的磁盘分成一个区。
但是这样当一个分区出现文件系统错误,可能所有的数据都没有了。
而划分多个分区,每个分区的Inode不同。即使某个分区的数据错误,也不至于影响到整个系统。

跨设备创建链接,可以使用
#ln -s /test /home/test/test

# ls -li /test
13 -rw-r--r--  1 root root 0 Dec 29 17:45 /test

# ls -li /home/test/test
1697478 lrwxrwxrwx  1 root root 5 Dec 29 17:46 /home/test/test -> /test

这样就是一个软链接,它是可以跨设备的,在文件类型这里会显示l
可以看到它们的Inode号码是不同的,而且链接的数目分别各为1
因此它们并不是同一个文件。

在软链接的情况下,修改链接到的文件/home/test/test,也修改了被链接的文件/test。
但这种方式和硬链接是不同的。这是通过某种机制来实现同步。而不是同一个文件的修改。

删除链接到的文件,对被链接文件没有任何影响。
而删除被链接文件/test,链接到的文件依然存在,但是会闪烁,提示被链接的文件不存在了
也不能读取该文件了
#rm /test
# ls -il /home/test/test
1697478 lrwxrwxrwx  1 root root 5 Dec 29 17:51 /home/test/test -> /test
# more /home/test/test
/home/test/test: No such file or directory

事实上/home/test/test是存在的,作为一种特殊的文件类型为l的文件。
只不过软链接的一些规则决定了它不可读。

为了加深对文件Inode的理解,可以从mv和cp文件来说明:

# cp test1 test2
# ls -i
654076 test1  654081 test2

这两个文件Inode值不一样,cp的实质是复制创建了一个新文件

#mv  test1 test3
#ls -i
654076 test3

跟上面的比较,test1和test3的Inode是相同。
所以mv只是更改了文件的名称,也即是访问文件的入口
它们是指向同一个位置磁盘上的文件。

但是如果mv一个文件到另一个分区。
如前面所讲,不同分区的Inode值是不同的。
# ls -il /root/fs/test
654076 -rw-r--r--  1 root root 29 Dec 29 17:32 /home/fs/test
# mv test /boot/test
# ls -il /boot/test
16 -rw-r--r--  1 root root 29 Dec 29 17:32 /boot/test

所以mv文件到不同的分区,是删除掉原分区的文件之后在另外一个分区重新创建这个文件。
因此有了一个不同的Inode值。

2.7 调整Inode数量
另外,每个分区的Inode数目是有限的。
可以使用df -i来查看

# df -i
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/mapper/VolGroup00-LogVol00
                     1958400   97435 1860965    5% /
/dev/sda1              26104      32   26072    1% /boot
/dev/shm               23850       1   23849    1% /dev/shm

当一个磁盘分区(文件子系统)的Inode被用光的时候,是无法再创建文件的。
这种情况在邮件服务器或者BBS这种系统中容易出现,因为用户创建了很多小文件的情况。

要增加Inode的数量只能umount文件系统,然后用mke2fs命令来调整

#mke2fs.ext3 /dev/sda1 [ -N number-of-inodes ]
直接指定需要的Inode数目
或者
#mke2fs.ext3 /dev/sda1 [ -i bytes-per-inode ]
调整字节/inode的比例,来达到增加创建inode数目的目的。
磁盘空间一定的情况下,当然比例越小,能创建的Inode就越多

当然如果你是只有很少但是很大的数据库文件。就可以调高bytes/inode的比例。
(更多的关于mke2fs的信息,自己去man)

不过注意。调整Inode相当于重新格式化了分区。原有的文件都会丢失
所以确实需要调整的话,先要备份数据;
或者在服务器搭建时预先设置好。

[ 本帖最后由 tanyear 于 2005-12-30 07:37 编辑 ]
作者: 寂寞烈火    时间: 2005-12-30 01:34
stty,确实值得研究
作者: hongzjx    时间: 2005-12-30 08:54
楼主,继续!
作者: tanyear    时间: 2005-12-30 12:46
好,继续啦

第3章 shell的使用

3.1 命令行结构

最简单的命令就是一个字,比如date,who等
一条命令通常以换行结束,也可以用分号;结束

通常有很多个顺序执行的命令可以用;连接
com1;com2;com3

$ set -x
$ date;who|wc
+ date
Fri Dec 30 11:30:10 CST 2005
+ wc
+ who
      1       6      61

注:为了方便看到Shell命令的执行情况,设置了set -x,
输出中以+开头的表示跟踪到的命令执行

;的优先级比较高,所以先执行了date命令,并标准输出;
然后执行who,并通过管道送到wc。

如果需要date和who的输出都送到wc,可以用括号()组合命令
$ (date;who)|wc
+ wc
+ date
+ who
      2      12      90

这样date和who的输出被串连成单个数据流,送入管道

tee命令可以将通过管道的数据流截获并放进某个文件。
$ (date;who)|tee tmp|wc
+ tee tmp
+ wc
+ date
+ who
      2      12      90
$ more tmp
+ more tmp
Fri Dec 30 11:39:19 CST 2005
root     pts/1        Dec 30 08:52
运行tee命令对执行顺序并没有影响,对于管道来说是透明的。
它只是将它的输入复制一份到文件,然后再把输入复制到输出。

另一个命令常用符号&。
&不等命令结束,就将运行的命令放入后台。
用户可以继续与shell交互

$ (sleep 5;date) & date
[1] 7004
+ date
+ sleep 5
Fri Dec 30 11:51:58 CST 2005
$ + date
Fri Dec 30 11:52:03 CST 2005



3.2 元字符

<, >, |, ;和&这些特殊字符,都是由shell来解释的,而不是shell中命令的参数。
在shell中有特殊用途的字符,称为元字符(metacharacter)。

常见的如*代表任何字符串的意思。
不过需要注意,文件名匹配字符串不能匹配以点.开头的的文件名。
以免与.和..相匹配,带来副作用。

$ echo *
+ echo avi rm txt
avi rm txt

$ echo .*
+ echo . .. .bash_logout .bash_profile .bashrc .emacs
. .. .bash_logout .bash_profile .bashrc .emacs

从命令的执行可以看到,由shell解释了*和.*的含义。
而echo程序本身并不了解发生了什么事。

列表一些对于初学者来说,不太常见,但比较有用的元字符:

`...`         运行...中的命令;输出运行结果作为输入
$ `pwd`
++ pwd
+ /home/mysql
-bash: /home/mysql: is a directory

\               \C将C字符作为字符,转义
'...'            ...表示文字
"..."          在...中的$、`...`和\得到解释后,将...作为文本文字
p1&&p2   运行p1;若成功,再运行p2
p1||p2     运行p1;若不成功,再运行p2

具体的一些应用和区别,会在后面慢慢讲。

当元字符不作为元字符使用时,可以有几种方法来表示它们。
防止shell解释元字符最简单的办法就是用单引号'...'

$ echo ***
+ echo avi rm txt
avi rm txt

$ echo '***'
+ echo '***'
***

$ echo \*\*\*
+ echo '***'
***

此外也可以用反斜杠\来转义。
而一种引号也可以保护另一种引号

$ echo "Don't do that"
+ echo 'Don'\''t do that'
Don't do that

这里shell解释的时候自动添加了一个\转义。

$ echo x * y
+ echo x avi rm txt y
x avi rm txt y

$ echo x '*' y
+ echo x '*' y
x * y

$ echo x \* y
+ echo x '*' y
x * y

另外可以在一行末尾加入反斜杠\,表示该行未完,这样可以避免shell中的输入行过长
$ ps -ef| \
> grep java\
> |wc
+ grep java
+ wc
+ ps -ef
      2      28     441

另外关于echo的一个选项 -n,表示不换行。
$ echo -n haha
+ echo -n haha
haha$

如果要echo出-n。可以使用这样的方式
$echo '-n
>'
+ echo '-n
'
-n

[ 本帖最后由 tanyear 于 2005-12-30 14:44 编辑 ]
作者: tanyear    时间: 2005-12-31 02:44
3.3 位置变量(命令参数)

首先介绍$0,$1,$2...$9
$0指正在执行的程序的名字,$1到$9指程序的参数位置,也叫位置变量。
不详述了。
注意$10会被shell解释成$1后面跟一个0。

比较特殊的一个命令参数,$*,它表示所有的参数
但用户超过9个参数时,它比较有用。
无论给出多少参数,或者有无参数,都能正常执行。

一个使用位置变量最简单的例子
用cx来代替chmod +x

# echo 'chmod +x $1'>cx
# sh cx cx
# echo echo Hi >hello
# ./cx hello
# ./hello
Hi

$1指跟在./cx命令后的第一个参数。在这里就是hello
然后将cx移到/usr/bin就可以当作命令到处使用了。

不过还有更简单的办法。就是设置成alias

之前有帖子说Redhat里面有ll命令可以代替list -l

# alias
alias cp='cp -i'
alias l.='ls -d .* --color=tty'
alias ll='ls -l --color=tty'
alias ls='ls --color=tty'
alias mv='mv -i'
alias rm='rm -i'

其实可以看到,也就是Redhat设置了一个alias而已。
ll='ls -l'
还有ls以点号开头的文件的l.命令,这是昨天说的ls .*的使用

我们也可以用alias设置自己的精简命令,比如cx命令
# alias cx='chmod +x'
验证一下

# echo echo Hello>hi
# cx hi
# ./hi
Hello

如果要取消掉某个alias,使用unalias命令
#unalias cx

[ 本帖最后由 tanyear 于 2006-1-2 11:12 编辑 ]
作者: muyuking    时间: 2006-01-01 18:12
加油啊~楼主 ~~~~~~~
作者: tanyear    时间: 2006-01-02 08:55
标题: 回复 15楼 muyuking 的帖子
Thanks, I went out for a classmate's party yesterday.
OK, Let's continue to learn "The Unix Programming Environment"

3.4 shell变量

shell变量除了上节的位置变量,还包括环境变量和普通变量(用户自定义变量)

用set命令可以查看当前的环境变量
# set
BASH=/bin/bash
BASH_ENV=/root/.bashrc
BASH_VERSINFO=([0]="2" [1]="05b" [2]="0" [3]="1" [4]="release" [5]="i386-redhat-linux-gnu")
BASH_VERSION='2.05b.0(1)-release'
COLORS=/etc/DIR_COLORS
...
PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
PIPESTATUS=([0]="0")
PPID=16266

只查看某个环境变量用echo就够了
举最常用的环境变量PATH的例子。
PATH指定了shell查找命令的目录列表,每个目录之间用:分开

# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

# PATH=$PATH:/root/path
# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/root/path

一个变量的值与创建它的shell有关,这个值并不会自动地传递给子shell
# x=hello
# echo $x
hello
# bash        //创建子shell
# echo $x
                    //只有换行符
# exit          //ctrl+d 或者exit 退出子shell,回到原来的shell
# echo $x
hello

所以编写shell程序的时候,应该注意运行一个shell程序,一般变量只在当前的子shell中有效。

# echo 'x="Good Bye"
> echo $x'>setx                             //创建一个两行的shell文件,设置变量x
# cat setx
x="Good Bye"
echo $x
# echo $x                   //在当前shell中
hello
# bash setx                //运行setx shell脚本;在setx运行的子shell脚本中,x是Goodbye
Good Bye                    
# echo $x                   //在原shell中,依然是hello
hello

3.5 运行shell程序的几种方式

如果我们需要使用一个shell程序来改变当前的环境变量怎么办呢?
一个典型的例子就是我们安装了一个新的程序,希望利用shell程序或脚本,把它的目录添加到PATH变量中。

# cat ap                                   //这是一个添加PATH,并且打印PATH的简单脚本
PATH=$PATH:/root/path
echo $PATH

# bash ap                                //用bash运行ap,在运行的子shell中,PATH中添加了/root/path
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/root/path
# echo $PATH                          //程序运行结束后,在原shell中,PATH不变,没有/root/path
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

# . ap                                       //用.运行ap
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/root/path
# echo $PATH                           //当前的shell的PATH也已经改变。
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/root/path

这是运行shell程序的两种不同方式:
bash ap,创建了一个子shell,并运行其中的命令。运行结束退出子shell
. ap的方式并没有创建一个子shell。它是把shell程序里面的命令看作是用户在终端交互式的键入这些命令。
即shell的标准输入被重定向到这个程序。事实上,这个文件只是被读取,而不是被执行。

在这本书写的时候,作者说,目前用.号运行shell程序还不能接受位置变量。我在fc4上面试了一下,现在是可以的。

此外还有用./ ap来运行shell程序的方式。这种方式和bash ap一样,也是创建子shell并运行命令。
不过这种方式需要ap这个shell文件具有可执行权限。

3.6 shell变量传递

如果需要临时改变环境变量,另外一种发方法是,在命令行的命令之前显式的赋值,也即是行内赋值

# echo 'echo $x'> echox
# chmod +x echox
# ./echox
         
# x=hello ./echox
hello
# echo $x

#

这样行内赋值,把x=hello 传给了子shell
所以3.5中add path的ap脚本,如果只是需要在子shell中使用/root/path
也可以采用行内赋值的方式,传给子shell

#vi echopath
echo $PATH
#chmod +x echopath
# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
# ./ap ./echopath                   //行内赋值
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin:/root/path
# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

当希望在子shell中能够访问某个变量的时候,我们可以使用shell的export命令。
# export x=hello
# echo $x
hello
# bash        //创建子shell
# echo $x
hello             //现在子shell也可以访问x变量
#x=hi            //在子shell中改变x的值
#echo $x
hi
# exit          //ctrl+d 或者exit 退出子shell,回到原来的shell
# echo $x    //原shell的x值并没有变化。
hello

export可以看成是把一个变量设置为全局变量。

通常来说,不要贪图方便,把临时的变量都export出来,应该export那些希望出现在所有shell和子shell中的变量。
比如某些程序的PATH路径等。

3.7 怎样使环境变量登录时生效

如果要使整个系统,每个用户登录时的环境变量进行设置,可以修改/etc/profile文件

在/etc/profile文件这个位置中添加下划线的那一行
# Path manipulation                         //这是/etc/profile脚本里面的一个函数。
if [ `id -u` = 0 ]; then                      //判断id -u这个命令执行的结果是不是等于0。id -u是查看登录的id的uid,root的uid是0。
        pathmunge /sbin
        pathmunge /usr/sbin
        pathmunge /usr/local/sbin
        pathmunge /root/path
fi


再次登录就可以发现/root/path已经被自动加进去了
# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/root/path:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

或者简单的在/etc/profile里面的export PATH前添加
PATH=$PATH:/root/path
再次登录
# echo $PATH
/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/path:/root/bin

这两种方式/root/path的位置不同,这是/etc/profile这个shell程序所决定的。需要仔细了解的话,可以去读读这个shell脚本。

----------------------

如果我们只是针对某个用户的环境变量进行设置,可以在用户的环境配置文件中添加。
比如在用户的$HOME目录下,对于bash来说有这两个文件可以修改
.bash_profile .bashrc
而csh则可以修改
.cshrc

这几个文件,都是很短小的shell,适合初学者阅读。
比如.bash_profile

# more .bash_profile
# .bash_profile

# Get the aliases and functions              //设置aliase。.bashrc文件里面主要包括的是alias设置的信息。
if [ -f ~/.bashrc ]; then                           //判断home目录下,.bashrc文件是否存在,存在则运行它
        . ~/.bashrc
fi

# User specific environment and startup programs            //设置用户特定的环境变量和自启动程序。

PATH=$PATH:$HOME/bin                                        //在这里添加对应的路径就可以了
BASH_ENV=$HOME/.bashrc
USERNAME="root"

export USERNAME BASH_ENV PATH


# more .bashrc
# .bashrc

# User specific aliases and functions

alias rm='rm -i'                               //3.3节所说的自己设置的alias cx='chmod +x'就可以添加到这里,让它每次登录自动生效。
alias cp='cp -i'
alias mv='mv -i'

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

[ 本帖最后由 tanyear 于 2006-1-6 08:38 编辑 ]
作者: tanyear    时间: 2006-01-03 11:41
3.7 进一步讨论I/O重定向

在缺省状态下,每个程序启动时都会创建3个文件。用“文件描述符”的小字符标识。
标准输入和标准输出分别用0和1表示,经常被重定向到文件和管道。
而2表示标准错误输出,通常被送往终端。

可以使用command 2> file 把标准错误输出重定向到文件(注意2和>之间没有空格)
command 2>&1 把标准错误输出合并到标准输出  这里的&仅仅是一个记号
command 1>&2 也可以把标准输入加入到标准错误输出中。

记得刚开始学shell的时候,想把错误信息保存下来,发现怎么重定向都不行,咔咔,
后来才知道错误信息输出和标准输出是不同的输出。


shell还提供了把命令的标准输入和命令放在一起的功能,这种结构叫here document
这里举一个简单的例子。用一个自动登录ftp的脚本来说明。

# cat 411
ftp -n localhost <<End         //使用ftp -n的选项
user tony tony         //user后跟用户名和密码
ls
quit
End


# bash 411
Please login with USER and PASS.
Please login with USER and PASS.
KERBEROS_V4 rejected as an authentication type
-rw-r-----    1 1017     105         12603 Oct 28 13:57 ACML-EULA.txt
drwxr-xr-x   11 0        0            4096 Dec 12 10:38 ATLAS
#

说明,here document这种结构说明文件的输入就是该文件的一部分,而不是其他文件。
<<指示了这种情况,后面跟的单字(本例为End)用来终止输入。
作者: tanyear    时间: 2006-01-06 08:36
标题: 回复 17楼 tanyear 的帖子
这周比较忙啊。都没时间来看书。
昨天帮自动化的研究生mm找她选修的软件开发环境的复习资料,
结果问我读研同学,发现我们学校计算机研究生的这门课程,用的就是偶看的这本Unix Programming Environment。
那个寒啊,难怪我之前扫描到后面几章发现都是编程了。

所以结束这个帖子。因为其实这本书不太适合系统管理。倒是可以作为一定的补充和提高

[ 本帖最后由 tanyear 于 2006-1-11 19:47 编辑 ]




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2