【问题标题】:Advanced I/O redirection in a C program.(Simple Shell)C 程序中的高级 I/O 重定向。(简单外壳)
【发布时间】:2013-12-25 19:13:36
【问题描述】:

我正在设计一个简单的 shell,但我遇到了高级重定向问题。

我可以这样做:ls -al > a.txt

但我不能这样做:wc b.txt

我该怎么做?

这是我执行 i/o 重定向的地方:

char *inpu=NULL; //Inpu is a global variable.
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_TRUNC)
#define CREATE_FLAGS1 (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_FLAGS2 (O_RDONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define MAXCHARNUM 128 
#define MAXARGNUM 32 


char *argsexec[MAXARGNUM]; /*This stores my executable arguments like cd ls.*/

void ioredirection(int f){
   int k,i,m;
   int input=-1;
   int output=-1;
   int append=-1;   
   int fdin,fdout;    

        for(k=0; k<f; k++){
            if(strcmp(argsexec[k],"<")==0){
                input=k;  // argument place of "<"
                m=1;   
        argsexec[k]=NULL;              
           }
            else if(strcmp(argsexec[k],">")==0){
                output=k; // argument place of ">"
                m=2; 
        argsexec[k]=NULL;                   
           }
            else if(strcmp(argsexec[k],">>")==0){
                append=k; // argument place of ">>"
                m=3;
        argsexec[k]=NULL;
           }
        }

           if(m==1){
            int inp1;
    fdin=open(argsexec[input+1],O_RDONLY,CREATE_MODE);
    dup2(fdin,STDIN_FILENO);
    close(fdin);
    inp1=execlp(argsexec[0],argsexec[0],NULL);
           }    

           if(m==2){          
            fdout = open(argsexec[output+1], CREATE_FLAGS, CREATE_MODE);
            dup2 (fdout, STDOUT_FILENO);
            close(fdout);
            execvp(argsexec[0],argsexec);   
           }

           if(m==3){
        fdout = open(argsexec[append+1], CREATE_FLAGS1, CREATE_MODE) ;
            dup2 (fdout, STDOUT_FILENO);
        close(fdout);
        execvp(argsexec[0],argsexec);
           }        
}

b 是这样变化的:

inpu=strtok(str," ");
while(inpu!=NULL){
  argsexec[b]=inpu;
  b++;
  inpu = strtok(NULL, " ");
}   

我用子进程调用它。

if (pid==0){  
   ioredirection(b);

我希望清楚地理解,我的完整代码真的很长,我试着像这样剪掉它。任何建议将不胜感激。

【问题讨论】:

  • 你同时使用制表符还是空格?代码格式确实不一致,代码很难阅读。
  • 实际上今晚我有这个学校项目的截止日期。这就是为什么我无法正确格式化它的原因。抱歉。
  • JFYI 格式良好且结构良好的代码通常更容易支持和调试,即使在截止日期临近时也是如此。
  • 这对您当前的问题没有帮助,但是我使用的几乎每个主要 IDE 都有某种自动格式化程序。当您的项目完成后,为您的特定开发环境查找“美化”或“漂亮打印”或“格式代码”。然后,你真的不必担心它。只需运行该工具,它就会保持一致。 :-)

标签: c string shell unix redirect


【解决方案1】:

我通过格式化程序运行您的代码并得到(大大简化):

void ioredirection(int f) {
    for (k over all arguments) {
        set m based on argsexec[k]
    }
    test m against 1, 2, and 3
}

因此,如果有两个(或更多)“重定向”操作,循环(for k)将设置m 两次(或更多)次。然后,循环终止,您(最终)针对三个可能值中的每一个测试一次m

第一个问题现在应该很清楚了(但由于这是一个学校项目,我不会为你解决它:-))。

第二个问题只有在查看对m 执行的三个测试时才清楚。在这里只看一个就足够了:

    if (m == 1) {
        int inp1;
        fdin = open(argsexec[input + 1], O_RDONLY, CREATE_MODE);
        dup2(fdin, STDIN_FILENO);
        close(fdin);
        inp1 = execlp(argsexec[0], argsexec[0], NULL);
    }

