回忆之城
生命在于折腾
posts - 575,comments - 9,trackbacks - 0

最近一直忙于shell脚本的学习,感觉收获颇多,在此与大家共享。

脚本iout.sh

#!/bin/sh
SHDIR="$(dirname -- $(readlink -f -- "$0"))"   //查找当前脚本的所在目录
FILENAME="out"

echo "beginning"
cd $SHDIR
if [ -f "$SHDIR/$FILENAME" ]; then                    //判断缓存文件是否存在
         rm -f $FILENAME
         echo "remove file"        
fi
:>$FILENAME
rpm -qa | grep -i mysql* 1>$FILENAME              // 搜索计算机中所安装的mysql


while read line                                                  //将缓存中的mysql读取删除
do 
    rpm -e --nodeps "$line"
done < $FILENAME

echo "end"

引用:

http://www.ibm.com/developerworks/cn/linux/l-iotips/

http://blog.csdn.net/ltx19860420/archive/2010/05/05/5561006.aspx

2004 年 11 月 01 日

Linux I/O 重定向虽然很简单,但在脚本编写、系统管理时却要常常打交道,搞清其中使用技巧非常有用。

首先说一下什么是I/O重定向,所谓I/O重定向简单来说就是一个过程,这个过程捕捉一个文件,或者命令,程序,脚本,甚至脚本中的代码块(code block)的输出,然后把捕捉到的输出,作为输入发送给另外一个文件,命令,程序,或者脚本。

如 果谈到I/O重定向,就涉及到文件标识符(File Descriptor)的概念, 在Linux系统中,系统为每一个打开的文件指定一个文件标识符以便系统对文件进行跟踪,这里有些和C语言编程里的文件句柄相似,文件标识符是一个数字, 不同数字代表不同的含义,默认情况下,系统占用了3个,分别是0标准输入(stdin),1标准输出(stdout), 2标准错误(stderr), 另外3-9是保留的标识符,可以把这些标识符指定成标准输入,输出或者错误作为临时连接。通常这样可以解决很多复杂的重定向请求。

标准输入通常指键盘的输入 
标准输出通常指显示器的输出 
标准错误通常也是定向到显示器

请看以下例子,来描述一下他们的关系

#ls /dev

这个命令列出/dev目录下的所有文件,在屏幕上输出结果。

这里 /dev 就是作为命令ls的标准输入(从键盘输入),而打印在屏幕的结果就是标准输出(/dev目录中的内容)

还是回到标题,重定向就是把标准的输入或者输出更改成其他的方式,请参看如下例子 
或者等同于

#ls /dev 1>filename                                  #注意:"1"和">"中间没有空格

以上命令会把命令的标准输出重新定向到一个文件filename,而不是显示到屏幕上,如果不指明文件标识符,系统默认的就是1, 因此1可以省略

如果把上面例子重的">"改成">>"则表示把输出追加到filename文件的末尾,如果文件不存在则创建它。如下

#ls /dev >>filename

也可以把标准错误重新定向到文件

#ls -qw  /dev  2>filename

显然 -qw是一个错误参数,通常会在显示器上报告一个错误信息,但由于把2标准错误(stderr)重新定向到了文件filename,因此显示器没有错误信息,而信息写到了文件里面

以下命令是把标准输出和错误都定向到文件

#ls /dev &>filename

"&"在这里代表标准输出和标准错误,这里无论是正常输出还是错误信息都写到filename中了。

重新定义标准输入,输出,和错误的文件标识符

重新定义文件标识符可以用i>&j命令,表示把文件标识符i重新定向到j,你可以把"&"理解为"取地址"

请看以下例子

#exec 5>&1

表示把文件标识符5定向到标准输出,这个命令通常用来临时保存标准输入。

同样标准输入也是可以重新定向的,请参考下面例子

# grep search-word <filename或者grep search-word 0<filename

一般来说grep命令在给定文件中搜索字符串,以上命令把文件filename作为grep命令的标准输入,而不是从键盘输入。

前面曾经提到,系统为每一个打开的文件指定一个文件标识符以便系统对文件进行跟踪,那么默认的文件标识符是什么呢?答案是0,也就是标准输入,或者可以说从键盘输入。当然这个文件标识符也可以自己指定,请参考下面例子

#echo 123456789 >filename   把字符串写到文件filename中

#exec 3<>filename 把文件filename打开,并指定文件标识符为3

#read -n 4 <&3 从文件中读4个字符,句柄已经指到第四个字符末尾

#echo -n . >&3 在第5个字符处写一个点,覆盖第5个字符,-n表示不换行

#exec 3>&- 关闭文件标识符3

现在cat filename文件的结果就成了1234.6789

命令j<>filename表示把文件打开,并指明文件标识符为j 
"&-"表示关闭文件标识符

有关关闭文件标识符的操作请参考下面

n<&- 关闭输入文件标识符n

0<&-或<&- 关闭标准输入stdin

n>&- 关闭输出文件标识符n

1>&-或>&-关闭标准输出stdout

另外还有一些其他命令,如下参考

2.	:> filename 或者 > filename

表示把文件filename设置成空,也就是清空文件内容,如果文件不存在,则创建一个空文件,(等同于touch命令) :表示一个空输出,两个命令的唯一区别就是>filename不是在所有shell都可以正常工作的。

Shell的I/O语法比较复杂,难以理解,更难以正确使用。Shell的输入输出语法有两个难点:重定向和文件描述符。我以前对Shell脚本输入/输出的理解也有错误。最近重新整理思路,决定写一篇文章来总结I/O重定向。


一、先介绍几个基本概念:

