10

How to know when there is input through the terminal pipe line on C++ 11?

I may call my program like this:

1. ./main solved.txt
2. cat unsolved.txt | ./main
3. cat unsolved.txt | ./main solved.txt

I am using this to know whether I need to read data from the pipe line or not on C POSIX Standard:

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <unistd.h>

int main( int argumentsCount, char* argumentsStringList[] )
{
    std::stringstream inputedPipeLineString;

    if( argumentsCount > 1 )
    {
        printf( "argumentsStringList[1]: %s", argumentsStringList[ 1 ] );
    }

    // If it is passed input through the terminal pipe line, get it.
    if( !isatty( fileno( stdin ) ) )
    {
        // Converts the std::fstream "std::cin" to std::stringstream which natively
        // supports conversion to string.
        inputedPipeLineString << std::cin.rdbuf();

        printf( "inputedPipeLineString: %s", inputedPipeLineString.str().c_str() );
    }
}

But now I want to use the C++ 11 Standard, and my loved fileno and isatty are out of it. So there is an alternative to them on the C++ 11?

Related threads:

  1. checking data availability before calling std::getline
  2. Why does in_avail() output zero even if the stream has some char?
  3. Error "'fdopen' was not declared" found with g++ 4 that compiled with g++3
  4. stdio.h not standard in C++?
  5. error: ‘fileno’ was not declared in this scope
  6. GoogleTest 1.6 with Cygwin 1.7 compile error: 'fileno' was not declared in this scope

The problem is that when compiling with the -std=C++11, the fileno and isatty are undefined on the stdio.h/cstdlib because they are POSIX stuff. So, one solution would be to use -std=GNU++11 instead of -std=C++11. But is it possible to write something else to compile using the -std=C++11?

Community
  • 1
  • 1
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
  • 2
    There is no portable way I am aware of. What I normally do is use the pipe if there is no command line parameter present. – Galik Oct 29 '16 at 21:20
  • 1
    POSIX doesn't disappear into thin air just because C++11 appears. The `fileno` and `isatty` functions were never a part of C++. – Dietrich Epp Oct 29 '16 at 21:20
  • What is the problem with -std=gnu++11 ? – cmourglia Oct 29 '16 at 21:49
  • 1
    That's could be tricky, but you can check if a `cin` will perform a blocking read with `cin.rdbuf()->in_avail()`. If it returns 0, it's likely that there's no input data pending to be read (a subsequent read wouldn't wait for user input) and thus no pipeline, unless the user could write something in microseconds, of course. A subsequent non-blocking read can be seen has an indirect proof of pipeline presence (or `<<<` input). – ABu Oct 30 '16 at 01:39
  • `std::cin.rdbuf()->in_avail()` is always returning 0. Seems a bug on GCC: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24206, but we almost got it working. – Evandro Coan Oct 30 '16 at 14:43
  • What @Galik said. – JJF Dec 15 '16 at 20:52
  • @addons_zz I got that working in the past but first I had to decouple the streams from `stdio` using `std::ios::sync_with_stdio(false);` But I could not find support for it in the standard so who knows if it will work from one version of the compiler to another? – Galik Dec 15 '16 at 20:57
  • When I got working on this, I will to find a way creating another thread to get the information, then if after a certain minimum time it does not respond, then I assume there is not input from the sdt::in and kill it. – Evandro Coan Dec 15 '16 at 22:33
  • `cin.rdbuf()->in_avail()` has nothing to do with it whatsoever. It is not a bug. It is simply not supposed to work this way. – n. m. could be an AI Jan 13 '22 at 07:41
  • Under POSIX, `fileno(stdin)` is the same as `STDIN_FILENO` which is defined as 0. No need to call this function. You only need `isatty`. – n. m. could be an AI Jan 13 '22 at 07:45

3 Answers3

9

C++ POSIX Standard

As far as I know, there is no such thing. There is a C POSIX library, which is part of POSIX standard.

So there is an alternative to them on the C++ 11?

There is no alternative in standard C++ (not before C++11, so far not after either).

You will need to depend on POSIX to get the functionality that you need.

Even in POSIX, it is the unistd.h which defines isatty. You've neglected to include it in your program.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

I'm not aware of a completely portable way to do this. As far as I know, standard C++ knows no information of where it's input comes from so you should just use isatty if you are working on a posix system.

gowrath
  • 3,136
  • 2
  • 17
  • 32
1

In C++17, you have the <filesystem> header which has a std::filesystem::status() function that returns a file_status object. You can access its type through the type() function, via the result for instance, and use a quick comparison to see if it's a type of your intended target. One really naïve approach to this would be something like:

auto result {std::filesystem::status(fd)};
if (result.type() == std::filesystem::file_type::character)
{
   // do some stuff in here 
}

where fd is the file descriptor to check. The above isn't full proof since there's no additional checks, but if it's a character type, it can almost certainly be equated to a terminal. If you have a compiler that supports C++17, <filesystem> can be really handy https://en.cppreference.com/w/cpp/filesystem/file_type

NOTE: I am just starting out with working with <filesystem> so there may be some nuances I'm missing here and welcome any updates to the answer