【问题标题】:How to write a Bash script for a C program that takes input multiple times?如何为需要多次输入的 C 程序编写 Bash 脚本?
【发布时间】:2019-02-13 08:57:41
【问题描述】:

所以我有一个 C 程序,它接受一个命令行参数,并用它做一些事情。片刻之后,它会重新提示用户输入一些标准输入。

例如,典型用法可能如下所示:

./prac1 4
4
Enter something else: _hello_
hello

我的问题是,我如何编写一个能够重新输入更多输入(通过标准输入)的 bash 脚本,以便在程序输出后响应程序的重新提示 输入其他内容: ?

到目前为止我有这个:

cc=gcc
EXEC=prac1
SRC=prac1.c
input="4"

printValue=`./$EXEC $input`
if [ "$?" == '1' ];
then
    echo "Error"
    exit
fi
echo "printValue = $printValue"
上面的

prac1是指以下代码:

int main(int argc, char** argv) {
    char* input[70];
    printf("%c\n", argv[1][0];
    fflush(stdin);
    printf("Enter something else: ");
    fscanf(stdin, "%s", input);
    printf("%s\n", input);
}

bash 脚本使用命令行参数“4”调用可执行文件prac1。当我运行此脚本时,在它使用 printValue=`./$EXEC $input` 执行后,我必须在“输入其他内容:”提示之后手动输入一些内容。这是可以预料的。

但是,我试图弄清楚如何让 bash 自动输入某些内容。我的意思是这样我就不必自己手动输入了。

非常感谢您。

【问题讨论】:

  • 您当前的脚本有语法错误;将其粘贴到shellcheck.net 以进行诊断。
  • 也许你想看看expect(1)
  • @BasileStarynkevitch 我并不是要听起来傲慢,但您可以在提供的代码顶部添加“include ”,它是可编译/可运行的。
  • 刷新输入流是未定义的行为。如果它实际上丢弃了输入,它将破坏您的意图。当然,如果程序要求更多输入,它应该是接受它的程序 - 重新运行程序没有什么意义。最好在 bash 脚本中发出该提示。

标签: c bash unix


【解决方案1】:

当您开始运行您的程序时,您可以一次将所有输入字符串传递到标准输入(例如,作为here document);操作系统缓冲仅在接收者真正想要读取它们时才提供它们。

printValue=$(./prac1 "$input"<<\____
first answer
Here's my answer to the second prompt.
If the script reads multiple lines of input
until EOF at any point, that of course will
consume all of the remaining here document.
____
)

注意我们如何使用$(...) 语法而不是过时的`...` 反引号语法,并避免将一个无论如何只使用一次的值放入变量中(即。可执行文件的名称)。

顺便说一句,除非退出代码 1 是您唯一要捕获的代码,否则只需在上面第一行的末尾添加 || exit(假设您的程序在退出并出现错误时会打印诊断信息;或者重构为一个 shell 函数 die,它会打印一个警报并然后以当前退出代码退出)。

在您的具体示例中,由于您的程序只需要一行输入,我们可以将其作为字符串传递:

printValue=$(./prac1 "$input" <<<"something else")

或随身携带

printValue=$(echo "something else" | ./prac1 "$input")

&lt;&lt;&lt;"here string" 语法仅适用于 Bash。)

如果您的程序在使用行缓冲输入时表现不佳,也许可以求助于expect 进行自动化处理;但要意识到这也使用户在未来更难围绕您的工具编写脚本。也许你想让它变得更容易,也许通过允许用户通过传递命令行选项或配置文件来避免交互式提示(无论如何都是邪恶的)。

【讨论】:

  • 这最终解决了我的问题。谢谢你回答我的问题:)
  • “这里的文档”是如何解决您的问题的?他们不允许脚本和prac1 同时运行(正如您问题的初始形式所建议的那样)。在什么意义上这里的文档使您的prac1能够接受输入多次次?
【解决方案2】:

你真正想要什么不清楚,你没有解释你的脚本的目的(所以你的问题听起来像一些XY problem)以及你的prac1程序正在做什么和应该做什么。

所以阅读Bash reference manual。也许您想使用read bash builtin。您还可以决定通过positional parametersgetopts builtin 或从here-documents 重定向为您的脚本提供一些数据。

也许bash 可能不是适合您的工具。可能您需要一些build automation 工具(例如GNU makeninja)。如果您的实际目的是在某些文件已被编辑时重建某些代码,那么这是更好的方法。

