Piping in shell - linux

i have created my own shell in linux. it works fine with commmands. Now I want to add pipes in it. i want to implement multiple piping in it. Can some one guide me how to do do? I havent used Linux. Iam new to it.
I have seen many source codes and sites but i am still not clear about the idea of executing commands having multiple pipes!
This is code i have implemented so far!
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <cstring>
#include <sys/types.h>
using namespace std;
int main ()
{
while (true){
char * input;
string insert;
char * token;
char * parsed[9];
int count=0;
char * cmd1[6];
char * cmd2[6];
cout<<"My Shell $";
getline(cin,insert); // take input from user
input= new char [insert.size()+1];
strcpy(input, insert.c_str());
for (int i=0; i<9; i++)
parsed[i]=NULL;
token=strtok(input, " ");
while (token!=NULL) // parse the input
{
parsed[count] = new char[strlen(token) + 1];
strcpy(parsed[count++],token);
token=strtok(NULL, " ");
}
delete input;
delete token;
int j= count-1;
int pipe_position[4]={0};
int counter=0;
for (int i=0; i<j; i++) // finding position of pipe
{
if ((strcmp(parsed[i],"|"))==0)
pipe_position[counter++]=i;
}
bool pipe_exists=false;
if (pipe_position[0]!=0)
pipe_exists=true;
if(pipe_exists==false) // if there isnt any pipe in the command
{
pid_t mypid=fork();
if (mypid==0)
{
execlp (parsed[0],parsed[0],parsed[1],parsed[2],parsed[3],parsed[4], parsed[5],parsed[6],parsed[7],parsed[8],(char*) NULL);
}
else if (mypid>0)
{
wait(NULL);
for(int i=0; i<9; i++)
delete[]parsed[i];
}
}
} //end of while
}

Related

Named Pipe, Communication between 2 children

