5.7 pick命令:空格和参数
我们已经接触了书写shell的pick命令需要的多数命令。我们只需要一种新的机制来接收用户输入。shell内部命令read提供了这一功能,即从标准输入读一行正文,并把读到的文本(不含换行)赋给命名变量:
read最常用于注册时在.profile文件里设置环境,主要是建立shell环境变量,如TERM。
read只能读取标准输入,而且不能被重定向。shell内部命令(与控制流原语不同,如for)都不能使用>或<重定向:
这也许可以说是shell的一个缺陷,但这就是现实。幸运的是,重定向read外围的循环可以达到同样的目的。这是pick命令实现的关键:
echo-n抑制最后一个换行符,因此用户的响应可以打印在提示符的同一行。当然,提示符打印在/dev/tty上,因为这里的标准输出绝大多数情形下并不是终端。
break语句取自C语言,表示终止内部循环。上例中,输入q键时终止for循环。q为quit的缩写,易用,这一退出响应几乎已成为惯例,其他很多程序也采用q。
pick命令行参数里的空格是很有趣的:
要观察pick程序如何读取参数,运行pick并在每次提示符出现后按回车键。程序工作情况很好:for i适当地处理参数。我们还可以用其他方法写循环:
这种方式不能成功,因为循环中的操作数被重复扫描,这样第一个参数中的空格符将它变成两个参数。尝试使用引号括住参数$*:
还是不能正常工作,因为“$*”是由空格符分隔的所有参数连在一起构成的单个词。
可能的解决办法是,shell对字符串“$@”作特殊处理,并将它转换为严格的shell文件参数:
如果不用引号括住$@,它与$*相同;$@只有用双引号括住时才具有特殊性质。我们在overwrite程序中用它来保护用户的命令参数。
上述内容可以归纳为如下规则。
$*和$@扩展为参数,并被重复扫描;参数的空格符将字符串分成多个参数。
“$*”表示shell文件的所有参数及其空格符连在一起作为单个词处理。
“$@”与shell文件接收的参数等价,参数中的空格被忽略,其结果是等同于原来参数的一个单词列表。
如果pick程序没有参数,可以读取标准输入,所以可以使用命令
这里我们不研究这个版本的pick:它的复杂度和难以理解的程度远远超过下一章将要介绍的用C语言编写的程序。
下面练习中的前两个题目比较难,属于高级shell程序员教程的内容。
练习5-24 写一个pick程序,当命令行中没有参数支持时它从标准输入读取参数。程序应能正确处理空格符。能否处理q响应?如果不能,试做下一个练习。
练习5-25 虽然shell的内部命令如read和sed不能重定向,但shell本身可以暂时重定向。阅读sh(1)节中对exec命令的描述,考虑如果不调用子shell如何将read定向到/dev/tty。(可以先参阅第7章。)
练习5-26(比较容易) 在.profile文件里使用read初始化TERM和其他相关参数,如Tab停止位。