如何使用argv和输入文件重定向同时读取一个输入文件

3

我的程序需要接受以下三种类型的输入命令:

./Myprogram input.txt
./Myprogram < input.txt
./Myprogram

我在考虑使用argc来检查参数数量,以解决前两种情况(因为重定向不算作参数)。但是我卡在了最后一种情况,它只是等待用户输入。

我想知道是否有办法判断shell命令中是否存在重定向?

对于更复杂的场景,例如重定向和argv形式混合的情况(请参见下文)。有没有办法做到这一点,或者这只是一个处理用户命令的不好设计?

./Myprogram input1.txt input2.txt input3.txt
./Myprogram input1.txt < input2.txt input3.txt
./Myprogram

任何帮助都将不胜感激!
Z.Zen

1
你对第三种情况想做什么?如果你只是从标准输入读取,第二种和第三种情况可以被视为相同。另一方面,如果你使用例如curses或者readline,那么第三种情况可能会有所不同。 - Matthew Flaschen
请查看Herb Sutter的《更多异常情况下的C++》。如果我没记错的话,它会涉及到这个问题。 - Saurabh Manchanda
5个回答

6

重定向永远不会被视为程序的参数。所以在:

./Myprogram input.txt
./Myprogram < input.txt
./Myprogram

第二个和第三个表格是相同的。至于你提到的第二组可能性:
./Myprogram input1.txt input2.txt input3.txt
./Myprogram input1.txt < input2.txt input3.txt
./Myprogram

第二行代码的含义是相等的:
./Myprogram input1.txt input3.txt < input2.txt

并且它也与以下内容无法区分:

./Myprogram input1.txt input3.txt

唯一的区别在于标准输入实际来自哪里。

一些程序处理从标准输入和通过命令行指定的文件混合输入的典型方法是接受"-"作为特殊文件名,表示"在参数列表中的此位置使用标准输入作为输入文件"。如果参数列表为空,则许多此类程序将默认处理一个单例列表的"-"。


谢谢!看来我忘记了第二和第三种情况对程序来说是一样的。既然我不需要知道另一端是否有人,那么这两种情况的处理代码应该是相同的。我想在复杂的场景中尽量避免第二种情况,因为它不是用户通常期望的,而且对于我的程序目的来说是不必要的。 - Z.Zen

4
基本算法如下:

if (there are no arguments left after parsing options)
    call_function(stdin);
else
{
    foreach remaining argument
    {
        FILE *fp;
        if (strcmp(argument, "-") == 0)
            call_function(stdin);
        else if ((fp = fopen(argument, "r")) == 0)
            ...error handling...
        else
        {
            call_function(fp);
            fclose(fp);
        }
    }
}

您可以将文件名传递给'call_function()',有时我会指定输出文件流来编写代码。该函数('call_function()')处理一个文件-读取到文件的末尾。它不会关闭文件;它已经得到了打开的文件,不应该关闭它。
第一个“if”处理I/O重定向的情况,当然。
很多年前,我写了一个函数来处理这个循环。每当我需要按照这种“UNIX过滤器”习惯用法编写命令时,它都会简化我的生活-这种情况相当常见。还有一套标准化的错误报告包,大大简化了生活。将其作为函数也使我能够使用它的变体,例如在覆盖文件之前创建文件备份的变体,或者在函数成功完成后安全地覆盖文件的变体。

谢谢告诉我这个经过实战检验的过滤算法! - Z.Zen
1
特别处理 '-' 参数作为 stdin 可能会很有用。 - jfs
1
@J.F.Sebastien:哦,是的;好主意 - 这个在函数中有点深藏不露(我上次修改它是2年半前)。if (strcmp(argument, "-") == 0) call_function(stdin); else { FILE *fp = ...as above...} - Jonathan Leffler

1

@R..在通常情况下是正确的。

如果您想在第3种情况下具有交互行为,但不是第2种情况,除了让终端按行缓冲用户输入之外,您可以使用{{link1:isatty}}(特别是isatty(0))来确定是否有人在另一端。

这不是标准C,但终端或shell的概念也不是!


0
我在想是否有一种方法可以告诉 shell 命令是否存在重定向?
没有。从您的程序的角度来看,这两种情况没有区别:
./Myprogram < input.txt
./Myprogram

在这两种情况下,程序不会接受任何命令行参数,并且将从标准输入获取输入。
在第一种情况下,是shell将文件input.txt的内容连接到您程序的stdin上,而您的程序对此一无所知。

1
虽然这个答案是正确的,但 OP 可能真正想知道的是如何检测 stdin 是终端还是普通文件。 - R.. GitHub STOP HELPING ICE

0

通过在stdin上使用select(),可以确定是否有数据可读。这不会告诉您是否存在重定向(当文件为空或由于某种原因用户在程序测试之前将某些内容放在stdin中时,您将无法判断)。它是否适用于您的情况取决于您在边界情况下想要做什么。

或者,您可以在stdin上使用isatty()来查找它是否为tty。这是大多数程序用来确定它们是否交互式的方法,对于您的情况可能更好。

现在,您可能会注意到,在第三种情况下阻塞等待用户输入是所有标准工具都会执行的操作,也可能是大多数用户期望您的程序执行的操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接