LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Redirecting non-standard file descriptors (https://www.linuxquestions.org/questions/programming-9/redirecting-non-standard-file-descriptors-542790/)

gd2shoe 04-02-2007 06:31 PM

Redirecting non-standard file descriptors
 
On a normal executable, the system will prepare file descriptors 0, 1, and 2 for stdin, stdout and stderr. This allows for shell commands such as:
Code:

myBin < myInputFile > myOutputFile 2> myErrorLogFile
I have several questions regarding non-standard file descriptors. For example, is this valid (I believe I have heard of programs doing this):
Code:

myBin 3< WeirdInputFile
(Does not produce errors in the shell)

How would you access such a thing in C++ (or C for that matter)? How can you query the OS to see if a given file descriptor (here 3) is open already when execution starts? I would prefer to work with <iostream> classes, but am willing to deal with C functions if I need to.

Background: I am working on a project for school. Besides being curious about this topic, I think this would simplify my testing code. The instructor is emphasizing drivers and stubs, and stubs would be much easier if each could read from it's own stream. Something like:
Code:

myBin < test42.in > test42.myout 2>test42.myerr 3< test42.firststub 4< test42.secondsstub 5< test42.thirdstub
diff test42.out test1.myout
diff test42.err test1.myerr

(And no, I think I will be using one at a time in addition to stdin for almost every possible case, but may want more once or twice.)

Any clues would be appreciated.
(And before you say it, yes I could use normal file IO but that wouldn't satisfy my curiosity. And named pipes are cool too.)

wjevans_7d1@yahoo.co 04-02-2007 08:05 PM

If there are any C++ experts out there, please jump right in, because when it comes to C++, I am but dust and ashes. But maybe I can help anyway.

This post is divided into two parts: the C part (for whose accuracy I can vouch), and the C++ part.

=== Part 1: the C part.

In C, you can read using low-level system calls open(), close(), read(), and so on. Or you can use high-level library calls fopen(), fclose(), fgets(), and so on. (Don't ever, ever use gets().)

The following program illustrates doing what you want for unit 3:

Code:

#define UNIT_NUMBER 3

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

int main(int    argc,
        char **argv
        )
{
  int  read_result;

  char buffer[48];

  FILE *phyle;

  if(argc<2)
  {
    read_result=read(UNIT_NUMBER,buffer,sizeof(buffer)-1);

    if(read_result==-1)
    {
      fprintf(stderr,"for read(), unit %d does not seem to be assigned\n",UNIT_NUMBER);

      exit(1);
    }

    buffer[read_result]=0;

    printf("The first %d bytes are these:\n%s\n",
          sizeof(buffer)-1,
          buffer
          );

    return 0;
  }
  else
  {
    phyle=fdopen(UNIT_NUMBER,"r");

    if(phyle==NULL)
    {
      fprintf(stderr,"for fdopen(), unit %d does not seem to be assigned\n",UNIT_NUMBER);

      exit(1);
    }

    if(fgets(buffer,
            sizeof(buffer),
            phyle
            )
      ==NULL
      )
    {
      fprintf(stderr,"fgets() failed\n");

      exit(1);
    }

    printf("the first line is this:\n%s",
          buffer
          );

    return 0;
  }

} /* main() */

Running that code would work like this:

Code:

evans:~$ cc tuesday.c -o tuesday
evans:~$ tuesday
for read(), unit 3 does not seem to be assigned
evans:~$ tuesday x
for fdopen(), unit 3 does not seem to be assigned
evans:~$ tuesday 3< tuesday.c
The first 47 bytes are these:
#define UNIT_NUMBER 3

#include <stdio.h>
#incl
evans:~$ tuesday x 3< tuesday.c
the first line is this:
#define UNIT_NUMBER 3
evans:~$

=== Part 2: the C++ part.

For C++, I googled:

Code:

C++ "file descriptor"
and got oodles of responses. In the first few, this seemed most promising:

Code:

http://wwwwbs.cs.tu-berlin.de/user-taipan/kraxel/gnuinfo/iostream/Files.html
Hope this helps.

gd2shoe 04-02-2007 08:18 PM

It does help. Thank you for taking the time to respond. I'm still looking for the C++ way to do it. Now at least I know I have a way that I can imitate.

Thanks again.

wjevans_7d1@yahoo.co 04-02-2007 08:24 PM

I have since edited my first response to include a possible C++ approach. We missed by minutes. Sigh.

gd2shoe 04-07-2007 03:59 AM

Quote:

The GNU C++ Iostream Library - Files
...
Constructor ifstream::ifstream (int fd)
Make an ifstream for reading from a file that was already open, using file descriptor fd. (This constructor is compatible with other versions of iostreams for posix systems, but is not part of the ansi working paper.)
...
Constructor ofstream:: ofstream (int fd)
Make an ofstream for writing to a file that was already open, using file descriptor fd.
I don't know why I didn't try this earlier. Note that it is not part of the ANSI standard. That would explain why these didn't show up on the other reference pages that I have been using.

Maybe it's too late at night, and I'm not thinking clearly, but I can't get it to compile. Here's my code:

Code:

#include <iostream>

using namespace std;

int main()
{
        istream thirdin(3);

        string buffer;

        while (thirdin >> buffer)
        {
                cout << buffer << endl;
        }
}

And my compile error:
Code:

$ g++ fdStream.cpp
fdStream.cpp: In function ‘int main()’:
fdStream.cpp:7: error: invalid conversion from ‘int’ to ‘std::basic_streambuf<char, std::char_traits<char> >*’
fdStream.cpp:7: error:  initializing argument 1 of ‘std::basic_istream<_CharT, _Traits>::basic_istream(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]’

I'm hoping that I look at this again tomorrow and laugh at a simple mistake. As it stands, it looks like that constructor does not exist!

For reference

Code:

$ g++ -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --with-tune=i686 --enable-checking=release i486-linux-gnu
Thread model: posix
gcc version 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)


wjevans_7d1@yahoo.co 04-08-2007 08:05 AM

I looked at more of the google results. I don't have the time to learn more about C++ at this point (I wish I did), but does this link help?

Code:

http://docs.sun.com/source/819-3704/bas_4928.htm

gnashley 04-08-2007 09:58 AM

Since you are using the latest compiler you will have less luck with backward compatibility when using non-standard code.

kaz2100 04-08-2007 02:27 PM

Hya,

May be a little bit off topic. However,
Code:

$thisIsMyprogram 3< thisFileIsWhatIWantToFeedThrough3
$

works with bash, but not tcsh (csh....) At least, with my Penguin, same Penguin, only shell is different, exact same binary.

Happy Penguins!

ta0kira 04-08-2007 08:22 PM

Quote:

Originally Posted by wjevans_7d1@yahoo.co
If there are any C++ experts out there, please jump right in, because when it comes to C++, I am but dust and ashes. But maybe I can help anyway.

Wow, that is one of the most useful things I've ever encountered. Do you happen to know of it's safe to dup2 to an arbitrary int greater than 2 (provided no other files are open)? That would save me a huge hassle when vfork'ing with alternate file descriptors.

Here is what I am thinking:

Code:

//old

char *command[] = { "program", "program", NULL };

int output_temp = dup(STDOUT_FILENO);
int input_temp  = dup(STDIN_FILENO);

int out_pipes[2];
int in_pipes[2];

pipe(out_pipes);
pipe(in_pipes);

dup2(out_pipes[1], STDOUT_FILENO); //normally need some sort of I/O control starting here
dup2(in_pipes[0], STDIN_FILENO);

if (!vfork()) execv(command[0], command);

dup2(output_temp, STDOUT_FILENO);
dup2(input_temp, STDIN_FILENO);

Code:

//new

char *command[] = { "program", "program", NULL };

int out_pipes[2];
int in_pipes[2];

pipe(out_pipes);
pipe(in_pipes);

dup2(out_pipes[1], 4);
dup2(in_pipes[0], 3);

if (!vfork()) execv(command[0], command); //then command dup2's 3 -> STDIN_FILENO, etc.

Sorry, I haven't tested it since I am Windows'ing it up right now. I will later. Thanks.
ta0kira

wjevans_7d1@yahoo.co 04-09-2007 05:48 AM

ta0kira, let's discuss that after we've solved gd2shoe's problem. Either that, or post it in a different thread. It's amazing how easy it is for well-meaning people (including me) to hijack a thread without meaning to.

gd2shoe, how's it going? Found an answer yet?


All times are GMT -5. The time now is 05:53 PM.