Hey so I am using the summer to learn unix and c. I want to expand on a current program I have by implementing pipes. How can I modify my code so that it parses "|". I will be using execvp to run commands. In the shell if I type cat file | tr a A I want to split cat and tr so that it executes both Here is my current code
numBytes = read(0, buffer, INPUT_BUFFER_SIZE);
char *token;
inputBuffer[numBytes] = '\0';
token = strtok(buffer, " \n");
int i = 0;
while(token != NULL){
userInput[i] = token;
token = strtok(NULL, " \n");
++i;
}
userInput[i] = 0;
You can use our program pipeline.c which is shows you how to implement pipes. You split a pipeline who | awk '{print $1}' | sort | uniq -c | sort -n and use arrays to execute the pipeline:
static char *cmd0[] = {"who", 0};
static char *cmd1[] = {"awk", "{print $1}", 0};
static char *cmd2[] = {"sort", 0};
static char *cmd3[] = {"uniq", "-c", 0};
static char *cmd4[] = {"sort", "-n", 0};
When you have split the command into arrays like above, you can use code like below.
#define _XOPEN_SOURCE 500
/*
* pipeline.c
* One way to create a pipeline of N processes using execvp
* written 2015 by: Jonathan Leffler
* test, packaging and maintenance by Niklas Rosencrantz nik#kth.se
* /
#ifndef STDERR_H_INCLUDED
#define STDERR_H_INCLU, DED
#endif /* STDERR_H_INCLUDED */
/* pipeline.c */
#include <assert.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
typedef int Pipe[2];
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
//fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_syswarn(char const *fmt, ...) {
va_list args;
// va_start(args, fmt);
err_vsyswarn(fmt, args);
// va_end(args);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
//va_start(args, fmt);
err_vsyswarn(fmt, args);
//va_end(args);
exit(1);
}
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds) {
assert(ncmds >= 1);
if (ncmds > 1) {
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0) {
/* Child */
exec_pipe_command(ncmds - 1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds - 1][0], cmds[ncmds - 1]);
err_sysexit("Failed to exec %s", cmds[ncmds - 1][0]);
/*NOTREACHED*/
}
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output) {
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds) {
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
static void exec_arguments(int argc, char **argv)
{
/* Split the command line into sequences of arguments */
/* Break at pipe symbols as arguments on their own */
char **cmdv[argc/2]; // Way too many
char *args[argc+1];
int cmdn = 0;
int argn = 0;
cmdv[cmdn++] = &args[argn];
for (int i = 1; i < argc; i++)
{
char *arg = argv[i];
if (strcmp(arg, "|") == 0)
{
if (i == 1)
err_sysexit("Syntax error: pipe before any command");
if (args[argn-1] == 0)
err_sysexit("Syntax error: two pipes with no command between");
arg = 0;
}
args[argn++] = arg;
if (arg == 0)
cmdv[cmdn++] = &args[argn];
}
if (args[argn-1] == 0)
err_sysexit("Syntax error: pipe with no command following");
args[argn] = 0;
exec_pipeline(cmdn, cmdv);
}
/* who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = {"who", 0};
static char *cmd1[] = {"awk", "{print $1}", 0};
static char *cmd2[] = {"sort", 0};
static char *cmd3[] = {"uniq", "-c", 0};
static char *cmd4[] = {"sort", "-n", 0};
static char **cmds[] = {cmd0, cmd1, cmd2, cmd3, cmd4};
static int ncmds = sizeof(cmds) / sizeof(cmds[0]);
int main(int argc, char **argv) {
/* The most basic example */
char *execArgs[] = { "ls", "-al", 0 };
execvp("ls", execArgs);
if (argc == 1) {
/* Run the built in pipe-line */
exec_pipeline(ncmds, cmds);
} else {
/* Run command line specified by user */
exec_arguments(argc, argv);
}
return(0);
}
Related
I am trying to implement a custom Unix shell, however, the last command being passed to it is never recognized. When I run a "ls" nothing happens but if i do "ls ls" it acts as if only one ls has been passed, or if i do "ls -o" it only does "ls" but if i do "ls -o -o" it does "ls -o", it should function similarly to a standard Unix shell.
#include "apue.h"
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int parse(char* buf, char **cmds);
static void sig_int(int);
int
main(void)
{
char *path;
path = getenv("PATH");
char buf[MAXLINE];
char *envp[] = {path, NULL};
pid_t pid;
int status;
char *command[MAXLINE];
char *input;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal error");
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
parse(buf, command);
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
input = command[0];
execvpe(input, command, envp);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
/*parses input into something runable*/
int parse(char* buf, char **cmds)
{
char *sep;
int count;
count = 0;
while((sep = strchr(buf, ' ')))
{
cmds[count]= buf;
*sep = '\0';
buf = sep +1;
count++;
while (*buf && (*buf == ' ')) /* Ignore spaces */
buf++;
}
cmds[count] = NULL;
if(count == 0)
return 1;
return 0;
}
I am trying to write my own function for piping in C. First, once I have already forked and am in the child, I check to detect where the pipe is input, as below. I am looping through my own StringArray I've created (sa), and copying the token before into char cmd1[64] (initialized previously) and the token after into char cmd2[64]. The int piping is given a value for the next step.
if(strcmp(sa[i], "|") == 0)
{
printf("got to the pipe\n");
sa[i]=NULL;
strcpy(cmd1, sa[i-1]);
strcpy(cmd2, sa[i+1]);
piping=2;
}
Then, the program reaches this statement:
if (piping)
{
if (pipe(fd) != 0){
perror("Failed to create pipe");
}
if ((cpPid = fork()) == -1){
perror("Failed to fork");
}
if (cpPid == 0){
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
execvp(cmd1, sa);
error("failed to exec command 1");
}
else
{
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
execvp(cmd2, sa);
error("failed to exec command 2");
}
}
My program completely crashes and only gives out Unknown error 10275024. Can someone help me to figure out what is going wrong?
Please have a look at this working example that should help you execute a pipeline using C.
#include <assert.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
typedef int Pipe[2];
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);
static void err_vsyswarn(char const *fmt, va_list args) {
int errnum = errno;
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_syswarn(char const *fmt, ...) {
va_list args;
err_vsyswarn(fmt, args);;
}
static void err_sysexit(char const *fmt, ...) {
va_list args;
err_vsyswarn(fmt, args);
exit(1);
}
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds) {
assert(ncmds >= 1);
if (ncmds > 1) {
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0) {
/* Child */
exec_pipe_command(ncmds - 1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds - 1][0], cmds[ncmds - 1]);
err_sysexit("Failed to exec %s", cmds[ncmds - 1][0]);
/*NOTREACHED*/
}
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output) {
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds) {
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
/* who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = {"who", 0};
static char *cmd1[] = {"awk", "{print $1}", 0};
static char *cmd2[] = {"sort", 0};
static char *cmd3[] = {"uniq", "-c", 0};
static char *cmd4[] = {"sort", "-n", 0};
static char **cmds[] = {cmd0, cmd1, cmd2, cmd3, cmd4};
static int ncmds = sizeof(cmds) / sizeof(cmds[0]);
int main(int argc, char **argv) {
exec_pipeline(ncmds, cmds);
return (0);
}
The pipeline example in the code is
who | awk '{print $1}' | sort | uniq -c | sort -n
You can use any pipeline.
Test:
dac#dac-Latitude-E7450 ~/C/>
who | awk '{print $1}' | sort | uniq -c | sort -n
1 dac
dac#dac-Latitude-E7450 ~/C/> gcc main.c
dac#dac-Latitude-E7450 ~/C/> ./a.out
1 dac
I have something like a basic shell.
My problem is that when i do something like ./test ls -l it doesnt use the -l argument.
It's exactly the same as if i did ./test ls.
According to man, int execvp(const char *file, char *const argv[]), so isn't execvp suposed to read the entire **argv?
int mySystem(char* str[]){
wordexp_t p;
char **w;
wordexp(str[1],&p,0);
w = p.we_wordv;
int q= fork();
if(q==0){
execvp(w[0],w);
perror("erro");
_exit(-1);
}
int sp;
wordfree(&p);
wait(&sp);
if(WIFEXITED(sp)){
return WEXITSTATUS(sp);
}
return 0;
}
int main(int argc,char **argv){
mySystem(argv);
return 0;
}
Thanks.
Your argument should look like this (note the trailing 0 and that there are no quotes)
static char *cmd[] = { "ls", "-l", 0 };
So you can try and hard-code the call to exec and see that it works.
I can't debug your code because it is not complete. If you use a debugger you can inspect the values. Remember that the args must be in an array to exec. Here comes some basic code that does it and explains it, and also admits you to make a pipeline.
/* One way to create a pipeline of N processes */
/* stderr.h */
#ifndef STDERR_H_INCLUDED
#define STDERR_H_INCLUDED
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);
#endif /* STDERR_H_INCLUDED */
/* pipeline.c */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/*#include "stderr.h"*/
typedef int Pipe[2];
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
if (ncmds > 1)
{
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0)
{
/* Child */
exec_pipe_command(ncmds-1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
/*NOTREACHED*/
}
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
/* Collect dead children until there are none left */
static void corpse_collector(void)
{
pid_t parent = getpid();
pid_t corpse;
int status;
while ((corpse = waitpid(0, &status, 0)) != -1)
{
fprintf(stderr, "%d: child %d status 0x%.4X\n",
(int)parent, (int)corpse, status);
}
}
/* who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = { "who", 0 };
static char *cmd1[] = { "awk", "{print $1}", 0 };
static char *cmd2[] = { "sort", 0 };
static char *cmd3[] = { "uniq", "-c", 0 };
static char *cmd4[] = { "sort", "-n", 0 };
static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 };
static int ncmds = sizeof(cmds) / sizeof(cmds[0]);
static void exec_arguments(int argc, char **argv)
{
/* Split the command line into sequences of arguments */
/* Break at pipe symbols as arguments on their own */
char **cmdv[argc/2]; // Way too many
char *args[argc+1];
int cmdn = 0;
int argn = 0;
cmdv[cmdn++] = &args[argn];
for (int i = 1; i < argc; i++)
{
char *arg = argv[i];
if (strcmp(arg, "|") == 0)
{
if (i == 1)
err_sysexit("Syntax error: pipe before any command");
if (args[argn-1] == 0)
err_sysexit("Syntax error: two pipes with no command between");
arg = 0;
}
args[argn++] = arg;
if (arg == 0)
cmdv[cmdn++] = &args[argn];
}
if (args[argn-1] == 0)
err_sysexit("Syntax error: pipe with no command following");
args[argn] = 0;
exec_pipeline(cmdn, cmdv);
}
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
printf("%s", argv[1]);
err_setarg0(argv[0]);
if (argc == 1)
{
/* Run the built in pipe-line */
exec_pipeline(ncmds, cmds);
}
else
{
/* Run command line specified by user */
char *original = "ls";
char **pointer_to_original = &original;
for (int i = 0; i < argc; ++i) { printf("blarg blarg %i\n", argc);
printf("%s", argv[i]);// = "ls";
}
exec_arguments(argc, argv);
}
corpse_collector();
return(0);
}
/* stderr.c */
/*#include "stderr.h"*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_syswarn(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
You don't need wordexp to interpret command-line arguments. The shell will already have done that for you. Just pass argv + 1 to execvp.
The only reason you would need wordexp would be to me interpret entire command lines passed as single arguments (eg. `myutil "ls -l *.txt") or read from a file or some such. But why would you want all that complication?
I have a program being called by the arguments:
pipeline -f outfile2 < infile > outfile1
It is supposed to mimick the functionality of the bash script:
(rev | sort | uniq -c | tee outfile2| wc) < infile > outfile1
I understand how to get the piping set up, but I do not understand how to get the initial read. I've captured the outfile2 filename to a variable, but I think I can keep the outer two and the operating system will pick them up as stdin and stdout respectively. How would I programatically read the stdin into a pipe in my parent process?
UPDATE follows: The following code does nothing to modify the output files with the command line argument: pipeline -f outfile2 outfile1
obviously with real file names.
main:
char *fp;
int c;
/* parse command line arguments */
parse_argv(argc, argv);
if (pipe(pipeOne) == -1){ /* Create the pipe */
perror("pipe one");
error(-1);
}
if (pipeOne[1] != 1){
dup2(pipeOne[1], stdout);
exit(-1);
}
while ((c = getc(stdin)) != EOF){
if ((write(pipeOne[1], c, 1)) < 1)
{
perror("Write to pipe failed.");
exit(-1);
}
}
wc_child();
/* begin reading file to pipe */
if (close(pipeOne[0]) == -1){ /* Close unused read end */
perror("closing pipes");
exit(-1);
}
close (pipeOne[1]);
wc_child:
void wc_child(){
int numread;
switch (fork()) { /* Create a child process */
case -1:
perror("fork for rev_child");
case 0: /* Child */
/*if (close(pipeOne[1]) == -1) /* Close unused write end */
/*perror("closing pipes");*/
if (close(pipeOne[1]) == -1){ /* Close unused write end */
perror("closing pipes");
exit(-1);
}
dup2(pipeOne[0], stdin);
/* dup2(pipeFive[0], stdin);*/
/* Not sure how to set up stdout since it will be going to a file.*/
for(;;){
execlp("wc","wc");
}
break;
default: /* Parent */
return 0;
}
return -1; /*return -1 because it never should reach this code. If it does that indicates a problem*/
}
You could #include <unistd.h> and then read straight from the file descriptor STDIN_FILENO. dup2(pipeOne[0], stdin); should probably also use STDIN_FILENO, as stdin is a FILE * and not a descriptor.
I don't think you really want to do any of that, though. Instead of reading from stdin, you should be hooking stdout up to the write end of a pipe (and the next stage of the pipeline to the read end) and then execing to begin the first stage of your pipeline. The invoked subprocess will then read from stdin, transform the input and write to stdout, filling the pipe with data.
There are quite a lot of different ways you could do it; it is probably sufficient to use fgets() to read the data, and then write() carefully to write to the pipe:
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
size_t len = strlen(line);
if (write(pipefd[1], line, len) != len)
{
fprintf(stderr, "Failed to write to pipe\n");
exit(1);
}
}
To simulate the pipeline, you really don't need to copy anything from standard input to a pipe; you can simply let rev read standard input.
Here is code swiftly derived from my answer to C Minishell — Adding Pipelines that does what you need.
/*
** How to write from stdin to a pipe in C
** https://stackoverflow.com/questions/19826211
**
** Write program pipeline to be invoked as:
**
** pipeline -f outfile2 < infile > outfile1
**
** It should mimic the functionality of the bash script:
**
** (rev | sort | uniq -c | tee outfile2 | wc) < infile > outfile1
**
** Refactored, with equivalent functionality:
**
** rev < infile | sort | uniq -c | tee outfile2 | wc > outfile1
**
** Based on answer to SO 13636252 C Minishell adding pipelines
*/
/* pipeline.c */
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/* who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = { "rev", 0 };
static char *cmd1[] = { "sort", 0 };
static char *cmd2[] = { "uniq", "-c", 0 };
static char *cmd3[] = { "tee", 0, 0 };
static char *cmd4[] = { "wc", 0 };
static char **cmds[] = { cmd0, cmd1, cmd2, cmd3, cmd4 };
static int ncmds = sizeof(cmds) / sizeof(cmds[0]);
static char const usestr[] = "[-f filename]";
typedef int Pipe[2];
/* These functions normally declared in stderr.h */
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);
static void err_usage(char const *usestr);
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
if (ncmds > 1)
{
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0)
{
/* Child */
exec_pipe_command(ncmds-1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
/*NOTREACHED*/
}
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
/* Collect dead children until there are none left */
static void corpse_collector(void)
{
pid_t parent = getpid();
pid_t corpse;
int status;
while ((corpse = waitpid(-1, &status, 0)) != -1)
{
fprintf(stderr, "%d: child %d status 0x%.4X\n",
(int)parent, (int)corpse, status);
}
}
int main(int argc, char **argv)
{
int opt;
char *filename = "outfile2"; // Default file name
err_setarg0(argv[0]);
while ((opt = getopt(argc, argv, "f:")) != -1)
{
switch (opt)
{
case 'f':
filename = optarg;
break;
default:
err_usage(usestr);
break;
}
}
if (optind != argc)
err_usage(usestr);
/* Set the file name for tee to write to */
cmd3[1] = filename;
exec_pipeline(ncmds, cmds);
corpse_collector();
return(0);
}
/* Normally in stderr.c */
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_usage(char const *usestr)
{
fprintf(stderr, "Usage: %s %s\n", arg0, usestr);
exit(1);
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_syswarn(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
Example output (outfile1 when infile is a copy of the program's source code):
125 691 4879
Example output (first and last 10 lines of outfile2):
22
1 )... ,tmf* tsnoc rahc(nrawsys_rre diov citats
1 )... ,tmf* tsnoc rahc(tixesys_rre diov citats
1 )0 < ))(krof = dip(( fi
1 )0 < ))(krof = dip(( fi
1 )0 =! )tupni(epip( fi
1 )0 =! dip( fi
1 )0 =! munrre( fi
1 )0 == dip( fi
1 )0vgra* rahc tsnoc(0grates_rre diov citats
...
1 >h.tressa< edulcni#
1 C ni epip a ot nidts morf etirw ot woH **
1 eman elif tluafeD // ;"2eliftuo" = emanelif* rahc
1 senilepip gnidda llehsiniM C 25263631 OS ot rewsna no desaB **
10 {
3 {
2 {
10 }
3 }
2 }
(I note that the word count is the same whether the lines are reversed or not, so the rev command serves no purpose in the pipeline.) There is some diagnostic output that gets caught; you can easily suppress it.
This is my first question. Hopefully it will not be dumm. Im having problems to write a program that produce "ps aux | grep firefox | tee processes.txt". I succeeded with 1 pipe like "ps aux | grep firefox ", but when I tried to generalize I have problems. My intention is to use pipes to understand them. I know that is possible with open. Any hint would be great!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void)
{
char *** args = NULL;
pid_t fork_id = -1;
int ** pipes = NULL;
int status = 0;
int i = 0, k = 0;
int check = 0;
args = calloc(3, sizeof(char **));
args [0] = calloc(3, sizeof(char *));
args [1] = calloc(3, sizeof(char *));
args [2] = calloc(3, sizeof(char *));
args[0][0]= "ps";
args[0][1]="aux";
args[0][2]= NULL;
args[1][0]= "grep";
args[1][1]= "firefox";
args[1][2]= NULL;
args[2][0]= "tee";
args[2][1]= "processes.txt";
args[2][2]= NULL;
pipes=calloc(2,sizeof(int *));
for(i=0;i<2;++i){
pipes[i]=calloc(2,sizeof(int));
}
for(i=0;i<2;++i){
pipes[i]=calloc(2,sizeof(int));
check=pipe(pipes[i]);
if(check<0){
perror("pipe");
exit(EXIT_FAILURE);
}
}
for(i=0;i<3;++i){
if ((fork_id = fork()) < 0) {
perror("fork()");
exit(1);
}
if ((i ==0)&& (fork_id == 0)){
close(pipes[i][0]);
close(1);
dup(pipes[i][1]);
close(pipes[i][1]);
execvp(args[i][0], args[0]);
} else if ((i!= 0)&&(i != 2) && (fork_id == 0)){
close(0);
dup(pipes[i][0]);
close(pipes[i][0]);
close(1);
dup(pipes[i][1]);
close(pipes[i][1]);
execvp(args[i][0], args[i]);
} else if ((i==2)&&(fork_id != 0)){
close(pipes[i-1][1]);
close(0);
dup(pipes[i-1][0]);
close(pipes[i-1][0]);
execvp(args[i][0], args[i]);
}
wait(&status);
}
for(i=0;i<2;++i){
for(k=0;k<2;++k){
check=close(pipes[i][k]);
if(check<0){
perror("close pipe");
exit(EXIT_FAILURE);
}
}
}
return(0);
}
thx!!
This might be what you're looking for - no need for all those execs and forks and pipes:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *p;
int status;
char line[1024];
p = popen("ps aux | grep firefox | tee processes.txt", "r");
if (!p) {
fprintf(stderr, "Error.");
exit(1);
}
while (fgets(line, sizeof(line) - 1, p)) {
puts(line);
}
pclose(p);
}
The accepted solution to SO 13636252 is trivially adaptable to this pipeline:
/*
** How to create a pipeline of N processes?
** SO 18582446: ps aux | grep firefox | tee processes.txt
** Adaptation of accepted answer to:
** SO 13636252 C Minishell adding pipelines
*/
/* stderr.h */
#ifndef STDERR_H_INCLUDED
#define STDERR_H_INCLUDED
static void err_setarg0(const char *argv0);
static void err_sysexit(char const *fmt, ...);
static void err_syswarn(char const *fmt, ...);
#endif /* STDERR_H_INCLUDED */
/* pipeline.c */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
/*#include "stderr.h"*/
typedef int Pipe[2];
/* Now: ps aux | grep firefox | tee processes.txt */
/* Was: who | awk '{print $1}' | sort | uniq -c | sort -n */
static char *cmd0[] = { "ps", "aux", 0 };
static char *cmd1[] = { "grep", "firefox", 0 };
static char *cmd2[] = { "tee", "processes.txt", 0 };
static char **cmds[] = { cmd0, cmd1, cmd2, };
static int ncmds = sizeof(cmds) / sizeof(cmds[0]);
/* exec_nth_command() and exec_pipe_command() are mutually recursive */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output);
/* With the standard output plumbing sorted, execute Nth command */
static void exec_nth_command(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
if (ncmds > 1)
{
pid_t pid;
Pipe input;
if (pipe(input) != 0)
err_sysexit("Failed to create pipe");
if ((pid = fork()) < 0)
err_sysexit("Failed to fork");
if (pid == 0)
{
/* Child */
exec_pipe_command(ncmds-1, cmds, input);
}
/* Fix standard input to read end of pipe */
dup2(input[0], 0);
close(input[0]);
close(input[1]);
}
execvp(cmds[ncmds-1][0], cmds[ncmds-1]);
err_sysexit("Failed to exec %s", cmds[ncmds-1][0]);
/*NOTREACHED*/
}
/* Given pipe, plumb it to standard output, then execute Nth command */
static void exec_pipe_command(int ncmds, char ***cmds, Pipe output)
{
assert(ncmds >= 1);
/* Fix stdout to write end of pipe */
dup2(output[1], 1);
close(output[0]);
close(output[1]);
exec_nth_command(ncmds, cmds);
}
/* Execute the N commands in the pipeline */
static void exec_pipeline(int ncmds, char ***cmds)
{
assert(ncmds >= 1);
pid_t pid;
if ((pid = fork()) < 0)
err_syswarn("Failed to fork");
if (pid != 0)
return;
exec_nth_command(ncmds, cmds);
}
/* Collect dead children until there are none left */
static void corpse_collector(void)
{
pid_t parent = getpid();
pid_t corpse;
int status;
while ((corpse = waitpid(0, &status, 0)) != -1)
{
fprintf(stderr, "%d: child %d status 0x%.4X\n",
(int)parent, (int)corpse, status);
}
}
static void exec_arguments(int argc, char **argv)
{
/* Split the command line into sequences of arguments */
/* Break at pipe symbols as arguments on their own */
char **cmdv[argc/2]; // Way too many
char *args[argc+1];
int cmdn = 0;
int argn = 0;
cmdv[cmdn++] = &args[argn];
for (int i = 1; i < argc; i++)
{
char *arg = argv[i];
if (strcmp(arg, "|") == 0)
{
if (i == 1)
err_sysexit("Syntax error: pipe before any command");
if (args[argn-1] == 0)
err_sysexit("Syntax error: two pipes with no command between");
arg = 0;
}
args[argn++] = arg;
if (arg == 0)
cmdv[cmdn++] = &args[argn];
}
if (args[argn-1] == 0)
err_sysexit("Syntax error: pipe with no command following");
args[argn] = 0;
exec_pipeline(cmdn, cmdv);
}
#include <signal.h>
typedef void (*SigHandler)(int signum);
static void sigchld_status(void)
{
const char *handling = "Handler";
SigHandler sigchld = signal(SIGCHLD, SIG_IGN);
signal(SIGCHLD, sigchld);
if (sigchld == SIG_IGN)
handling = "Ignored";
else if (sigchld == SIG_DFL)
handling = "Default";
printf("SIGCHLD set to %s\n", handling);
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
sigchld_status();
if (argc == 1)
{
/* Run the built in pipe-line */
exec_pipeline(ncmds, cmds);
}
else
{
/* Run command line specified by user */
exec_arguments(argc, argv);
}
corpse_collector();
return(0);
}
/* stderr.c */
/*#include "stderr.h"*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
static const char *arg0 = "<undefined>";
static void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
static void err_vsyswarn(char const *fmt, va_list args)
{
int errnum = errno;
fprintf(stderr, "%s:%d: ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
}
static void err_syswarn(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
}
static void err_sysexit(char const *fmt, ...)
{
va_list args;
va_start(args, fmt);
err_vsyswarn(fmt, args);
va_end(args);
exit(1);
}
Popen might be of interest to you.
FILE *p = popen("ps aux | grep firefox | tee processes.txt", "r");