167

Is it possible to combine output from these two commands?

node ~/projects/trunk/index.js 
python ~/projects/trunk/run.py run

Neither command exits so I'm not sure how to do this.

Steffo
  • 107
  • 4
chovy
  • 2,069
  • 4
    If the programs don't finish, presumably they write output continuously? What do yo want to do with their output? Interleave lines, ...? Why do you want to do this? – vonbrand Feb 14 '13 at 00:37
  • 2
    The node command doesn't output much, but it still needs to run. The python one outputs all requests, I want to capture both and watch them both in the same shell window. – chovy Feb 14 '13 at 05:23
  • The output of both of those commands is already being written to the same stream (they each inherit the same file descriptor for stdout). So you don't have to do anything. – William Pursell Nov 20 '19 at 16:06

9 Answers9

209

You can combine two commands by grouping it with { } :

{ command1 & command2; }

so far, you can redirect the group to a file (last ; before } is mandatory), and the space between the open and closing bracket too.

{ command1 & command2; } > new_file

if you want to separate STDOUT and STDERRin two files :

{ command1 & command2; } > STDOUT_file 2> STDERR_file

If you don't want to run the first command in the background, use this form :

{ command1; command2; }

or

{ command1 && command2; }

to run the second command only if the first is a success

  • 5
    Doesn't matter that they programs don't finish. 'tail -f' doesn't "finish" either, but this still works and combines the outputs of both programs. Works for more than two commands as well. ^c to quit kills only one of the grouped commands. You'll have to kill the other's manually, though. – SuperMagic Feb 14 '13 at 04:58
  • Does not work for me either. I am simply combining two grep commands but when grouped no output is produced and program seems waiting for input. – Petr Peller Jul 31 '14 at 16:39
  • 5
    Seems like you lack the last ; before }, it's mandatory ! – Gilles Quénot Dec 02 '14 at 18:27
  • works great. tested by starting multiple static-server one-liners piped to cat and grep --line-buffered. also, SuperMagic's statement was true for me, ^c only killed one of the commands, rest stayed bg'd. – Mike D Mar 27 '16 at 11:26
  • 6
    Be warned: This doesn't preserve whole lines! You'll get unreliable outputs as lines get split up part way and mixed up among each other. You can try this with { yes {1..20} & yes {1..20}; } | grep -v '^1 2 3' which ideally won't print anything if lines aren't broken. – antak Jan 17 '17 at 01:41
  • 32
    I would rather use && instead of &!

    command1 & command2 - this runs command1 in background and starts command2 immediately, thus running both commands in parallel and messing up the output.

    command1 && command2 - this runs command1 (in foreground) and then, if command1 succedded, runs command2.

    – DUzun Apr 05 '18 at 11:04
  • 6
    @DUzun OP said neither command exits, so with your solution, the second command will never run – Zoey Hewll May 31 '18 at 02:25
  • @ZoeyHewll command1 and command2 are just placeholders - replace them with real commands that produce some output. As an example: { echo 123 && echo 321; } > /tmp/test – DUzun Jun 01 '18 at 05:19
  • 3
    @DUzun "Neither command exits so I'm not sure how to do this." The OP specified that the commands produce indefinite streams of output, and do not exit. This is not comparable to commands which produce finite output and then end. For example, { yes && echo no } will never print "no" – Zoey Hewll Jun 01 '18 at 05:42
  • 2
    @ZoeyHewll I see what you mean. { yes; echo no; } in this case will output 'no' after you press Ctrl+C. { yes & no; } will scrumble output of yes with 'no', and most likely 'no' would be on its own line. The right tool for the right task. – DUzun Jun 02 '18 at 06:32
  • 1
    also note that the space between the open and closing bracket is required! – danielpops Jan 14 '19 at 18:59
  • Can we write alias for { command1 & command2; } ? – alper Nov 06 '20 at 16:30
  • I've given this a down vote due to use of & instead of &&. Using & makes the command not very general purpose. For example, if I run a long running command on the console I can't stop it because it's executing in the background – MikeKulls Nov 22 '22 at 01:06
  • @MikeKulls please read the reply by Zoey to the comment of DUzun who made the same remark. – AdminBee Nov 22 '22 at 12:56
  • @AdminBee I think offering a solution that runs in the background as the default option is a bad answer. I would expect the first command to run, in the foreground followed by the second command in the foreground. If the person answering had offered background processing as an alternate solution then fair enough. Or if they'd explained foreground vs background better it might make sense. End of the day double ampersand is far better and is going to give the behaviour that 99.9% of people expect. This is what the voting system is for to put the best answers forward. This isn't the best answer. – MikeKulls Nov 24 '22 at 11:15
  • @MikeKulls But if, as OP says neither command exits, then the second command will never run if the first one isn't started in the background. So as far as I can see, a double-ampersand approach wouldn't answer the question in the first place. – AdminBee Nov 24 '22 at 11:24
105

More generally, it's possible to use either a subshell or command grouping, and redirect the output of the whole group at once.

Code:

( command1 ; command2 ; command3 ) | cat

{ command1 ; command2 ; command3 ; } > outfile.txt

