2017-07-22 21:12:22 Ruoshuiss 阅读数 1488

重定向是什么

在计算机领域,重定向是大多数命令行解释器所具有的功能,包括各种可以将标准流重定向用户规定地点的Unix shells。Linux作为一种类UNIX系统,支持重定向。Linux下重定向是指对原来系统命令的默认执行方式进行改变,比如说有时候我们不想看到输出结果到屏幕,而是希望输出到某一文件或设备中,就可以通过Linux重定向来进行这项工作。

下图是本篇文章的思维导图:

在学习重定向之前,我们要了解程序的输入和输出。程序=指令+数据,每个程序的执行需要读入数据和输出数据。早期的计算机通过纸带打孔读入数据,通过二极管发光输出数据。今天我们不再使用这种古老的设备了,那么,思考一下我们的输入来自哪?输出又输到哪呢?

首先,我们来了解一下linux的文件描述符。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。在linux下,打开的文件都有一个文件描述符(file descriptor)。

linux启动后,会默认打开3个文件描述符,给程序提供三种I/O设备,我们可以在linux下输入tty查看当前终端, cd /etc/fd 进入目录,可以看到下图的0,1,2三个文件,/dev/pts/1是当前终端,这代表:

    标准输入(standard input) -0 默认接受来自键盘的输入

    标准输出(standard output)-1 默认输出到终端窗口

    标准错误(standard error) -2 默认输出到终端窗口

对于任何一条linux 命令执行,过程如下图所示:

 

一个linux命令执行了:

先有一个输入(standard input):输入可以从键盘,也可以从文件得到

命令执行完成(standard output):成功了,会把成功结果输出到屏幕当前终端窗口或者输出到文件中

命令执行有错误(standard error):会把错误默认输出到当前终端窗口或者输出到文件中

 

2 I/O重定向

输出重定向

格式:command [1或2] > device或file或其它

上面这条命令的意思是:将一条命令执行结果(标准输出,或者错误输出,本来是默认打印到当前终端设备即屏幕上面的),

重定向其它输出设备(文件,文件操作符,等等),1,2分别是标准输出,标准错误输出。

实例:

当我们执行一条命令,默认打印到当前终端设备即屏幕上面,如下图

如果不想在屏幕上看到命令执行结果,这时候就需要用到输入输出重定向啦。

(1)标准输出 >

echo hello > a.txt,仔细看,发现了什么?本来应该输出到屏幕上的hello不见了,查看 a.txt文件,我们发现hello被写进了a.txt文件里,这时就已经将命令执行结果重定向到文件里了。

当然了,输出不只能重定向到文件中,下面来试试将输出重定向到其它终端设备吧!

我打开了两个终端设备,分别是/dev/pts/1和/dev/pts/2。在/dev/pts/1终端执行echo hello > /dev/pts/2,回车,屏幕上没有输出结果,这时我们切到/dev/pts/2终端,在屏幕上看到了"hello"这个输出结果,也就是说,输出可以重定向到其它终端设备。

重定向中,标准输出1是默认项,所以1可以写也可以不写,下图中,可以看到标准正确输出中加不加结果无变化

(2)标准错误输出    2>

但是输出标准错误时必须写上2,不要忘记哦。

            

(3)标准输出和错误输出各自定向至相同或不同位置

格式:COMMAND > file/device 2> file/device

有时候我们既要输出正确结果,又要输出错误结果,这时候可以标准输出和错误输出各自定向至相同或不同位置。下面是具体实例:

存在/etc/passwd这个文件,但是当前test目录为空目录,并没有passwd文件,查询这两个文件有错误输出也有正确输出,直接执行ls /test/passwd /etc/passwd >/dev/pts/3 ,会看到错误输出显示在当前终端屏幕上,这代表没有重定向成功。

正确输出显示在/dev/pts/3终端屏幕上,重定向成功了。该怎么办呢?别忘了我们刚才提到的错误输出要加上2

这该怎么办呢?别着急,别忘了我们刚才提到的错误输出要加上2,我们分别把标准输出和错误输出重定向到/dev/pts/3中。执行ls /test/passwd /etc/passwd >/dev/pts/3 2>/dev/pts/3 这条命令,就可以把它们都显示在/etc/pts/3终端上啦!

当然,重定向位置也可以不同,将标准输出和错误输出各自定向至不同位置。

标准输出重定向到/dev/pts/2

标准错误输出重定向到/dev/pts/3