I have a problem with my code. I want to make communication between 2 children process. One of them is a server, which opens a file and sends each letter to the second process. The second process is counting letters and it should make a new file and save results. I have problems with the last step because the first process gonna finish faster than the second, what causes the end of the program. I have no idea how fix it. Looking for some tips :).
Here you got result.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
//stale
#define FIFO "my_fifo"
#define SIZE 26
//zmienne globalne
int desk; //deskryptor pliku
int tab[SIZE];
//prototypy funkcji
void parentKillAll();
void server(FILE * file);
void client();
void cleanUp(FILE * file);
int checkEntryData(int argc, char *argv);
void replaceTabWithZero(int * tab);
void countLetters(int * tab, char ch);
void saveResults(int * tab, char *title);
void showTab(int * tab);
int main(int argc, char *argv[]) {
if (!checkEntryData(argc, argv[1]))
return 1;
replaceTabWithZero(tab);
FILE *file = fopen(argv[1], "r");
umask(0);
mkfifo(FIFO, 0666);
if (file) {
if (fork() == 0) {
server(file);
exit(0);
} else if (fork() == 0) {
client();
saveResults(tab, strcat(argv[1], "Result"));
showTab(tab);
exit(0);
} else {
cleanUp(file);
parentKillAll();
}
} else {
perror("Error");
}
return 0;
}
void parentKillAll() {
sleep(1);
kill(0, SIGKILL);
exit(0);
}
void server(FILE * file) {
char ch;
while ((ch = fgetc(file)) != EOF) {
desk = open(FIFO, O_WRONLY);
write(desk, &ch, 1);
}
}
void client() {
char ch;
while (1) {
desk = open(FIFO, O_RDONLY);
read(desk, &ch, 1);
countLetters(tab, ch);
printf("%c", ch);
}
}
void cleanUp(FILE *file) {
wait(0);
fclose(file);
close(desk);
}
int checkEntryData(int argc, char *argv) {
if (argc < 2) {
fprintf(stderr, "Nie poprawna ilosc argumentow\n");
return 0;
}
if (access(argv, F_OK)) {
fprintf(stderr, "Podany plik \'%s\' nie istnieje\n", argv);
return 0;
}
if (access(argv, R_OK)) {
fprintf(stderr, "Brak uprawnien do odczytu pliku \'%s\'\n", argv);
return 0;
}
return 1;
}
void replaceTabWithZero(int * tab) {
for (int i = 0; i < SIZE; i++)
tab[i] = 0;
}
void countLetters(int *tab, char ch) {
int chVal = ch;
if (chVal > 92)
chVal -= 32;
if (chVal > 64 && chVal < 91)
tab[chVal-65] += 1;
}
void saveResults(int *tab, char * title) {
FILE *plik = fopen(title, "w");
if (plik) {
for (int i = 0; i < SIZE; i++)
fprintf(plik, "%c - %d\n", (i+97), tab[i]);
} else {
perror("Error");
}
fclose(plik);
}
void showTab(int * tab) {
for (int i = 0; i < SIZE; i++)
printf("\n%d", tab[i]);
}
The real problem is that the client process can never finish, because it runs an infinite while(1) loop without any exit conditions.
You should rewrite it so that it exits after reading all available data:
void client() {
char ch;
// Open the fifo only once, instead of once per character
desk = open(FIFO, O_RDONLY);
// Loop until there is no more data to read
while(read(desk, &ch, 1) > 0) {
countLetters(tab, ch);
printf("%c", ch);
}
}
This is technically sufficient to make it work, but you should also look into a series of other issues:
You should have two wait(0) calls so that you wait for both processes, and you shouldn't try to kill anything.
The server process should only be opening the fifo once, not once per character.
You should be comparing fgetc output to EOF before forcing the value into a char. Since you do it after, running your program on a ISO-8859-1 terminal will cause it to confuse EOF and the letter ΓΏ
You are using strcat on argv[1], even though you don't know how much space that array has. You should use your own buffer of a known length.
You should check the return value of all your system calls to ensure they succeed. Checking with access and then assuming it'll be fine is not as good since calls can fail for other reasons.
Canonical Unix behavior is to exit with 0 for success, and >= 1 for error.
It's good practice to use a larger buffer (e.g. 65536 bytes instead of 1) when using read/write directly. stdio functions like fgetc already uses a larger buffer behind the scenes.
Using a named pipe obviously works, but since you spawn both processes it would be more natural to use an unnamed one.

Alternate Without Semaphores

The program below creates two processes.
The parent process prints the numbers from 0 to 9, while the
child process prints the characters from A to J.
How can I make it alternate the printing using only signals? so no using semaphores or any IPCs.
#include <stdio.h>
int main()
{
int i = 0;
if (fork() == 0) /* Child process */
{
for (i = 0; i < 10; i++) {
printf("%c\n", i + 65);
}
} else /* Parent process */
{
for (i = 0; i < 10; i++) {
printf("%d\n", i);
}
}
}
I figured it out.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
int main()
{
int i=0;
int status;
int childPID = fork();
if (childPID == 0) /* Child process */
{
for(i=0;i<10;i++){
printf("%c\n",i+65);
kill(getpid(), SIGSTOP);
}
} else /* Parent process */
{
for(i=0;i<10;i++) {
waitpid(childPID,&status,WUNTRACED);
printf("%d\n",i);
kill(childPID, SIGCONT);
}
}
}

Identifier "stack" (my class) is undefined even though headers are properly included