The main difference between the two is that the first one splits of a child process, while the second one operates in the context of the main shell. This can have consequences regarding the setting and use of variables and other environment settings, as well as performance.

Don't forget that the closing bracket in command grouping (and functions) must be separated from the contents by either a semicolon or a newline. This is because "}" is actually a command (keyword) of its own, and must be treated like one.

TPS
  • 2,481
j9s
  • 1,051
  • 1
  • 7
  • 2
  • 2
    Redirection from ( ) works fine too. – muru Jan 01 '15 at 12:49
  • 4
    } isn't a command at all. It's a reserved word. Same goes for {. I usually write such lists like so: { command1;command2;} > outfile.txt. You can add spaces after the semicolons but it's not necessary. The space after { is necessary, though. – Wildcard May 06 '16 at 02:45
  • 1
    Be warned: This doesn't preserve whole lines! You'll get unreliable outputs as lines get split up part way and mixed up among each other. You can try this with ( yes {1..20} & yes {1..20}; ) | grep -v '^1 2 3' which ideally won't print anything if lines aren't broken. (H/t to @antak). – Ole Tange Nov 15 '17 at 07:36
  • 3
    Sometimes you want to run command2 only if command1 succeded: ( command1 && command2 && command3 ) | cat – DUzun Apr 05 '18 at 11:08
  • i prefer the round brackets () as with the curly brackets {} it runs as a background progress and then you have to deal with the output from that. Also pipe to cat | cat is a nicer alternative then > /dev/stdout – DarkMukke Jun 19 '18 at 10:36
  • This is an old answer so I realise I might not get a response, but when you say "more generally", do you mean in cases where the commands are not infinite? if so, it may be worth noting explicitly, as neither of these solutions will actually solve the problem as stated, where each command produces indefinite output. – Zoey Hewll Apr 28 '20 at 02:29
  • Why should I use | cat here? Seems to me as it works without cat just as fine. Or is this just to show some differences between () and {}? – Tim Malich May 07 '20 at 06:53
15

Most of the solutions so far deal badly with the partial line problem. Assume for a second that the programs are:

cmd1() {
    perl -e 'while(1) { print "a"x3000_000,"\n"}'
}
export -f cmd1
cmd2() {
    perl -e 'while(1) { print "b"x3000_000,"\n"}'
}
export -f cmd2

When running those in parallel you want the output to have full lines of as followed by full lines of bs. What you do not want is as and bs mixing on the same line (tr -s ab replaces repeating as with a single a, so it is easier to see what happens):

# This is bad - half lines are mixed
$ (cmd1 & cmd2 ) | tr -s ab
bababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa
ababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab

If you instead use GNU Parallel, you get nice clean full lines with either as or bs but never mixed:

$ parallel --line-buffer ::: cmd1 cmd2 | tr -s ab
a
a
b
b
b
b
a

Newer versions of GNU Parallel even avoids filling up your disk: The above can run forever.

Ole Tange
  • 35,514
  • $ parallel --line-buffer ::: ls who | tr -s ab Academic tradition requires you to cite works you base your article on. When using programs that use GNU Parallel to process data for publication please cite: O. Tange (2011): GNU Parallel - The Command-Line Power Tool, ;login: The USENIX Magazine, February 2011:42-47. This helps funding further development; AND IT WON'T COST YOU A CENT. If you pay 10000 EUR you should feel free to use GNU Parallel without citing. To silence this citation notice: run 'parallel --citation'.
      Seriously!? Imagine if every utility did this
    
    – peter karasev Mar 30 '22 at 23:29
  • I assume you are no longer a meager student. 10k Euro is steep, but I am happy to send you 100 Euro to remove the text and submit patch upstream. – peter karasev Mar 30 '22 at 23:31
  • @peterkarasev Read: https://git.savannah.gnu.org/cgit/parallel.git/tree/doc/citation-notice-faq.txt – Ole Tange Mar 30 '22 at 23:53
7

For the special case of combining multiple BASH command outputs onto one line, here's a recipe to run each command in turn, removing any newlines between their outputs.

(echo 'ab' && echo 'cd' && echo 'ef') | tr -d '\n'
>>> abcdef

As a real-world example, the code below will embed an ASCII message between two fixed strings of bytes (forming a print command, in this case)

#   hex prefix           encode a message as hex    hex suffix    | strip newline | hex to binary | (then, for example, send the binary over a TCP connection)
(echo '1b40' && echo "Test print #1" | xxd -p && echo '1d564103') | tr -d '\n'    | xxd -r -p     | nc -N 192.168.192.168 9100

(Note: this method only works if the commands exit. For combining stdout from commands that don't exit, see other answers.)

Luke
  • 171
  • (1) Please show the (expected) output of your second command. (2) Please show how the OP would use this technique to solve his problem. – Scott - Слава Україні Jul 11 '19 at 19:28
  • 1
  • The output of the second command is non-ascii binary, so it wouldn't be useful to show it. 2) OP likely solved his specific problem between 2013 and now. This question is now effectively a reference on combining the stdout of multiple Bash commands, so I believe a technique for combining them on one line is a useful "recipe" to be mentioned here (since I came here looking for it and couldn't find it).
  • – Luke Jul 15 '19 at 05:34