如果重定向到相同位置,这样写就比较麻烦,要写两次相同位置,有没有更好的方法呢?,当然是有的!

下面介绍三种把所有输出重定向到文件/设备的方法:

我们可以将错误输出转为正常输出 2>&1或者将正确输出转为错误输出 1>&2,就可以将标准输出和错误输出重定向到同一文件或设备了。

  • 将错误输出转为正确输出

格式:command > file/device 2>&1

执行 ls /test/passwd /etc/passwd >/dev/pts/4 2>&1命令

切换到/dev/pts/4终端,可以看到,标准输出和错误输出结果都显示了

 

  • 正确输出转为错误输出

格式:command 2> file/device 1>&2

执行ls /test/passwd /etc/passwd 2>/dev/pts/5 1>&2

切换到/dev/pts/5终端,可以看到,标准输出和错误输出结果都显示了

            

  • 把所有输出重定向到文件/设备

格式:command &> file/device

执行ls /test/passwd /etc/passwd &>/dev/pts/5

切换到/dev/pts/5终端,可以看到,标准输出和错误输出结果都显示了

 

(4)>和>>的区别

>:当文件不存在时创建,当文件存在时覆盖

>> :当文件不存在时创建,当文件不存在时追加

示例:

执行ls a.out 可以看到test目录下本来不存在a.out文件,通过> filename 可以创建文件。

将"hello"写入a.out文件,查看a.out文件,可以看到"hello"这时再利用>将"world" 写入a.out文件,可以看到a.out文件中"hello"被"world"覆盖掉了,只剩下"world",即文件存在时覆盖原来的内容。

command >> filename 追加内容到文件中。下图中原来a.out文件中只有"world",通过>>追加了"nihao"到a.out文件中

文件被覆盖容易造成数据的丢失,为了安全起见,可以执行set –C 防止覆盖,在防止覆盖模式下,临时想覆盖文件可以执行command >| filename,强制             覆盖文件,执行set +C允许覆盖文件。

(5)合并多个程序的标准输出

利用括号()可以将多个程序的标准输出都输入到一个文件或设备上

示例:(cal 7 2007;cal 1 2017)> all.txt,可以看到2017年7月和1月的日历都写入了all.out文件中。    

输入重定向

输入重定向的符号是 <

默认接受来自键盘的输入,当我们需要从文件中获取数据时,可以利用输入重定向。

很多命令需要标准输入,例如cat,mail等

利用标准输入发邮件。mail.txt文件中的内容是"hello,world",mail命令从mail.txt文件中得到输入发给xiaomi这个用户,主题为"hello"

执行su – xiaomi ,切换到xiaomi用户,查看邮件是否成功,从图中红色框选部分可以看到刚才所写的邮件内容,证明发邮件已经成功。

3 tr命令

接下来我们要学习一个功能特别实用的工具——tr,它既能支持标准输入,也能支持标准输出。哦?那有人该问了,它是做什么用的。答案来了,tr转换、压缩或者删除从标准输入得到的字符,并写入标准输出。

格式:tr [OPTION]... SET1 [SET2]

选项:

-c–C --complement:取字符集的补集

-d--delete:删除所有属于第一字符集的字符

-s--squeeze-repeats:把连续重复的字符以单独一个字符表示

-t--truncate-set1:将第一个字符集对应字符转化为第二字符集对应的字符

对应的字符:

[:digit:]:任意数字,相当于0-9

[:lower:]:任意小写字母

[:upper:]: 任意大写字母

[:alpha:]: 任意大小写字母

[:alnum:]:任意数字或字母

[:blank:]:水平空白字符

[:space:]:水平或垂直空白字符

[:punct:]:标点符号

[:print:]:可打印字符

[:cntrl:]:控制(非打印)字符

[:graph:]:图形字符

[:xdigit:]:十六进制字符

使用:

tr SET1 SET2

tr'a-z''A-Z',将输入的小写字母转化为大写字母。注意:有两个字符集

如果两个字符集长度一样,就对应字符进行转换,字符集之外的字符不转换

两个字符集不一定一样长,这时会发生什么事情呢?

第一个字符集长度比较长的情况下,进行转换时,用第二个字符集的最后一个字符替换第一个字符集比第二个字符集长度超出的部分字符。字符集'abcd'比'xyz'多一个字符,执行tr 'abcd' 'xyz'命令,输入'abcd',可以看到转化的字符为'xyzz',第二个字符集的最后一个字符'z'替换了多出的'd'

