1

Basically, is there a simple way to get a list of all bash commands in the PATH environment variable in C++? My current solution is to run a command beforehand that lists all the commands into a .txt, which is then read into the C++ program. I want to be able to cut out this step, if possible.

ls ${PATH//:/ } > commands.txt
  • 2
    Bash commands do not exist on the filesystem. They are part of Bash. If you wish to find executables in locations specified by the PATH environment variable, then use standard techniques for listing files in a location and filter only files that are executable. Check [`std::filesystem::directory_iterator`](https://en.cppreference.com/w/cpp/filesystem/directory_iterator) for a modern solution, or look at [`opendir` _et al_](https://linux.die.net/man/3/opendir) from POSIX. – paddy Mar 11 '20 at 23:39
  • Not really sure if I got your question. `PATH` env. var. is used to specify directories where executable programs are located. If you want to look at which commands have you run on your bash sessions, then you are probably looking for (by default) `~/.bash_history`. – woz Mar 11 '20 at 23:44

1 Answers1

1

If you do NOT need to use stdin in your C++ program

This is the easy solution. Just pipe the output of the ls command to your C++ program. Then, in your C++ program, read the contents of the file from stdin like you would read from a normal file. Literally use stdin wherever you need to provide a file descriptor. So, your command would look something like

ls ${PATH//:/ } | ./a.out

The | denotes a pipe in bash. It takes stdout from the first program (here ls) and redirects it to stdin of the second program (here your C++ program).


If you do need to use stdin in your C++ program

This is going to be tricky. You essentially need to make your C++ program do everything itself. The first way to this that comes to mind is

  1. Read $PATH using getenv().
  2. Parse $PATH by replacing all occurrences of : with (a blank space). This is easy enough to do in a loop, but you could also use std::replace.
  3. Now that you have the directory paths from $PATH, you simply need the contents of each directory. This post will help you get the contents of a directory.

UPDATE: Another Approach

I've thought of another way to approach your problem that allows you to use IO redirection (ie. use the pipe), and also use stdin at the same time. The problem is that it is probably not portable.

The basic idea is that you read the output of ls from stdin (using the pipe operator in bash). Next, you essentially reset stdin using freopen. Something along the lines of

#include <stdio.h>

int main(void)
{
  char buf[BUFSIZ];

  puts("Reading from stdin...");
  while(fgets(buf, sizeof(buf), stdin))
    fputs(buf, stdout);

  freopen("/dev/tty", "rw", stdin);

  puts("Reading from stdin again...");
  while(fgets(buf, sizeof(buf), stdin))
    fputs(buf, stdout);

  return 0;
}

The above code is from here. It reads stdin, resets stdin, and reads from stdin again. I would suggest not using this approach for anything important, or for something that needs to work on several platforms. While it is more convenient since it allows you to use IO redirection while retaining the ability to use stdin, it is not portable.

Sagar
  • 1,617
  • 9
  • 17