查看几个小的 free software 项目(例如在 github 上,或从您的 Linux 发行版中)并研究它们的源代码以获得灵感。

也许将更多功能移至您的prac1.c 是实现您(未说明的)目标的最简单方法。 prac1 可能接受几个 程序参数。阅读Glibc 文档的Parsing Program Arguments 章节。

如果您想编写一个与正在运行的 进程交互的脚本,这很复杂。你可以考虑coprocesses

为了完整起见,我会提到expect,但我建议不要在您的情况下使用它(改进prac1 会更简单)。请注意,expect 在终端级别工作,而 Unix 的 terminal emulatorsttys 和 ptys 很难理解(阅读 termios(3)pty(7)、关于 line disciplineThe Tty Demystified )。如果您真的想编写终端 IO 代码,请考虑使用 ncursesreadline 之类的库。

您还可以通过阅读像 ALP(或更新的东西)这样的书来了解更多关于 Linux 编程的信息,并参考可用的 syscalls(2)。特别注意使用例如多路复用输入和输出的能力poll(2).

如何编写能够重新输入更多输入的 bash 脚本

您应该避免这种情况。如果你真的需要一个脚本来驱动其他程序并且让脚本和程序一起运行,你应该考虑一些其他的方法(包括用其他一些脚本语言编写脚本,例如 Python、AWK,...)。

shell 脚本的传统用法是驱动劣质程序,而不是与它们交互地共存(即使这可以通过协进程等技巧实现;在 30 多年的编程中,我从来没有用过)。

当一个特性难以用 shell 脚本实现时,这表明你的系统应该以不同的方式组织。 shell不是所有问题的答案(有时使用其他工具更简单,与任务更相关,甚至开发驱动其他进程的 C 程序)。根据经验,我避免使用过于复杂的 shell 脚本。我可能有一些长的(但概念上很简单,因为是顺序的)shell 脚本。我也有一些程序(例如在awkm4、C 或 Python 中)生成(长)shell 脚本(而autoconf 长期以来一直这样做)。但我避免手动编写然后自己维护复杂的 shell 脚本(并将它们视为设计错误的症状)。

为了说明我的上述观点:makeninja(以及 scons 和其他构建自动化工具)都在驱动其他程序。原则上,理论上你可以通过编写一个可怕的 shell 脚本来实现他们的目标。但事实上,没有人这样做(并且有充分的理由:shell 在实践中不是通用工具)。所以教训是:不要试图用 bash 做一些它不打算做的事情容易(在我看来,这意味着:当你的 shell 脚本变得太复杂时,把它扔掉并使用或编写其他东西)。

另请阅读Unix philosophy。您的脚本和 C 程序的组合与 Unix 哲学相悖。也许您应该有一个程序(可能用 C 编写,但比您向我们展示的 prac1.c 复杂得多),或者您可能想要一个围绕运行 prac1 的某个命令的 简单 shell 循环。

Shell 是一种方便的工具(在简单 脚本中使用时),但它是一种糟糕的编程语言(并且很难静态分析或推理;例如查看@987654349 @ 研究项目,请参阅 Yann Regis-Gianas FOSDEM2018 谈话 Parsing Posix [S]hell)。 对于足够复杂的任务,你不不想使用unix shells,你真的想使用更好的东西,你应该扔掉你的shell脚本当它变得不可读或不可维护时

PS。您的问题隐含地假设bash 适用于大多数任务。但这种情况并非如此;在 Linux 上,您还有许多其他脚本语言,例如PythonawkOcamlPerlIoGuilemakeninjaLuaRubyPHPOcsigenHaxe, m4, ...),其中许多比 @​​987654390@ 更合适,至少在一些领域。您最好选择适合您目标的工具。

【讨论】:

  • 不,您仍然需要改进您的问题以使其成为minimal reproducible example。您的问题应该包括简化形式的prac1.c,但它仍然没有
  • 即使有了你的新prac1.c,我仍然不明白你的总体目标。你为什么以这种方式组织你的项目?为什么你不能(例如)避免需要任何 shell 脚本并在prac1.c 中添加更多功能。似乎prac1 和您的脚本都需要实现您的(仍未说明的)目标,我不明白您为什么将项目分为两部分(prac1.c 和您的 shell 脚本)
猜你喜欢
  • 2020-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
相关资源
最近更新 更多