execvp() 打印无效选项 -- '
execvp() prints invalid option -- '
我正在创建简单的 shell 解释器。我正在使用一个扫描器和一个使用 lex 和 yacc 实用程序的解析器来创建这个 shell。当我给命令一个参数时出现问题,因为它 returns 无效 option.For 例如当我输入 ls -l
它 returns ls: invalid option -- '
即使我已经检查了arg_list 的值,它保存参数和命令的值并存储正确的参数。
谁能帮我理解为什么会出现此错误?对于我的代码,只要字符串匹配,lex 就会扫描输入并 returns 向解析器发送一个标记。
(这段程序代码只能运行一个参数)。
这是我的 lex 规范文件。
"shell.l"
%{
#include<stdio.h>
#include<stdlib.h>
#include"y.tab.h"
extern int len;
%}
%option noyywrap
letter [a-zA-Z]+
%%
(\-)?{letter} {yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {;}
. {return NOTOKEN;}
%%
这是我的 yacc 规范文件,也是我的 main()。
"shell.y"
%{
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
extern int yylex();
void yyerror();
int append =0;
void insertArg();
void insertComm();
char *arg_list[20];
int i,count=1;
int len;
char command[20];
%}
%union{
float f;
char *id;
}
%start C
%token <id> WORD
%token NOTOKEN
%type <id> C A
%%
A:A WORD {$$=;insertArg(,len);count++;}
|
;
C:WORD {$$=;insertComm(,len);/*printf("%s\n",$$);*/} A
;
%%
void insertComm(char *c,int len)
{
for(i=0;i<len;i++)
{
command[i]=c[i];
}
arg_list[0]=&command[0];
}
void insertArg(char *arg,int len)
{
arg_list[count]=&arg[0];
}
void yyerror(char *msg){
fprintf(stderr,"%s\n",msg);
exit(1);
}
int main()
{
int status;
while(1){
yyparse();
//arg_list[count]='[=11=]';
printf("\n arg_list[0]= %s",arg_list[0]);
for(i=0;arg_list[i]!='[=11=]';i++)
{
printf("\n arg_list[%d]= %s",i,arg_list[i]);
}
//printf("%s",sizeof(arg_list[1]));
execvp(arg_list[0],arg_list);
}
}
诊断
您的代码最终在参数中的 -l
之后包含换行符,并且 ls
抱怨它没有换行符作为有效的选项字母。
诊断技术
我修改了你的 main 中的打印代码为:
printf("arg_list[0]= %s\n", arg_list[0]);
for (i = 0; arg_list[i] != NULL; i++)
{
printf("arg_list[%d] = [%s]\n", i, arg_list[i]);
}
fflush(stdout);
产生了:
$ ./shell
ls -l
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
请注意,在 -l
之后和 ]
之前有一个换行符,它标志着字符串的结尾。
代码中有各种更改需要注意。首先,打印格式字符串 end 带有换行符,而不是开始——这有助于确保及时输出输出。如果末尾没有换行符,输出可能会无限期地延迟,直到某些东西确实产生了换行符。
其次,我的编译器警告我:
shellg.y: In function ‘main’:
shellg.y:60:24: warning: comparison between pointer and zero character constant [-Wpointer-compare]
for(i=0;arg_list[i]!='[=12=]';i++)
^~
shellg.y:60:13: note: did you mean to dereference the pointer?
for(i=0;arg_list[i]!='[=12=]';i++)
^
(旁白:我将文件命名为 shell.l
和 shellg.y
— 两次使用相同的基本名称让我不知所措,因为在我的正常构建机制下这两个目标文件都是 shell.o
.我猜你使用不同的规则来编译你的代码。)
我将 '[=33=]'
(这是一个 "null pointer constant",但它是一个常规的,通常表示作者感到困惑)更改为 NULL。
循环中的打印格式很重要;请注意我是如何将 %s
包含在标记字符 [%s]
中的。 ]
在输出中的位置立即表明换行符是问题所在。这是一项有价值的技术;它使不可见的再次可见。
最后的 fflush(stdout)
在此上下文中并不是真正重要的,但它确实确保在 execvp()
替换程序并永远丢失该输出之前生成任何未决的标准输出。使用 fflush(0)
或 fflush(NULL)
以确保其他文件流(标准错误)也被完全刷新是合理的,但标准错误通常不会缓冲太多。
处方
显然,解决方法是升级词法代码以不在参数中包含换行符。然而,为什么会这样还不是很明显。我更改了语法以进行一些打印:
%%
A:A WORD {printf("R1: len %d [%s]\n", len, ); $$=;insertArg(,len);count++;}
|
;
C:WORD {printf("R2A: len %d [%s]\n", len, ); $$=;insertComm(,len);/*printf("%s\n",$$);*/} A
{printf("R2B: len %d [%s]\n", len, );}
;
%%
请特别注意 R2B
行;那里可能有一个动作,但你没有。
当这是 运行 时,我得到:
$ ./shell
ls -l
R2A: len 2 [ls]
R1: len 2 [-l]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
有趣的是 -l
出现在 R1
输出中没有换行符,但在 R2B
输出时添加了换行符。那
出乎意料,却是偶然。我添加了打印以确保覆盖范围完整;我很高兴我做到了!
那么,是什么原因呢?为什么不将令牌重置为空?为什么要添加换行符?为什么不制作令牌的副本,而是存储指向 yytext
?
的指针
长期的解决办法是复制一份;这样我们就可以开始了。我假设 strdup()
对你可用并且会使用它。我将 #include <string.h>
添加到包含并使用:
void insertArg(char *arg,int len)
{
arg_list[count] = strdup(arg);
}
嗨,太棒了!期望的输出:
$ ./shell
ls -l
R2A: len 2 [ls]
R1: len 2 [-l]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
total 128
-rw-r--r-- 1 jleffler staff 1443 Apr 29 08:36 data
-rwxr-xr-x 1 jleffler staff 24516 Apr 29 09:08 shell
-rw-r--r-- 1 jleffler staff 297 Apr 29 08:36 shell.l
-rw-r--r-- 1 jleffler staff 13568 Apr 29 08:38 shell.o
-rw-r--r-- 1 jleffler staff 4680 Apr 29 09:08 shellg.o
-rw-r--r-- 1 jleffler staff 1306 Apr 29 09:08 shellg.y
-rw-r--r-- 1 jleffler staff 2245 Apr 29 09:08 y.tab.h
$
进一步观察
您将需要确保释放重复的字符串。
您还需要使用警告选项进行编译。与我通常的做法相反,我只使用默认(几乎没有)警告进行编译。编译语法显示:
yacc -d shellg.y
shellg.y:28.3: warning: empty rule for typed nonterminal, and no action
shellg.y:30.3-31.44: warning: unused value:
gcc -O -c y.tab.c
mv y.tab.o shellg.o
rm -f y.tab.c
你应该解决这两个问题。你没有为你定义的函数声明原型——你有:
extern int yylex();
void yyerror();
int append =0;
void insertArg();
void insertComm();
有4个函数声明,但其中none个是函数原型。您需要在不需要参数的地方添加预期参数或 void
。
还有其他问题。使用我的正常编译选项(rmk
是 make
的变体;-u
表示 'unconditional rebuild'),我得到:
$ rmk -ku
yacc shellg.y
shellg.y:28.3: warning: empty rule for typed nonterminal, and no action
shellg.y:30.3-31.44: warning: unused value:
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c y.tab.c
shellg.y:7:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
extern int yylex();
^~~~~~
shellg.y:8:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void yyerror();
^~~~
shellg.y:10:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void insertArg();
^~~~
shellg.y:11:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void insertComm();
^~~~
shellg.y:36:6: error: no previous prototype for ‘insertComm’ [-Werror=missing-prototypes]
void insertComm(char *c,int len)
^~~~~~~~~~
shellg.y:45:6: error: no previous prototype for ‘insertArg’ [-Werror=missing-prototypes]
void insertArg(char *arg,int len)
^~~~~~~~~
shellg.y: In function ‘insertArg’:
shellg.y:45:30: error: unused parameter ‘len’ [-Werror=unused-parameter]
void insertArg(char *arg,int len)
^~~
shellg.y: At top level:
shellg.y:50:6: error: no previous prototype for ‘yyerror’ [-Werror=missing-prototypes]
void yyerror(char *msg){
^~~~~~~
shellg.y: In function ‘main’:
shellg.y:57:9: error: unused variable ‘status’ [-Werror=unused-variable]
int status;
^~~~~~
cc1: all warnings being treated as errors
rmk: error code 1
lex shell.l
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c lex.yy.c
lex.yy.c:1119:16: error: ‘input’ defined but not used [-Werror=unused-function]
static int input (void)
^~~~~
lex.yy.c:1078:17: error: ‘yyunput’ defined but not used [-Werror=unused-function]
static void yyunput (int c, register char * yy_bp )
^~~~~~~
cc1: all warnings being treated as errors
rmk: error code 1
'shell' not remade because of errors.
'all' not remade because of errors.
$
我也懒得解决所有这些问题。
我认为您也需要修复词法分析器。白色的space应该不包含在返回的token中,但是好像是加在最后的,不过不太确定how/why.
$ ./shell
ls -l abelone ducks
R2A: len 2 [ls]
R1: len 2 [-l]
R1: len 7 [abelone]
R1: len 5 [ducks]
R2B: len 5 [ducks
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
arg_list[2] = [abelone]
arg_list[3] = [ducks]
ls: abelone: No such file or directory
ls: ducks: No such file or directory
$
R2B
打印让我很困惑。 How/why是相对于之前的R1
进行了修改,显示ducks
且没有换行。我想你需要找到它。
正在向分析器添加诊断:
%%
(\-)?{letter} {printf("L1: [%s]\n", yytext); yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {printf("L2: [%s]\n", yytext);}
. {printf("L3: [%s]\n", yytext); return NOTOKEN;}
%%
和运行宁它产生:
$ ./shell
ls -l
L1: [ls]
R2A: len 2 [ls]
L2: [ ]
L1: [-l]
R1: len 2 [-l]
L2: [
]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
total 224
-rw-r--r-- 1 jleffler staff 1443 Apr 29 08:36 data
-rw-r--r-- 1 jleffler staff 303 Apr 29 09:16 makefile
-rwxr-xr-x 1 jleffler staff 24516 Apr 29 09:29 shell
-rw-r--r-- 1 jleffler staff 385 Apr 29 09:29 shell.l
-rw-r--r-- 1 jleffler staff 13812 Apr 29 09:29 shell.o
-rw-r--r-- 1 jleffler staff 4680 Apr 29 09:08 shellg.o
-rw-r--r-- 1 jleffler staff 1306 Apr 29 09:08 shellg.y
-rw-r--r-- 1 jleffler staff 41299 Apr 29 09:16 y.tab.c
-rw-r--r-- 1 jleffler staff 2245 Apr 29 09:08 y.tab.h
$
祝您跟踪愉快 how/why R2B
打印输出包括换行符。
详细追踪——打印地址
JFTR,我正在开发 Mac 运行ning macOS 10.13.4 High Sierra,GCC 7.3.0(自制),Bison 2.3 运行宁为 Yacc,Flex 2.5.35 Apple(flex-31) 运行宁为 Lex。
这是一个清理过的 shell.l
— 它在我严格的警告制度下编译得很干净:
%{
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"
extern int len;
%}
%option noyywrap
%option noinput
%option nounput
letter [a-zA-Z]+
%%
(\-)?{letter} {printf("L1: [%s]\n", yytext); yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {printf("L2: [%s]\n", yytext);}
. {printf("L3: [%s]\n", yytext); return NOTOKEN;}
%%
这里是 shellg.y
的诊断性更强的版本(在我严格的警告制度下也可以干净地编译)。我恢复了 insertArg()
中将地址复制到 arg_list
数组中的原始代码,但我还添加了代码以在每次调用时打印出 arg_list
的完整内容。结果证明这是有用的!
%{
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<unistd.h>
extern int yylex(void);
void yyerror(char *msg);
int append =0;
void insertArg(char *arg, int len);
void insertComm(char *arg, int len);
char *arg_list[20];
int i,count=1;
int len;
char command[20];
%}
%union{
float f;
char *id;
}
%start C
%token <id> WORD
%token NOTOKEN
%type <id> C A
%%
A: A WORD {printf("R1A: len %d %p [%s]\n", len, , ); $$=;insertArg(,len);count++;}
| /*Nothing */
{printf("R1B: - nothing\n");}
;
C: WORD
{printf("R2A: len %d %p [%s]\n", len, , ); $$=;insertComm(,len);}
A
{printf("R2B: len %d %p [%s]\n", len, , );}
;
%%
void insertComm(char *c, int len)
{
printf("Command: %d [%s]\n", len, c);
for (i = 0; i < len; i++)
{
command[i] = c[i];
}
arg_list[0] = &command[0];
}
void insertArg(char *arg, int len)
{
printf("Argument: %d [%s]\n", len, arg);
//arg_list[count] = strdup(arg);
arg_list[count] = arg;
for (int i = 0; i < count; i++)
printf("list[%d] = %p [%s]\n", i, arg_list[i], arg_list[i]);
}
void yyerror(char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
int main(void)
{
while (1)
{
yyparse();
printf("arg_list[0]= %s\n", arg_list[0]);
for (i = 0; arg_list[i] != NULL; i++)
{
printf("arg_list[%d] = [%s]\n", i, arg_list[i]);
}
fflush(stdout);
execvp(arg_list[0], arg_list);
}
}
编译后运行,我可以得到输出:
$ ./shell
ls -l -rt makefile data shell
L1: [ls]
R2A: len 2 0x7f9dd4801000 [ls]
Command: 2 [ls]
R1B: - nothing
L2: [ ]
L1: [-l]
R1A: len 2 0x7f9dd4801003 [-l]
Argument: 2 [-l]
list[0] = 0x10ace8180 [ls]
L2: [ ]
L1: [-rt]
R1A: len 3 0x7f9dd4801006 [-rt]
Argument: 3 [-rt]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt]
L2: [ ]
L1: [makefile]
R1A: len 8 0x7f9dd480100a [makefile]
Argument: 8 [makefile]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile]
list[2] = 0x7f9dd4801006 [-rt makefile]
L2: [ ]
L1: [data]
R1A: len 4 0x7f9dd4801013 [data]
Argument: 4 [data]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile data]
list[2] = 0x7f9dd4801006 [-rt makefile data]
list[3] = 0x7f9dd480100a [makefile data]
L2: [ ]
L1: [shell]
R1A: len 5 0x7f9dd4801018 [shell]
Argument: 5 [shell]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile data shell]
list[2] = 0x7f9dd4801006 [-rt makefile data shell]
list[3] = 0x7f9dd480100a [makefile data shell]
list[4] = 0x7f9dd4801013 [data shell]
L2: [
]
R2B: len 5 0x7f9dd4801018 [shell
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l -rt makefile data shell
]
arg_list[2] = [-rt makefile data shell
]
arg_list[3] = [makefile data shell
]
arg_list[4] = [data shell
]
arg_list[5] = [shell
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
注意 arg_list
中的指针如何指向单个字符串中的不同位置。词法分析器在 returns 时在标记后插入一个空值,但用空白或换行符(如果我键入任何内容,则用制表符)替换该空值。这说明了为什么需要复制令牌。 What's "in" arg_list[1]
随着词法分析的进行而变化。这就是出现换行符的原因。
注意评论
请注意 rici 的评论并关注 link:
我正在创建简单的 shell 解释器。我正在使用一个扫描器和一个使用 lex 和 yacc 实用程序的解析器来创建这个 shell。当我给命令一个参数时出现问题,因为它 returns 无效 option.For 例如当我输入 ls -l
它 returns ls: invalid option -- '
即使我已经检查了arg_list 的值,它保存参数和命令的值并存储正确的参数。
谁能帮我理解为什么会出现此错误?对于我的代码,只要字符串匹配,lex 就会扫描输入并 returns 向解析器发送一个标记。
(这段程序代码只能运行一个参数)。
这是我的 lex 规范文件。 "shell.l"
%{
#include<stdio.h>
#include<stdlib.h>
#include"y.tab.h"
extern int len;
%}
%option noyywrap
letter [a-zA-Z]+
%%
(\-)?{letter} {yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {;}
. {return NOTOKEN;}
%%
这是我的 yacc 规范文件,也是我的 main()。 "shell.y"
%{
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
extern int yylex();
void yyerror();
int append =0;
void insertArg();
void insertComm();
char *arg_list[20];
int i,count=1;
int len;
char command[20];
%}
%union{
float f;
char *id;
}
%start C
%token <id> WORD
%token NOTOKEN
%type <id> C A
%%
A:A WORD {$$=;insertArg(,len);count++;}
|
;
C:WORD {$$=;insertComm(,len);/*printf("%s\n",$$);*/} A
;
%%
void insertComm(char *c,int len)
{
for(i=0;i<len;i++)
{
command[i]=c[i];
}
arg_list[0]=&command[0];
}
void insertArg(char *arg,int len)
{
arg_list[count]=&arg[0];
}
void yyerror(char *msg){
fprintf(stderr,"%s\n",msg);
exit(1);
}
int main()
{
int status;
while(1){
yyparse();
//arg_list[count]='[=11=]';
printf("\n arg_list[0]= %s",arg_list[0]);
for(i=0;arg_list[i]!='[=11=]';i++)
{
printf("\n arg_list[%d]= %s",i,arg_list[i]);
}
//printf("%s",sizeof(arg_list[1]));
execvp(arg_list[0],arg_list);
}
}
诊断
您的代码最终在参数中的 -l
之后包含换行符,并且 ls
抱怨它没有换行符作为有效的选项字母。
诊断技术
我修改了你的 main 中的打印代码为:
printf("arg_list[0]= %s\n", arg_list[0]);
for (i = 0; arg_list[i] != NULL; i++)
{
printf("arg_list[%d] = [%s]\n", i, arg_list[i]);
}
fflush(stdout);
产生了:
$ ./shell
ls -l
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
请注意,在 -l
之后和 ]
之前有一个换行符,它标志着字符串的结尾。
代码中有各种更改需要注意。首先,打印格式字符串 end 带有换行符,而不是开始——这有助于确保及时输出输出。如果末尾没有换行符,输出可能会无限期地延迟,直到某些东西确实产生了换行符。
其次,我的编译器警告我:
shellg.y: In function ‘main’:
shellg.y:60:24: warning: comparison between pointer and zero character constant [-Wpointer-compare]
for(i=0;arg_list[i]!='[=12=]';i++)
^~
shellg.y:60:13: note: did you mean to dereference the pointer?
for(i=0;arg_list[i]!='[=12=]';i++)
^
(旁白:我将文件命名为 shell.l
和 shellg.y
— 两次使用相同的基本名称让我不知所措,因为在我的正常构建机制下这两个目标文件都是 shell.o
.我猜你使用不同的规则来编译你的代码。)
我将 '[=33=]'
(这是一个 "null pointer constant",但它是一个常规的,通常表示作者感到困惑)更改为 NULL。
循环中的打印格式很重要;请注意我是如何将 %s
包含在标记字符 [%s]
中的。 ]
在输出中的位置立即表明换行符是问题所在。这是一项有价值的技术;它使不可见的再次可见。
最后的 fflush(stdout)
在此上下文中并不是真正重要的,但它确实确保在 execvp()
替换程序并永远丢失该输出之前生成任何未决的标准输出。使用 fflush(0)
或 fflush(NULL)
以确保其他文件流(标准错误)也被完全刷新是合理的,但标准错误通常不会缓冲太多。
处方
显然,解决方法是升级词法代码以不在参数中包含换行符。然而,为什么会这样还不是很明显。我更改了语法以进行一些打印:
%%
A:A WORD {printf("R1: len %d [%s]\n", len, ); $$=;insertArg(,len);count++;}
|
;
C:WORD {printf("R2A: len %d [%s]\n", len, ); $$=;insertComm(,len);/*printf("%s\n",$$);*/} A
{printf("R2B: len %d [%s]\n", len, );}
;
%%
请特别注意 R2B
行;那里可能有一个动作,但你没有。
当这是 运行 时,我得到:
$ ./shell
ls -l
R2A: len 2 [ls]
R1: len 2 [-l]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
有趣的是 -l
出现在 R1
输出中没有换行符,但在 R2B
输出时添加了换行符。那
出乎意料,却是偶然。我添加了打印以确保覆盖范围完整;我很高兴我做到了!
那么,是什么原因呢?为什么不将令牌重置为空?为什么要添加换行符?为什么不制作令牌的副本,而是存储指向 yytext
?
长期的解决办法是复制一份;这样我们就可以开始了。我假设 strdup()
对你可用并且会使用它。我将 #include <string.h>
添加到包含并使用:
void insertArg(char *arg,int len)
{
arg_list[count] = strdup(arg);
}
嗨,太棒了!期望的输出:
$ ./shell
ls -l
R2A: len 2 [ls]
R1: len 2 [-l]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
total 128
-rw-r--r-- 1 jleffler staff 1443 Apr 29 08:36 data
-rwxr-xr-x 1 jleffler staff 24516 Apr 29 09:08 shell
-rw-r--r-- 1 jleffler staff 297 Apr 29 08:36 shell.l
-rw-r--r-- 1 jleffler staff 13568 Apr 29 08:38 shell.o
-rw-r--r-- 1 jleffler staff 4680 Apr 29 09:08 shellg.o
-rw-r--r-- 1 jleffler staff 1306 Apr 29 09:08 shellg.y
-rw-r--r-- 1 jleffler staff 2245 Apr 29 09:08 y.tab.h
$
进一步观察
您将需要确保释放重复的字符串。 您还需要使用警告选项进行编译。与我通常的做法相反,我只使用默认(几乎没有)警告进行编译。编译语法显示:
yacc -d shellg.y
shellg.y:28.3: warning: empty rule for typed nonterminal, and no action
shellg.y:30.3-31.44: warning: unused value:
gcc -O -c y.tab.c
mv y.tab.o shellg.o
rm -f y.tab.c
你应该解决这两个问题。你没有为你定义的函数声明原型——你有:
extern int yylex();
void yyerror();
int append =0;
void insertArg();
void insertComm();
有4个函数声明,但其中none个是函数原型。您需要在不需要参数的地方添加预期参数或 void
。
还有其他问题。使用我的正常编译选项(rmk
是 make
的变体;-u
表示 'unconditional rebuild'),我得到:
$ rmk -ku
yacc shellg.y
shellg.y:28.3: warning: empty rule for typed nonterminal, and no action
shellg.y:30.3-31.44: warning: unused value:
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c y.tab.c
shellg.y:7:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
extern int yylex();
^~~~~~
shellg.y:8:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void yyerror();
^~~~
shellg.y:10:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void insertArg();
^~~~
shellg.y:11:5: error: function declaration isn’t a prototype [-Werror=strict-prototypes]
void insertComm();
^~~~
shellg.y:36:6: error: no previous prototype for ‘insertComm’ [-Werror=missing-prototypes]
void insertComm(char *c,int len)
^~~~~~~~~~
shellg.y:45:6: error: no previous prototype for ‘insertArg’ [-Werror=missing-prototypes]
void insertArg(char *arg,int len)
^~~~~~~~~
shellg.y: In function ‘insertArg’:
shellg.y:45:30: error: unused parameter ‘len’ [-Werror=unused-parameter]
void insertArg(char *arg,int len)
^~~
shellg.y: At top level:
shellg.y:50:6: error: no previous prototype for ‘yyerror’ [-Werror=missing-prototypes]
void yyerror(char *msg){
^~~~~~~
shellg.y: In function ‘main’:
shellg.y:57:9: error: unused variable ‘status’ [-Werror=unused-variable]
int status;
^~~~~~
cc1: all warnings being treated as errors
rmk: error code 1
lex shell.l
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -c lex.yy.c
lex.yy.c:1119:16: error: ‘input’ defined but not used [-Werror=unused-function]
static int input (void)
^~~~~
lex.yy.c:1078:17: error: ‘yyunput’ defined but not used [-Werror=unused-function]
static void yyunput (int c, register char * yy_bp )
^~~~~~~
cc1: all warnings being treated as errors
rmk: error code 1
'shell' not remade because of errors.
'all' not remade because of errors.
$
我也懒得解决所有这些问题。
我认为您也需要修复词法分析器。白色的space应该不包含在返回的token中,但是好像是加在最后的,不过不太确定how/why.
$ ./shell
ls -l abelone ducks
R2A: len 2 [ls]
R1: len 2 [-l]
R1: len 7 [abelone]
R1: len 5 [ducks]
R2B: len 5 [ducks
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
arg_list[2] = [abelone]
arg_list[3] = [ducks]
ls: abelone: No such file or directory
ls: ducks: No such file or directory
$
R2B
打印让我很困惑。 How/why是相对于之前的R1
进行了修改,显示ducks
且没有换行。我想你需要找到它。
正在向分析器添加诊断:
%%
(\-)?{letter} {printf("L1: [%s]\n", yytext); yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {printf("L2: [%s]\n", yytext);}
. {printf("L3: [%s]\n", yytext); return NOTOKEN;}
%%
和运行宁它产生:
$ ./shell
ls -l
L1: [ls]
R2A: len 2 [ls]
L2: [ ]
L1: [-l]
R1: len 2 [-l]
L2: [
]
R2B: len 2 [-l
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l]
total 224
-rw-r--r-- 1 jleffler staff 1443 Apr 29 08:36 data
-rw-r--r-- 1 jleffler staff 303 Apr 29 09:16 makefile
-rwxr-xr-x 1 jleffler staff 24516 Apr 29 09:29 shell
-rw-r--r-- 1 jleffler staff 385 Apr 29 09:29 shell.l
-rw-r--r-- 1 jleffler staff 13812 Apr 29 09:29 shell.o
-rw-r--r-- 1 jleffler staff 4680 Apr 29 09:08 shellg.o
-rw-r--r-- 1 jleffler staff 1306 Apr 29 09:08 shellg.y
-rw-r--r-- 1 jleffler staff 41299 Apr 29 09:16 y.tab.c
-rw-r--r-- 1 jleffler staff 2245 Apr 29 09:08 y.tab.h
$
祝您跟踪愉快 how/why R2B
打印输出包括换行符。
详细追踪——打印地址
JFTR,我正在开发 Mac 运行ning macOS 10.13.4 High Sierra,GCC 7.3.0(自制),Bison 2.3 运行宁为 Yacc,Flex 2.5.35 Apple(flex-31) 运行宁为 Lex。
这是一个清理过的 shell.l
— 它在我严格的警告制度下编译得很干净:
%{
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"
extern int len;
%}
%option noyywrap
%option noinput
%option nounput
letter [a-zA-Z]+
%%
(\-)?{letter} {printf("L1: [%s]\n", yytext); yylval.id=yytext;len=yyleng;return WORD;}
[ \t\n] {printf("L2: [%s]\n", yytext);}
. {printf("L3: [%s]\n", yytext); return NOTOKEN;}
%%
这里是 shellg.y
的诊断性更强的版本(在我严格的警告制度下也可以干净地编译)。我恢复了 insertArg()
中将地址复制到 arg_list
数组中的原始代码,但我还添加了代码以在每次调用时打印出 arg_list
的完整内容。结果证明这是有用的!
%{
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<unistd.h>
extern int yylex(void);
void yyerror(char *msg);
int append =0;
void insertArg(char *arg, int len);
void insertComm(char *arg, int len);
char *arg_list[20];
int i,count=1;
int len;
char command[20];
%}
%union{
float f;
char *id;
}
%start C
%token <id> WORD
%token NOTOKEN
%type <id> C A
%%
A: A WORD {printf("R1A: len %d %p [%s]\n", len, , ); $$=;insertArg(,len);count++;}
| /*Nothing */
{printf("R1B: - nothing\n");}
;
C: WORD
{printf("R2A: len %d %p [%s]\n", len, , ); $$=;insertComm(,len);}
A
{printf("R2B: len %d %p [%s]\n", len, , );}
;
%%
void insertComm(char *c, int len)
{
printf("Command: %d [%s]\n", len, c);
for (i = 0; i < len; i++)
{
command[i] = c[i];
}
arg_list[0] = &command[0];
}
void insertArg(char *arg, int len)
{
printf("Argument: %d [%s]\n", len, arg);
//arg_list[count] = strdup(arg);
arg_list[count] = arg;
for (int i = 0; i < count; i++)
printf("list[%d] = %p [%s]\n", i, arg_list[i], arg_list[i]);
}
void yyerror(char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
int main(void)
{
while (1)
{
yyparse();
printf("arg_list[0]= %s\n", arg_list[0]);
for (i = 0; arg_list[i] != NULL; i++)
{
printf("arg_list[%d] = [%s]\n", i, arg_list[i]);
}
fflush(stdout);
execvp(arg_list[0], arg_list);
}
}
编译后运行,我可以得到输出:
$ ./shell
ls -l -rt makefile data shell
L1: [ls]
R2A: len 2 0x7f9dd4801000 [ls]
Command: 2 [ls]
R1B: - nothing
L2: [ ]
L1: [-l]
R1A: len 2 0x7f9dd4801003 [-l]
Argument: 2 [-l]
list[0] = 0x10ace8180 [ls]
L2: [ ]
L1: [-rt]
R1A: len 3 0x7f9dd4801006 [-rt]
Argument: 3 [-rt]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt]
L2: [ ]
L1: [makefile]
R1A: len 8 0x7f9dd480100a [makefile]
Argument: 8 [makefile]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile]
list[2] = 0x7f9dd4801006 [-rt makefile]
L2: [ ]
L1: [data]
R1A: len 4 0x7f9dd4801013 [data]
Argument: 4 [data]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile data]
list[2] = 0x7f9dd4801006 [-rt makefile data]
list[3] = 0x7f9dd480100a [makefile data]
L2: [ ]
L1: [shell]
R1A: len 5 0x7f9dd4801018 [shell]
Argument: 5 [shell]
list[0] = 0x10ace8180 [ls]
list[1] = 0x7f9dd4801003 [-l -rt makefile data shell]
list[2] = 0x7f9dd4801006 [-rt makefile data shell]
list[3] = 0x7f9dd480100a [makefile data shell]
list[4] = 0x7f9dd4801013 [data shell]
L2: [
]
R2B: len 5 0x7f9dd4801018 [shell
]
arg_list[0]= ls
arg_list[0] = [ls]
arg_list[1] = [-l -rt makefile data shell
]
arg_list[2] = [-rt makefile data shell
]
arg_list[3] = [makefile data shell
]
arg_list[4] = [data shell
]
arg_list[5] = [shell
]
ls: illegal option --
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
$
注意 arg_list
中的指针如何指向单个字符串中的不同位置。词法分析器在 returns 时在标记后插入一个空值,但用空白或换行符(如果我键入任何内容,则用制表符)替换该空值。这说明了为什么需要复制令牌。 What's "in" arg_list[1]
随着词法分析的进行而变化。这就是出现换行符的原因。
注意评论
请注意 rici 的评论并关注 link: