除了管道和重定向,还有命令行参数

除了管道和重定向,还有命令行参数

管道和重定向,在Unix Shell

Script入门教程里都要提到。在稍微深入一点的dos/windows批处理教程里也要提到。管道,一般在用前一个进程产生的输出,作为后一个进程的输入;重定向,一般用在把输出由控制台转到文件,或者用文件替代控制台输入。管道和重定向满足了进程的输入和输出的转向。

输入和输出,似乎本来就是进程跑起来以后唯一与外界的联系。根据与外界的联系判定那是个什么东西,而不是根据它的内心,这据说是萨特的观点,小资们不可不知。

不过,输入和输出,并非进程与外界唯一的联系。除了输入以外,命令行参数,也是设定进程行为的重要依据。在这一点上,如果把进程当成一个对象,命令行参数有点像构造函数的参数,虽然跑起来以后就跟它无关了。而且,unix程序一般地,只有命令行参数才是命令,影响进程的行为,而以后从标准输入读进来的所有东西,都只是数据而已,是被进程处理的东西,对进程本身没有影响。

shell (像python一样),是一种不错的粘合剂,它能组装一系的进程,让它们协同工作。管道和重定向,能让它们完成生产者-消费者这样的组合。

不过,如果我们需要一个进程的输出,不是作为另一个进程的输入,而是作为它的命令行参数呢?

下面的写法,

a | b

完成的,是把a的输出作为b的输入。

而如果我们希望下面这样,如何表达?

b -para <此处是a的输出>

有不止一种方法。

1. backtick

backtick,就是"`"。这不是单引号,而是键盘左上角,波浪线 (~)下面的那个小点,像个反向的单引号。

它的作用是,把括起来的命令执行了,并用执行结果中的每一行去替换命令所在的位置。

ls `echo -l`

相当于

ls -l

我们把其中的一段拿出来单独执行一下。

1 $ echo -l

2 -l

可见,"echo -l"的结果,就是"-l"。其中的""中为了避免echo把"-l"当成自己

的参数。

而"-l"替换了"ls `echo -l`"中反引号括起来的部分,所以"ls `echo -l`"就

变成了"ls -l"。

再举个例子。

1 $ grep -w main `find . *c`

2 ./samples/hidraw/hid-example.c:int main(int argc, char **argv)

3 ./samples/trace_events/Makefile:# have that tracer file in its main

search path. This is because

4 ./Makefile:# $(vmlinux-main). Most are built-in.o files from

top-level directories

5 ./Makefile:# +–< $(vmlinux-main)

6 ./Makefile:vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

7 ./Makefile:vmlinux-all := $(vmlinux-init) $(vmlinux-main)

8 …

这条命令的作用是,先执行 "find . *c",找到当前目录下所有子目录中的 c文件,然后对每个符合条件的文件执行 "grep -w main"。

命令"grep -w main `find . *c`"中,`find . *c`部分将被替换为很多个匹配的文件名,然后每个文件名都作为

"grep -w main"的命令行参数,类似于:

grep -w main a.c

grep -w main b.c

grep -w main c.c

这样。

2. $(cmd)

backtick倒是挺方便,不过很多同学反映,"`"这个符号太丑陋了。最丑陋的特性之一就是,它跟单引号"'"长得太过地相似了。像我这样认人脸有困难的,本来就希望大家服饰发型更多样化一些,所以你能够理解我现在看到女演员们长得都越来越像,该是多么大的困惑。我指的不光是韩国的,小锥子脸大眼睛面孔白而无表情。backtick也存在这样的问题,跟别人像,本来就是毛病。

所以有别的写法,比如这样:

grep -w main $(find . *c)

这样的效果跟backtick是一样的。一样一样的。区别呢?多少也有一些。比如 $(cmd) 这样写法是可以嵌套的。

3. xargs

不过上述这些需要"代换"的,似乎有些程序员理解起来有困难?后来出现了一个命令,xargs,专门用来做这件事。

find . *c | xargs grep -w main {}

请注意到find和grep的顺序换了。find的输出结果,由xargs转给grep作为命令行参数。事实上,是xargs调用了grep,管道的末端不是grep,而是xargs。这不同于管道的末端是grep,如果是这样,就成了find的输出作为grep的输入,是管道应用的更一般的情况。args把管道的输出转向了grep的命令行参数,而不是作为grep的输入。

"{}"在这里,将被find的输出替代。如果命令行参数的位置不重要,或者在最末端,那么还可以把"{}"省略掉,效果是一样的。像这样:

find . *c | xargs grep -w main

4. -exec

似乎有了xargs以后,还是有些程序员觉得不够简单?又有些命令自带了"-exec"参数,意谓:如果匹配了,就把那些输出作为"-exec后面的进程的命令行参数"。话说,有些事情由于它的领域模型就那么复杂,是不太可能因为表达方式而变得简单的。

以下命令,效果是一样的:

find . *c -exec grep -w main {} -nH ;

行尾的";",是为了标识"-exec"部分结束,其中的""是因为";"是个特殊字符。grep需要"-nH"参数,是因为不然输出的匹配行里没有文件名和行号。grep在此前的几个命令里用的是别名,这里是真正的grep本身。

5. 总结

以下几个命令等价:

$ cat `which 20.sh`

$ cat ${which 20.sh}

$ which 20.sh | xargs cat

cat不支持"-exec"参数,那是find这样的复杂命令的特色,因此以上总结中未包括。

———-

本文的命令在 emacs eshell 不好使,因为 eshell 对管道支持不充分,shell-mode则没有问题。

——————–

博客会手工同步到以下地址:

—-

杨贵福

东北师范大学 计算机科学与信息技术学院



Sincerely,

YANG Guifu

School of Computer Science and Information Technology

Northeast Normal University

Changchun, P.R.China

重剑无锋,大巧不工。

无不大工。

Leave a Reply