1

From this question I learned how to add a prefix to each output of a command:

command | sed "s/^/[prefix] /"

But this only adds the prefix for each line from stdout.
I successfully used the following to add the prefix also to stderr output.

command 2>&1 | sed "s/^/[prefix] /"

But this sends the result to stdout only.

How can I prefix any output of command while pushing the lines to the previous output (preserving both stdout and stderr)?

pLumo
  • 397
  • 1
  • 13
  • Why not redirect in like manner? `command 2>&1 | sed "s/^/[prefix] /" 1>&2` ?? You redirect `stderr` to `stdout`, you pipe to `sed`, and then if you want to issue that output on `stderr`, you redirect `stdout` to `stderr`. Each pipe just directs the `stdout` of the first process to the `stdin` of the next. If you want your final output on `stderr`, then you must redirect the final `stdout` to `stderr`. – David C. Rankin Mar 27 '18 at 08:33
  • 1
    But then I send Everything to stderr. I want to preserve stdout and stderr – pLumo Mar 27 '18 at 08:37
  • If you want the info out on `stdout`, then you have to do nothing, that is the default output stream. It's only if you need to change it that you redirect. – David C. Rankin Mar 27 '18 at 08:38

2 Answers2

4

As a combination of iBug's answer and this and especially this answer, I came up with a one-liner that uses temporary file descriptors:

command 1> >(sed "s/^/[prefix]/") 2> >(sed "s/^/[prefix]/" >&2)

Or as a function:

function prefix_cmd {
    local PREF="${1//\//\\/}" # replace / with \/
    shift
    local CMD=("$@")
    ${CMD[@]} 1> >(sed "s/^/${PREF}/") 2> >(sed "s/^/${PREF}/" 1>&2)
}
prefix_cmd "prefix" command
pLumo
  • 397
  • 1
  • 13
  • Note that when you use new variables in a function, it's usually a good idea to make them `local`. That way, you won't clobber pre-existing variables `$PREF` and `$CMD`, in case you're using those names elsewhere in your script. – ghoti Mar 27 '18 at 11:47
1

You can only pipe stdout using the shell pipe syntax. You need two pipes if you want to process stdout and stderr separately. A named pipe may work here.

Here's a sample script that demonstrates the solution

#!/bin/bash

PREF="$1"
shift

NPOUT=pipe.out
NPERR=pipe.err
mkfifo $NPOUT $NPERR

# Make two background sed processes
sed "s/^/$PREF/" <$NPOUT &
sed "s/^/$PREF/" <$NPERR >&2 &

# Run the program
"$@" >$NPOUT 2>$NPERR
rm $NPOUT $NPERR

Usage:

./foo.sh "[prefix] " command -options

It will feed command with its stdin and send command's stdout and stderr to its stdout and stderr separately.

Note I didn't suppress sed's stderr, which may interfere with the output. You can do so like this:

sed "s/^/$PREF/" <$NPOUT 2>/dev/null &
                         ^^^^^^^^^^^
iBug
  • 35,554
  • 7
  • 89
  • 134
  • How to avoid `[1] 1178 [2] 1179` ... `[1]- Done sed "s/^/$PREF/" < $NPOUT [2]+ Done sed "s/^/$PREF/" < $NPERR 1>&2` ? – pLumo Mar 27 '18 at 11:26
  • This solution is nice because apart from the `$CMD` array, it's POSIX compliant (and therefore portable). Any reason you need to use `$CMD` this way? Would `$@` alone be sufficient? – ghoti Mar 27 '18 at 11:50
  • @RoVo Those shouldn't pop up if you run it in a script. It's generated by Bash and only appears in interactive mode. – iBug Mar 27 '18 at 12:34