How to color specific parts of the rsync output

colorsgreprsyncsed

I run this command to rsync some files from Windows (Cygwin) to my local NAS:

rsync -PaSq --delete -e "/cygdrive/C/cygwin64/bin/ssh -i keyfile -p XXXX" "/source/" admin@192.168.1.1:/destination/

I now want to highlight in red any error messages. By that I mean highlighting the errors from rsync once it connects and not the ssh connection errors.

So this ssh connection error message remains unchanged:

ssh_exchange_identification: Connection closed by remote host
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(226) [sender=3.1.2]

But this is highlighted in red:

rsync: delete_file: unlink(test/test.txt) failed: Permission denied (13)

I've tried the following with no luck.
Any ideas appreciated.

rsync -PaSq --delete -e "/cygdrive/C/cygwin64/bin/ssh -i keyfile -p XXXX" "/source/" admin@192.168.1.1:/destination/ | tput setaf 1; sed -n '/rsync:/p'

rsync -PaSq --delete -e "/cygdrive/C/cygwin64/bin/ssh -i keyfile -p XXXX" "/source/" admin@192.168.1.1:/destination/ | egrep --color '.*rsync:.*|$'

rsync -PaSq --delete -e "/cygdrive/C/cygwin64/bin/ssh -i keyfile -p XXXX" "/source/" admin@192.168.1.1:/destination/ | grep --color=auto '.*rsync:.*|$'

Best Answer

The problem you face comes from the fact that rsync outputs errors to the standard error (file descriptor 2), but the pipe by default transfers only the standard output (file descriptor 1), leaving standard error untouched. So the command after the pipe (sed or grep in your example) doesn't see errors which came from rsync.

You can redirect both stdout and stderr using |& aka 2>&1 |. Another approach is to redirect only stderr, leavng stdout where it pointed, so try something like:

rsync ... |& grep --color '.*rsync:.*'

or

{ rsync ...  2>&1 >&3 3>&- | grep --color '.*rsync:.*' 3>&-; } 3>&1

Second approach is a little bit complicated because we need to temporarily redirect stdout somewhere (to descriptor 3 in this case) in order to not pass it to the grep.

Related Question