In my source file when I define my object s, Visual Studios says Identifier "stack" is undefined. I'm pretty sure I've separated the headers correctly but I don't know why I'm getting this error. Also side note when I put everything together in one source file and compile it, it just exits immediately without any reason. Thanks for the help in advance.
This is the source file
// pa3.cpp : Defines the entry point for the console application.
//
#include "stack.h"
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
//#include <ctype.h>
using namespace std;
int main()
{
//int count;
stack s; //assign s object to stack
string input;
cout << "Please enter the name of the input file: \n";
//cin >> input;
getline(cin, input);
ifstream file(input);
string readline;
//ifstream file(input);
while (getline(file, readline)) //take first line of file and assign to readline
{
s.push(readline); //send it off to push
s.retrieveUPPER();
//file.close();
system("pause");
}
return 0;
}
This is the stack.h file
//#pragma once
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
//#include <ctype.h>
using namespace std;
class stack
{
public:
int count;
void push(string);
//void pop();
void check(string);
void retrieveUPPER();
private:
static string firstline[1];
static string diskeywords[3];
static char upperword[100];
static char lowerword[100];
static char operatorsarr[100];
static char delimitersarr[100];
};
This is the stack.cpp
#include "stack.h"
#include "stdafx.h"
#include <iostream>
string stack::firstline[1] = { 0 };
string stack::diskeywords[3];
char stack::upperword[100];
char stack::lowerword[100];
char stack::operatorsarr[100];
char stack::delimitersarr[100];
void stack::retrieveUPPER()
{
for (int i = 0; i < 100; i++)
{
cout << upperword[i] << "\n";
}
}
void stack::push(string readline)
{
firstline[0] = readline;
count++;
check(readline);
}
void stack::check(string readline)
{
int length;
char letter;
int leftperenthe = 0;
int rightperenthe = 0;
//int capital;
//int wordFOR;
//int wordBEGIN;
//char keywords[3][8] = { "FOR", "BEGIN", "END" };
char operators[] = "+-=*/;";
char delimiters[] = { ',',';' };
length = readline.length();
for (int i = 0; i < length; i++)
{
for (int j = 0; j < 5; j++)
{
letter = readline[i];
if (isupper(letter)) //if capital letter
{
upperword[i] = letter;
}
else if (islower(letter)) //if lowercase letter
{
lowerword[i] = letter;
}
else if (letter == operators[j]) //if encounters a operator
{
operatorsarr[i] = letter;
}
else if (letter == delimiters[j]) //if encounters a delimiter
{
delimitersarr[i] = letter;
}
else if (letter = ' ') //if encounters a space
{
lowerword[i] = ' ';
operatorsarr[i] = ' ';
delimitersarr[i] = ' ';
}
else if (letter = '(') //if left perenthesis
{
leftperenthe++;
}
else if (letter = ')') //if right perenthesis
{
rightperenthe++;
}
}
}
}
I also had a similar situation:
#include<iostream>
#include<stack.h>
#include<vector>
using namespace std;
int main();
std::vector<int>stickPan (std::vector<int>arr)
{
stack<int>s;
...
}
I used VScode, used mingw-w64, and added paths for * .h files to .json files, but it didn't work.
Errors are reported:
identifier "stack" is undefined.
type name is not allowed.
identifier "s" is undefined

Good way to tokenize line from file without using external libraries?

