Bash script - store stderr in a variable
Solution 1
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
.
Solution 2
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
Solution 3
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.
thornate
Updated on July 08, 2022Comments
-
thornate almost 2 years
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 ?
-
Stephen M. Harris almost 11 yearsDoesn't work in bash v3.2:
unexpected token `{out}'
. Does this syntax require Bash 4? -
osirisgothra about 10 yearsit worked fine in bash 4.2, and 4.1, i even tried the shopts: compat32, compat31, and it worked there fine too, maybe it is a bug in 3.2 since when bash is in compatibility mode it works, unless compatibility only encompasses items that would break not due to 4.2, not the features that would be a part of 4.2... (i think most people use 4.2 these days right?) Anyhow this is an old post but just to clear it out, it does work and was just what i needed to assign errors to variables while displaying normal text on screen...:)
-
koby meir almost 9 years@AdamCrume how would i change this script so that in log file I will have both stderr & stdout ( out \n err )
-
Adam Crume almost 9 years@kobymeir: The easiest thing would be to do something like
foo > out.log 2> err.log
and thencat out.log err.log > combined.log
. To avoid clobbering files in the current directory, you want to usemktemp
to create out.log and err.log in /tmp, and then delete them afterword. -
koby meir almost 9 years@AdamCrume but using these technique i won't have the stderr and stdout won't be in the correct order that they appeared. if i have:
echo err1 >&2 echo out >&1 echo err2 >&2
i want the log to be:err1 out err2
and the error variable will be:err1 err2
-
Adam Crume almost 9 years@kobymeir: I thought that's what you wanted, since you specified (out \n err). If you want them interleaved, it's even easier:
myvar=$(foo 2>&1)
. -
koby meir almost 9 years@AdamCrume ok, my point wasn't clear enough,On the one hand i want them interleaved, on the other hand i want to capture all the stderr into
$error
variable. -
Adam Crume almost 9 years@kobymeir: I'm pretty sure that can't be done with just Bash. When you redirect a stream, it can only go one place (although the Bash man page confusingly calls it "duplication"), so stderr can't go into the log file and be captured by the
$error
variable. If you try something likeerror=$((foo >> log) |& tee -a log)
, then stdout and stderr won't be interleaved properly. I suggest opening a new question. -
lfk over 5 yearsWhat's the difference between
`statement`
and$(statement)
? -
Admin about 5 yearsAn excellent and elegant solution. Just one thing to add: you need to close the file descriptor afterwards:
exec {out}>&-