Shell - Pipe to multiple commands in a file

I want to run 2 commands at the input to the channel and want to print (in stdout) the output of both.

Each command is a combination of grep, sed and awk.

Both of these commands must be in the same .sh file.

Examples of commands:

cat mult_comm.sh sed 's/World/Boy/g'|grep Boy ; grep World # Input cat input.log Hello World # This command HAS to work EXACTLY like this cat input.log | bash mult_comm.sh 

Expected Result

 Hello Boy Hello World 

Actual output

 Hello Boy 

I tried to use tee

 cat mult_comm.sh tee >(sed 's/World/Boy/g'|grep Boy) | grep World 

But it only gives

 Hello World 

I can change the .sh file as I want, but the pipeed command cannot be changed. Any ideas?

This is similar to OS X / Linux: a pipe in two processes? and pipe output to two different commands , but I can’t figure out how to use named pipes inside the script.

+4
source share
2 answers

By doing

 tee >(some_command) 

bash creates a subshell to run some_command . The stdin assigned half the pipe reading. bash leaves the name of this channel on the command line, so tee will pump its input into the pipe. The subshell stdout and stderr remain unchanged, so they are all the same as tee .

So when you do

 tee >(some_command) | some_other_command 

Now bash first creates a process to start tee and assigns it stdout half of the channel record, and another process to start some_other_command , and its stdin assigned to read half of the same pipe. He then creates another process to run some_command as described above, assigning it stdin half the read of another channel and leaving it stdout and stderr unchanged. However, stdout already redirected to some_other_command and that some_command inherits.

In your actual example

 tee >(sed 's/World/Boy/g'|grep Boy) | grep World 

in the end we get:

  --> sed 's/World/Boy/g' --> grep Boy -- / \ input --> tee --< \ \ \ ----------------------------------------------> grep World 

In one of the questions related to the OP, there is a (not accepted, but correct) answer of F. Hauri , which I adapted here:

 echo Hello World | ((tee /dev/fd/5 | grep World >/dev/fd/4) \ 5>&1 | sed 's/World/Boy/' | grep Boy) 4>&1 

It takes a little practice to read the basisms like the one above. The important part is that

 ( commands ) 5>&1 

Creates a subshell ( ( ) ) and gives the fd subshell number 5, originally copied from stdout ( 5>&1 ). Inside the subshell /dev/fd/5 refers to this fd. Within a subshell, you can redirect stdout , but this will happen after copying stdout to fd5.

+11
source

You can use pee(1) in Debian / Ubuntu, which is available in the moreutils package.

Use for your example, slightly more readable than redirect magic

 echo Hello World | pee 'grep World' 'sed "s/World/Boy/" | grep Boy' 
+1
source

Source: https://habr.com/ru/post/1494613/


All Articles