(1)文件(File):在Unix/Linux中,文件类型有七种,这七种类型是:目录、符号链接(指向另一个文件)、套接字文件、块设备文件、字符设 备文件、命名管道文件、普通文件。这意味着,I/O设备被Unix/Linux视为“特殊”的文件,所以对设备进行输入输出等同于读写文件。

(2)文件描述符(File Descriptor ):实际上,Shell中的文件描述符等同于C语言中的文件结构(参见《The C Programming Languag》的7.5节),它是一个包含文件信息的结构,这些信息包括:缓冲区的位置,缓冲区中当前字符的位置,文件是否到达末尾等。Shell脚本 通过文件描述符来读写文件。一个Shell脚本有10个文件描述符。其中,0、1、2是标准输入、标准输出和标准错误,我们可以任意使用的文件描述符是3 到9。

(3)重定向(Redirect):在Shell中,我们可以对命令(命令就是程序或脚本)的标准输入、标准输出和标准错误进行重定向,使其指向其他文件。

(4)重定向的作用域(Redirect's Scope ):重定向的“作用域”这个概念是我自己提出的,它对理解重定向的作用效果非常重要!我们来看下面这几个例子:

例1:重定向的“作用域”局限于一个命令
#!/bin/bash
echo "hello world"  > result.txt
解析:这个重定向的作用域是echo命令,命令结束重定向也就结束了。

例2:重定向的“作用域”局限于一个循环
#!/bin/bash
while read line
do
    echo "$line"
done < name.txt
解析:这个重定向的作用域是while循环,在循环执行期间,标准输入都被重定向到了当前目录的的name.txt文件,即每次read都是从文件中读取一行,且read是顺序读取文件的所有行。

while循环结束时,重定向就结束了。
#!/bin/bash
while read line < name.txt
do
    echo "$line"
done
解析:这个重定向的作用域是read命令,每次read都重新进行一次重定向。这造成了可怕的结果,每次都读取文件中的第一行,无限循环下去。


例3:重定向的“作用域”局限于整个脚本
#!/bin/bash
#This script called ltx_test
while read line
do
    echo "$line"
done

在终端运行这个脚本,执行命令
ltx_test < name.txt
解析:这个重定向的作用域是整个脚本,在脚本执行期间,标准输入都被重定向到了当前目录的的name.txt文件,即每次read都是从文件中读取一行,且read是顺序读取每一行。脚本结束时,重定向就结束了。



二、下面开始讨论Shell脚本中输入/输出的的两种情况:

(1)脚本只使用标准输入、标准输出和标准错误

    Shell会自动为我们打开和关闭0、1、2这三个文件描述符,我们不需要显式地打开或关闭它们。标准输入是命令的输入,默认指向键盘;标准输出是命令的输出,默认指向屏幕;标准错误是命令错误信息的输出,默认指向屏幕。

    如果没有显式地进行重定向,命令通过文件描述符0从屏幕读取输入,通过文件描述符1和2将输出和错误信息输出到屏幕。但如果我们想从其他文件(再次强 调,I/O设备在Unix/Linux中也是文件)读取输入或产生输出,就需要对0、1、2使用重定向了。其语法如下:


command < filename                         把标准输入重定向到filename文件中
command 0< filename                       把标准输入重定向到filename文件中

command > filename                         把标准输出重定向到filename文件中(覆盖)
command 1> fielname                       把标准输出重定向到filename文件中(覆盖)

command >> filename                       把标准输出重定向到filename文件中(追加)
command 1>> filename                     把标准输出重定向到filename文件中(追加)

command 2> filename                       把标准错误重定向到filename文件中(覆盖)
command 2>> filename                     把标准输出重定向到filename文件中(追加)

command > filename 2>&1               把标准输出和标准错误一起重定向到filename文件中(覆盖)
command >> filename 2>&1             把标准输出和标准错误一起重定向到filename文件中(追加)

command < filename >filename2        把标准输入重定向到filename文件中,把标准输出重定向

                                                        到filename2文件中
command 0< filename 1> filename2   把标准输入重定向到filename文件中,把标准输出重定向

                                                        到filename2文件中

重定向的使用有如下规律:

1)标准输入0、输出1、错误2需要分别重定向,一个重定向只能改变它们中的一个。
2)标准输入0和标准输出1可以省略。(当其出现重定向符号左侧时)
3)文件描述符在重定向符号左侧时直接写即可,在右侧时前面加&。
4)文件描述符与重定向符号之间不能有空格!


(2)脚本使用外部文件

      当Shell脚本使用外部文件而不是默认的I/O设备作为输入输出时,我们“可以”使用3到9等几个文件描述符显式地打开或关闭外部文件。它的作用域是从 显式打开文件到显式关闭文件之间。之所以说可以而不是必需,是因为我们可以重定向标准输入、输出和错误到外部文件来达到同样的目的。
      使用文件描述符显式打开文件需要使用内置命令exec,格式如下:
      exec  文件描述符(没有空格)重定向符号  文件
      使用文件描述符显式关闭文件,格式如下:
      exec  文件描述符(没有空格)<&-

例4:使用外部文件
#!/bin/bash
exec 3< name.txt
while read line <&3
do
    echo "$line"
done
exec 3<&-
解析:这个例子是从例2修改得来的,可以做比较。显式打开外部文件后,在循环执行期间,3都被重定向到了当前目录的的name.txt文件,即每次 read都是从文件中读取一行,且read是顺序读取文件的所有行。while循环结束时,重定向没有结束。直到显式文件为止。

在此声明,可以转载或引用,但必须注明出处,谢谢。

posted on 2014-04-20 22:21 回忆之城 阅读(105) 评论(0)  编辑 收藏 引用 所属分类: unix/linux
只有注册用户登录后才能发表评论。