       .filter(-> options.type is 'full' or stream_type is options.type)
       # The string null gets turned into actual null later when these are parsed
       .map((obj) -> if stream_type is 'left' then [obj, 'null'] else ['null', obj])
-      # Don't just pipe into head because we also pipe into head later and node streams don't
+      # Don't just pipe into out because we also pipe into out later and node streams don't
       # handle multiple sources for a stream very well. See understream.combine for how it
       # would need to be handled if we wanted to do it (which we don't because it adds
       # unnecessary complexity)
-      .each (obj) -> head.push obj
+      .each (obj, cb) -> out.write obj, cb
 
     streams.push _(have_join_key).stream()
       .map ([key, obj], cb) ->

    file_names.push file_name

  async.each streams, ((stream, cb_e) -> stream.run cb_e), (err) ->
    return head.emit 'error', err if err
    spawn_opts = [
      '-o', '1.2,2.2'     # Display only the stringified objects
      '-e', 'null'        # Replace any missing data fields with null

      .split('\n')
      .filter((line) -> line) # Filter out trailing newline
      .map((line) -> line.split options.delim) # Split out left and right into a pair
      .pipe(out)
      .run (err) ->
        async.each file_names, rimraf, -> # Swallow errors from deleting files
        out.emit 'error', err if err
  out
