find+xargs+grep+sed+awk系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
0.学习资料推荐
1.awk入门:看视频、找博客或者看《AWK程序设计语言》的第1-3章。
2.awk进阶:《awk程序设计语言》剩余内容(剩余的我也没看过,哈哈),man awk
3.awk编程语言:用于掌控awk的语法和方方面面,推荐书籍《gnu awk: Effective AWK Programming》
1. awk简介和基本语法格式
Awk自动地搜索输入文件,并把每一个输入行切分成字段。许多工作都是自动完成的,例如读取每个输入行、字段分割、存储管理、初始化等。在AWK中不需声明变量数据类型,它内置字符串类型和数值类型。
一般来说,在CentOS上安装的awk默认是gawk。它的调用格式为:
awk [OPTIONS] -f program_file [--] filename_list
awk [OPTIONS] [--] program filename_list
program是awk程序的重中之重,称为awk的程序,它的格式为PATTERN{ACTIONS}
。awk每读入一行,都会先与PATTERN做匹配比较,当找到符合条件的数据就执行对应的ACTION。
其中PATTERN或ACTIONS二者可省一。省略PATTERN时表示对所有行都执行ACTIONS,省略ACTIONS表示对符合条件的行执行默认的print动作。因为二者可省一,所以用大括号{}将ACTIONS部分包围起来,以区分PATTERN和ACTIONS。
一个简单的例子,输出/etc/passwd中用户shell为/bin/bash的用户名,其中使用"-F"选项指定冒号作为分隔符。
awk -F':' '$7 == "/bin/bash"{print "who use bash shell: ",$1}' /etc/passwd
其中位置变量$1,$2...为该行的第几个字段,"$0"表示整行。
如果要输出多个字段,则字段之间使用逗号","分隔,例如{print $1,$5}
。但输出时,仍默认以空格分隔各输出字段。
如果action为print $1 $5
,则结果会将"$1"和"$5"拼接在一起,因为空格是awk中的拼接字符。例如变量赋值name = "abc" "bcd"
等价于name="abcbcd"
。其实不算是拼接符,而是因为awk会忽略任何不被引号包围的空白。
2.print和printf格式化输出
awk使用print和printf输出数据,不仅可以输出到标准输出中,还可以重定向到文件中,使用管道传递给另一个命令。
- print
将 $0 打印到标准输出。等价于print $0
。 - print expression, expression, …
打印各个 expression, expression 之间由 OFS 分开, 由 ORS 终止 - print expression,expression,… > filename
文件名filename必须使用双引号包围,否则被当作变量。且文件只会被打开一次。 - print expression,expression,… >> filename
- print expression,expression,… | command
将数据传递给系统命令。命令需要使用双引号包围。 - printf(format,expression,expression,…)
- printf(format,expression,expression,…) > filename
- printf(format,expression,expression,…) >> filename
- printf(format,expression,expression,…) | command
- close(filename), close( command)
断开 print 与 filename (或 command) 之间的连接 - system(command)
执行 command; 函数的返回值是 command 的退出状态
如果print或printf的参数列表中含有操作符,则需要使用括号包围,否则容易产生歧义。如:
print($1, $3) > ($3 > 100 ? "bigpop" : "smallpop")
print $1, ($2 > $3)
执行系统命令的方式,可以通过管道的方式,也可以通过system()函数。注意包围命令的引号加的位置。
awk 'BEGIN{name="ma long shuai";print (1,2,3,4) | "echo " name}'
awk 'BEGIN{while (("fdisk -l" | getline) >0){print $0}}'
awk 'BEGIN{system("fdisk -l")}'
awk 'BEGIN{name="ma long shuai";system("echo " name)}'
printf命令可以输出更格式化的数据。
printf(format, value1, value2, ... , valueN)
format是一个字符串,包含按字面输出的纯文本,还包含输出格式,格式使用格式说明符"%"描述,后面跟着几个字符,这些字符控制一个value的输出格式。第一个"%"描述value1的输出格式,第二个"%"描述value2的输出格式,依次类推。因此,"%"的数量应该和被输出的value数量一样多。
例如:
{ printf("total pay for %s is $%.2f\n", $1, $2 * $3) }
{ printf("%-8s $%6.2f\n", $1, $2 * $3) }
第一个程序包含了两个要格式化的value,分别是"$1"和"$2 * $3"。这两个value的输出格式分别被"%s"和"%.2f"描述,前者表示按字符串格式输出"$1",后者表示按小数值格式输出"$2 * $3",且小数位占2位。由于printf不自带尾随换行符,因此手动加一个换行符"\n"。
第二个程序,"%-8s"表示"$1"按字符串格式输出,但短横线"-"表示要左对齐输出,"8"表示占用8个字符宽度,不足之数在右边空格补齐。"%6.2f"表示按小数格式输出"$2 * $3",且小数位占用2位,总字符数占用6位。注意,小数点也占用一个字符宽度。因此,一个可能的输出值为"123.20"。
格式说明符"%"后可跟以下几个常见字符:
- 格式符:
- %d,%i:十进制整数;
- %f:显示浮点数;
- %s:显示字符串;
- %u:无符号整数;
- %%:显示%自身。
- 修饰符:
- N:显示宽度;N为数值,宽度不足时若为左对齐则右边空格补足,若右对齐则左边空格补足。
- -:左对齐;
- +:显示数值正负号。
- 0:表示以0填充。
3.输入行的字段分隔符和行分隔符
使用"-F"选项或设置内置变量"FS"可以控制输入行的字段分隔符,默认分隔符为" "。可通过正则表达式指定分隔符,其实可以认为总是以正则方式指定分隔符。
以下是几个示例和需要注意的空格分隔符:
-
-F " "
:默认会压缩所有前导空白,包括制表符和空格。 -
-F " :"
:当空格后跟一个冒号时作为分隔符。会压缩前导空格,但不会匹配制表符,更不会压缩制表符。 -
-F "[ ]'
:只表示一个空格,不压缩任何空白。 -
-F "|"
:指定竖线作为分隔符。 -
-F ",[ \t]*|[ \t]+"
:逗号后跟0或多个空白,或者只有1或多个空白时作为分隔符。
也就是说,当空格写在最前面且不被中括号包围限制的时候,总会忽略前导空格,但不一定能匹配制表符。
使用内置变量"RS"可以控制输入行的行分隔符,默认为"\n",只有遇到行分隔符时才作为"一行"记录被读取。
将其读作行分隔符不标准,应该读为"记录分隔符"。例如设置以制表符作为记录分隔符。
RS="\t"
记录分隔符变量"RS"只识别第一个字符,若设置为"\t\t",则第二个"\t"被忽略。但是控制输出记录分隔符的内置变量OFS则可识别多字符。
可通过设置FS="\n";RS=""
使得awk能处理多行记录。但此时,原本的每行数据整体变成一个字段。
4.BGEIN和END
BEGIN和END是一个特殊的PATTERN,BEGIN引导的程序是在awk读取第一个文件第一行前要执行的awk程序,END引导的程序是在awk处理完最后一个文件最后一行后要执行的awk程序。通常BEGIN用于输出一个标题,或者初始化一些格式、变量等,END则用于最后的总结性输出。
所以awk稍微完整一点的格式为:
BGEIN{ACTIONS}PATTERN{ACTIONS}END{ACTIONS}
刨去BEGIN和END引导的两个程序,中间处理输入文件的程序PATTER{ACTIONS}称为"主输入循环(main input loop)"。在进入主输入循环之前,可以不用提供输入流,但进入主输入循环后,必须提供输入流。
例如,在开始处理文件前,设置输出报表的头部,在最后输出总共输出了多少行。其中print ""
表示输出一个空行。
BEGIN{print "ID NAME GENDER GENDER";print ""}{print $0}END{print "total num: " NR}
5.数组
awk的数组和shell的数组类似,都支持数值index的普通数组和字符串index的关联数组,其实数值index仍然会转换成字符串格式的index,所以awk的数组类型都是关联数组。
数组格式:array_name[index]
数值赋值:array_name[1]=value1
引用数组:array_name[1]
需要注意的是,关联数组的index必须使用双引号包围,例如array_name["ma"]
,如果写成array_name[ma]
,则表示使用变量"ma"的值作为index。若"ma"变量未定义,则这会定义一个新的数组array_name[""]
。
使用index in array_name
的方式可以判断数组array_name中是否有index下标对应的数组元素。如果有,它会返回1,否则返回0。所以判断语句可以如下:
if ( "ma" in array_name )
其实,判断某个数组变量的值是否为空也可判断该数组元素是否存在,如下。但这有副作用,当该元素不存在时,会创建它。
if ( array_name["ma"] != "" )
for循环的一种变体:
for (i in array_name){
do something about array_name[i]
}
可以用于遍历数组,其中变量"i"是遍历数组时的index,array_name是数组名。这是以遍历index的方式遍历数组。由于index的顺序随机,所以遍历时顺序也是随机的。当然,遍历数组的方式有多种,以上只是for循环遍历的一种方式。
使用delete语句可以删除数组中的元素或者删除整个数组。如下:
delete array_name["ma"] # 删除array_name中下标为ma的元素
delete array_name # 删除数组array_name
6.流程控制语句
在ACTION中,可以使用流程控制语句。包括但不限于:
if (expression) statements
if (expression) statements else statements
while (expression) statements
for (expression; expression; expression) statements
for (expression in array) statements
do statements while (expression)
还有几个能影响循环的动作:
break:退出循环。
continue:退出当前循环,进入下一个循环
next:读入下一行,并awk程序的顶端从头开始。这个awk程序是PATTERN{action}这部分,不包括BEGIN{action}。
exit code:直接进入END,若本就在END中,则直接退出awk。如果END中的exit没有定义code,则采用前一个exit的code。
6.1 条件判断语句
if格式:
PATTERN {
if (test_cmd){
cmd1
cmd2
...
}
}
if-else格式为:
PATTERN {
if (test_cmd){
cmd1
cmd2
......
}
else
cmd3
}
当if或else结构中的命令只有一个,则其内可省大括号,如果超过一个,则需要使用大括号。
若采用一行书写格式,则如下:
PATTERN {if (test_cmd){cmd1;cmd2;...}else {cmd3;cmd4...}}
还有if-else if-else格式。
PATTERN {
if (test_cmd){cmd_list1}
else if {cmd_list2}
else if {cmd_list3}
else {cmd_list}
}
还支持多目操作符。
expression ? action1 : action2
其中"?"和":"还可以继续嵌套。
6.2 while循环
结构:
PATTERN {
cmd1
while (test_cmd)
cmd
}
当cmd有多个时,使用大括号包围。
PATTERN {
cmd1
while (test_cmd){
cmd2
cmd3
....
}
}
一行书写格式:
PATTERN{cmd1;while (test_cmd){cmd1;cmd2}}
6.3 do循环
和while循环类似,地位和shell中的until循环一样。都是至少执行一次命令列表。
结构:
PATTERN {
do{
cmd1
cmd2
} while (test_cmd)
}
6.4 for循环
结构大致如下:
PATTERN {
for (i=1;i<=10;++i){
cmd1
cmd2
}
}
for后括号中包括:变量初始值,条件判断和计数器增长表达式。
7.更完整的awk程序格式和表达式
更完整的awk程序的语法格式有以下几种:
BEGIN{actions}
END{actions}
expr{actions}
/regexp/{actions}:可被regexp匹配的行才执行actions
expr1,expr2{actions}:表示范围,从满足expr1的行开始,到满足expr2的行结束
其中:
-
expr
是表达式。- 比较操作符有:
< <= == != >= > ~ !~
。 - 算术操作符有:
+ - * / % ^(取幂) **(取幂)
。其中**
非POSIX标准,不可移植。 - 赋值操作符有:
++ -- += -= *= /= %= ^= **=
。awk支持复合赋值,例如FS = OFS = "\t"
表示字段分隔符和输出字段分隔符都被赋值为制表符。
- 比较操作符有:
-
/regexp/
为正则匹配模式,表示该行能被regexp匹配则为真。还有以下两种匹配表达式,分别表示给定的字符串能(不能)匹配就为真。- string ~ /regexp/
- string !~ /regexp/
- 还有复合模式的表达式。使用逻辑操作符"&&"、"||"和"!"连接。如
$4 == "Asia" && $3 > 500
,! (NR > 1 && NF > 3)
。
awk中字符串和数值数据类型是自动转换的。如果想要得到一个字符串值,可以value ""
进行转换,同理,如果想要得到一个数值,可以value + 0
。
另外,正则表达式可以不用包围在两个斜杠中。可以将正则表达式赋值给一个变量,然后使用该变量取匹配数据。例如:
reg="^[0-9]+$"
$2 ~ reg
甚至直接使用双引号替换斜杠也允许。但不建议使用,因为一个元字符可能需要多个反斜杠来保护,使得看上去极其晦涩。
8.awk中的变量
普通变量:给变量赋值时,如果要赋值字符串,则该字符串应该使用双引号包围,特别是包含特殊字符时。赋值数值时无所谓。 字段变量:$1,$2,$3,...,$NF,还有"$0"表示整行内容。另外,可以直接赋值一个新字段或修改字段值。但这都会影响"$0"。同理,修改"$0"也会影响各字段值。
内置变量:其实可以分为两类,一类是awk内部自动修改的变量,如行数变量NR,一类是内部不会改动的系统变量,如输入字段分隔符变量FS,完全需要手动修改,这类一般都有默认值。
- ARGV:命令行参数数组。从0开始计数直到ARGC-1。
- ARGC:ARGV数组元素的个数。
- FILENAME:当前处理的文件名。
- FNR:当前处理文件的记录号。(file record num)
- NR:已处理的总记录数。多个文件时不重置。(record num)
- NF:当前记录的字段总数。(field num)
- FS:输入的字段分隔符。默认为空白。(file separate)
- OFS:输出的字段分隔符。默认为空白。(output record separate)
- RS:输入流的记录分隔符。默认为"\n"。(record separate)
- ORS:输出流的记录分隔符。默认为"\n"。(output record separate)
- OFMT:printf输出数值转换成字符串输出时的格式。默认为"%.6g"。
- CONVFMT:printf输出数值转换成字符串输出时的格式。会被OFMT覆盖。默认为"%.6g"。
- RLENGTH:被match函数匹配的字符串的长度。
- RSTART:被match函数匹配的字符串的开始位置。
- SUBSEP:下标分隔符。默认为"\034",ASCII中034代表的是双引号'"'。
注意,像NR、FNR、RS等的对象是记录(record),而非行。只有当RS="\n"时,读取了一行才表示读取了一条记录。
9.awk中的内置函数
awk有两类内置函数:算术函数和字符串函数。还支持自定义函数。
算术函数:
- cos(x):取x的余弦。
- sin(x):取x的正弦。
- sqrt(x):取x的平方根。
- rand():返回一个随机数r,其中
0<=r<1
。 - srand(x):设置rand()的种子值为x。种子值相同时,rand()的结果相同。可
print srand()
输出当前种子值。 - int(x):取x的整数部分。
因此,要生成一个范围[1,n]的随机数,使用int(n*rand() + 1)
,要四舍五入一个数值,使用int(x + 0.5)
。
随机数的种子值相同时,rand的结果总是相同。如下两次运行结果,两次结果中,前两个rand()值相同,后两个rand()值不同,因为中间使用了srand()重设种子值。
awk 'BEGIN{print rand();print rand();srand();print rand();print rand();print srand()}'
0.237788
0.291066
0.109925
0.983692
1504560578
awk 'BEGIN{print rand();print rand();srand();print rand();print rand();print srand()}'
0.237788
0.291066
0.96322
0.670495
1504560604
字符串函数:建议下面的所有regexp都使用"//"包围。
-
index(str1,str2)
:返回子串str2在字符串str1中第一次出现的位置。如果没有指定str1,则返回0。 -
length(str1)
:返回字符串str1的长度。如果未给定str1,则表示计算"$0"的长度。 -
substr(str1,p)
:返回str1中从p位置开始的后缀字符串。 -
substr(str1,p,n)
:返回str1中从p位置开始,长度为n的子串。 -
match(str1,regexp)
:如果regexp能匹配str1,则返回匹配起始位置。否则返回0。它会设置内置变量RSTART和RLENGTH的值。 -
split(str1,array,sep)
:使用字段分隔符sep将str1分割到数组array中,并返回数组的元素个数。如果未指定sep则采用FS的值。因此该函数用于切分字段到数组中,下标从1开始。 -
sprintf(fmt,expr)
:根据printf的格式fmt,返回格式化后的expr。 -
sub(regexp,rep,str2)
:将str2中第一个被regexp匹配的字符串替换成rep,替换成功则返回1(表示替换了1次),否则返回0。注意是贪婪匹配。 -
sub(regexp,rep)
:将"$0"中第一个被regexp匹配的字符串替换成rep,替换成功则返回1,否则返回0。注意是贪婪匹配。 -
gsub(regexp,rep,str2)
:将str2中所有被regexp匹配的内容替换成rep,并返回替换的次数。 -
gsub(regexp,rep)
:将"$0"中所有被regexp匹配的内容替换成rep,并返回替换的次数。 -
toupper(str)
:将str转换成大写字母,并返回新串。 -
tolower(str)
:将str转换成小写字母,并返回新串。
关于替换函数sub和gsub,可以在替换字符串rep中使用"&"符号表示反向引用,引用的是整个被匹配的部分。
awk 'BEGIN{
print index("banana","na")
print length("banana")
print match("banana","na.*")
print toupper("banana")
print substr("banana",3)}'
3
6
3
BANANA
nana
awk 'BEGIN{str1="x&x";str2="banana"
print sub(/a.*n/,str1,str2)
print str2}'
1
bxananxa
awk 'BEGIN{
print match("banana",/a.*n/)
print RSTART,RLENGTH}'
2
2 4
awk 'BEGIN{print sprintf("hello %i world %5s","123","abc")}'
hello 123 world abc
awk 'BEGIN{
name="Ma long shuai"
split(name,myname)
for (i in myname){
print myname[i]}
}'
Ma
long
shuai
纵观上述字符串函数,没有一个函数可以将匹配成功的字符串输出出来。但借助match()和RSTART、RLENGTH可以实现。
例如,取出"Ma:long:shuai"中的"long"并输出。
awk 'BEGIN{
name="Ma:long:shuai"
if (match(name,/:[^:]*:/)){
print substr(name,RSTART+1,RLENGTH-2)}}'
long
10.自定义函数
function name(parameter-list) {
statements
}
函数中的变量不影响函数外的变量,但可以使用外部变量。参数列表使用逗号分隔,这些参数只在函数内部生效。
可以在awk的引号内任意位置处定义函数(即使是BEGIN之前或END之后),且函数的调用位置可以在函数的定义位置之前。但注意,函数必须不能定义在BEGIN或主输入循环或END内部,否则自定义函数的大括号会和包围action的大括号冲突而报错。即如下(1)-(4)处位置可定义定义函数,在任意位置处调用函数。
awk '(1)BEGIN{ACTIONS}(2)PATTERN{ACTIONS}(3)END{ACTIONS}(4)'
在函数的statements中,可以使用return expression
语句,表示函数的返回值。
例如,创建一个"向字符串指定位置处插入一个字符"的函数。
awk 'function insert(STRING, POS, INS) {
before_tmp = substr(STRING, 1, POS)
after_tmp = substr(STRING, POS + 1)
return before_tmp INS after_tmp
}
BEGIN{print insert("banana",3,"x")}'
11.getline函数
getline函数用于从文件、标准输入或管道中读取数据,并按情况设置变量的值。getline可以自动不断的加载下一行。如果能读取记录,则getline的返回值为1,遇到输入流的尾部时,返回值为0,不能读取记录(如文件没有读取权限、文件不存在)时,返回值为“-1"。
其中:
-
getline
:会从主输入文件中读取记录。会同时设置$0,NF,NR,FNR。 -
getline var
:会从主输入文件中读取记录,并将读取的记录赋值给变量var。会同时设置var,NR,FNR。 -
getline <file
:从外部文件file中读取记录。同时会设置$0,NF。 -
getline var <file
:从外部文件file中读取记录,并将读取的记录赋值给变量var。会同时设置var。 -
cmd | getline
:从管道中读取记录。会同时设置$0,NF。 -
cmd | getline var
:从管道中读取记录,并将读取的记录赋值给变量var。会同时设置var。
也就是说:
- 当getline从非主输入文件读取记录时,不会设置NR和FNR;
- 当getline后没有给定变量var时,会将读取的记录赋值给$0,于是会同时设置NF并切分成字段;否则将读取的记录赋值给变量var,不会设置NF切分字段。
仍然注意,从外部文件file中读取记录时,需要使用双引号包围文件名,否则被当成awk中的变量。
例如,执行Linux下的who命令并传递给getline读取,每读取一行记录,变量n自增1。
while ("who" | getline)
n++
将Linux命令date的结果保存到awk的变量date中。
"date" | getline date
当写成循环时,如:
while (getline <"file"){
cmd...
}
这是不安全的,因为当无法读取file时,返回值为"-1",而while循环的判断条件是0和非0,所以"-1"也会进入死循环。所以,安全的写法为:
while (getline <"file" >0){
cmd...
}
12.向awk传递变量
awk很重要且必备的能力是接受外界的变量,例如shell中的变量,shell中命令执行的结果,或者是在开始执行awk前应该初始化的变量。
例如,在shell中定义一个变量name,传递给awk使用。
awk -v awk_name="$name" 'BEGIN{print awk_name}'
Ma longshuai
有三种方式可以向awk传递变量:
1.将待传递变量当作文件名被awk解析。awk识别后发现是赋值语句,就认为其是变量传递。变量赋值语句必须定义awk program之后。此法定义的变量不可在BEGIN中使用,因为它是被当成文件解析的,只有在需要读取主输入文件的时候才会被解析。
awk 'BEGIN{}PATTERN{print var1,var2,var3}' var1=value1 var2=value2 file1 var3=value3 var1=value4 file2
在上面的语句中,当awk执行完BEGIN程序后,准备读取主输入,于是开始解析program后的输入文件。解析时发现,var1和var2都是赋值语句,于是当成变量处理,当读取到file1时,发现只有一个参数,则当作输入文件,于是开始处理该文件。在处理file1时,var1和var2都是有效的,但var3还未赋值,因此var3无效。当处理完file1后,继续解析下一个主输入文件,此时var3被赋值,并开始处理file2。在处理file2时,var1、var2和var3都是有效的,但var1被新值覆盖。
此外,还可以将shell命令的结果赋值给这些预定义变量。如下展示了几种变量定义的方式:
name="Ma longshuai"
awk 'program' OFS=":" var1="$name" var2="`echo Ma longshuai2`" var3="Ma longshuai3" var4=Malongshuai4 filename
不仅可以定义普通变量,还可以定义内置变量(如上OFS)。注意加引号的方式:为了安全,应该对所有赋值语句的value部分加上双引号,除非所赋的值不包含特殊字符。所以,如果上面的var1赋值语句写成var1=$name
,将被awk解析成var1=Ma longshuai
,于是var1的值为Ma,主输入文件为longshuai。
2.使用"-v"选项传递。变量赋值语句必须定义在awk program之前。这种方法定义的变量可以在BEGIN程序中使用。
除了定义在program之前,定义方式同上。每定义一个变量,都需要使用一个"-v"选项。如:
name="Ma longshuai"
awk -v OFS=":" -v var1="$name" -v var2="`echo Ma longshuai2`" -v var3="Ma longshuai3" 'program' filename
3.通过参数数组ARGV的方式。
ARGV是内置的数组变量。awk内部会将命令行切分,并按规则将各参数存放到ARGV数组中,数组下标从0开始,这是awk中唯一下标从0开始的数组。在存放到ARGV时,所有的选项和program会被忽略。
每存储一个数组变量,特殊变量ARGC的值增加1。因此ARGC的值代表的是参数的个数。所以,数组变量从ARGV[0]到ARGV[ARGC-1]。
可使用类似下面的循环来遍历ARGV数组。
awk -F "\t" -v var1="value1" 'BEGIN{
for(i=0;i<ARGC;++i){
print "ARGV[" i "]: " ARGV[i]
}
print "ARGC: " ARGC
}' "a" "b" "v=1" file
ARGV[0]: awk
ARGV[1]: a
ARGV[2]: b
ARGV[3]: v=1
ARGV[4]: file
ARGC: 5
注意,ARGV[0]存储的是awk命令,"-F"和"-v"选项都没有存储到ARGV中。
ARGC和ARGV数组变量的值都可以手动修改。命令行分割存储完成之后,开始处理BEGIN,再处理主循环输入。因此,在BEGIN中修改ARGV中输入文件对应的值,可以改变awk所读取的输入文件,若将其设置为空,则该数组变量直接被跳过,也就不再读取该输入文件。
需要注意的是,当增加ARGV元素时,必须同时递增ARGC的值,因为awk是根据AGRC来读取ARGV的。同理,只增加ARGC的值,将导致新建ARGV数组元素,且这些新元素的值为空。也因此,如果减小ARGC的值,将导致无法访问超出ARGC-1边界的ARGV元素。