system command for wc

Write a C++ program called wcount.cpp that uses the system command wc to count
the number of lines, words and bytes in a given text le. Your C++ program should
rst request the name of a text le and then invoke the command wc three times. First,
to count the number lines, then to count the number of words and nally to count the
number of bytes in the speci ed text le. Assume that the speci ed text le is located
in the same directory as your program.

Anybody have an idea on how to use this in a program. I know how to do it in the terminal. Just need some guidance. Thanks
It is odd that all the "fi"'s have been removed from your post (from the words "file", "first", and "finally").

The easiest way to run another program and read its output is to use the popen command.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>
#include <stdio.h>  // popen

int main() {
    FILE *f = NULL; // for use with popen
    std::string command;
    std::string filename("wcount.cpp"); // this file (for testing)
    int n = 0;

    command = "wc -l " + filename;
    f = popen(command.c_str(), "r");
    fscanf(f, "%d", &n);
    pclose(f);
    std::cout << "Number of lines: " << n << '\n';
}

Last edited on
Hi tpb, I’ve had this happen a few times. No idea why but sorry about that.

Is there a way to do it using only the wc commands. I’ve tried creating command and setting it equal to “wc -l “ + file name and passing command to system() but it fails. I don’t really understand why. I would send my code but my power is currently out and my laptop,is dead.

Thanks
"it fails"
HOW does it fail? Tell us exactly what happens. Is it a compile error or a runtime error? If it's a compiler error, what's the error & line number?
I have a feeling it's a particular error, but it's important for you to be able to identify what it is. (Hint: system() expects a const char*)

Also, tpb has given a good bit of code, you shouldn't have to change it too much to match your prompt. But anyway, at least make an attempt to tell us specifically what's wrong.
Last edited on
OT:
It is odd that all the "fi"'s have been removed from your post

"fi" can be a ligature https://en.wikipedia.org/wiki/Typographic_ligature

If the original text has ligatures, then a copy to (ascii?) forum can miss them.
Still OT:
Actually, if you copy OP's text to a text editor, you may see that the character is still there. Downloading the thread with CURL and inspecting the file with a hex editor tells me the codepoint is 0x0C. Hypothesis: OP uses a locale that defaults to a non-ISO/IEC 8859-1 encoding for single byte strings and their system also automatically replaces "fi" with the ligature for all text input.
@keskiverto, nice catch. Never would have thought of that.

@helios, you're right. Simply highlighting and copying it and then pasting it into a terminal as input to "cat -A" shows "^L" (i.e., 0x0C) for all the missing "fi"'s.

"ligature". Weird.


OP, system("wc -l filename.txt") will probably not work on Windows, if that's what you are trying to run it on.
Last edited on
Windows isn't specifically mentioned in the OP, but if anyone wants wc to work on windows...
CoreUtils for Windows
CoreUtils: collection of basic file, shell and text manipulation utilities

http://gnuwin32.sourceforge.net/packages/coreutils.htm
Last edited on
Windows isn't specifically mentioned in the OP

Yeah, it's just a guess that maybe the course uses *nix but the OP is trying to make it work on Windows. I'm not sure what the "ligature" problem indicates, although it may mean he's typing up his question in a word processor (MS Word?) that is replacing the "fi" with the ligature (and as helios said, saving it in some weird code page instead of unicode). He should type up his question in "notepad" instead (if that still exists). EDIT: Actually, I remember something called "notepad++" that was pretty good.
Last edited on
Yeah, notepad++ is a great alternative to notepad on Windows if you still want a "lightweight" editor. The fact that notepad is such trash after all these years shows that Microsoft doesn't care about making its default programs any good.
Im running Ubuntu through a VM on mac.

I dont understand the system() command and the only info my lecturer gave me was to look at man pages on Linux. I've done that but I still don't understand. I've only been doing c++ for about 6 months and this is my first time using these commands on an OS.

here is my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 #include <iostream>
#include <cstring>
#include <string>
#include <fstream>
using namespace std;

int main()
{
    string fileName;
    cout<<"Enter file name: "<< fileName;
    getline(cin, fileName);
	string command;
	
    command = "wc -l " + fileName;
    
    system(command);
    system("wc -w device_data.txt");
    system("wc -c device_data.txt");
    
 
    return 0;
}


here are my errors I recieve:
1
2
3
4
5
6
7
8
g++ -c wcount.cpp
wcount.cpp: In function ‘int main()’:
wcount.cpp:16:19: error: cannot convert ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ to ‘const char*’ for argument ‘1’ to ‘int system(const char*)’
     system(command);
                   ^
makefile:4: recipe for target 'wcount.o' failed
make: *** [wcount.o] Error 1


So I'm not sure what to do.
Last edited on
Well, system doesn't take a std::string. It takes a const char *.
In the code that I posted I showed that you can get a const char * "version" of a std::string with the c_str() method. So try command.c_str() instead of just command.
Glad you posted the actual error message, we can help you translate it for next time.

Error: cannot convert ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ to ‘const char*’ for argument ‘1’ to ‘int system(const char*)’

What it's saying is, "Cannot convert std::string to const char*" in the call to system.

system expects a null-terminated c-style string to be passed into it. It can't handle a C++ std::string. What you need to do is pass in the c-string version of it.

In other words, instead of
system(command);
you must do
system(command.c_str());

Ganado thank you so much. It works, sorry for taking so long to uplaod the error. I havent had power since last night and I've finally been able to charge my laptop.

Can I ask what this error means, I recently installed Ubuntu and I'm not sure if I maybe messed up something ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
g++ -c file_read.cpp
file_read.cpp: In function ‘int main()’:
file_read.cpp:11:10: error: ‘std::basic_istream<_CharT, _Traits>::basic_istream() [with _CharT = char; _Traits = std::char_traits<char>]’ is protected within this context
  istream input;
          ^~~~~
