How does shell execute piped commands? - shell

I want to understand that how does shell executes piped commands ? e.g. cat | more. I am aware that for executing a normal command shell does a fork, execute it and then child returns. But how does shell internally handle the execution of piped commands ?

Considering for example cat | grep, the shell first forks itself to start cat, and then forks itself once more to start grep.
Before calling one of the exec* family of functions in the two newly created processes to start the two programs, the tricky part is setting up the pipe and redirecting the descriptors. The pipe(2) system call is used in the shell process before forking to return a pair of descriptors which both children inherit - a reading end and a writing end.
The reading end will be closed in the first process (cat), and stdout will be redirected to the writing end using the dup2(2) system call. Similarly, the writing end in the second process (grep) will be closed and stdin will be redirected to the reading end again using dup2(2).
This way both programs are unaware of a pipe because they just work with the standard input/output.

It sets up a pipe using the pipe system call, forks two processes instead of one and attaches one end of the pipe to the first process' stdout and the other end to the second process' stdin.

The same, just the stdout of one application is the same as the next stdin. http://unixwiz.net/techtips/remap-pipe-fds.html

Related

Execution order of subshells?

While trying to solve other problems, I have come across the following bash script in Alex B's answer in this question:
#!/bin/bash
(
# Wait for lock on /var/lock/.myscript.exclusivelock (fd 200) for 10 seconds
flock -x -w 10 200 || exit 1
# Do stuff
) 200>/var/lock/.myscript.exclusivelock
I have problems understanding that script. According to flock's manual, the file descriptor (the 200) in flock -x -w 10 200 must relate to an open file.
Where is that descriptor / file opened? If it is the 200>/var/lock/.myscript.exclusivelock which opens the descriptor, that would mean that this part is executed before the subshell, which is the opposite of what I have thought when I initially have looked at this script.
This leads me to my question: What is the execution order of subshells in bash, in relation to the main script (i.e. the script opening the subshells) as well as in relation to other subshells which the same main script might spawn?
From reading other articles and the bash manual, I believe I have only learned that subshells are executed "concurrently", but I didn't see any statement explaining if there are execptions from this (one obvious exception would be when the main script would need the output of a subshell, like echo foo $(cat bar)).
200>, the redirection operator, opens the file using descriptor 200. It is indeed processed before the subshell. That file descriptor is then inherited by the subshell.
There is nothing inherently concurrent about subshells. You may be thinking of pipelines, like a | b | c, where a, b, and c are all commands that run concurrently. The fact that each is run in a subshell (usually a subprocess proper, if they are external commands, but even shell built-ins execute in a subshell) is an implementation detail of the pipeline.
To elaborate,
First, the shell parses this command. It identifies the complex command (...) with an output redirection.
It opens /var/lock/.myscript.exclusivelock in write mode on file descriptor 200.
It executes the subshell, which inherits all open file descriptors, including 200.
In the subshell, it executes flock, which inherits all open file descriptors from its parent, the subshell. It does its thing on file descriptor 200, as requested by its argument.
Once the subshell exits, any file opened by one of its redirection operators is closed by the shell.

Piping input to a shell command and keeping the created shell alive

My overarching program is a shell script. This shell script calls a C program that I need to pipe input to, and ultimately the C program will create a shell.
However, when I pipe my input into the C program within the shell script
Do_Other_Stuff
./my_prog < file1
I can't get the shell to stay alive. Running just,
Do_Other_Stuff
./my_prog
works, as I have to input the stdin myself, and the shell correctly spawns when my_prog exits. I'm pretty sure wrapping up the ./my_prog call in a C program, and compiling and running that would work, but I'm curious as to whether there's a cleaner way with shell.
I've tried several combinations of using cat file1 | ./my_prog and using & in different situations, and haven't had any success.
Thanks!
Try:
cat file1 - | ./myprog
Many programs recognize the "filename" - to mean stdin.
Do you have access to the C program source code? My guess is that the C program is using istty(0) to determine if stdin is coming from a terminal. It probably only creates an interactive shell when that is the case. Using stdin redirection, whether from a file or a pipe, means that istty(0) returns false.

What is a simple explanation for how pipes work in Bash?

