错误这么多,这么不严谨,怎么拿的这么高分的

可能因为涉及内容比较广,讲得比较浅,所以很多地方没讲清楚。其实不太适合入门吧,至少不太适合强迫症患者入门。
有些地方可以简略,但是为了保持严谨应该加一些"其实还有这些...情况,限于篇幅不在这里不讨论"之类的说明的。再不济用"可以如此", "一种做法是"之类的非绝对性描述也行啊。不然用了绝对性描述又没有补充说明,就只能算为错误了。
一时找不到更好的入门书,先继续看吧。然后边看边在这里补充勘误表(邮电社第二版中文版):
【231页】if [ -n "$val1" ] ...
前面字符串比较的时候没有加引号,这里介绍-n参数时忽然就加引号了,也没有说明为什么要加引号——分词时(word splitting),显式的空串(''和"")会被保留,而参数展开后生成的非显式的空串则会被丢弃;因此倘若val1为空串的话,`[ -n $val1 ]`实际执行的是`[ -n ]`,即`test -n`,该命令始终返回0。
【240页】 除了test命令使用的标准数学运算符,表11-4列出了双圆括号中会用到的其他运算符
前面介绍的test命令只支持-eq, -ne等运算符,不支持四则运算,而表里也没有包括双圆括号支持的四则运算符。
【241页】在模式匹配中,你可以定义一个正则表达式来匹配字符串值:
下面的例子写的是"[[ $User == r* ]]",这不是正则表达式匹配,*是glob通配符,正则表达式用的是" =~"运算符。
【250页左右】IFS=$'\n' ... IFS=:
用到了$'...'这种字符串,却全篇没有说明为什么要加美元符号,而后面的不用。
【267页】继续外部for循环的一个例子:... continue 2 ...
这里偏偏要选一个continue 1和continue 2输出结果没有任何区别的例子来讲解continue n的使用。
【276页】代码:
params=$#
echo The Last parameter is $params
echo The Last parameter is ${!#}
这里没有介绍${!#}的意思(我也没找到),不过这就算了,关键是echo The Last parameter is $params 这行,实际上得到的是参数数量!!!只不过调用的时候`./test 1 2 3 4 5`,刚好参数数量和最后一个参数都是5而已。。。这个算得上是目前看到的最脑残的一个错误了。
【277页】代码:
for param in "$*"
...
for param in "$@"
这个例子里只有给$*和$@加上双引号才能体现出差别。加双引号时,"$*"展开为"$1c$2c...",其中c为IFS的第一个字符(默认是空格),而"$@"展开为"$1" "$2" ...;不加双引号的话,因为有分词(word splitting),$*和$@都会被拆分成单独的字段,且单个参数本身还可能进一步拆分成多个字段。举个例子,如果原脚本调用时用的参数是rich "foo bar",则"$*"遍历输出的是"rich foo bar",而"$@"是"rich"和"foo bar";如果去掉双引号,依然用参数rich "foo bar"调用,$*和$@遍历输出的都是"rich","foo"和"bar"。
【284页-286页】...getopt命令并不擅长处理带空格的参数值...
...每次调用getopts时,它只处理一个命令行上检测到的参数。处理完所有的参数后,它会退出并返回一个大于零的退出状态码...
...在getopts处理每个选项时,它会将OPTIND环境变量值增一...
关于getopt和getopts命令的描述有好些不准确的地方。一、首先getopts命令每次处理一个选项以及可能存在的选项的参数值,然后返回0;如果遇到非选项参数或没剩余参数了,返回1。比如循环调用`getopts :ab:c opt`,而参数列表是"-b test1 test2 -a -c",则处理到test2时循环就退出了,-a和-c都不会被处理。二、getopts每次调用的时候是根据OPTIND(其初始值为1)来选择要处理的参数值的,退出前会把OPTIND设为下一个要处理的参数位置(加0,加1或加2)。所以如果一开始设OPTIND为2,getopts命令会忽略第一个参数。三、getopt命令可以处理选项在非选项参数后面的情况,而且支持长选项,选项支持可选参数,另外现在有实现(不知道是不是主流实现都支持了,可以man getopt看说明)可以处理带空格的参数值了——如果getopt命令本身带了选项,则它输出的参数是带引号的,比如我环境里运行命令 getopt -q ab:c -a -b test1 -c "test2 test3" test4,输出的内容为 -a -b 'test1' -c -- 'test2 test3' 'test4'。在脚本里可以用 eval set--`getopt-qab:c"$@"` 这样来处理。
【297页】在重定向到文件描述符时,你必须在文件描述符数字前加一个&符号
正确说法应该是"在>符号后面加&",因为此时">&"是作为一个整体,中间不能有空格,而&符号和数字之间可以有空格。Shell需要&符号来确认">&2"是重定向到描述符为2的文件,而不是文件名为2的文件。另外,如果后面跟的不是数字(或符号-),则">&"和"&>"的意思是一样的,都表示">file 2>&1"。
【293页&304页】每个进程一次最多可以有9个文件描述符...对你来说,只有9个文件描述符可以使用...
使用"{var}>file",shell会分配一个大于等于10的文件描述符,并把该值赋给var,之后可以用"{var}>&-"来删除该描述符。如`echo "xxx" {myfd}>logx 1>&${myfd}`,该命令把内容"xxx"输出到logx文件。
还有一点要补充的是,重定向符号前面的文件描述符可以省略,">file"等于"1>file","<file"等于"0<file",而"<>file"等于"0<>file"。
另外【209页】的时候介绍了内联输入重定向(inline input redirection),man page里叫"Here Documents",这里再说明一下"Here Documents"和"Here Strings"的用法。
"Here Documents"的形式为(方括号表示可以省略): [n]<<[-]delim_string lines delim_string 重定向符号"<<"或"<<-"后面的第一个字段将作为开始和结束的标记,在两个标记之间的行将送到stdin(可用文件描述符n指向其他文件);开始标记后面还可以有其他命令行参数字段(不过建议把其他命令行参数都放在重定向符号之前);如果重定向符号为"<<-"的话,最后一行和中间lines开头的tab会被忽略(方便缩进,不过注意只有tab会被忽略,空格不会),除此之外最后一行应仅包含结束标记(连尾随的空格或tab都不能有)。另外delim_string最好完全为字面串(文档里只说不会对这个标记串进行变量展开、算术展开、命令置换和路径展开,不确定用其他两种展开是什么效果);不过delim_string可以用引号括起来,此时结束标记为引号移除(quote removal)后的结果,且中间的lines不会被展开,否则的话默认lines会经历变量展开、算术展开和命令置换,而且空行会被忽略。
"Here Strings"的形式为: [n]<<<expression 这种重定向也可以把字符串送到stdin(或n指向的其他文件),其中expression会应用波浪号展开、变量展开、算术展开和命令置换和引号移除,不过没有分词和路径展开。(感觉用命令置换的话跟管道有点像,比如 ls | wc -w和wc -w <<<`ls`,输出的结果是一样的。)