I've built a simple Linux shell in c++. It can take in standard bash commands, redirect to a file, etc. But, what I want is for it to be able to take in input, for example, if I have a commands.txt file that contain
ls -l /
some_invalid_command
echo this displays stuff
and I pipe it into my a.out like so
$./a.out < commands.txt
I want it to run the commands inside as if I had typed them in. However, when I do that, I run into an infinite loop where the '$' prompt displays over and over until I hit ctrl+c. I'm pretty sure the cause is that I'm not checking for an end of file anywhere in my code.
So my question is, how would I go about checking for the end of file in this situation? I think the character is '^d' but I'm not sure how I'd check for that. Would something like this work?
if (inputString == "\^d") {
exit = true;
}
I'm only concerned that perhaps I want to input more information into the shell after the file has ran. setting exit to true would tell the shell to turn off.
Edit: Requested code...
void Shell::getInput() {
emptyQueue();
cout << "$ ";
getline(cin, inputString);
fillQueue();
};
void Shell::fillQueue() {
if (inputString.empty()) {
return;
};
int i = inputString.find_first_not_of(" ");
int j = inputString.find_last_not_of(" \r\n");
int k;
string stringToAdd;
while (i != -1) {
k = i;
while (inputString.at(k) != ' ') {
if (k == j) {
break;
};
k++;
};
if (k != j) {
k--;
};
stringToAdd = inputString.substr(i, k - i + 1);
i = inputString.find_first_not_of(" ", k + 1);
commandQueue.push_back(stringToAdd);
};
i = 0;
while (i < commandQueue.size()) {
pointers[i] = commandQueue[i].c_str();
i++;
};
};
An end of file is not a character itself.
When you reach the end of the file, any attempt to read more characters returns with an indication that 0 characters were read.
You did not show your code in question, but you must be running a loop that reads the next line of text from standard input. Typically, you would have either an explicit check that the number of characters were read is 0, or if you were using a std::istream, the eof() method.
If the file is opened in (default) text mode, the Ctrl+D is translated into end of file. So you just have to make sure that your looping until there's no input anymore, typically:
while ( getline(cin, inputString) ) { // or while (cin>>inputString)
...
}
void Shell::getInput() {
emptyQueue();
cout << "$ ";
getline(cin, inputString);
if(feof(stdin)) {
exit = true;
return;
};
fillQueue();
};
Related
void DataHousing::FileOpen() {
int count = 0;
// attempt to open the file with read permission
ifstream inputHandle("NumFile500.txt", ios::in);
if (inputHandle.is_open() == true) {
while (!inputHandle.eof()) {
count++;
}
inputHandle.close();
}
else {
cout << "error";
}
cout << count;
}
This is getting stuck in the while loop. But shouldn't the while loop end when it gets to the end of file? Also, I'm not even sure yet if it is counting correctly.
A fairly easy way to do this would be to use std::cin instead. Assuming that you want to count the number of integers in a file you can just use a while loop like so:
int readInt;
int count = 0;
while(std::cin >> readInt){
count++;
}
Then you just pass in the file as an argument parameter to your executable as so:
exec < filename
If you prefer to go through the route you're going then you can just replace your while loop condition with !inputHandle.eof() && std::getline(inputHandle, someStringHere) Then proceed to check if someStringHere is an int and increment your count if it is like so:
int count = 0;
std::string s;
ifstream inputHandle("NumFile500.txt", ios::in);
if (inputHandle.is_open() == true) {
while (!inputHandle.eof() && std::getline(inputHandle, s)) {
if(check to see if it's a number here)
count++;
}
inputHandle.close();
}
I am learning c++ so bear with me and apologize for any idiocy beforehand.
I am trying to write some code that matches the first word on each line in a file called "command.txt" to either "num_lines", "num_words", or "num_chars".
If the first word of the first line does not match the previously mentioned words, it reads the next line.
Once it hits a matching word (first words only!) it prints out the matching word.
Here is all of my code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
ifstream comm_in("commands.txt"); // opens file
string command_name = "hi"; // stores command from file
bool is_command() {
if (command_name == "num_words" || command_name == "num_chars" || command_name == "num_lines") {
return true;
} else {
return false;
}
}
// FIND a first word of a line in file THAT MATCHES "num_words", "num_chars" or "num_lines"
void get_command() {
string line;
char c;
while (!is_command()) { // if command_name does not match a command
// GET NEXT LINE OF FILE TO STRING
getline(comm_in, line);
// SUPPOSED TO GET THE FIRST WORD OF A STRING (CANT USE SSTREAM)
for (int i = 0; i < line.size(); i++) { // increment through line
c = line[i]; // assign c as index value of line
if (c == ' ' || c == '\t') { // if c is a space/tab
break; // end for loop
} else {
command_name += c; // concatenate c to command_name
} // if
} // for
} // while
return;
}
int main() {
get_command();
cout << command_name; // supposed to print "num_lines"
}
The contents of the command.txt file:
my bear is happy
and that it
great ha
num_lines sigh
It compiles properly, but when I run it in my terminal, nothing shows up; it doesn't seem to ever stop loading.
How can I fix this?
Unless you really want to hate yourself in the morning (so to speak) you want to get out of the habit of using global variables. You'll also almost certainly find life easier if you break get_command into (at least) two functions, one specifically to get the first word from the string containing the line.
I'd write the code more like this:
bool is_cmd(std::string const &s) {
return s == "num_words" || s == "num_chars" || s == "num_lines";
}
std::string first_word(std::istream &is) {
std::string line, ret;
if (std::getline(is, line)) {
auto start = line.find_first_not_of(" \t");
auto end = line.find_first_of(" \t", start);
ret = line.substr(start, end - start);
}
return ret;
}
void get_command(std::istream &is) {
std::string cmd;
while (!(cmd = first_word(is)).empty())
if (is_cmd(cmd)) {
std::cout << cmd;
break;
}
}
This still isn't perfect (e.g., badly formed input could still cause it to fail) but at least it's a move in what I'd say is a better direction.
If something goes wrong and you reach the end of file the loop will never stop. You should change getline(comm_in, line) to if(!getline(comm_in, line)) break;, or better yet, use that as the condition for the loop.
You also have to reset command_name for each pass:
while(getline(comm_in, line))
{
command_name = "";
for(int i = 0; i < line.size(); i++)
{
c = line[i];
if(c == ' ' || c == '\t')
break;
else
command_name += c;
}
if(is_command())
break;
}
// FIND a first word of a line in file THAT MATCHES "num_words", "num_chars" or "num_lines"
void get_command()
{
string line;
char c;
while (!is_command()) { // if command_name does not match a command
// GET NEXT LINE OF FILE TO STRING
if(getline(comm_in, line),comm_in.fail()){
// end reading
break;
}
//clear
command_name = "";
// SUPPOSED TO GET THE FIRST WORD OF A STRING (CANT USE SSTREAM)
for (int i = 0; i < line.size(); i++) { // increment through line
c = line[i]; // assign c as index value of line
if (c == ' ' || c == '\t') { // if c is a space/tab
break; // end for loop
} else {
command_name += c; // concatenate c to command_name
} // if
} // for
} // while
return;
}
The key of this problem is that you didn't clear the command_name.
What's more, you have to add a judge about whether reaching the end of the file.
ps: if(getline(comm_in, line),comm_in.fail()) is equal to if(getline(comm_in, line)),
I have a python function which I was hoping to translate into C++ to try and gain some extra speed (as it will be used to parse >100GB files). I am very inexperienced with C++ and was horrified to find my C++ function running much slower after my basic translation. Any pointers as to why this is, or what I can do to improve my C++ code would be much appreciated.
Script overview: the function reads stdin from another program, checks each line for any substring matches, and prints each line to stdout
Python function:
def find_tagPy(conditions):
# conditions e.g. ['TTAT', 'TAT'] etc
for line in stdin:
# Check conditionss against this line
l = line.split("\t")
if l[0][0] == "#":
stdout.write(line)
continue
FLAG = int(l[1])
if 1 & FLAG: # Read has a pair
for bases in conditions:
if bases in l[9]:
ADD_MATE = 1
stdout.write(line)
break # stop looking
C++ function:
void find_tagCpp (vector<string> conditions) {
cin.sync_with_stdio(false);
cin.tie(NULL);
string line;
while (getline(cin, line)) {
vector<string> l;
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
if (l[0][0] == '#') {
cout << line << "\n";
continue;
}
int FLAG = stoi(l[1]);
int pair_FLAG = 1;
if (pair_FLAG & FLAG) { // Read has a pair
for (int i=0; i < conditions.size(); i++) { // If bases in SEQ
if (l[9].find(conditions[i]) != string::npos) {
cout << line << "\n";
break; // Stop looking
}
}
}
}
}
An example of a stdin line is:
FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31
On my machine the python function takes 1.97 s and the C++ function takes 11.05 s (file size around 25 mb, but this includes processing with upstream and downstream tools)
EDIT:
I've found a bottleneck in boost::split which is a bit suprising:
Python:
for i in range(100000):
l = line.split("\t")
C++:
for (int i=0; i < 100000; i++) {
vector<string> l;
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
}
Python = 0.0325 s
C++ = 1.245 s
However my file is only 156,980 lines, so this cant be the whole problem.
The split copies the pieces into new strings. This is slow and you do not need them. Instead search the line for the start of the piece you want ( 10th ) and then call find starting from there.
I realised my original code is not amenable to testing, so I thought I would refactor it here, and discuss what I have found.
I turned on compiler optimisations using the -Ofast (fastest, aggressive optimisations, Apple LLVM 6.1) as suggested, and for comparison Python is 2.7.10.
Python function
import time
def fun(line):
l = line.split(" ", 10)
if 'TTAGGG' in l[9]:
pass
line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31"
time0 = time.time()
for i in range(100000):
fun(line)
print time.time() - time0
C++ function
void fun(string* line, string* substring) {
vector<string> l;
boost::split(l, *line, boost::is_any_of(" "));
if (l[9].find(*substring) != string::npos) {
// Do nothing
}
}
int main(int argc, const char * argv[]) {
string line = "FCC2CCMACXX:4:1105:10758:14389# 81 chrM 1 32 10S90M = 16151 16062 CATCACGATGGATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTTTCCATGCATTTGGTATTTTCGTCTGGGGGGTGTGCACGCTTAGGGGATAGCATTG bbb^Wcbbbbccbbbcbccbba]WQG^bbcdcb_^_c_^`ccdddeeeeeffggggiiiiihiiiiihiiihihiiiihghhiihgfgfgeeeeebbb NM:i:1 AS:i:85 XS:i:65 RG:Z:1_DB31";
string substring = "TTAGGG";
boost::timer t;
for (int i=0; i<100000; i++) {
fun(&line, &substring);
}
cout << t.elapsed() << endl;
return 0;
}
On my machine I now time the c++ function at 205 ms and the python function at 66 ms. Interestingly, now almost the entire runtime is taken up by the boost::split function.
If I get rid of this function and use string.find to search the whole line (not quite what I wanted though):
if ((*line).find(*substring) != string::npos) {
// Do nothing
}
The c++ runtime is reduced to about <1 ms! So it appears boost::split was the problem all alone. Thanks for the suggestions.
Try this code with some optimization
C++ function:
void find_tagCpp (vector<string> conditions) {
cin.sync_with_stdio(false);
cin.tie(NULL);
string line;
vector<string> l;
while (getline(cin, line)) {
l.clear();
boost::split(l, line, boost::is_any_of("\t"), boost::token_compress_on);
if (l[0][0] == '#') {
cout << line << "\n";
continue;
}
int FLAG = stoi(l[1]);
int pair_FLAG = 1;
if (pair_FLAG & FLAG) { // Read has a pair
for (int i=0; i < conditions.size(); i++) { // If bases in SEQ
if (l[9].find(conditions[i]) != string::npos) {
printf("%s\n", line.c_str());
break; // Stop looking
}
}
}
}
}
I have a text file, that is formatted somewhat like this:
1 3 4 5 6
6 7 8
4 12 16 17 18 19 20
20
0
A line can contain 1 to 10000 integers. What I need to do, is read all of them line by line.
Pseudocode like this:
line=0;
i=0;
while(!file.eof()){
while(!endLine){
array[0][i++]=file.readChar();
}
line++;i=0;
}
So, I have an array , into which I would like to read every line, and each line would consist of each of these integers.
The problem I'm having, is how to check if the end of a line has come.
Note, I can't use strings.
Yes, This is for a homework, but the main task for the assignment is to build a tree and then transform it. I can do that, but I've no idea how to read the integers from the file.
Probably something like this:
after reading an int, I manually skip spaces, tabs, carriage return and end of line (for this one you'll have to implement your logic).
To read an int I read it directly using the C++ functions of ifstream. I don't read it character by character and then recompose it as a string :-)
Note that I skip \r as "spaces. The end of line for me is \n.
#include <iostream>
#include <fstream>
#include <vector>
int main()
{
std::ifstream file("example.txt");
std::vector<std::vector<int>> ints;
bool insertNewLine = true;
int oneInt;
//The good() here is used to check the status of
//the opening of file and for the failures of
//peek() and read() (used later to skip characters).
while (file.good() && file >> oneInt)
{
if (insertNewLine)
{
std::vector<int> vc;
ints.push_back(vc);
//With C++11 you can do this instead of the push_back
//ints.emplace_back(std::vector<int>());
insertNewLine = false;
}
ints.back().push_back(oneInt);
std::cout << oneInt << " ";
int ch;
while ((ch = file.peek()) != std::char_traits<char>::eof())
{
if (ch == ' '|| ch == '\t' || ch == '\r' || ch == '\n')
{
char ch2;
if (!file.read(&ch2, 1))
{
break;
}
if (ch == '\n' && !insertNewLine)
{
std::cout << std::endl;
insertNewLine = true;
}
}
else
{
break;
}
}
}
//Here we should probably check if we exited for eof (good)
//or for other file errors (bad! bad! bad!)
return 0;
}
There is a function called getline() which will read a whole line. Link
You need a function to read a value from a file or indicates an end of line or end of file condition, something like:
result_type GetNextValue (input_file, &value)
{
if next thing in file is a number, set value and return number_type
if next thing in file is an end of line, return end_of_line_type
if end of file found, return end_of_file_type
}
and then your array building loop becomes:
line = 0
item = 0
eof = false
while (!eof)
{
switch (GetNextValue (input_file, value))
{
case value_type:
array [line][item++] = value
case end_of_line_type:
line++;
item = 0;
case end_of_file_type:
eof = true
}
}
I'll leave the details to you as it's homework.
You could read the numbers in a char and check against carriage return. A snippet that I had just tried is given below:
ifstream ifile;
ifile.open("a.txt");
char ch;
while((ch = ifile.get()) != EOF)
{
std::cout<<ch<<"\n";
if (ch == '\n')
std::cout<<"Got New Line";
}
ifile.close();
I am trying to create a simple shell in Unix. I read a lot and found that everybody uses the strtok function a lot. But I want to do it without any special functions. So I wrote the code but I can't seem to get it to work. What am I doing wrong here?
void process(char**);
int arg_count;
char **splitcommand(char* input)
{
char temp[81][81] ,*cmdptr[40];
int k,done=0,no=0,arg_count=0;
for(int i=0 ; input[i] != '\0' ; i++)
{
k=0;
while(1)
{
if(input[i] == ' ')
{
arg_count++;
break;
}
if(input[i] == '\0')
{
arg_count++;
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0';
if(done == 1)
{
break;
}
}
for(int i=0 ; i<arg_count ; i++)
{
cmdptr[i] = temp[i];
cout<<endl;
}
cout<<endl;
}
void process(char* cmd[])
{
int pid = fork();
if (pid < 0)
{
cout << "Fork Failed" << endl;
exit(-1);
}
else if (pid == 0)
{
cout<<endl<<"in pid";
execvp(cmd[0], cmd);
}
else
{
wait(NULL);
cout << "Job's Done" << endl;
}
}
int main()
{
cout<<"Welcome to shell !!!!!!!!!!!"<<endl;
char input[81];
cin.getline(input,81);
splitcommand(input);
}
Several things:
you don't return anything from the splitcommand function
everything you do in the splitcommand function is done in local variables, so it (esp. the strings you make) will not survive its end
the code that attaches the null terminator is wrong (you put it to the following string, not the current one)
using fixed-size buffer is a great choice; people love it
note that in real UNIX shells, not every space designates an argument, and not every argument is designated by spaces
I'd suggest you use strings and some (real) parser framework, provided it is not too special for you.
This is almost certainly homework. There is no reason to avoid library functions unless you were told to. In fact, most likely you were told to implement strtok.
strtok isn't really a special function as it's standard function in standard include string.h, so there is no good reason not to use it.
If you decide to make your shell more complicated then you may reason about using a tool for lexical analysis.
For example:
http://en.wikipedia.org/wiki/Flex_lexical_analyser
The problem is with the
arg_count++;
inside the if(input[i] == ' ') and if(input[i] == '\0')
when you are parsing the command line and you find a space or you reach the end of the command line you are increment arg_count before you put a \0 at the end of the command you were reading.
So change it to:
if(input[i] == ' ')
{
// arg_count++; REMOVE THIS.
break;
}
if(input[i] == '\0')
{
// arg_count++; REMOVE THIS.
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0'; // add null-char at the end.
arg_count++; // increment should happen here.
More bugs:
You are not returning anything from
splitcommand
You cannot just return cmdptr
because they point to local char
arrays(temp) which will not persist
after the function returns. So you'll
have to make sure that the array
temp persists even after function
call by allocating it dynamically or
making it global.
Arguments to execvp look good to
me. Others please take a look.