I often use pipes in Bash, e.g.:
dmesg | less
Although I know what this outputs, it takes dmesg and lets me scroll through it with less, I do not understand what the | is doing. Is it simply the opposite of >?
Is there a simple, or metaphorical explanation for what | does?
What goes on when several pipes are used in a single line?
Is the behavior of pipes consistent everywhere it appears in a Bash script?
A Unix pipe connects the STDOUT (standard output) file descriptor of the first process to the STDIN (standard input) of the second. What happens then is that when the first process writes to its STDOUT, that output can be immediately read (from STDIN) by the second process.
Using multiple pipes is no different than using a single pipe. Each pipe is independent, and simply links the STDOUT and STDIN of the adjacent processes.
Your third question is a little bit ambiguous. Yes, pipes, as such, are consistent everywhere in a bash script. However, the pipe character | can represent different things. Double pipe (||), represents the "or" operator, for example.
In Linux (and Unix in general) each process has three default file descriptors:
fd #0 Represents the standard input of the process
fd #1 Represents the standard output of the process
fd #2 Represents the standard error output of the process
Normally, when you run a simple program these file descriptors by default are configured as following:
default input is read from the keyboard
Standard output is configured to be the monitor
Standard error is configured to be the monitor also
Bash provides several operators to change this behavior (take a look to the >, >> and < operators for example). Thus, you can redirect the output to something other than the standard output or read your input from other stream different than the keyboard. Specially interesting the case when two programs are collaborating in such way that one uses the output of the other as its input. To make this collaboration easy Bash provides the pipe operator |. Please note the usage of collaboration instead of chaining. I avoided the usage of this term since in fact a pipe is not sequential. A normal command line with pipes has the following aspect:
> program_1 | program_2 | ... | program_n
The above command line is a little bit misleading: user could think that program_2 gets its input once the program_1 has finished its execution, which is not correct. In fact, what bash does is to launch ALL the programs in parallel and it configures the inputs outputs accordingly so every program gets its input from the previous one and delivers its output to the next one (in the command line established order).
Following is a simple example from Creating pipe in C of creating a pipe between a parent and child process. The important part is the call to the pipe() and how the parent closes fd1 (writing side) and how the child closes fd1 (writing side). Please, note that the pipe is a unidirectional communication channel. Thus, data can only flow in one direction: fd1 towards fd[0]. For more information take a look to the manual page of pipe().
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
Last but not least, when you have a command line in the form:
> program_1 | program_2 | program_3
The return code of the whole line is set to the last command. In this case program_3. If you would like to get an intermediate return code you have to set the pipefail or get it from the PIPESTATUS.
Every standard process in Unix has at least three file descriptors, which are sort of like interfaces:
Standard output, which is the place where the process prints its data (most of the time the console, that is, your screen or terminal).
Standard input, which is the place it gets its data from (most of the time it may be something akin to your keyboard).
Standard error, which is the place where errors and sometimes other out-of-band data goes. It's not interesting right now because pipes don't normally deal with it.
The pipe connects the standard output of the process to the left to the standard input of the process of the right. You can think of it as a dedicated program that takes care of copying everything that one program prints, and feeding it to the next program (the one after the pipe symbol). It's not exactly that, but it's an adequate enough analogy.
Each pipe operates on exactly two things: the standard output coming from its left and the input stream expected at its right. Each of those could be attached to a single process or another bit of the pipeline, which is the case in a multi-pipe command line. But that's not relevant to the actual operation of the pipe; each pipe does its own.
The redirection operator (>) does something related, but simpler: by default it sends the standard output of a process directly to a file. As you can see it's not the opposite of a pipe, but actually complementary. The opposite of > is unsurprisingly <, which takes the content of a file and sends it to the standard input of a process (think of it as a program that reads a file byte by byte and types it in a process for you).
In short, as described, there are three key 'special' file descriptors to be aware of. The shell by default send the keyboard to stdin and sends stdout and stderr to the screen:
A pipeline is just a shell convenience which attaches the stdout of one process directly to the stdin of the next:
There are a lot of subtleties to how this works, for example, the stderr stream might not be piped as you would expect, as shown below:
I have spent quite some time trying to write a detailed but beginner friendly explanation of pipelines in Bash. The full content is at:
https://effective-shell.com/docs/part-2-core-skills/7-thinking-in-pipelines/
A pipe takes the output of a process, by output I mean the standard output (stdout on UNIX) and passes it on the standard input (stdin) of another process. It is not the opposite of the simple right redirection > which purpose is to redirect an output to another output.
For example, take the echo command on Linux which is simply printing a string passed in parameter on the standard output. If you use a simple redirect like :
echo "Hello world" > helloworld.txt
the shell will redirect the normal output initially intended to be on stdout and print it directly into the file helloworld.txt.
Now, take this example which involves the pipe :
ls -l | grep helloworld.txt
The standard output of the ls command will be outputed at the entry of grep, so how does this work?
Programs such as grep when they're being used without any arguments are simply reading and waiting for something to be passed on their standard input (stdin). When they catch something, like the ouput of the ls command, grep acts normally by finding an occurence of what you're searching for.
Pipes are very simple like this.
You have the output of one command. You can provide this output as the input into another command using pipe. You can pipe as many commands as you want.
ex:
ls | grep my | grep files
This first lists the files in the working directory. This output is checked by the grep command for the word "my". The output of this is now into the second grep command which finally searches for the word "files". Thats it.
The pipe operator takes the output of the first command, and 'pipes' it to the second one by connecting stdin and stdout.
In your example, instead of the output of dmesg command going to stdout (and throwing it out on the console), it is going right into your next command.
| puts the STDOUT of the command at left side to the STDIN of the command of right side.
If you use multiple pipes, it's just a chain of pipes. First commands output is set to second commands input. Second commands output is set to next commands input. An so on.
It's available in all Linux/widows based command interpreter.
All of these answere are great. Something that I would just like to mention, is that a pipe in bash (which has the same concept as a unix/linux, or windows named pipe) is just like a pipe in real life.
If you think of the program before the pipe as a source of water, the pipe as a water pipe, and the program after the pipe as something that uses the water (with the program output as water), then you pretty much understand how pipes work.
And remember that all apps in a pipeline run in parallel.
Regarding the efficiency issue of pipe:
A command can access and process the data at its input before previous pipe command to complete that means computing power utilization efficiency if resources available.
Pipe does not require to save output of a command to a file before next command to access its input ( there is no I/O operation between two commands) that means reduction in costly I/O operations and disk space efficiency.
If you treat each unix command as a standalone module,
but you need them to talk to each other using text as a consistent interface,
how can it be done?
cmd input output
echo "foobar" string "foobar"
cat "somefile.txt" file *string inside the file*
grep "pattern" "a.txt" pattern, input file *matched string*
You can say | is a metaphor for passing the baton in a relay marathon.
Its even shaped like one!
cat -> echo -> less -> awk -> perl is analogous to cat | echo | less | awk | perl.
cat "somefile.txt" | echo
cat pass its output for echo to use.
What happens when there is more than one input?
cat "somefile.txt" | grep "pattern"
There is an implicit rule that says "pass it as input file rather than pattern" for grep.
You will slowly develop the eye for knowing which parameter is which by experience.