(另外两个使用execvp而不是execlp1)如果exec*族函数调用成功,当前进程将立即被替换,这样exec*永远不会回来。因此,如果您需要重定向两个(或更多)*_FILENO 值,则必须推迟最终的 exec* 调用,直到所有其他重定向完成。


1其中一个功能不合适。好的,不要在这里跳舞:execvp 是正确的选择。 :-)


如果重定向后面没有文件名(见下文),则会出现第三个问题。

这段代码 sn-p 的最后两个潜在问题不太明显,需要全面了解。一个是否是“真正的错误”取决于你的 shell 的简单程度。

argsexec 数组最多可容纳 128 个char * 指针值。 argsexec[0] 中的条目应该是要运行的二进制文件的名称——毕竟它是提供给execvp 的——并且要使用execvpargsexec[0] 必须是顺序中的许多char *s 中的第一个:

  • argsexec[0] 变为 argv[0](在您调用的程序中),
  • argsexec[1] 变为 argv[1](程序的第一个参数),
  • argsexec[2] 变为 argv[2]
  • 等等……直到:
  • argsexec[i],对于最小整数 i,是 NULL: 这告诉 execv* 系列函数:“好的,你现在可以停止复制了。”

让我们稍微跳过明确的错误 #3,谈谈潜在的错误 #4:这里不清楚是否有任何 argsexec[i] 已设置为 NULLfiodirection 的参数是 last iargsexec[i] 必须 notNULL;从这段代码片段中,我们无法判断某些(大概是f+1th)argsexec[i] 是否是NULL。要使用execvp 函数,它需要是NULL

如果有一些 I/O 重定向,您会将一些 argsexec[i] 设置为 NULL。这将终止数组并使execvp 调用正常工作。如果不是……?

这会导致潜在的错误 #5:在“真正的”Unix shell 中,您可以将 I/O 重定向放置在命令的“中间”:

prog > stdout arg1 2> stderr arg2 < stdin arg3

这运行 prog 并带有三个参数(arg1arg2arg3)。事实上,您甚至可以将重定向放在首位:

<stdin >stdout 2>stderr prog arg1 arg2 arg3

在某些 shell 中,您可以多次“重定向”(如果您不想要一个命令,甚至不需要运行命令):

$ ls
$ > foo > bar > baz
$ ls
bar     baz     foo

(但其他 shell 禁止这样做:

$ rm *; exec csh -f
% > foo > bar > baz
Ambiguous output redirect.

并不是说 csh 是可以效仿的。 :-) )

如果——这是一个很大的“如果”——你想允许这个或类似的东西,你需要“执行并删除”每个出现的 I/O 重定向,将剩余的参数向下移动,以便 argsexec将提供给程序的各种char * 值保持“密集填充”。例如,如果len 是数组中有效的非NULL 条目的长度,那么argsexec[len] 就是NULL,那么您需要“删除”argsexec[j]argsexec[j + 1](其中分别包含类似“>”的重定向和文件名),然后:

for (i = j + 2; i <= len; i++) {
    argsexec[i - 2] = argsexec[i];
}

可以解决问题(循环运行到i &lt;= len,因此它也会复制终止的NULL)。

所以,最后,确定的错误 #3:如果重定向位于最后一个位置,argsexec[f - 1],会发生什么? for (k ...) 循环运行 k0f - 1 包括在内。如果k == f - 1argsexec[k]是重定向时,文件名必须在argsexec[f]中。

但我们刚刚注意到(上面)argsexec[f]需要NULL。也就是说,如果有人尝试:

ls >

那么argsexec[] 应该包含"ls""&gt;"NULL

这就是“真正的 shell”shcsh 在这种情况下所做的:

$ ls >
Syntax error: newline unexpected

% ls >
Missing name for redirect.

您将需要类似的东西:如果重定向后的文件名丢失,则拒绝尝试的方法。

【讨论】: