Why do I get double output from pipe? - c

I'm trying to read from the pipe once and print out the results, but I get a double output.
I thought the read and write sizes were incorrect (Why is the output printed twice with write() and not with print() in IPC using pipe?), but I printed out the size in the child before the write and then tried inputted the same size to the read function and I still get a double output.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
typedef struct
{
int PID;
char *filename;
int wordCount;
int lineCount;
int byteCount;
} filestr;
int pipe(int pd[2]);
void mywc(FILE *fp, char *name, filestr *fileA);
int isParam(char* fileName);
int getParam(int argCount, char **args);
void error_exit(char *s);
int main(int argc, char *argv[])
{
int pd[2], status, pid, param,
wordT=0, lineT=0, byteT=0;
filestr fileAtr;
//param = getParam(argc, argv);
//printf("PARAM: %d\n", param);
if(pipe(pd) == -1)
error_exit("pipe() failed");
for(int i = 1; i < argc; ++i)
{
pid = fork();
if(pid == -1)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
FILE *file = fopen(argv[i], "r");
if(file != 0){
mywc(file, argv[i], &fileAtr);
wait(NULL);
close(pd[0]);
printf("SIZE: %d\n", sizeof(fileAtr));
if(write(pd[1], &fileAtr, sizeof(fileAtr)) == -1)
error_exit("write() failed");
}
exit(0);
}
}
int sum;
for(int j = 0; j < argc; ++j)
{
close(pd[1]);
if(read(pd[0], &fileAtr, (32*sizeof(int))) == -1)
error_exit("read() failed");
printf("PID : %d\n", fileAtr.PID);
printf("File Name : %s\n", fileAtr.filename);
printf("Words : %d\n", fileAtr.wordCount);
printf("Lines : %d\n", fileAtr.lineCount);
printf("Bytes : %d\n\n", fileAtr.byteCount);
wordT += fileAtr.wordCount;
lineT += fileAtr.lineCount;
byteT += fileAtr.byteCount;
}
printf("Grand Total: word: %d line: %d byte: %d\n", wordT, lineT, byteT);
}
void mywc(FILE *fp, char *name, filestr *fileA)
{
int c, lineCount=0, wordCount=0, byteCount=0;
while( (c = getc(fp)) != EOF )
{
if( c == ' ' )
{
wordCount++;
byteCount++;
}
else if ( c == '\n')
{
wordCount++;
byteCount++;
lineCount++;
}
else
{
byteCount++;
}
}
fileA->PID = getpid();
fileA->filename = name;
fileA->wordCount = wordCount;
fileA->lineCount = lineCount;
fileA->byteCount = byteCount;
}
int getParam(int argCount, char **args)
{
int param;
for(int i = 0; i < argCount; i++)
{
if((strcmp(args[i], "-lwc") == 0) || (strcmp(args[i], "-lcw") == 0) || (strcmp(args[i], "-wlc") == 0)
|| (strcmp(args[i], "-wcl") == 0) || (strcmp(args[i], "-cwl") == 0) || (strcmp(args[i], "-clw") == 0))
param = 3;
else if((strcmp(args[i], "-w") == 0))
param = 0;
else if((strcmp(args[i], "-l") == 0))
param = 1;
else if((strcmp(args[i], "-c") == 0))
param = 2;
}
return param;
}
void error_exit(char *s)
{
fprintf(stderr, "\nError: %s\n", s);
exit(1);
}
My Ouput:
SIZE: 32
PID : 14896
File Name : test
Words : 4
Lines : 4
Bytes : 26
PID : 14896
File Name : test
Words : 4
Lines : 4
Bytes : 26
Grand Total: word: 8 line: 8 byte: 52
EDIT: Added full code

The loop that prints your output
for(int j = 0; j < argc; ++j)
{
...
}
Goes from j = 0 instead of j = 1. argc is 2. So it prints the output twice.

A combination of two problems: First, for(int i = 1; i < argc; ++i) will run argc - 1 times, but for(int j = 0; j < argc; ++j) will run argc times. Second, if(read(pd[0], &fileAtr, (32*sizeof(int))) == -1) only handles one thing that read can do. In addition to failing with -1, it can also return 0 when it gets to EOF, or return some bytes, but fewer than you asked for (called a partial read). In your case, the EOF is happening, but your code doesn't notice, so it just happily goes along with the data that happened to already be there (in this case, a duplicate of the final result).

Related