如果想只要对应的字符进行转换,可以加上选项-t

tr -t 'abcd' 'xyz'

如果第二个字符集长度比较长,进行转换时,只转化与第一字符集相对应的字符,第二字符集多出的字符并不参与转换。

r

    

上面是从屏幕上获得输入,tr也可以接受从文件中导入的标准输入

tr'a-z''A-Z'</etc/issue,该命令会把/etc/issue中的小写字符都转换    成大写字符

 

tr–d abc< a.out删除a.txt文件中的所有'abc'中任意字符

tr -s 'a' 把得到的输入中多个a用一个表示

tr -c 'a' 'A' < a.txt ,a.txt文件中内容为abcd,转换后,'bcd'还有换行符被转换为'A'

 

4 管道

管道(使用符号"|"表示)用来连接命令

命令1 | 命令2 | 命令3 | …

将命令1的STDOUT发送给命令2的STDIN,命令2的STDOUT发送到命令3的STDIN

STDERR默认不能通过管道转发,可利用2>&1 或|& 实现

最后一个命令会在当前shell进程的子shell进程中执行用来组合多种工具的功能

ls | tr 'a-z' 'A-Z',将ls的结果发送给tr 'a-z' 'A-Z'

5

 

5 tee命令

命令1 | tee[-a ] 文件名| 命令2

把命令1的STDOUT保存在文件中,做为命令2的输入,STDOUT既可以显示在屏幕上,又可以保存至文件中。

tee -a 追加,如果不加-a,当文件已存在时,则覆盖

使用:

保存不同阶段的输出

复杂管道的故障排除

同时查看和记录输出

将当前系统登录用户的信息转换为大写后保存至/tmp/who.out文件中

who | tr 'a-z' 'A-Z' | tee -a /tmp/who.out

 

好了,这篇文章就介绍到这里了。更多内容请查看man文档或者去官方网站查询。

2014-06-15 10:44:38 liaoqianwen123 阅读数 1497

linux重定向简介

1、简介

      在计算领域,重定向是大多数命令行解释器所具有的功能,包括各种可以将标准流重定向用户规定地点的Unix shells。类Unix操作系统的程序可以通过dup2系统调用完成重定向,或者通过缺少一些灵活性但是更高一级层次的freopen(3)和popen来完成。

 

2、重定向标准输入输出

     重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

    command1 > file1

     上面这个命令执行command1然后将输出的内容存入file1.注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。

     command1 < file1

     执行command1,使用file1作为用来替代键盘的输入源。

     command1 < infile > outfile

     同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。

 

3、管道

     多个程序可以一起运行,一个程序可以直接将另外一个程序的输出作为其输入,并且不需要借助中间文件:

     command1 | command2

     执行command1,将其输出作为command2的输入。这种方式被称为管道,因为"|"字符被称为"管道".这种方式和使用2个重定向及一个临时文件的方式向等价:

     command1 > tempfile

     command2 < tempfile

     rm tempfile

     一个使用命令管道的很好例子是使用echo和另外一个命令达到在一个非交互式shell中达到一定的交互效果:

     echo -e "user\npass" | ftp localhost

     这个例子运行ftp客户端,然后输入用户名,回车,然后再输入密码。

 

4、标准文件句柄的重定向

     源自Bourne Shell的许多Unix shells,可以将一个数字(文件描述符)放在重定向符号前,这样可以影响用于重定向的数据流。Unix得标准输入输出流是:  

Handle  Name    Description

0       stdin         标准输入

1       stdout       标准输出

2       stderr       标准错误输入

   例如:

   command1 2> file1

   执行command1,然后将标准错误输出重定向到文件file1

   一些源自csh的shells,将&符号放在重定向符号后,达到同样的效果。

   另外一个很有用的功能是将一个标准文件句柄重定向到另一个。最流行的一种用法是将标准错误输出融合到标准输出中去,这样错误信息可以和其他普通的输出信息一起处理。例如:

   find / -name .profile > results 2>&1

   上面这个命令会找到所有名字为.profile的文件。如果没有重定向,它会输出结果到标准输入,错误(例如在遍历过程中缺少访问某些受保护目录的权限)到标准错误输出。如果标准输出被重定向到一个文件,那么错误信息就会出现在控制台上。通过使用重定向2>&1,输出的结果和错误信息都被写入了文件results.

   可以将2>&1放置在">"前,但是这样并不能达到我们想要的效果。因为当解释器读到2>&1, 它并不知道标准输出已经被重定向到哪里,所以标准错误输出并没有和标准输出融合。

   如果融合过的输出通过管道作为另外一个程序的输入,那么这个融合2>&1必须在管道符号之前

   find / -name .profile 2>&1 | less

   一个命令

      command > file 2>&1

   的简化版本 (不使用于bourne shell)

      command &>file

   或者

      command >&file

 

