Skip to content

Program that allows you to pass the content of a file through multiple shell commands using pipes.

Notifications You must be signed in to change notification settings

S-LucasSerrano/pipex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pipex | slucas-s

This program replicates the functionality of the shell command < infile cmd1 | cmd2 | cmd3 | ... > outfile. Taking whatever is in infile and using it as the input for the first command, passing the result as the input of the next command with a pipe, and so on. Saving the final result in outfile. To use it, once generated with the Makefile, just do ./pipex infile “cmd1” “cmd2” “cmd3” … outfile.

Index


Code


First, as always, I check if the parameters of the program are valid. If there are at least 4 parameters: the name of the input file, 2 commands and an output file. And if we have read permission to the infile.

After that, I initialize the pipex data structure. Which contains all variables the program needs to work. A couple of file descriptors for the input and output files, and a list of commands. The list, as done in the libft, contains a pointer to a command structure for the content, and a pointer to the next element of the list.

The command struct has a file string and a argv string array. This is the info that we need to pass to the execve function, that we use to execute the commands.

typedef struct s_command	// Each element of the list has a pointer to one of these as its content
{
	char				*file;
	char				**argv;
}	t_command;

typedef struct s_pipex
{
	int		in_fd;		// the fd of the opened input file
	int		out_fd;		// the fd of the output file
	t_list		*cmd_list;	// the list of commands to execute
}	t_pipex;

To initialize the list I use the PATHS environment variable. I add each command passed as a program parameter to each path and try to open it. If one of the openings works, it’s because the command exists. So I add a new element to the list saving that as the file of the command struct and a split of the pipex program argument as the argv. If something in this process fails, it prints an error with what was wrong -like an invalid command or there not existing paths in envp- and the program ends.

So if, for example, I do ./pipex infile “ls -l” “wc -w” outfile. I will end up with a list wich first element has /bin/ls as file and {“ls”, “-l”, NULL} as argv. The second element of the list will be the same for the wc command.

Executing the commands

Now that I have all that is needed in the data structure, I start a recursive loop that executes all commands in the command list. The function receives the data structure and a pointer to the command of the list that it should execute. The first call to the function would execute the last command of the list.

void	exec_cmd(t_pipex *data, t_list *current);

Then I generate a pipe with pipe(int [2]) < https://www.tutorialspoint.com/unix_system_calls/pipe.htm >. This function generates a couple of file descriptors. The fd[0] is for reading, and fd[1] for writing. Next, if the current element is not the first one of the command list,int fork() is called < https://man7.org/linux/man-pages/man2/fork.2.html >. This creates a new copy of the current process. Essentially, your program is now running twice, both copies with the same info. But Fork will return a different value to each copy. The new process, the child, received 0. While the father has the process identifier of the new child process. Now a couple of ifs statements allow us to make something different in each case.

If we are the child process, we need to make sure that the standard output, where all commands write by default, is the write end of the pipe using dup2(int fd1, int fd2) < https://man7.org/linux/man-pages/man2/dup.2.html >. This function makes the file descriptor fd2 be the same as fd1. With this, the next command will write its output in the pipe. Then, the child process calls the recursive function again, passing the data structure and the previous command to current of the list.

The father process waits until the child ends. Uses dup2 to make the standard input the read end of the pipe. So whatever was written in the pipe by the previous command will be the input for the current one. And calls the function execve (...) to replace this process with a new one. For that it needs to receive the file to execute, with its path. A char ** that will be the new process’ argv and other char ** that will be its envp. Calling this function ends the current process, so nothing after the call will ever be executed.

To execute < ls | grep | wc >, the program will do:
---(fork)---(wait to child)---> wc
      ---(fork)---(wait to child)---> grep
            ---------> ls

Contact


Feel free to clone this project, check the code or contact me if you want or find something wrong or missing in this documentation.

Good luck with your projects!

About

Program that allows you to pass the content of a file through multiple shell commands using pipes.

Topics

Resources

Stars

Watchers

Forks