3

When I start a process under Linux, I can pass multiple input and output redirections. For example, if I'd like to have 14 output files, I'll do that:

command >f1 3>f2 4>f3 5>f4 6>f5 7>f6 8>f7 9>f8 10>f9 11>f10 12>f11 13>f12 14>f13 15>f14

Can my command know exactly how many such files were passed on the command line?

I know I can always add a command line option such as --count 14. I hope we don't have to.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • Out of curiosity, why not just pass them in as regular command line arguments? – Lou Franco Jan 18 '20 at 01:54
  • in c I think these get set to file descriptors in the file descriptor table. https://stackoverflow.com/questions/2423628/whats-the-difference-between-a-file-descriptor-and-file-pointer – Lou Franco Jan 18 '20 at 01:55
  • 1
    @LouFranco That won't work. File descriptors opened from the command line are not referenced in `stdio`. – S.S. Anne Jan 18 '20 at 02:03

4 Answers4

3

There is no way to distinguish between redirections established on the command line and redirections which were already present in the execution environment.

For example, when utility util runs in the script

exec 4<file4
# ...
util 3<file3

it will see both fd 3 and fd 4 open for reading; it can't tell that only one was opened on the command line. If you care about that, which you probably should because there can be a number of these.

That aside, you can certainly figure out which fds are currently open, for example by cycling through all of them or examining the /proc/self/fd pseudo-directory. See this question for some example solutions to finding all the open file descriptors under Linux.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Right. Actually it won't be opened by the console as a redirection since these will be pipes, but I thought that to ask the question, it would be clearer to describe it that way. The `/proc/self/fd` is what I was looking for. I suppose that I have to be careful since it uses an fd to read a directory... – Alexis Wilke Jan 18 '20 at 02:45
  • 1
    @Alexis: Indeed. And there is no guarantee that there aren't other open files left over. I will add that. – rici Jan 18 '20 at 02:47
2

Can my command know exactly how many such files were passed on the command line?

Yes, if you order them sequentially. You can open() a dummy file (like /dev/null) and then subtract 3 from the fd returned by open to get the number of extra open files (other than stdin, stdout, and stderr) in the process. You can loop through them by starting from 3 and looping until you reach the dummy fd.

Example:

int dummy_fd = open("/dev/null", O_RDONLY);
printf("number of files open: %d\n", dummy_fd - 3);
for(int fd_ctr = 3; fd_ctr < fd; fd_ctr++)
    /* ... */;
close(dummy_fd);

I know I can always add a command line option such as --count 14.

Good idea. While you're at it, how about you just pass the file names as command-line arguments, too?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
1

Your process never sees any of those redirections. What happens is that the shell will connect up all the files to the equivalent file handles (opening them as necessary) and then run your code.

All your code can do is use the file handles that have been opened for you. Any tricks you use to find the maximum open handle inside your code will not work if you have gaps, like:

command >f1 5>f5

I suspect you'd be better off just using command line options to do this, something like:

command --output=file1 --output=file2 --output==file3

Then you have full control over how you handle those arguments in your code. For example, you can create a map of the files as you open each one.

It will also allow you to use different handles for other purposes, such as data files you need to access (I imagine you'd be rather miffed if you overwrote them with output data unrelated to the expected content simply because they were a file handle that was open).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

On linux, you can just inspect the files in /proc/self/fd/. Each filename will correspond to an open fd.

Such example program:

#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

int main() {
        printf("Open fds:");
        DIR *dir = opendir("/proc/self/fd/");
        assert(dir != NULL);
        struct dirent *d;
        while (d = readdir(dir), d != NULL) {
                if (d->d_type != DT_LNK) continue;
                errno = 0;
                const int i = atoi(d->d_name);
                assert(errno == 0);
                printf(" %d", i);
        }
        printf("\n");
}

When executes does this:

$ ./a.out 5>a 6>b 100>c
Open fds: 0 1 2 3 5 6 100
KamilCuk
  • 120,984
  • 8
  • 59
  • 111