Using fork and waitpid functions in C to count characters, words, and lines from file with multiple processes

I am trying to use multiple processes with the fork and waitpid functions to modify a program that reads through a file and return the total number of lines, words, and characters. The program compiles and runs fine, but the output always displays zero for all three values even though the file is not empty. Does anyone know how I can fix this? The user enters the file name and the number of child processes they want when they run the program. Here is my code:
//wc.h
#ifndef WC_H
#define WC_H
#include <stdio.h>
typedef struct count_t {
int linecount;
int wordcount;
int charcount;
} count_t;
count_t word_count(FILE* fp, long offset, long size);
extern int crashRate;
#endif
//wc_mul.c
#include "wc.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#define MAX_PROC 100
#define MAX_FORK 100
int crashRate = 0;
count_t word_count(FILE* fp, long offset, long size)
{
char ch;
long rbytes = 0;
count_t count;
// Initialize counter variables
count.linecount = 0;
count.wordcount = 0;
count.charcount = 0;
printf("[pid %d] reading %ld bytes from offset %ld\n", getpid(), size, offset);
if(fseek(fp, offset, SEEK_SET) < 0) {
printf("[pid %d] fseek error!\n", getpid());
}
while ((ch=getc(fp)) != EOF && rbytes < size) {
// Increment character count if NOT new line or space
if (ch != ' ' && ch != '\n') { ++count.charcount; }
// Increment word count if new line or space character
if (ch == ' ' || ch == '\n') { ++count.wordcount; }
// Increment line count if new line character
if (ch == '\n') { ++count.linecount; }
rbytes++;
}
srand(getpid());
if(crashRate > 0 && (rand()%100 < crashRate))
{
printf("[pid %d] crashed.\n", getpid());
abort();
}
return count;
}
int main(int argc, char **argv)
{
long fsize;
FILE *fp;
int numJobs;
//plist_t plist[MAX_PROC];
count_t total, count, buf;
int i, j, pid, status, p[2];
int nFork = 0;
if(argc < 3) {
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
if(argc > 3) {
crashRate = atoi(argv[3]);
if(crashRate < 0) crashRate = 0;
if(crashRate > 50) crashRate = 50;
}
printf("crashRate RATE: %d\n", crashRate);
numJobs = atoi(argv[1]);
if(numJobs > MAX_PROC) numJobs = MAX_PROC;
total.linecount = 0;
total.wordcount = 0;
total.charcount = 0;
// Open file in read-only mode
fp = fopen(argv[2], "r");
if(fp == NULL) {
printf("File open error: %s\n", argv[2]);
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fclose(fp);
// calculate file offset and size to read for each child
for(i = 0; i < numJobs; i++) {
if(nFork++ > MAX_FORK) return 0;
pid = fork();
printf("%d\n", pid);
if(pid < 0) {
printf("Fork failed.\n");
} else if(pid == 0) {
// Child
fp = fopen(argv[2], "r");
if (pipe(p) != 0)
exit(1);
count = word_count(fp, 0, fsize);
write(p[1], count, MAX_PROC);
close(p[0]);
// send the result to the parent through the message queue
fclose(fp);
return 0;
}
}
waitpid(pid, &status, 0);
close(p[1]);
for (j=0; j < numJobs; j++) {
read(p[0], buf, MAX_PROC);
total.linecount += buf.linecount;
total.wordcount += buf.wordcount;
total.charcount += buf.charcount;
}
// Parent
// wait for all children
// check their exit status
// read the result from normalliy terminated child
// re-crete new child if there is one or more failed child
printf("\n========== Final Results ================\n");
printf("Total Lines : %d \n", total.linecount);
printf("Total Words : %d \n", total.wordcount);
printf("Total Characters : %d \n", total.charcount);
printf("=========================================\n");
return(0);
}
No code modifies total.linecount in the process that prints it and this variable was not placed in shared memory. So the output will always be zero.
You either need to place the variable in shared memory or you need to modify it in the same process in which you print it. The fork function creates a new process that inherits its parent's current view of memory but does not share modifications to memory unless memory is explicitly made shareable.
Were this not so, your code would explode as all the different fork children try to modify the same fp variable!
Where is the code that sums the results from the various children?

bad file descriptor in c program with forks

this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.
I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.
I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.
I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.
#include "parser.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#define READ 0
#define WRITE 1
void fork_error() {
perror("fork() failed)");
exit(EXIT_FAILURE);
}
void close_error() {
perror("Couldn't close file descriptor");
exit(EXIT_FAILURE);
}
void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
if (read_pipe != NULL) {
if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
perror("Failed to redirect STDIN to pipe");
exit(EXIT_FAILURE);
}
}
if (write_pipe != NULL) {
if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
perror("Failed to redirect STDOUT to pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
close_error();
}
}
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:
printf("Pid of %s: %d\n", argv[0], pid);
break;
}
}
void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
for (int i = 0; i < n; i++) {
if (n == 1) {
fork_cmd(argvs[i], n, NULL, NULL, fds);
}
// n > 1
else if (i == 0) {
fork_cmd(argvs[i], n, NULL, fds[i], fds);
}
else if (i == n - 1) {
fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
}
else {
fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
close_error();
}
}
}
void get_line(char* buffer, size_t size) {
getline(&buffer, &size, stdin);
buffer[strlen(buffer)-1] = '\0';
}
void wait_for_all_cmds(int n) {
// Not implemented yet!
for (int i = 0; i < n; i++) {
int status;
int pid;
if ((pid = wait(&status)) == -1) {
printf("Wait error");
} else {
printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
(long) getpid(), (long) pid, WEXITSTATUS(status));
}
}
}
int main() {
int n;
char* argvs[MAX_COMMANDS][MAX_ARGV];
size_t size = 128;
char line[size];
printf(" >> ");
get_line(line, size);
n = parse(line, argvs);
// Debug printouts.
printf("%d commands parsed.\n", n);
print_argvs(argvs);
int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2
for (int i = 0; i < n - 1; i++) {
if (pipe(fds[i]) == -1) {
perror("Creating pipe error"); // Creating pipe error: ...
exit(EXIT_FAILURE);
}
printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
}
fork_cmds(argvs, n, fds);
wait_for_all_cmds(n);
exit(EXIT_SUCCESS);
}
The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
{
close_error();
}
}

