Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash script - store stderr in a variable [duplicate]

I'm writing a script to backup a database. I have the following line:

mysqldump --user=$dbuser --password=$dbpswd  \
   --host=$host $mysqldb | gzip > $filename

I want to assign the stderr to a variable, so that it will send an email to myself letting me know what happened if something goes wrong. I've found solutions to redirect stderr to stdout, but I can't do that as the stdout is already being sent (via gzip) to a file. How can I separately store stderr in a variable $result ?

like image 781
thornate Avatar asked Jun 28 '10 06:06

thornate


3 Answers

Try redirecting stderr to stdout and using $() to capture that. In other words:

VAR=$((your-command-including-redirect) 2>&1)

Since your command redirects stdout somewhere, it shouldn't interfere with stderr. There might be a cleaner way to write it, but that should work.

Edit:

This really does work. I've tested it:

#!/bin/bash                                                                                                                                                                         
BLAH=$((
(
echo out >&1
echo err >&2
) 1>log
) 2>&1)

echo "BLAH=$BLAH"

will print BLAH=err and the file log contains out.

like image 57
Adam Crume Avatar answered Nov 17 '22 01:11

Adam Crume


For any generic command in Bash, you can do something like this:

{ error=$(command 2>&1 1>&$out); } {out}>&1

Regular output appears normally, anything to stderr is captured in $error (quote it as "$error" when using it to preserve newlines). To capture stdout to a file, just add a redirection at the end, for example:

{ error=$(ls /etc/passwd /etc/bad 2>&1 1>&$out); } {out}>&1 >output

Breaking it down, reading from the outside in, it:

  • creates a file description $out for the whole block, duplicating stdout
  • captures the stdout of the whole command in $error (but see below)
  • the command itself redirects stderr to stdout (which gets captured above) then stdout to the original stdout from outside the block, so only the stderr gets captured
like image 26
Joat Avatar answered Nov 16 '22 23:11

Joat


You can save the stdout reference from before it is redirected in another file number (e.g. 3) and then redirect stderr to that:

result=$(mysqldump --user=$dbuser --password=$dbpswd  \
   --host=$host $mysqldb 3>&1 2>&3 | gzip > $filename)

So 3>&1 will redirect file number 3 to stdout (notice this is before stdout is redirected with the pipe). Then 2>&3 redirects stderr to file number 3, which now is the same as stdout. Finally stdout is redirected by being fed into a pipe, but this is not affecting file numbers 2 and 3 (notice that redirecting stdout from gzip is unrelated to the outputs from the mysqldump command).

Edit: Updated the command to redirect stderr from the mysqldump command and not gzip, I was too quick in my first answer.

like image 6
hlovdal Avatar answered Nov 16 '22 23:11

hlovdal