5、连锁管道

   重定向和管道符可以联合使用,这样可以组合出更加复杂的命令, 例如:

    ls | grep '\.sh' | sort > shlist

    上面这个命令将当前目录的内容列出来,将其作为grep命令的输入内容,grep将以没有以".sh"结尾的内容过滤掉,然后将内容输出给sort命令作为输入参数,sort将输入内容按照字符的顺序排序以后,将最终的输入内容写入shlis文件。在Unix和Linux操作系统中,这个样的组合命令非常的常见。

 

6、重定向到多个输出

    命令tee可以将一个命令的输出重定向到几个目标:

    ls -lrt | tee xyz

   上面这个例子将ls的输出重新定向到标准输出和文件xyz。

2006-11-10 18:17:00 sdnasky 阅读数 3641
    在Unix系统中,任何命令,包括Shell本身,默认情况下总是读取来自终端键盘输入的数据,这个数据输入源通常称作标准输入(stdin),其文件描述符为0.
    默认情况下,命令的处理结果总是输出到用户终端的屏幕上,这个输出目的通常称作标准输出(stdout),其文件描述符为1.
    另外,在命令的执行期间,如果出现问题,相应的错误信息默认情况下也将输出到用户的终端屏幕上,这个输出目的通常称作标准错误输出(stderr),其文件描述符为2.

    有上面的基础知识后,我们现在来讨论Linux/Unix系统命令行的精妙之处:输入输出重定向.
    例如:
       ls -l > fname
    上述命令就是把ls命令的返回结果输出到fname这个文件中去,但是要注意的事情是输出前将清空原文件内容(如果原文件存在).使用ls -l >>fname这个命令是将结果附加到原文件后面.
       grep root < fname
    上述命令是在fname这个文件中寻找带有root这个字段的内容,是把标准输入重定向为文件输入.

上述命令其实是简化后的形式,其原始形式为
    0 < fname   把标准输入重定向到指定文件中
    1 > fname   把标准输出重定向到文件中
    2 > fname   把标准错误输出重定向到文件中

还有一个重要的用法
    i>& j   把文件描述符i表述的输出文件重定向到文件描述符j表示的文件
    例如: 
       command_1 2 >& 1   把这个错误命令的标准错误输出重定向到标准输出

其原理是用指针实现的.
2016-03-16 15:46:34 gz153016 阅读数 812

实验1:用户的登录与退出、IO重定向与简单文件操作

1、实验目的

(1)、掌握系统开启和关闭的方法及正常关闭系统的意义和必要性;

(2)、掌握用户的登录与退出的方法及用户退出系统或注销的意义和必要性;

(3)、了解UNIX/Linux系统的图形界面全貌及使用办法;

(4)、掌握命令操作方法,I/O的重定向和引号机制。

2、实现设备

一台装有Windows操作系统PC机,上装有虚拟机系统VMWare,实验过程通过VMWare系统启Linux系统工作。

3、实验方法与注意事项

实验室内的实验环境与系统是共用设施,请不要在系统内做对系统或对其他用户不安全的事情。

要求每个同学登录后系统后,要在自己的家目录内容以自己(拼音)名字或学号,创建一个子目录。以后所有工作都要在自己的目录内进行。建议以后的实验都在同台计算机上做,这样可以保持连续性。

用户要按通常实验要认真书写实验报告。

4、实验过程

(1)系统的开启

通过Windows系统启动VMware Workstation系统。

在VMware Workstation界面上,单击►启动Red Hat Linux;

注:当鼠标陷入Linux系统中时将无法移出Linux窗口,此时可通过组合键“Ctrl+Alt”来释放鼠标。

(2)系统的注册与登录

当系统启动过程结束后,请分别在图形和字符界面下以超级用户身份登录:

用户名:root

password:123456

注:操作界面的切换。当从图形界面向字符界面切换时使用组合键“Ctrl+Alt+F#”,从字符界面面字符界面或图形界面切换时请使用组合键“Alt+F#”。其中,F7为图形界面,F1~F6为字符界面。请分别在不同的界面上登录。

