我在使用C语言实现shell命令时遇到了问题。
我想这是因为我使用指针错误,因为我对这种语言很新。
现在,我正在研究“cd”和“export”命令。仅当我在代码中指定路径而不是在用户键入路径时,更改目录才有效。我很确定这是因为args[1]
。我试图打印它,但报告了一个分段错误。出口报告相同,我认为这是因为“args”管理不善。
我做错了什么?
注意:我说西班牙语,但我没有翻译代码的某些部分,因为我认为它们仍然可以理解。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define PROMPT "$"
#define MAX_LINE 512
int parse_args(char **args, char *line){
int n=0;
char* token;
char delimit[]=" \t\r\n\v\f";
token=strtok(line,delimit);
while(token!=NULL){
printf("token%i: %s\n",n,token);
args=token;
n++;
args++;
token=strtok(NULL,delimit);
}
printf("token%i: %s\n",n,token);
args=token;
return n;
}
char *read_line(char *line){
printf("%s%s ",getenv("USER"),PROMPT);
fflush(stdout);
line=fgets(line,MAX_LINE,stdin);
return line;
}
int execute_line(char *line){
char **args;
parse_args(args,line);
check_internal(args);
return 0;
}
int check_internal(char **args){
if( strcmp(args, "cd")==0 ){
internal_cd();
} else{
if( strcmp(args, "export")==0 ){
internal_export();
}else{
if( strcmp(args, "source")==0 ){
internal_source();
}else{
if( strcmp(args, "jobs")==0 ){
internal_jobs();
}else{
return 0;
}
}
}
}
}
int internal_cd(char **args){
char buff[50];
printf("Comando cd \n");
char directorio []= "/home/jamengual1/Escritorio/FlashDRIVE";
printf("%s", args+1);
if (chdir(directorio) == -1){
fprintf(stderr, "Error %d: %s\n", errno, strerror(errno));
perror("Error");
return -1;
} else{
printf("Estás en el directorio: %s \n", getcwd(buff, 50));
return 1;
}
}
//doing it this way(which should be the right one) it reports a Bad Address error
/*int internal_cd(char **args){
printf("%s","cambio de directorio\n");
char buff[50];
printf("Comando cd \n");
//printf("%s", args[1]); //segmentation fault
if (chdir(args[1]) == -1)
{
//fprintf(stderr, "Error %d: %s\n", errno,strerror(errno));
perror("Error");
return -1;
}
printf("Estás en el directorio: %s \n", getcwd(buff, 50));
return 1;
}
*/
int internal_export(char **args) {
printf("%s","éste es el export\n");
char *variable;
char *nuevo_valor;
char *aux;
variable = strtok(args[1], "=");
nuevo_valor = strtok(NULL, args[1]);
aux = getenv(variable);
if((int)aux == -1)
{
perror("Error: getenv");
return -1;
}
printf("VAR: '%s'. Valor: '%s'. Nuevo valor: '%s'\n", variable, aux, nuevo_valor);
if(!nuevo_valor){
perror("Error: error de sintaxis");
return -1;
}
if ((setenv(variable,nuevo_valor,1)== -1))
{
perror("Error: llamada al sistema con setenv");
return -1;
}
aux = getenv(variable);
if((int)aux == -1)
{
perror("Error: llamada al sistema con getenv");
return -1;
}
printf("Nuevo valor: '%s': '%s'\n", variable, aux);
return 1;
}
int internal_source(char **args) {
printf("%s","éste es el source\n");
return 1;
}
int internal_jobs(char **args){
printf("%s","éste es el jobs\n");
return 1;
}
void main(){
char line[MAX_LINE];
while(read_line(line)){
execute_line(line);
}
}
答案 0 :(得分:3)
发布的代码有很多问题。我觉得OP实际上对学习很感兴趣,所以我想回答,但有很多回答要做,所以我将这个做成社区维基,希望其他人能够参与进来。
最大的问题是你在没有对他们有好感的情况下自由地使用指针。例如,行if((int)aux==-1)
永远不会评估您想要的方式。 aux
是char *,这意味着它是指向分配给一个或多个字符的内存区域的指针。 aux
的值是一些任意数字 - 它是getenv
结果的内存地址。它永远不会是-1;它更像是一个类似于0xdeadf00d的东西 - 只是一个存储位置的任意地址。但是,(在我的示例中),在0xdeadf00d,您可能有字符'-'
,在0xdeadf00e,您可能有字符'1'
,在0xdeadf00f,您可能有字符'\0'
。也就是说,指针指向的内存位置中的一系列字符将包含字符串&#34; -1&#34;。 (注意,字符串的结尾由一个设置为0的字节表示,这就是我们知道它结束的方式。当我们讨论你的args变量时,这个概念将变得很重要。)你不能将0xdeadf00d强制转换为int (它将是3735941133),并期望它与-1比较。
要使该行正常工作,您可以做两件事:您可以比较该位置的字符串,即使用if (strcmp(aux, "-1") == 0)
,或者您可以将aux转换为int,即{{1} }
始终知道指针指向的是什么。了解他们指向一个格式化为特定数据类型的内存区域。
char **是指向包含一个或多个char *的内存区域的指针,char *是指向将包含一个或多个字符的内存区域的指针。在这种情况下,(atoi(aux) == -1)
应该是&#34;字符串列表&#34;。创建一个args列表实际上是项目中最难的部分。您需要处理分配正确大小的内存,并处理您正在进行动态内存分配的事实。对于初学者来说,这些都是艰难的事情。
你所说的args
的每一行都是非常错误的。正如您所提到的,它会产生不兼容类型的错误。您想将char *设置为char *。由于您可以将指针视为数组,因此args=token
之类的内容将有效,但这会在同一位置重写每个令牌;你只获得最后一个令牌。你应该想要像args[0] = token
这样的东西。但是存在一个问题 - args并没有指向任何东西!你需要为它分配空间! args本身应该是指向至少一个char *的内存位置,但它从未设置过。它通常使用args[n] = token
设置。
我将推荐一种避免动态分配的方法。现在还有很多东西需要学习,而不需要处理分配和随后的内存泄漏风险;这至少会让你专注于指针。
malloc
我将其称为MAX_ARGC,因为在c中有一个约定,我们用一个该名称的变量跟踪arg列表中的参数数量。但这个名字并不重要。
请注意,我在最后添加了0。就像你终止一个&#34;字符串&#34;通过放置#define PROMPT "$"
#define MAX_LINE 512
#define MAX_ARGC 128
char **args[MAX_ARGC] /* this defines args as a global char**, holding up to MAX_ARGC values */
int parse_args(char **args, char *line){
/* char **args is no longer necessary, since I made it global, but you can leave it */
int n=0;
char* token;
char delimit[]=" \t\r\n\v\f";
token=strtok(line,delimit);
while(token!=NULL){
if (n>MAX_ARGC) {
perror("Too many arguments")
}
printf("token%i: %s\n",n,token);
args[n]=token;
n++;
token=strtok(NULL,delimit);
}
printf("token%i: %s\n",n,token);
args[n] = NULL;
return n;
}
字符,您可以使用空指针终止指针列表。循环遍历列表时,在等于NULL时完成循环。 (这避免了argc的事情。)
这样做的动态方法是找出你有多少个参数,为末尾的NULL指针为那多个指针分配空间+ 1,然后将每个指针放入分配的空间。如果需要,您必须这样做;我提供的东西可以成为一个很好的垫脚石,所以你可以先控制你的指针。
您可能在使用fgets的返回值'\0'
时遇到问题。我相信大多数编译器都会让你逃脱它,但它是官方未定义的行为。我将其留了一天。
确保将args传递给internal_cd。