execvp() not working in my shell

I am trying to make a tiny shell. My problem is that when I call execvp() - I get errors.
For example, when I type in ls -l it returns ls: invalid option -- '
Can someone, please, help me understand why I am getting this error? For my code, the function command split gets the user input, and splits them up into separate commands. Separate commands are seperated by ; character.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CHARACTERS 512
#define HISTORY_SIZE 10
int commandSplit(char *c, char *a[], int t[]) {
int count = 0;
int total = 0;
char *temp[MAX_CHARACTERS];
char *readCommands = strtok(c, ";");
while(readCommands != NULL) {
printf("Reading full command: %s\n", readCommands);
temp[count] = readCommands;
count++;
readCommands = strtok(NULL, ";");
}
printf("Done reading full commands\n");
for(int i = 0; i < count; i++) {
char *read = strtok(temp[i], " ");
int track = 0;
while(read != NULL) {
printf("Reading individual command: %s\n", read);
a[total] = read;
track++;
total++;
read = strtok(NULL, " ");
}
t[i] = track;
}
return count;
}
int main() {
int exitProgram = 0;
char *args[MAX_CHARACTERS];
while(!exitProgram) {
char *commands = (char *)(malloc(MAX_CHARACTERS*sizeof(char)));
int tracker[MAX_CHARACTERS];
int numOfCommands = 0;
printf("tinyshell> ");
fgets(commands, MAX_CHARACTERS, stdin);
if(strlen(commands) == 0) continue;
numOfCommands = commandSplit(commands, args, tracker);
printf("There are %i commands!\n", numOfCommands);
if(strcmp(args[0], "exit") == 0) {
printf("Exiting\n");
exitProgram = 1;
continue;
}
int l = 0;
for(int i = 0; i < numOfCommands; i++) {
int status;
char *holder[tracker[i]+1];
for(int j = 0; j < tracker[i]; j++) {
holder[j] = args[l];
printf("Assiging holder:%s\n", holder[j]);
l++;
}
holder[tracker[i]] = NULL;
printf("What is holder? \n");
for(int o = 0; o < tracker[i]; o++) printf("%s", holder[o]);
pid_t p = fork();
pid_t waiting;
if(p == 0) {
printf("I am in child process\n");
execvp(holder[0], holder);
fprintf(stderr, "Child process could not execvp!\n");
exit(1);
}
else {
if(p < 0) {
fprintf(stderr, "Fork FAILED!\n");
}
else {
waiting = wait(&status);
printf("Child %d, status %d\n", waiting, status);
}
}
for(int i = 0; i < numOfCommands; i++) {
args[i] = NULL;
}
}
}
return 0;
}
Your problem is that fgets() also reads the newline character. As a result, the last argument of execvp() arguments array contains a newline, causing ls complain about an unrecognized argument: what you acctually pass to ls is -l\n; what you need to pass is just -l without the newline.
Try adding this code after the fgets call to trim the input buffer:
int len;
len = strlen(commands);
if (len > 0 && commands[len-1] == '\n') {
commands[len-1] = '\0';
}