How does find and printf work when using pipes in bash scripting

Suppose I use the printf in the find command like this:
find ./folder -printf "%f\n" | other command which uses the result of printf
in the other command part, I may be having a sort or something similar
what exactly does printf do in this case? where does it print the file names before the process in the part after "|" happens?
if I sort the filenames for example, it will first sort them, and then print them sorted on the monitor, but before that, how exactly does the part after | get the files unsorted in order to sort them? does the printf in this case give the filenames as input to the part after | and then the part after | prints the file names sorted in the output?
sorry for my english :(
Your shell calls pipe() which creates two file descriptors. Writing into one buffers data in the kernel which is available to be read by the other. Then it calls fork() to make a new process for the find command. After the fork() it closes stdout (always fd 1) and uses dup2() to copy one end of the pipe to stdout. Then it uses exec() to run find (replacing the copy of the shell in the subprocess with find). When find runs it just prints to stdout as normal, but it has inherited it from the shell which made it the pipe. Meanwhile the shell is doing the same thing for other command... with stdin so that it is created with fd 0 connected to the other end of the pipe.
Yes, that is how pipes work. The output from the first process is the input to the second. In terms of implementation, the shell creates a socket which receives input from the first process from its standard output, and writes output to the second process on its standard input.
... You should perhaps read an introduction to Unix shell programming if you have this type of questions.

Waiting for named pipe to be opened in subscript in BASH?

I've got two scripts, one that takes a couple filenames as input and writes data to the pipes (really passes the pipes as arguments to program I wrote). And then the other one calls the first script with some named pipes as inputs and then calls some other programs to process the data from the pipes.
My problem is that my pipes are stalling and what I think is happening is the first bash script is called in the background from the second script, which then goes on to immediately start up the consumer processes, so I think the readers are being opened before the writers (in the subscript), which can cause a stall?
Is there a way to synchronize on a named pipe and wait for it to be opened in bash?
I don't think that's your problem.
If the producer starts later than the consumer, no big deal.
Example:
Window 1
$ mkfifo foo.pipe
$ cat foo.pipe
(hangs)
Window 2
$ echo 'something' > foo.pipe
Window 1
something
(exits)
Perhaps your problem is that one process is consuming the output of the fifo, then the producer quits, then you're trying to read from the fifo again.
In that case, it would hang indefinitely.
e.g. after the above sequence:
Window 1
$ cat foo.pipe
hangs until you run another echo something > foo.pipe.

Resources