Heredoc 与 tmpfile

Posted

技术标签:

【中文标题】Heredoc 与 tmpfile【英文标题】:Heredoc with tmpfile 【发布时间】:2022-01-11 06:34:24 【问题描述】:

我正在编写我的 minishell 版本并尝试在 C 中实现 heredoc (<<)。我决定使用 tmpfile - 首先我将数据从 stdin 写入 tmpfile 直到到达分隔符,然后我更改程序的标准输入使用dup2 到tmpfile 的fd,然后尝试使用execve 执行cat 命令。

我试图简化程序并包含以下所有相关功能:

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

    t_shell     shell;
    ft_parse_envs_to_lst(&envs, env); // transform env to linked list
    shell.tmpfile = "path to tmpfile";
    while (1)
    
        char *buf = readline("bash: ");
        add_history(buf);
        shell.std_in = dup(0);
        shell.std_out = dup(1);
        shell.f_in = dup(0);
        shell.f_out = dup(1);
        /* Token is represented by linked list. "cat << eof" translates into "cat" -> ">>" -> "eof",
        with token pointing to "cat" */
        t_token *token = parse_buffer(buf); // parse_buffer will return pointer to the first token
        ft_execute_token(shell, token, env);
        free(buf);
    


void    ft_execute_token(t_shell *shell, t_token *token, t_envs_lst *env)

    process_next_cmd(shell, token, env);
    close(shell->f_in);
    close(shell->f_out);
    dup2(shell->std_in, STDIN_FILENO);
    dup2(shell->std_out, STDOUT_FILENO);
    close(shell->std_in);
    close(shell->std_out);


void    process_next_cmd(t_shell *shell, t_token *token, t_envs_lst *env)

    t_token *prev = ft_get_prev_token(token); // get prev separator (for example, <<) or NULL
    t_token *next = ft_get_next_token(token); // get next separator (for example, <<) or NULL
    if (prev && (prev->type == DOBINP)) // "<<" will be marked as DOBINP
        ft_handle_dobinp(shell, token);
    if (next)
        process_next_cmd(shell, next->next, env); // recursively go to the next command
    if (!prev) // won't run any command on the part after "<<"" but will run "cat" 
    
        ft_execute_cmd(token, env); // just execve on child process (created with fork), whilst parent is waiting for child
        if (next && next->type == DOBINP) // delete tmpfile
        
            char **argv = malloc(sizeof(char *) * 3);
            argv[0] = "/bin/rm";
            argv[1] = shell->tmpfile;
            argv[2] = NULL;
            pid_t pid = fork();
            if (pid == 0)
                execve("/bin/rm", argv, NULL);
        
    


void    handle_dobinp(t_shell *shell, t_token *token)

    int     rd;
    int     fd;
    int     buf_size;
    char    *buf;

    fd = open(shell->tmpfile, O_TRUNC | O_CREAT | O_WRONLY, 0777);
    buf_size = strlen(token->str);
    buf = malloc(buf_size + 1);
    printf("program: Start\n");
    rd = read(STDIN_FILENO, buf, buf_size);
    while (rd > 0)
    
        buf[rd] = '\0';
        printf("program: Looping (read %s)", buf);
        if (strncmp(buf, token->str, buf_size + 1) == 0)
            break ;
        write(fd, buf, rd);
        rd = read(STDIN_FILENO, buf, buf_size);
    
    free(buf);
    close(fd);
    shell->f_in = open(shell->tmpfile, O_RDONLY, 0777);
    dup2(shell->f_in, STDIN_FILENO);
    close(shell->f_in);


我想执行cat &lt;&lt; eof 命令。一切正常,但我面临handle_dobinp 函数中重复输出(测试期间)的问题。在main 中的while 循环中还发生了另一次迭代,输入为空(即程序执行空命令)。

只有一个进程在运行,所以我不确定这种行为的原因是什么?

更新:我根据Edwin Buck评论更新了程序的输出。

bash$ cat << eof
program: Start
foo
program: Looping (read foo
)
bar
program: Looping (read bar)
program: Looping (read 
)
eof
program: Looping (read eof)
foo
bar
bash$ 
bash$ 

【问题讨论】:

不要重复自己,做while((rd = read(STDIN_FILENO, buf, buf_size)) &gt; 0)while(rd = read(STDIN_FILENO, buf, buf_size), rd &gt; 0) @KamilCuk 同意,但我不能根据学校的编码指南在控制结构中使用分配 【参考方案1】:

改进您的日志记录。我想你的输出是正确的,但看起来像

bash$ cat << eof
program: Start
foo
program: Looping (read "foo")
program: Looping (read "\n")
bar
program: Looping (read "bar")
program: Looping (read "\n")
eof
program: Looping (read "eof")
program: foo
program: bar
bash$ 
bash$ 

【讨论】:

谢谢,你是对的。您能否提出任何关于如何避免最终执行空命令的想法? 我的猜测是您的“空命令”是eof 字符串末尾的最后一个“\n”。它的执行方式与您键入 的方式相同。相反,我会确保在您阅读时,在开始命令之前阅读(并包括)换行符 '\n'。这样你只会得到一个输入“eof\n”而不是两个“eof”,“\n” 我编写了自定义 get_next_line (从标准输入向上读取并包括“\n”)并且它有效。感谢您的帮助! @alexnik42 很高兴听到这个消息。尽情享受你的新外壳吧!

以上是关于Heredoc 与 tmpfile的主要内容,如果未能解决你的问题,请参考以下文章

heredoc

带电力线的 Heredoc

php中heredoc的使用方法

什么是PHP中的heredoc和nowdoc

php Heredoc 结构的字符串示例

在 Bash vs ZSH 中结合 heredoc 和输入重定向