Not printing first 5 characters of the file

The loop that reads from the file and prints the result is not printing the first 5 characters from all files read. If I print them 1 character at a time it works fine, but I'm reading floating point numbers from a file which I need to be processed later. The numbers are seperated by white space which is why I'm trying to print only when encountering white space.
here is my source code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define BUFFER_SIZE 100
#define READ_END 0
#define WRITE_END 1
//main function
int main(int argc, char *argv[])
{
//process ids
int processIds[argc - 1];
int pipes[argc - 1][2];
char *fileNames[argc - 1];
int status;
//check if at least one data file
if (argc == 1)
{
return -1;
//end program
}
//get file names and store in array fileNames
int i;
for (i = 0; i < (argc - 1); i++)
{
fileNames[i] = argv[i + 1];
}
//create child processes
for (i = 0; i < (argc - 1); i++)
{
//make a pipe for communication
if (pipe(pipes[i]))
{
printf("bad pipe");
return -1;
}
//make a child process
processIds[i] = fork();
//check if child or paren process
if (processIds[i] == 0)
{
//child process
//close unused end
close(pipes[i][READ_END]);
//make file
FILE *dataset = fopen(fileNames[i], "r");
//test if file opened
if (dataset == 0)
{
printf("could not find/open file");
return -1;
}
//read and process file
char *x;
char *num = "";
int min, max;
while ((*x = fgetc(dataset)) != EOF)
{
if ((x == " ") || (x == "\t") || (x == "\n"))
{
//end of number
printf("A%s B%s", num, num);
num = "";
}
else
{
strcat(num, x);
}
//printf("%c", x);
}
printf("\n\n");
char msg[BUFFER_SIZE];
write(pipes[i][WRITE_END], msg, strlen(msg) + 1);
fclose(dataset);
exit(0);
}
else if (processIds[i] < 0)
{
//error
return -1;
}
else
{
//parent process closes write end of pipe
close(pipes[i][WRITE_END]);
}
}
//wait for children
for (i = 0; i < (argc - 1); i++)
{
//create a read buffer
char read_buf[BUFFER_SIZE];
//wait and get pid of completed child
int pid = wait(&status);
//find which child completed
int j = 0;
while (pid != processIds[j] && j < (argc - 1))
{
//pid not recognized, should not happen
if (j >= (argc - 1))
{
printf("bad pid");
return -1;
}
j++;
}
//read from completed child
read(pipes[j][READ_END], read_buf, BUFFER_SIZE);
}
return 0;
}
i can spot the following misses concerning your code:
1) Memory allocation
you need to allocate your num variable
example :
char *num = malloc(10);
2) returned type
'fgetc' doesn't return a pointer nor a char, so you should define
int x;
x = fgetc(dataset);
3) your condition is wrong
if ((x == ' ') || (x == '\t') || (x == '\n'))
note that ' ' is not " "
NOW as my suggestion to read those separate strings:
1) read all the file in buffer :
char buff[SIZE];
dataset= fopen("fileName.txt", "r");
if( dataset!= NULL ){
while ( !feof(fp ) ){
memset(buff, '\0', sizeof( buff) );
fgets(buff, SIZE, (FILE*)dataset);
}
fclose(dataset);
}
2) use strchr to find next space and print your strings
see example of use here

Learning Pipes and Processes

