【问题标题】:Writing a C command prompt program with its own 'command line arguments'使用自己的“命令行参数”编写 C 命令提示符程序
【发布时间】:2015-12-08 08:51:19
【问题描述】:

在这个问题上被困了几个小时。如何在命令提示符程序中读取命令行参数并使用 execv() 系统调用执行它?以下是 YWIMC 为提示符的示例输出。

YWIMC > R /bin/ls
a.out ex2.c ...... //output from the “ls” command
YWIMC > R /bin/ls –l //same as executing “ls –l”
total 144
-rwx------ 1 sooyj compsc 8548 Aug 13 12:06 a.out
-rwx------ 1 sooyj compsc 6388 Aug 13 11:36 alarmClock
.................... //other files not shown

R 命令的语法为 R command_path [arg1 到 arg4],其中它们可以是 0 到 4 个参数。例如。 R /bin/ls 或 R /bin/ls -l

我应该使用 execv(我假设它更适合读取命令行参数,因为它使用 char 数组作为其参数,并且因为我的家庭作业要求我这样做)但我在读取参数时遇到了麻烦在。

当有任意数量的参数(0 到 4)时,我该怎么做? 在阅读参数时,我如何让程序认识到这是我给出的所有参数的结尾? (我遇到了一个问题,即使我将最大值设置为 4,我也会添加无限数量的参数。)以下是我现有的代码,只是我必须将 execl 更改为 execv