I am trying to tokenize a database dump separated by commas. I only need to read the first word, which will tell me if this is the line I need and then tokenize the line and save each separated string in a vector.
I have had trouble keeping all of the datatypes in order. I use a method of getline:
string line;
vector<string> tokens;
// Iterate through each line of the file
while( getline( file, line ) )
{
// Here is where i want to tokenize. strtok however uses a character array and not a string.
}
The thing is, I only want to continue reading and tokenize a line if the first word is what I am after. Here is a sample of a line from the file:
example,1,200,200,220,10,550,550,550,0,100,0,-84,255
So, if I am after the string example, it goes ahead and tokenizes the rest of the line for my use and then stops reading from the file.
Should I be using strtok, stringstream or something else?
Thank you!
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
void do(ifstream& file) {
string line;
string prefix = "example,";
// Get all lines from the file
while (getline(file,line).good()) {
// Compare the beginning for your prefix
if (line.compare(0, prefix.size(), prefix) == 0) {
// Homemade tokenization
vector<string> tokens;
int oldpos = 0;
int pos;
while ((pos = line.find(',', oldpos)) != string::npos) {
tokens.push_back(line.substr(oldpos, pos-oldpos));
oldpos = pos + 1;
}
tokens.push_back(line.substr(oldpos)); // don't forget the last bit
// And here you are!
}
}
}
How do I tokenize a string in C++?
http://www.daniweb.com/software-development/cpp/threads/27905
Hope this helps, though I am not proficient C/C++ programmer. For the record it would be nice if you could specify in the tags or in post language you are using.
Tokenizer.h
#ifndef TOKENIZER_H
#define TOKENIZER_H
#include <string>
#include <vector>
#include <sstream>
class Tokenizer
{
public:
Tokenizer();
~Tokenizer();
void Tokenize(std::string& str, std::vector<std::string>& tokens);
};
#endif /* TOKENIZER_H */
Tokenizer.cpp
#include "Tokenizer.h"
using namespace std;
string seps(string& s) {
if (!s.size()) return "";
stringstream ss;
ss << s[0];
for (int i = 1; i < s.size(); i++)
ss << '|' << s[i];
return ss.str();
}
void tok(string& str, vector<string>& tokens, const string& delimiters = ",")
{
seps(str);
string::size_type lastPos = str.find_first_not_of(delimiters, 0);
string::size_type pos = str.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos)
{
tokens.push_back(str.substr(lastPos, pos - lastPos));
lastPos = str.find_first_not_of(delimiters, pos);
pos = str.find_first_of(delimiters, lastPos);
}
}
Tokenizer::Tokenizer()
{
}
void Tokenizer::Tokenize(string& str, vector<string>& tokens)
{
tok(seps(str), tokens);
}
Tokenizer::~Tokenizer()
{
}
To tokenize a string
#include "Tokenizer.h"
#include <string>
#include <vector>
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
// Required variables for later below
vector<string> t;
string s = "This is one string,This is another,And this is another one aswell.";
// What you need to include:
Tokenizer tokenizer;
tokenizer.Tokenize(s, t); // s = a string to tokenize, t = vector to store tokens
// Below is just to show the tokens in the vector<string> (c++11+)
for (auto c : t)
cout << c << endl;
system("pause");
return 0;
}

How do I set up a function to convert vector of strings to vector of integers in VC++?

The question is in the title. Need help figuring out why my code compiles but doesn't work as intended. Thanks!
//This example demonstrates how to do vector<string> to vectro<int> conversion using a function.
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
vector<int>* convertStringVectorToIntVector (vector<string> *vectorOfStrings)
{
vector<int> *vectorOfIntegers = new vector<int>;
int x;
for (int i=0; i<vectorOfStrings->size(); i++)
{
stringstream str(vectorOfStrings->at(i));
str >> x;
vectorOfIntegers->push_back(x);
}
return vectorOfIntegers;
}
int main(int argc, char* argv[]) {
//Initialize test vector to use for conversion
vector<string> *vectorOfStringTypes = new vector<string>();
vectorOfStringTypes->push_back("1");
vectorOfStringTypes->push_back("10");
vectorOfStringTypes->push_back("100");
delete vectorOfStringTypes;
//Initialize target vector to store conversion result
vector<int> *vectorOfIntTypes;
vectorOfIntTypes = convertStringVectorToIntVector(vectorOfStringTypes);
//Test if conversion is successful and the new vector is open for manipulation
int sum = 0;
for (int i=0; i<vectorOfIntTypes->size(); i++)
{
sum+=vectorOfIntTypes->at(i);
cout<<sum<<endl;
}
delete vectorOfIntTypes;
cin.get();
return 0;
}
The code above has only one problem: You are deleting your vectorOfStringTypes before you pass it to your conversion function.
Move the line delete vectorOfStringTypes; to after you have called your convert function and the program works as intended.

Resources