【问题标题】:cat vs echo or printf to pass data to the stdin of a programcat vs echo 或 printf 将数据传递到程序的标准输入
【发布时间】:2015-10-28 15:31:51
【问题描述】:

假设我们有一个名为myprog的程序:

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

void main(){
        char buffer[32];
        gets(buffer);
        system("/bin/sh");
}
  1. 它占用缓冲区的空间
  2. 它等待来自标准输入的数据并将其复制到缓冲区中,直到找到换行符
  3. 它将shell作为子进程执行,然后它将等待来自标准输入的命令

所以我们有 2 条指令将从标准输入中查找数据。

假设我们想将这个数据重定向到其他程序的标准输出到myprog的标准输入,我们以echocat为例。

$ cat > cmds
ls
$ { echo "My string" ; cat cmds ; } | ./myprog
file1 file2 cmds myprog 

到目前为止一切顺利:echo 在“我的字符串”的末尾自动添加一个换行符,这会导致 gets 停止读取,/bin/shcat 的输出中读取。

但让我们尝试其他解决方案:

  1. echo "My string" ls | ./myprog

  2. printf "My string\nls\n" | ./myprog

  3. {echo "My string" ; echo ls ; } | ./myprog

这些解决方案似乎都不起作用。 使用单个文件和单个调用 cat 都不起作用:

$ cat > file
 My string
 ls
$ cat file | ./myprog

为什么会这样?每种情况究竟会发生什么?

【问题讨论】:

  • 你是怎么编译的?如果不清楚,则该脚本不编译就无法运行。它会产生任何错误消息吗?
  • 我要猜测标准输入和计时上的缓冲。 gets 也很可怕。
  • 好吧,#1 应该失败;它只输出一行。
  • Re: 定时,注意cat是一个外部程序,cat在“我的字符串”输出和文件内容输出之间有一点延迟。在所有其他示例中,bash internals 用于输出两行。这意味着gets 可能不仅仅是读取第一行并将其缓冲以供下次调用gets,这意味着/bin/sh 没有获得您期望的数据。您可以通过将/bin/sh 替换为cat 来确认,以查看调用system 时标准输入中的实际内容。
  • @l0b0 你什么意思?我用一个简单的gcc myprog.c -o myprog@EtanReisner 编译程序我知道这很可怕,这只是一个例子。 @chepner您是对的,第一个显然是错误的,我的错误。在使用 system("cat") 并查看缓冲区的内容之后,您的时间安排也是正确的,我可以看到像 { echo "My string" ; sleep 1 ; echo ls ; } 这样的东西可以正常工作。谢谢。另一方面,{echo "My string" ; echo ls ; } 导致gets 仅将"My string" 存储到缓冲区中。 ls 是否仍然进入gets 的输入而不被复制?

标签: shell printf echo cat


【解决方案1】:

我认为您对接收来自stdin 的程序的方法有点困惑。如果我正确理解你的目标是让system("/bin/sh"); 等待"ls" 作为stdin 的输入,那么如果你成功了,你会看到错误:

/usr/bin/ls: /usr/bin/ls: cannot execute binary file

您已经在gets 上看到了 cmets - 永远不要使用它。如果您的老师建议您使用它,您可以告诉他由于其安全漏洞,它已从 C 标准库中删除。请改用fgets

也就是说,如果您希望从 stdin 获取输入,并且希望在 system 调用中使用该输入,那么您需要创建一个字符串,其中包含您希望 system 执行的命令 - - 包括所需的任何参数。简单地调用system("/bin/sh"); 是行不通的,因为systemsubshel​​l 中执行它的命令。

只需构建您希望system 执行的字符串。一个简单的例子可能是:

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

#define MAXC 32

int main (void) {

    char buffer[MAXC] = {0};
    char script[MAXC] = {0};

    /* read line 1 into buffer & print */
    fgets (buffer, MAXC, stdin);
    printf ("\n buffer: %s", buffer);

    /* read line 2 into buffer & print */
    fgets (buffer, MAXC, stdin);
    printf ("\n buffer: %s", buffer);

    /* copy "/bin/sh" to script */
    strncpy (script, "/bin/sh ", strlen ("/bin/sh ") + 1);

    /* concatenate script & buffer ( "/bin/sh + line 2" ) */
    strcat (script, buffer);

    /* execute script */
    system (script);

    return 0;
}

它基本上从stdin 获取line 1 并打印它,然后用line 2 覆盖它,其中包含/bin/sh 调用的命令(脚本名称)。您可以使用一个简单的测试脚本,例如:

#!/bin/sh

printf "\n %s executed : %s\n\n" "$0" "hello /bin/sh"

exit 0

现在您可以使用printf 将第 1 行和第 2 行都传递给您的程序,类似于:

$ printf "%s\n%s\n" "buffer - line 1" "myscript.sh"

它一次将一行传递给您的程序。运行您希望看到的代码:

$ printf "%s\n%s\n" "buffer - line 1" "myscript.sh" | ./bin/system_buff_stdin

 buffer: buffer - line 1

 buffer: myscript.sh

 myscript.sh executed : hello /bin/sh

如果您还有其他问题,请尽管提问。

【讨论】:

  • 感谢您的回答。因为这实际上是一个练习,我无法更改源代码......但我完全同意你所说的。另一方面,system 在子 shell 中执行它的命令……嗯,我的目标是将输入从外部程序传递给该子 shell,例如可以使用echo 然后cat 来完成。我的问题是:为什么它不适用于输出 2 行的任何命令组合?
  • 抱歉,我明白你在说什么。您还可以使用流程替换来有效地做同样的事情。 (例如 ./bin/system_gets &lt; &lt;(echo "my cmds"; cat cmds) )这就是文件 cmds 保存 ls 的位置,如您的示例所示。当您pipe{...} 或使用进程替换&lt; &lt;(stuff)(这是一个bashism)时,您正在将包含您的两个命令的块(或子shell 的输出)重定向到您的程序。 (getssystem)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-02
  • 2015-01-06
  • 2013-05-05
相关资源
最近更新 更多