Bash script - хранить stderr в переменной

Я пишу script для резервного копирования базы данных. У меня есть следующая строка:

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

Я хочу назначить stderr переменной, чтобы он отправил мне электронное письмо, давая мне знать, что произошло, если что-то пойдет не так. Я нашел решения для перенаправления stderr на stdout, но я не могу этого сделать, поскольку stdout уже отправляется (через gzip) в файл. Как я могу отдельно хранить stderr в переменной $result?

Ответ 1

Попробуйте перенаправить stderr в stdout и с помощью $(), чтобы зафиксировать это. Другими словами:

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

Поскольку ваша команда перенаправляет stdout где-то, она не должна мешать stderr. Там может быть более чистый способ написать его, но это должно работать.

Edit:

Это действительно работает. Я протестировал его:

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

echo "BLAH=$BLAH"

напечатает BLAH=err, а файл log содержит out.

Ответ 2

Для любой общей команды в Bash вы можете сделать что-то вроде этого:

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

Обычный вывод появляется нормально, все, что stderr записывается в $error (цитируйте его как "$ error" при его использовании для сохранения новых строк). Чтобы записать stdout в файл, просто добавьте перенаправление в конце, например:

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

Разбивая его, читая снаружи, он:

  • создает описание файла $out для всего блока, дублируя stdout
  • захватывает команду stdout всей команды в $error (но см. ниже)
  • сама команда перенаправляет stderr на stdout (который захватывается выше), а затем stdout на исходный stdout из-за пределов блока, поэтому только stderr получает захваченный

Ответ 3

Вы можете сохранить ссылку на stdout до того, как она будет перенаправлена ​​в другой номер файла (например, 3), а затем перенаправить stderr на это:

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

Итак, 3>&1 перенаправит номер файла 3 в stdout (обратите внимание, что это происходит до того, как stdout перенаправляется с помощью этого канала). Затем 2>&3 перенаправляет stderr на номер файла 3, который теперь совпадает с stdout. Наконец, stdout перенаправляется путем подачи в канал, но это не влияет на номера файлов 2 и 3 (обратите внимание, что перенаправление stdout из gzip не связано с выводами команды mysqldump).

Изменить: обновлена ​​команда перенаправления stderr из команды mysqldump, а не gzip, я был слишком быстр в своем первом ответе.

Ответ 4

dd записывает как stdout, так и stderr:

$ dd if=/dev/zero count=50 > /dev/null 
50+0 records in
50+0 records out

два потока независимы и отдельно перенаправляются:

$ dd if=/dev/zero count=50 2> countfile | wc -c
25600
$ cat countfile 
50+0 records in
50+0 records out
$ mail -s "countfile for you" thornate < countfile

если вам действительно нужна переменная:

$ variable=`cat countfile`