(3)用户的退出或签退

在字符界面下方法有三:logout         exit         Ctrl_D

[root@localhost root]# logout
bash: logout: not login shell: use `exit'
[root@localhost root]#exit,Ctrl_D

在root用户退出使用exit.


在图形方式下:à注销(logout)à注销(logout)à确定

(4)语言切换(选做)

在图形界面下的登录两面下有“语言(L)”按钮,单击之在选择列表中选择语言后按“确定”,在下次的图形界面登录后将使用新设定的语言。

(5)关闭系统与重启动

①系统关闭

字符方式下方法有:

halt   [-p]

init 0

shutdown –y –g2 ”System will shutdown in 2 minutes,please logout”

shutdown –h +2 –t 20

shutdown –c

shutdown –k now ”Hey! Let’s go”

图形方式:开始à注销(logout)à关机(halt)à确定

②系统重启动

reboot

init 6

shutdown –r –g2 ”System will shutdown in 2 minutes,please logout ”

shutdown –r +2 –t 20

图形方式:开始à注销(logout)à重新启动(Restart the computer)à确定

(6)在图形界面下启动模拟字符终端(Xterm)

1)开始à系统工具à终端;

2)右击桌面在快捷菜单下选择终端。

(7)登录后的操作

1)使用man命令查看命令ls,cal,echo,halt和shutdown的用法。

2)按shell命令的执行方法,执行以下命令,并观察和记录执行的结果:


ls –l /usr    //ls为命令,-l和/usr为参数

1、[root@localhost root]# ls -l


总用量 96
-rwxr-xr-x    1 root     root        13355 2014-04-16  a.out
-rwxr-xr-x    1 root     root        12284 2014-04-16  app
-rw-r--r--    1 root     root          402 2014-04-16  app.c
-rw-r--r--    1 root     root         1003 2014-04-16  copy.c
-rwxr-xr-x    1 root     root        13315 2014-04-16  e
-rw-r--r--    1 root     root           50 2014-04-16  myfile
-rwxr-xr-x    1 root     root        13355 2014-04-16  s
-rw-r--r--    1 root     root           93 2014-04-16  s.cpp
-rw-r--r--    1 root     root         1996 2014-04-16  s.o
-rwxr-xr-x    1 root     root        11826 2014-04-16  t
-rw-r--r--    1 root     root          318 2014-04-16  t.c


2、[root@localhost root]# ls /usr
bin   etc    include   lib      local  share  tmp
dict  games  kerberos  libexec  sbin   src    X11R6



cal 3  2009       // cal为命令,3和2009为参数


3、[root@localhost root]# cal 3 2009
      三月 2009
日 一 二 三 四 五 六
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31


echo "We are studying UNIX OS now!\n"

4、[root@localhost root]# echo "we are !\n"
bash: !\n": event not found

redhat


字符->黑框ctrl+alt+f1

黑框->图形界面  ctrl+alt+f7


echo –e "We are studying UNIX OS now!\n"

5、[root@localhost root]# echo -e "we are"
we are
[root@localhost root]# echo -e "we are!\n"
bash: !\n": event not found


6、[root@localhost root]# echo "we ! we \n we are"
we ! we \n we are
[root@localhost root]# echo "we!"
bash: !": event not found
[root@localhost root]# echo "we\n"
we\n
[root@localhost root]# echo -e "we \n are"
we
 are
[root@localhost root]#


//当!\n与其他的字母之间没用空格的时候,则会报错

7、echo –n "We are studying UNIX OS now!\n"

[root@localhost root]# echo -n "wenow!\n"
bash: !\n": event not found
[root@localhost root]# echo -n "we now !\n"
bash: !\n": event not found
[root@localhost root]# echo -n "we now ! \n gaozhen"
we now ! \n gaozhen[root@localhost root]#



3)做shell命令输入输出的重定向和管道,并观察命令的行为。比如:


8、ls > dir.out       #以覆盖方式,将当前目录信息重定向到文件dir.out


[root@localhost /]# clear
 //创建文件目录
[root@localhost /]# mkdir gaozhen


[root@localhost /]# cd /gaozhen
[root@localhost gaozhen]# ls
[root@localhost gaozhen]# mkdir gao
[root@localhost gaozhen]# mkdir zhen
[root@localhost gaozhen]# ls
gao  zhen
[root@localhost gaozhen]# mkdir dir.out
[root@localhost gaozhen]# ls
dir.out  gao  zhen

//创建文件
[root@localhost gaozhen]# touch file


[root@localhost gaozhen]# ls
dir.out  file  gao  zhen

//查看文件的类型
[root@localhost gaozhen]# file dir.out
dir.out: directory

//可以创建一个文件,他的文件的名称叫做file


[root@localhost gaozhen]# file file
file: empty

[root@localhost gaozhen]# rm file
rm:是否删除一般空文件‘file’?
[root@localhost gaozhen]# ls
dir.out  file  gao  zhen

//删除文件和删除文件目录的命令是相同的
[root@localhost gaozhen]# rm -f file


[root@localhost gaozhen]# ls
dir.out  gao  zhen
[root@localhost gaozhen]# touch file.txt


1、>覆盖


[root@localhost gaozhen]# ls
dir.out  file.txt  gao  zhen

//将当前目录下的文件或者是文件的名称信息放到file.txt文件中

//覆盖


[root@localhost gaozhen]# ls > file.txt
[root@localhost gaozhen]# cat file.txt
dir.out
file.txt
gao
zhen


2、>>追加


//追加

//最好在 /usr下,比较的容易成功

ls /usr>> dir.out     #以追加方式将目录/usr信息重定向到文件dir.out

[root@localhost gaozhen2]# cat /gaozhen/file.txt
dir.out
file.txt
gao
zhen
[root@localhost gaozhen2]# ls >> /gaozhen/file.txt
[root@localhost gaozhen2]# cat /gaozhen/file.txt
dir.out
file.txt
gao
zhen
file.txt
move.file
[root@localhost gaozhen2]# ls > /gaozhen/file.txt
[root@localhost gaozhen2]# cat /gaozhen/file.txt
file.txt
move.file
[root@localhost gaozhen2]# ls
file.txt  move.file




ls –l   /home/www 2> err.out #将标准错误重定向到文件/tmp/err.out



ls –w 2>>/tmp/err.out    #将标准错误追加到文件/tmp/err.out

cat/tmp/err.out              #查看文件/tmp/err.out内容


//参数-n为文件的内容编一个序号


cat –n/tmp/err.out       #查看文件/tmp/err.out内容

[root@localhost root]# cat -n gao.txt
     1
     2  adsfsdafsdafsdfsd
     3
     4  dffsdafds
     5  dfsd
     6  fdfdsfdsf
     7  sdfdsfds
     8  sdvfsdfdsfdsfdsaaaaaaaaaaaaf
     9  fdsfds
    10  fdsf
    11  sd
[root@localhost root]# cat gao.txt
                                                                                
adsfsdafsdafsdfsd
 
dffsdafds
dfsd
fdfdsfdsf
sdfdsfds
sdvfsdfdsfdsfdsaaaaaaaaaaaaf
fdsfds
fdsf
sd
[root@localhost root]#



cat –E/tmp/err.out       #查看文件/tmp/err.out内容


-E, --show-ends
 display $ at end of each line


[root@localhost root]# cat -E gao.txt
$
adsfsdafsdafsdfsd$
$
dffsdafds$
dfsd$
fdfdsfdsf$
sdfdsfds$
sdvfsdfdsfdsfdsaaaaaaaaaaaaf$
fdsfds$
fdsf$
sd$


cat –En/tmp/err.out          #查看文件/tmp/err.out内容

wc dir.out      #统计dir.out的行、单词和字符信息。也可使用以下方式

wc < dir.out

4)变量的定义与使用,shell的引号机制(包括双引号,单引号和反单引号)。

① 变量的使用

my_var="My OS"

my_os="Unix or Linux"

my_os="$my_os is${my_var}"

echo "$my_os"

② 命令替换与参数

echo `whoami`;    echo '\`whoami\`'

today=`date | awk '{ print $1, $2,$3}'`

users=`who | wc -l`

me=`who am I | awk '{ print $1 }'`

echo "Today is $today, there$users users in system"

echo "I am $me, and who areyou?"

③ 变量替换

使用命令:echo 'echo $*' > DispAllVar

产生一个可以显示自己所有参数的shell脚本命令DispAllVar,使用命令cat DispAllVar查看它的内容。在②的基础上,按以下方法执行shell命令DispAllVar:

sh DispAllVar I am `whoami` andtoday is $today

观察并记录显示结果。

(8)系统关闭

实验完毕后,要关闭系统。

5、实验报告的内容与书写

以书面形式记录下你的每一步过程,包括输入、输出信息,遇到的问题和解决的办法,(输出较多者可精简)。

2014-01-10 18:36:07 smstong 阅读数 2515

“程序默认使用标准输入输出”,这是Unix哲学中的其中一条。

1 bash中的重定向模拟

用户登陆系统后,系统已经打开了终端,并在描述符表中使用三个描述符0,1,2来进行索引。由于Unix系统中描述符表是被子进程继承的,所以以后生成的任何进程都自动拥有了这三个描述符。其中的0用于索引标准输入设备,1用于索引标准输出设备,2则用于索引标准错误输出设备。

像C库中的printf()函数就是向描述符1索引的设备进行写入,scanf()则从描述符0指向的设备进行读取。如果新进程没有主动改变这三个描述符的索引内容,那么这些函数就是从终端进行读取并把输出写入终端。也可以这么说,输入输出函数只认识描述符,而不管描述符究竟指向什么设备。这样带来的好处就是,程序随时可以更改0,1,2这三个描述符实际指向的设备,从而动态的改变程序输入的来源,输出的目的地,这就是大名鼎鼎的“IO重定向”。

Unix哲学要求程序的输入、输出和错误输出要使用0,1,2这三个描述符,而不要指定描述符实际指向的设备。至于0,1,2究竟指向什么设备是由程序的调用者来指定的。这个调用者往往就是shell。在shell中可以通过简单的<来对0描述符进行指定设备,通过>对1描述符指定设备。下面是一个bash中简单的输入输出重定向例子。


smstongtekiMac-mini:~ smstong$ ls -l / > 1.txt

smstongtekiMac-mini:~ smstong$ cat 1.txt 

total 16445

drwxrwxr-x+ 78 root  admin     2652 Jan  2 09:32 Applications

drwxr-xr-x+ 63 root  wheel     2142 Nov 15 08:58 Library

drwxr-xr-x@  2 root  wheel       68 Aug 25 08:15 Network

drwxr-xr-x+  4 root  wheel      136 Oct 26 20:55 System

drwxr-xr-x   5 root  admin      170 Oct 26 21:01 Users

drwxrwxrwt@  3 root  admin      102 Jan 10 13:15 Volumes

drwxr-xr-x@ 39 root  wheel     1326 Oct 26 20:57 bin

drwxrwxr-t@  2 root  admin       68 Aug 25 08:15 cores

dr-xr-xr-x   3 root  wheel     4228 Jan  5 08:19 dev

lrwxr-xr-x@  1 root  wheel       11 Oct 26 20:46 etc -> private/etc

dr-xr-xr-x   2 root  wheel        1 Jan  5 08:19 home

-rwxr-xr-x@  1 root  wheel  8393256 Sep 20 13:22 mach_kernel

dr-xr-xr-x   2 root  wheel        1 Jan  5 08:19 net

drwxr-xr-x@  6 root  wheel      204 Oct 26 21:01 private

drwxr-xr-x@ 62 root  wheel     2108 Dec 23 09:02 sbin

lrwxr-xr-x@  1 root  wheel       11 Oct 26 20:47 tmp -> private/tmp

drwxr-xr-x@ 12 root  wheel      408 Nov 20 09:20 usr

lrwxr-xr-x@  1 root  wheel       11 Oct 26 20:48 var -> private/var

lrwxr-xr-x   1 root  wheel       49 Oct 26 17:08 用户信息 -> /Library/Documentation/User Information.localized


由于ls这个工具程序本身使用的是标准输入输出,而标准输入输出默认指向的都是终端。例子中,使用>把cat的输出重定向到了1.txt这个文件。这样1描述符就指向了1.txt这个文件,ls的输出就写入了1.txt文件中了。


cat进程的描述符表
描述符 指向设备
0 终端
1 终端     1.txt
2 终端
   
   

那么bash是如何设置cat进程的描述符表的呢?这就涉及到了bash的工作原理。bash在执行一个程序前,先fork出一个子进程,然后在这个子进程中执行指定的程序。由于子进程会继承父进程的描述符表,所以fork出来的子进程拥有和bash进程完全一样的描述符表,子进程在执行指定程序前,修改自身的描述符表,把1指向的设备修改为1.txt文件。这样随后在执行ls的时候,输出自然就写入1.txt文件了。


下面我们用C语言来实现对这种重定向的模拟。源码如下:

/*
 *  I/O Redirection
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main(int argc, char** argv)
{
    if(strcmp(argv[1], "redirection")==0){
        char* newDev = argv[2];  //重定向的目的文件名
        if(fork()==0){
            // 输出重定向为文件
            int fd = open(newDev, O_WRONLY|O_CREAT);
            dup2(fd,1);
            close(fd);

            // 执行ls程序
            char* av[] = {"ls", "-l", "/",  NULL};
            execv("/bin/ls", av); 
            printf("%s", strerror(errno));
        }else{
            exit(0);
        }   
    }   
    return 0;
}
为了避开和bash元字符的冲突,我们使用redireciton这个单词来代替bash中的>作为重定向关键字。

执行 ./a.out redirection 1.txt 会得到与上面完全一样的效果。

2 bash中管道的模拟

有了重定向,就可以轻易地改变一个程序的输入源,输出目的地。如下所示:

ls > 1.txt; wc < 1.txt

我们首先把ls的输出重定向为1.txt文件,然后又把wc的输入重定向为1.txt文件,这样通过1.txt这个中间媒介把ls的输出作为了wc的输入。这与bash中的管道作用相同。但是,从执行效率角度来看,靠中间文件连接两个程序的IO完全是低下的。为此,Unix在内核中专门为此设计了“管道”这种设备。首先,管道的存取数据完全是在内存中完成的,所以效率要比硬盘上的文件快得多;其次,管道具有很多文件不具备的特性,专门用于两个进程的通信。于是,上面的命令被下面的命令替代。

ls  | wc 

POSIX API中专门提供了pipe()函数用于生成一个管道。下面我们就使用这个函数来模拟bash中的管道操作符。同样为了避免与bash的管道操作符冲突,我们使用pipe关键字表示管道。

#include <stdio.h>
#include <unistd.h>
#include <string.h>

int fd[2]; //管道描述符, fd[0]用于从管道中读取数据, fd[1]用于向管道中写入数据

void sub_ls(char* path)
{
    dup2(fd[1], 1); // change the stdout(1) to reference fd[1]
    close(fd[0]);
    close(fd[1]);
    char* av[] = {"ls","-l", NULL};
    execv("/bin/ls", av);

}
void sub_wc(char* path)
{
    dup2(fd[0], 0); // change the stdin(0) to reference fd[0]
    close(fd[0]);
    close(fd[1]);
    char* av[] = {"wc", NULL};
    execv("/usr/bin/wc", av);
}

int main(int argc, char** argv)
{
    if(argc != 4 || strcmp(argv[2], "pipe")!=0){
        printf("Usage: ./a.out ls pipe wc\n");
        return -1; 
    }   
    pipe(fd);
    if(fork()==0){
        sub_ls(argv[1]);
    }else{
        if(fork()==0){
            sub_wc(argv[3]);
        }   
    }   
    return 0;
}

这样我们执行 ./a.out ls pipe wc就会得到与 ls | wc相同的效果了。

3 重定向带来的启示

(1)对于工具程序开发者,一定要遵守“默认使用标准输入、输出、错误输出”的哲学,不要直接操作文件或其他设备。
下面是一个不符合这条规范的例子:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int fd = open("/dev/tty", O_RDWR);
    dup2(fd,1);
    printf("I'm a bad tool!\n");
    return 0;
}
这个程序把自己的输出强制指定为/dev/tty这个终端设备。对于这样的程序,即使在bash中使用>对其进行了重定向也不起效果,因为程序自身对描述符1的修改在bash对描述符1的修改之后,所以相当于bash的重定向被程序抛弃。


另外,必要的时候要在输出前检查当前1描述符指向的设备类型,针对不同的类型产生不同的输出格式。ls就是一个很好的例子,它在输出前会检查输出设备类型,如果是终端,则会同时输出内容数据和颜色控制数据,便于终端用户查看;如果是管道或者文件,则只输出内容数据,便于后续处理。

(2)对于脚本开发者,熟悉常见工具程序特性,尽量使用管道让不同的工具协同工作。可以说,shell脚本是最重度的重定向和管道用户。

(3)对于一般程序开发者,对于有父子关系的进程们,可以使用管道完成进程间通信,使用前分析各种进程间通信机制(socket,管道,共享内存,消息队列,信号量,FIFO等等)的优缺点,择优选用。


Linux重定向的说明

阅读数 1781

没有更多推荐了,返回首页