else    {
            result = fork();
            if(result != 0) {   //Parent Code
                childID = wait(&status);
            }
            else    {   //Child Code
                if(strcmp(filePath, "/bin/ls") == 0)    { //specific /bin/ls command
                    execl("/bin/ls", "ls", NULL);
                }
                else    {   //run other programs
                    execl(filePath, NULL);
                }
                return 256;
            }

【问题讨论】:

  • 我不明白——你应该编写一个名为“R”的工具,它只执行它的第一个参数,最多有 4 个参数......或者你应该实现一种“shell”知道“R”作为一个命令来做到这一点?后者会做更多的工作,迫使你做一些字符串处理(应该支持引用和转义吗?)
  • 如果是后者,strtok-like function I wrote recently 可能会对您有所帮助,请参阅 here for it's usage
  • 有点像后者。编写一个 shell 类型的程序,读入 R 并识别出它是一个“请求”,读取第一个参数是文件路径,读取接下来的 4 个参数,这些参数可能没有到 4 个。我在循环读取时遇到了麻烦0 到 4 个参数,因为我不能只从 main 方法中删除它。
  • 是的,那么您的“主要问题”是您自己的命令行解析。在最基本的层面上,使用带有计数器变量的fgets()strtok()(参见各自的手册页)。如果您需要支持引用和转义,请参阅我上面链接的函数,以了解扩展 strqetok() 的可能实现(像 quote 和 escape 这样调用它)。如果你也想支持命令行编辑,fgets() 就不用了,那就看看libreadline吧。

标签: c command-line-arguments command-prompt


【解决方案1】:

您可以使用malloc 创建一个字符串数组。当你读入参数时,使用realloc 来增加数组的大小。然后你可以将结果数组传递给execv

int arglen = 1;
char **args = malloc(arglen * sizeof(char *));
args[0] = strdup(/* program to run */);
while (/* more args to read */) {
    arglen++;
    args = realloc(args, arglen * sizeof(char *));
    args[arglen-1] = strdup(/* next arg */);
}
arglen++;
args = realloc(args, arglen * sizeof(char *));
args[arglen] = NULL;
...
execv(/* prog to call */, args);        

【讨论】:

  • 我认为我的问题比这更基本。我想到的想法是读取文件/文件路径,接受所有参数,使用 execv(program,args)。这要么是不可能的,要么我有心理障碍,但我似乎无法创建一个 for 循环来读取范围从 0 到 4 个参数的参数。我记得尝试过很多事情,而 while(scanf("%s", &args[i])) 就是其中之一 - 我在那之前创建了一个数组 args[5][20]。
【解决方案2】:

我的想法是读取文件/文件路径,把所有 参数,使用 execv(program,args)。

你在正确的轨道上。该练习不仅关注execv的使用,还巧妙地关注命令行参数索引,您必须注意命令行上给出的第一个参数具有和1的索引(例如argv[1]),而需要保存要传递给execv的参数的数组将从索引0开始。

处理索引偏移并不太难,您只需要注意如何填充您将发送到execv 的数组。 注意:变量初始化很重要。您发送到execvargs 数组的最终指针必须为NULL,以充当execv 的哨兵,以了解在哪里停止处理参数。确保您在数组末尾提供NULL 指针的一种方法是初始化指向NULLALL 指针:

char *args[5] = {NULL};

那么你唯一的任务就是确保只填充数组中的前 4 个指针,留下最后一个 NULL。另外注意args 中的第一个指针必须包含您正在执行的程序的完整路径。上面显示R ls 的原始示例将不起作用,除非您将正确的路径附加到ls 的开头(例如/usr/bin/ls)。你如何做到这一点取决于你。一个简短的例子是:

#include <stdio.h>
#include <unistd.h>

int main (int argc, char **argv) {

    char *args[5] = {NULL};        /* all pointers initialized to NULL */
    int lim = argc < 5 ? argc : 5; /* limit the number of args to read */
    int nargs = lim - 1;           /* adjust the index to fill args    */
    int i;

    /* read the arguments from the command line */
    for (i = 1; i < lim; i++)
        args[i-1] = argv[i];

    /* output the arguments read */
    for (i = 0; i < nargs; i++)
        printf (" args[%d] = %s\n", i, args[i]);

    /* call execv (NOTE: you must provide a full-path
       to the program being executed, e.g. /usr/bin/ls) 
    */
    if (execv (args[0], args) == -1)
        fprintf (stderr, "error: execv failed - path correct?.\n");

    return 0;
}

编译

$ gcc -Wall -Wextra -o bin/execvargs execvargs.c

使用/输出

$ ./bin/execvargs /usr/bin/ls
 args[0] = /usr/bin/ls
3darrayaddr.c                   getline_minimal.c             reallocptr.c
BoggleData.txt                  getline_rdfile.c              rec_char_in_str.c
<snip>

$ ./bin/execvargs /usr/bin/ls -l
 args[0] = /usr/bin/ls
 args[1] = -l
total 7528
-rw-r--r-- 1 david david     376 Sep 23  2014 3darrayaddr.c
-rw-r--r-- 1 david david     192 Jun 27 01:11 BoggleData.txt
-rw-r--r-- 1 david david    3565 Jun 26  2014 DoubleLinkedList-old.c
<snip>

$ ./bin/execvargs my dog has fleas and so does the cat
 args[0] = my
 args[1] = dog
 args[2] = has
 args[3] = fleas
error: execv failed - path correct?.

仔细看看 execv

如果您想了解execv 如何调用您作为args[0] 传递的程序,您可以在shell 中创建一个小型测试脚本,该脚本将回显在execv 调用时收到的参数。简单的事情就好了:

#!/bin/bash

declare -i cnt=0

for i in "$@"; do
    printf "  arg[%d] : %s\n" $cnt "$i"
    let cnt=cnt+1
done

调用它test.sh 并使其可执行。 (例如chmod 0755 test.sh)然后提供./test.sh 以及您为程序选择的参数:

$ ./bin/execvargs ./test.sh my dog has fleas
 args[0] = ./test.sh
 args[1] = my
 args[2] = dog
 args[3] = has
  arg[0] : my
  arg[1] : dog
  arg[2] : has

【讨论】:

  • 这里有问题。我无法像您从 main 方法中那样获取命令行参数。我正在使用命令提示符运行另一个命令提示符程序,该程序将获取自己的命令行参数。我不只是使用命令行参数运行程序然后让程序(即命令提示符)退出。
  • 嗯..现在我很困惑。我以为你会调用这个程序 R 并且你希望让它拾取要运行的第二个程序和它的参数。它是可以修复的,唯一的问题是需要多少努力。让我们首先了解您的意思 我正在使用命令提示符运行另一个命令提示符程序,该程序将获取自己的命令行参数。您是说您有一些东西被调用为 PROMPT_COMMAND这是作为问题提示的一部分执行的?
  • 等一下,你只是使用R作为标志告诉这个程序读取下一个路径和参数传递给execv,如果@987654353 @ 没有指定,它应该什么都不做?
  • 还有其他命令。我只是在读取参数时遇到了麻烦,因为它不是来自命令行,每个人都知道哪个来自 main 方法。
  • 所以你需要看一个从文件中读取参数的例子。好的,所有参数都在文件的第一行,还是每行一个参数?如果在同一行,它们是用什么分隔的? (空格、制表符、逗号、分号)。我会做一个例子,你让我知道分隔符。
【解决方案3】:

根据 cmets,主要问题不是实际执行,而是在您自己的代码中解析命令行。因此,我只是在这里给出我能想到的最基本的解决方案,并发表了大量评论。它不支持任何操作,例如用户对命令行的引用、转义或编辑,但它应该有助于理解一般概念——尝试以它为起点。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define PROMPT "YWIMC > "

/* read in a command */
static char **getCommand(void)
{
    /* static buffers, no need to allocate dynamically for a simple example */
    static char cmdline[1024]; /* command-line */
    static char *command[7];   /* parsed command */

    /* initialize empty command */
    memset(command, 0, sizeof(command));

    do
    {
        /* show prompt: */
        fputs(PROMPT, stdout);

        /* read commandline: */
        if (fgets(cmdline, 1024, stdin))
        {
            int i = 0;

            /* get first token (the command name) */
            if ((command[i++] = strtok(cmdline, " \r\n\t")))
            {
                /* get up to 5 additional tokens (the parameters) */
                while (i < 6 && (command[i++] = strtok(0, " \r\n\t")));
            }
        }
    } while (!(command[0])); /* until something was entered */

    return command;
}

int main(void)
{
    /* endless loop */
    while (1)
    {
        /* get next command */
        char **command = getCommand();

        /* and try to execute it */
        if (!strncmp(command[0], "Q", 1))
        {
            /* quit command */

            puts("k tnx bye.");
            exit(0);
        }
        else if (!strncmp(command[0], "R", 1))
        {
            /* run command */

            /* check there was something to run given: */
            if (!command[1])
            {
                fputs("Command `R': Nothing to run.\n", stderr);
            }
            else
            {
                /* YOUR COMMAND EXECUTION GOES HERE */
                /* (try to create another "static" function for it) */
            }
        }
        else
        {
            int i = 0;

            /* some debugging on unrecognized commands */
            fprintf(stderr, "Unrecognized command: `%s' (args: ",
                    command[i++]);

            while (command[i])
            {
                fprintf(stderr, "`%s' ", command[i++]);
            }
            fputs(")\n", stderr);
        }
    }
}

要理解的一个关键问题是为什么command 有 7 个条目。第一个是为您的命令保留的。最后一个应该始终是NULL(或0),以便您的代码可以检测到参数列表的结尾。留下 5 个可用参数。如果您的某个命令应该将其第一个参数作为外部命令运行并允许 4 个附加参数,则您需要这 5 个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 2012-11-12
    • 2013-03-28
    • 2014-07-23
    • 2017-02-28
    • 2011-10-31
    • 2016-06-27
    相关资源
    最近更新 更多