I'm trying to get a better understanding of pipes and processes. I want to implement multiple chained pipes like cat test.txt | sort | uniq -c. I started my code with the cat test.txt, but it isn't working. It compiles, but when I provide a file name in the command line, for example, ./hwk ./test.txt. Nothing returns. Can someone take a look and give me some hints? I want to use loops because I want to be able to add more pipes. I know there's a lot of issues in my code, so I hope someone can give me some guidance on this topic. Thanks.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define SIZE 1024
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2];
int status;
pid_t pid;
char *str_ptr;
//Pass Command
char *arrayOfCommands[] = {"cat", NULL};
//Setting up pipes
int i;
for (i = 0; i < num_pipe; i++){
if(pipe(fds + i * 2) == -1) {
perror("Error creating pipes");
exit(1);
}
}
int j = 0;
for (i = 0; i < commands - 1; ++i) {
pid = fork();
if (pid == 0) {
if (i < commands) {
if (dup2(fds[j+1], 1) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
if (j != 0) {
if(dup2(fds[j-2], 0) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 2*num_pipe; i++) {
close(fds[i]);
}
if (execvp(arrayOfCommands[0], arrayOfCommands) < 0) {
perror("Array error");
exit(EXIT_FAILURE);
}
}
else if (pid < 0){
perror("Error");
exit(EXIT_FAILURE);
}
j += 2;
}
for (i = 0; i < 2 * num_pipe; i++){
close(fds[i]);
}
for (i = 0; i < num_pipe + 1; i++) {
wait(&status);
}
return 0;
}
I called this mainly minor adaptation of your program p3.c, compiling it to produce p3. Since there's only one command (cat) being invoked, I juggled things so that it will work correctly. When run as ./p3 p3.c, it prints out the content of the source code.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 0; // Just cat - no pipes
int commands = num_pipe + 1; // Number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *arrayOfCommands[3] = { "cat", NULL, NULL};
if (argc != 2)
err_exit("Missing filename argument");
arrayOfCommands[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s %s\n", (int)getpid(), arrayOfCommands[0], arrayOfCommands[1]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (i = 0; i < 2*num_pipe; i++)
close(fds[i]);
execvp(arrayOfCommands[0], arrayOfCommands);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
Check that works for you. Then you'll need to work out how you're going to create a second command. Your arrayOfCommands isn't going to help directly. You'll need another array of strings in some shape or form.
An extension to run cat file | rev. The changes are really quite minor. I created a_cat to handle the cat command, a_rev for the rev command, and a_cmds as the array of commands. It was also necessary to fix a loop on i to a loop on k.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *a_cat[3] = { "cat", NULL, NULL};
char *a_rev[2] = { "rev", NULL};
char **a_cmds[] = { a_cat, a_rev };
if (argc != 2)
err_exit("Missing filename argument");
a_cat[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s\n", (int)getpid(), a_cmds[i][0]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (int k = 0; k < 2*num_pipe; k++)
close(fds[k]);
execvp(a_cmds[i][0], a_cmds[i]);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
You aren't passing your program's command-line arguments through to the "cat" child process. You initialize arrayOfCommands like so -> char *arrayOfCommands[] = {"cat", NULL}; <- then you pass it as-is to the execvp() function as the second argument.
Okay your first problem is that in the line:
execvp(arrayOfCommands[0], arrayOfCommands);
you are using arrayOfCommands but I am not sure how you're populating arrayOfCommands for the case where the text file is not being displayed. I mean are you setting arrayOfCommands like the following earlier in the code:
char *arrayOfCommands[] = {"cat", "./test.txt", NULL};
If I understand you correctly your program is called hwk and for whatever reason you think ./hwk ./test.txt should be parsed but that means you should be parsing argv.
Okay now that that's out of the way let's look at the bigger problem of how you are setting things up.
So when a shell parses out pipes it does there's quite a bit going on. Consider the following:
foo fooparam1 fooparam2 | bar barparam1 | baz bazparam1 bazparam2
The shell uses recursion to solve the problem:
foo fooparam1 fooparam2 | ( bar barparam1 | baz bazparam1 bazparam2 )
So it would look SOMETHING like:
spawn_sub_pipes(const char *str) {
char *cmd = strtok(str, "|");
char *rest = strtok(NULL, "|");
int fds[2];
pipe(fds[]);
int pid = fork();
if ( pid < 0 ) {
perror("pipe error");
exit(-1);
}
if ( pid ) { /* parent is the writer */
close(fds[0]); /* close reading pipe */
dup2(fds[1], 1); /* we attach stdout to the pipe */
}
if ( pid == 0 ) {
close(fds[1]);
dup2(fds[0], 0); /* attach the pipe to stdin */
if ( rest ) { /* fork next children */
spawn_sub_pipes(rest);
}
execvpe(cmd);
}
}
IMPORTANT NOTE
I have just written the above code out without testing it. Get the idea from it but don't use it verbatim.

Resources