shell中参数和输入重定向的区别
输入命令时经常会把参数和输入重定向<这两个给搞混,出于知觉,输入重定向不就是代替键盘输入,把东西传递给命令,那么我把参数写到一个文件里,然后用<将这个文件重定向给命令不就行了?
1
2
3
4
5
6
7
$ cat a.txt
a
$ cat b.txt
b
$ cat list.txt
a.txt
b.txt
这里有三个文件,a.txt中的内容为a,b.txt内容为b,list.txt中内容则为这两个文件名。
按照设想,将list.txt重定向给cat就能起到同时输出a、b两个文件的作用。
1
2
3
4
5
6
$ cat a.txt b.txt
a
b
$ cat <list.txt
a.txt
b.txt
结果并不是这样的,实际的效果和下面预想的结果并不一致。
原因就在于输入(stdin)和参数(arguments)是两个完全不同的东西,最近看The C Programming Language,总算是搞懂了。从Unix开始,操作系统基本都是使用C语言写的,二者相辅相成,很多操作系统的操作都能和C语言中的语法相对应。
命令行参数
C语言中,程序的main函数相当于程序的入口,命令行中运行程序时,参数就会传递给main函数。
int main(int argc, char *argv[])
argc对应参数的个数,argv则对应实际的参数列表。其中,程序名会作为第一个参数传输,即argv[0]。也就是说,传入n个参数,会有n+1个参数值给到main。
参数是和main函数交互的。
输入重定向
输入则要更复杂一点。
在Unix的设计哲学中,一切皆文件。显示器、网卡、键盘、鼠标,这些都是文件。而shell在运行程序时,会为其创建三个特殊的文件:标准输入stdin、标准输出stdout、标准错误stderr。
为什么这三个文件很特殊,且无需程序自己创建?
首先,任何程序都有输入、输出的需要。程序如果不接受输入,那么每次产生的结果都是一样的,也就没有重复执行的必要;如果没有输出,我们就无法知道运行是否正确,甚至程序都没必要执行。
输入、输出相当于程序的命门,程序可以随意地进行数据的计算,这是无害的,但如果能随意地输入输出,也就有了损害其他程序和计算机数据的能力。因此操作系统剥夺了程序这一权力,作为看门人在背后掌控着一切。
程序如果想要其他文件,如硬盘上的一个文件,网络上的一张图片,只能通过操作系统来完成。程序只能通过操作系统事先设定好的接口(系统调用)打开文件,在进行系统调用的过程中,通过一种特殊的trap机制,一下子由操作系统接管了系统,进入了所谓的内核态。操作系统进行一系列校验,如文件是否存在,程序是否有对应的权限,如果检查无误,操作系统就会将文件打开,返回一个文件描述符(file descriptor),再次经过trap回到进入前的用户态,让程序接着执行。
程序打开的文件都被记录在一个表里,初始时stdin,stdout,stderr对应表中0、1、2三项。系统调用返回的文件描述符就是在这张表里的位置。
有了stdin,stdout,程序就有了和当前运行程序终的端交互的能力。一方面,简化了程序的工作,否则像scanf、printf这样基本的操作还要向操作系统系统申请打开一个输入文件、一个输出文件。hello world也就不是几行能搞定了。另一方面,提供了终端和进程交互的“界面”,因为stdin、stdout既是给程序使用的,同时又是由终端创建的,双方可以通过这两个文件彼此沟通。第三个文件stderr则是出于便于调试程序的需要。
输入重定向是和程序的stdin这个文件相关的。
输入重定向将某个文件(如filelist.txt)打开,作为程序的stdin这个特殊 文件,代替终端和程序进行交互。