In file included from /usr/include/c++/7/iostream:40:0,
                 from file_read.cpp:1:
/usr/include/c++/7/istream:606:7: note: declared protected here
       basic_istream()
       ^~~~~~~~~~~~~
file_read.cpp:16:8: error: ‘std::istream {aka class std::basic_istream<char>}’ has no member named ‘open’
  input.open(file.c_str());
        ^~~~
file_read.cpp:33:8: error: ‘std::istream {aka class std::basic_istream<char>}’ has no member named ‘close’
  input.close();
        ^~~~~
makefile:4: recipe for target 'file_read.o' failed
make: *** [file_read.o] Error 1



1
2
3
4
5
6
7
  istream input;
          ^~~~~
In file included from /usr/include/c++/7/iostream:40:0,
                 from file_read.cpp:1:
/usr/include/c++/7/istream:606:7: note: declared protected here
       basic_istream()
       ^~~~~~~~~~~~~

It's saying the constructor for istream is protected. istream is not meant to be constructed directly. You should use std::ifstream to read from files, std::ofstream to write to them.

The other errors stem from the original one: istream doesn't have open and close functions, [i/o]fstream does.
Last edited on
So just add those in as header files ?

But it's not a version error or anything like that ?
If you want to work with files, you need the <fstream> header. Hope that answers your question, if not post your code and we can help more.
Thanks it compiles now.

I'm having some trouble as well with pipes and forks, so if I could borrow your brain again that be amazing ?

In this task you are required to create and terminate
various processes using process spawning. These processes need to communicate with
each other using pipes. You will need to read up on pipes and interprocess communica-
tion (IPC). You can consult the following link for some basic information about pipes
(http://www.linfo.org/pipes.html).
For this task you will have to write a C/C++ program that simulates an IoT (Internet
of Things) controller. Any number of IoT devices can connect to this controller and
report their status. The controller will thus have a complete picture of all the IoT
devices that have reported to it, the current status of those devices and when last the
status changed/updated.
To simulate the messages sent by the various IoT devices, a text file called device data.txt
is provided that contains a number of device messages sent, with each line representing
a distinct message. Each message contains a device name, a status value and a Unix
epoch value which are separated by commas. Your C/C++ program will read this file
line by line and sent the contained message to a new child process. The child process will
separate the message into device name, status value and epoch value, convert the epoch
to a date and time string and send the data back to the parent process. The parent
process will use this data to create and maintain a IoT device list with associated status
value and time.

Here is my code so far:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <istream>
#include <fstream>
#include <string>
#include <unistd.h>

using namespace std;

main ()
{
	string fileName;
	int line=0;
	string deviceData;
	ifstream input;
	pid_t pid;
	
	cout<<"Enter the file name: ";
	cin>>fileName;
	
	input.open(fileName.c_str());
	
	if (!input)
	{
		cout<<"Couldn't open file"<<endl;
	}
	else
	{
		pid = fork();
		if (pid == 0)
			{
				//child process
				cout<<"Child Process"<<endl;
			}
			else if (pid > 0)
			{
				//parent process
				cout<<"Parent Process"<<endl;
				while (input>>deviceData)
				{	
					cout<<"Parent sent file line "<<line<<endl;
					cout<<deviceData<<endl;
					line++;
				}
		
			}
			else
			{
				//fork failed
				cout<<"Failed"<<endl;
				return 1;
			}
			
	}
	
	input.close();
	
	return 0;
	
}


There is an example of what the output should be but its an image. If need be I can type out the example and post it. Just let me know ?
To be blunt, I'll help with specific questions but I'm not going to bother figuring out an assignment.

Break your assignment into parts. If the forking/piping is confusing, ignore that part for now and just focus on, for example, this sentence: "separate the message into device name, status value and epoch value, convert the epoch to a date and time string"
Make a function that does that -- takes in one comma-delimited line, and break it into those parts.

If you want to focus on parent-child communication when it comes to forking, I would make a simple program, without files, that just makes 1 child process that fills in 1 number, and then sends that number back to the parent. Once you understand this, you'll be able to expand the logic.

What I would personally do here is first get the line from a file using getline(ifstream, line), and then feeding each line into a stringstream, where you can then call getline(sstream, line, ',') to break up the message on each comma.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Example program
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>

/// https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring

// trim from start (in place)
void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
}

// trim from end (in place)
void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

// trim from both ends (in place)
void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

struct Foo {
    std::string message;
    int number;  
};


Foo process_line(const std::string& line)
{
    std::istringstream iss(line);
    std::string msg_str;
    std::string num_str;

    std::getline(iss, msg_str, ',');
    std::getline(iss, num_str, ',');

    // C++ has no built-in "trim" function, sadly.
    // Do it ourselves
    trim(msg_str);
    
    int num = std::stoi(num_str);

    return {msg_str, num};
}

int main()
{
    // file
    std::istringstream fin(
      "hello,    123   \n"
      "test,-445\n"
      "goodbye,        91\n"
    );
    
    std::string line;
    while (getline(fin, line))
    {
        std::cout << "line: " << line << "\n";
        Foo foo = process_line(line);
        std::cout << "  foo.msg = " << foo.message << "\n";
        std::cout << "  foo.num = " << foo.number <<  "\n\n";
    }
}


line: hello,    123   
  foo.msg = hello
  foo.num = 123

line: test,-445
  foo.msg = test
  foo.num = -445

line: goodbye,        91
  foo.msg = goodbye
  foo.num = 91
Last edited on
Ok cool. That makes sense, I'll carry on with it and see what I come up with. Thanks for your time, highly appreciated.
Topic archived. No new replies allowed.