1

I have the following program (let's call it program.cc):

int main() {
  int x, y, sum;
  std::cout << "A program which adds two integers\n";
  std::cout << "Enter 1st integer: ";
  std::cin >> x;
  std::cout << "Enter 2nd integer: ";
  std::cin >> y;
  sum = x + y;
  std::cout << "Sum is " << sum << std::endl;
  return 0;
}

I'm currently writing a bash script that would take a file and push it through the command line as arguments being passed to this cpp program. As you can see, the above program asks for two inputs.

I know how you can pass the contents of a file for use as input on stdin. But how would you do this if you want to pass the contents of a file as arguments through the command line?

To elaborate, let's say file.txt is the file i'm interested in, which simply contains two integers on separate lines.

EDIT: I tried the xargs command:

       xargs program.cc < file.txt > outputresult.txt

But this ends up printing what I have in cout, e.g.:

     A program which adds two integers
     Enter 1st integer: Enter 2nd integer: Sum is 2

I only want what is cout'ed AFTER all my arguments have been passed. How do I do this?

12
  • Or askubuntu.com/q/497159/158442
    – muru
    Sep 29, 2015 at 23:00
  • @muru I tried the xargs approach in the links you sent. The file that i save the output of the program to is saving what is being cout'ed in the cpp program. How can I stop that?
    – E 4 6
    Sep 29, 2015 at 23:27
  • @E46 What is it you want in the "outputresult.txt"? Sep 29, 2015 at 23:47
  • @Zzzach..."Sum is x" where x is the sum of the two args
    – E 4 6
    Sep 30, 2015 at 0:05
  • you need to check if the input is a tty or not. Use isatty like in stackoverflow.com/a/5157076/592540 Sep 30, 2015 at 11:28

3 Answers 3

2

The way you have written it, you need to pass the contents of the file to your program, not the file itself (and there's no need for xargs, by the way). One way to do that is using input redirection:

a.out < file

For example:

$ cat file
4 
7
$ ./a.out < file
A program which adds two integers
Enter 1st integer: Enter 2nd integer: Sum is 11

Another way to do the same thing is to print the file's contents and pass the to your program with a pipe:

$ cat file | ./a.out 
A program which adds two integers
Enter 1st integer: Enter 2nd integer: Sum is 11

A much cleaner approach, of course, would be to write your program so that it can take arguments instead of reading standard input:

./a.out 4 7

Or, write it so that it can open files internally and parse them for its input data:

./a.out file

Anyway, in order to get only the last line in the output, print the rest to standard error:

#include <iostream>

int main() {
  int x, y, sum;
  std::cerr << "A program which adds two integers\n";
  std::cerr << "Enter 1st integer: ";
  std::cin >> x;
  std::cerr << "Enter 2nd integer: ";
  std::cin >> y;
  std::cerr << "\n";
  sum = x + y;
  std::cout << "Sum is " << sum << std::endl;
  return 0;
}

Now, run:

$ ./a.out < file > output.txt
A program which adds two integers
Enter 1st integer: Enter 2nd integer: 
$ cat output.txt
Sum is 11

Or, to give the values manually:

$ ./a.out > output.txt 
A program which adds two integers
Enter 1st integer: 4
Enter 2nd integer: 7

$ cat output.txt 
Sum is 11
2
  • Great answer! Why does whatever you give to cerr stream not get printed? Does it have anything to do with std::cerr << '\n'? How does it work? Sep 30, 2015 at 11:48
  • 1
    @daltonfury42 it is printed but to standard error, not standard output. > file only redirects standard output and not standard error. That's why you can see the cerr lines in the terminal but not in the output file. The std::cerr << '\n' is just to print a newline to make the output look correct.
    – terdon
    Sep 30, 2015 at 11:52
1

As terdon pointed out already, to do this you want to either redirect stdin from a file (and call the program using e.g. program <inputfile.txt) or change your program so that it parses arguments passed from the command line (and call the program using e.g. program 1 2);

In the first case you want to suppress the first two couts if stdin is not connected to a terminal, and in the second case you want to suppress the first two couts, e.g., if, say, not enough arguments have been passed from the command line;

Speaking of the first method, you can check whether a file descriptor is connected to a terminal using the isatty() function (POSIX function defined in unistd.h):

int isatty(int fd);

If fd is an opened file descriptor connected to a terminal, the function will return 1; If fd is not an opened file descriptor or is not connected to a terminal, the function will return 0:

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

int main() {
    int x, y, sum;
    std::cout << "A program which adds two integers\n";
    if(isatty(0)) {
        std::cout << "Enter 1st integer: ";
    }
    std::cin >> x;
    if(isatty(0)) {
        std::cout << "Enter 2nd integer: ";
    }
    std::cin >> y;
    sum = x + y;
    std::cout << "Sum is " << sum << std::endl;
    return 0;
}

This way the user will be prompted to insert the numbers only if stdin has not been redirected:

user@user-X550CL ~/tmp % ./a.out               
A program which adds two integers
Enter 1st integer: 1
Enter 2nd integer: 2
Sum is 3
user@user-X550CL ~/tmp % cat inputfile.txt 
3
4
user@user-X550CL ~/tmp % ./a.out <inputfile.txt 
A program which adds two integers
Sum is 7

Speaking of the second method, here's a very rough example on how to parse arguments passed from the command line (arguments passed from the command line are stored into argv, which is an array of pointers to a character, so you'll have to cast each argument to an integer, but don't use atoi()! Use something that allows you to check if the arguments are numeric or not. I used atoi() just for the sake of simplicity):

#include <iostream>
#include <cstdlib>

int main(int argc, char *argv[]) {
    int x, y, sum;
    std::cout << "A program which adds two integers\n";
    if(argc == 3) {
        x = atoi(argv[1]);
        y = atoi(argv[2]);
    }
    else {
        std::cout << "Enter 1st integer: ";
        std::cin >> x;
        std::cout << "Enter 2nd integer: ";
        std::cin >> y;
    }
    sum = x + y;
    std::cout << "Sum is " << sum << std::endl;
    return 0;
}

This way the user will be prompted to insert the numbers only if not enough arguments have been passed from the command line:

user@user-X550CL ~/tmp % ./a.out
A program which adds two integers      
Enter 1st integer: 1
Enter 2nd integer: 2
Sum is 3
user@user-X550CL ~/tmp % ./a.out 3
A program which adds two integers
Enter 1st integer: ^C
user@user-X550CL ~/tmp % ./a.out 3 4
A program which adds two integers
Sum is 7
0

So there are a few things you can do:

  1. You can output to file within C++ using something like this.
  2. You can remove the cout << "Enter... lines in your code so the only output of the program is what goes into the file.
  3. You can work around using the error output stream (cerr instead of cout) and manipulate the outputs accordingly. If you want cout visible and cerr in the file, do xargs program.cc < file.txt 2> outputresult.txt (using 2> instead of >). If you want cerr visible and cout in the file, do it as you had it above with just >.
  4. Keep the output as it is now, but run 2 more executions that removes the unwanted lines from your code:

cat outputresult.txt | grep Sum > tempfile && mv tempfile outputresult.txt

You can implement this with your code like this:

./program.cc < file.txt | grep Sum > outputresult.txt

This method requires newlines to be written, however, so you'd have to make sure that the output it each in new lines.

I would personally suggest using either the file management within C++ or the cout and cerr hack, depending on the use of the program.

2
  • Why use xargs? It doesn't do anything useful here. Also, there's no need for a temp file if you want to parse the output: program.cc < file | grep Sum > output will do it.
    – terdon
    Sep 30, 2015 at 11:31
  • True, but I was going off of the commands the OP was specifying above. I'll add the shorter version. And yea, xargs isn't needed, but again, it was what the OP was initially using. Oct 1, 2015 at 17:38

Not the answer you're looking for? Browse other questions tagged .