Как сохранить стандартную ошибку в переменной в скрипте Bash

Допустим, у меня есть скрипт, подобный следующему:

бесполезно. ш

echo "This Is Error" 1>&2
echo "This Is Output" 

И у меня есть другой сценарий оболочки:

также бесполезен. ш

./useless.sh | sed 's/Output/Useless/'

Я хочу захватить «Это ошибка», или любой другой stderr из бесполезных. ш, в переменную. Давайте назовем это ОШИБКА.

Обратите внимание, что я использую стандартный вывод для чего-то. Я хочу продолжать использовать stdout, поэтому перенаправление stderr в stdout в этом случае бесполезно.

Итак, в основном, я хочу сделать

./useless.sh 2> $ERROR | ...

но это, очевидно, не работает.

Я также знаю, что я мог сделать

./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`

но это безобразно и не нужно.

К сожалению, если здесь не будет ответов, это то, что я собираюсь сделать.

Я надеюсь, что есть другой способ.

У кого-нибудь есть идеи получше?

вопрос задан 7.06.2009
psycotica0
1010 репутация

13 ответов


  • 68 рейтинг

    Было бы лучше, чтобы захватить файл ошибок таким образом:

    ERROR=$(

    Оболочка распознает это и не должна запускать «cat» для получения данных.

    Большой вопрос сложен. Я не думаю, что есть простой способ сделать это. Вам нужно будет встроить весь конвейер в под-оболочку, в конечном итоге отправив окончательный стандартный вывод в файл, чтобы вы могли перенаправить ошибки в стандартный вывод.

    ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )
    

    Обратите внимание, что точка с запятой необходима (в классических оболочках - Bourne, Korn - точно; вероятно, в Bash тоже). «{}» выполняет перенаправление ввода / вывода через вложенные команды. Как написано, он также будет регистрировать ошибки от sed.

    (Формально непроверенный код - используйте на свой страх и риск. )

    ответ дан Jonathan Leffler, с репутацией 546469, 7.06.2009
  • 59 рейтинг

    также бесполезен. ш

    Это позволит вам направить вывод сценария useless.sh с помощью такой команды, как sed, и сохранить stderr в переменной с именем error. Результат канала отправляется на stdout для отображения или для передачи в другую команду

    Он устанавливает пару дополнительных файловых дескрипторов для управления перенаправлениями, необходимыми для этого.

    #!/bin/bash
    
    exec 3>&1 4>&2 #set up extra file descriptors
    
    error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )
    
    echo "The message is \"${error}.\""
    
    exec 3>&- 4>&- # release the extra file descriptors
    
    ответ дан Dennis Williamson, с репутацией 228759, 8.06.2009
  • 48 рейтинг

    Перенаправленный stderr в stdout, stdout в / dev / null, а затем использовать обратные клавиши или $() для захвата перенаправленного stderr:

    ERROR=$(./useless.sh 2>&1 >/dev/null)
    
    ответ дан Chas. Owens, с репутацией 51596, 7.06.2009
  • 6 рейтинг

    Существует много дубликатов для этого вопроса, многие из которых имеют несколько более простой сценарий использования, когда вы не хотите захватывать stderr и stdout и код выхода одновременно.

    if result=$(useless.sh 2>&1); then
        stdout=$result
    else
        rc=$?
        stderr=$result
    fi
    

    работает для общего сценария, где вы ожидаете либо правильного вывода в случае успеха, либо диагностического сообщения на stderr в случае сбоя.

    Обратите внимание, что управляющие операторы оболочки уже проверяют $? под капотом; так что-нибудь, что выглядит как

    cmd
    if [ $? -eq 0 ], then ...
    

    - это просто неуклюжий, однотипный способ сказать

    if cmd; then ...
    
    ответ дан tripleee, с репутацией 84431, 11.02.2018
  • 5 рейтинг
    # command receives its input from stdin.
    # command sends its output to stdout.
    exec 3>&1
    stderr="$(command &1 1>&3)"
    exitcode="${?}"
    echo "STDERR: $stderr"
    exit ${exitcode}
    
    ответ дан human9, с репутацией 51, 28.11.2013
  • 3 рейтинг

    Вот как я это сделал:

    #
    # $1 - name of the (global) variable where the contents of stderr will be stored
    # $2 - command to be executed
    #
    captureStderr()
    {
        local tmpFile=$(mktemp)
    
        $2 2> $tmpFile
    
        eval "$1=$(< $tmpFile)"
    
        rm $tmpFile
    }
    

    Пример использования:

    captureStderr err "./useless.sh"
    
    echo -$err-
    

    Это использует временный файл. Но, по крайней мере, уродливые вещи заключены в функцию.

    ответ дан tfga, с репутацией 46, 25.07.2012
  • 2 рейтинг

    Это интересная проблема, на которую я надеялся найти элегантное решение. К сожалению, я в конечном итоге с решением, аналогичным г-ну Леффлер, но я добавлю, что вы можете вызывать бесполезные изнутри функции Bash для улучшения читабельности:

    #!/bin/bash
    
    function useless {
        /tmp/useless.sh | sed 's/Output/Useless/'
    }
    
    ERROR=$(useless)
    echo $ERROR
    

    Все другие виды перенаправления вывода должны быть поддержаны временным файлом.

    ответ дан does_not_exist, с репутацией , 8.06.2009
  • 1 рейтинг

    Захват и печать stderr

    ERROR=$( ./useless.sh 3>&1 1>&2 2>&3 | tee /dev/fd/2 )
    

    Авария

    Вы можете использовать $() для захвата stdout, но вместо этого вы хотите захватить stderr. Таким образом, вы меняете stdout и stderr. Использование fd 3 в качестве временного хранилища в стандартном алгоритме подкачки.

    Если вы хотите захватить И распечатать, используйте tee, чтобы сделать копию. В этом случае выходные данные tee будут перехвачены $(), а не отправлены на консоль, но stderr (из tee) все равно пойдет на консоль, поэтому мы используем его как второй выход для tee через специальный файл /dev/fd/2, поскольку tee ожидает Путь к файлу, а не номер FD.

    ПРИМЕЧАНИЕ. Это очень много перенаправлений в одной строке, и порядок имеет значение. $() захватывает стандартный вывод tee в конце конвейера, и сам конвейер направляет стандартный вывод ./useless.sh к стандартному выводу tee ПОСЛЕ того, как мы поменяли стандартные и стандартный вывод на ./useless.sh.

    Использование стандартного вывода. /бесполезный. ш

    ОП сказал, что он все еще хочет использовать (а не только печатать) стандартный вывод, например ./useless.sh | sed 's/Output/Useless/'.

    Нет проблем, просто сделайте это ПЕРЕД обменом stdout и stderr. Я рекомендую переместить его в функцию или файл (также бесполезный. ш) и называть это вместо. /бесполезный. ш в строке выше.

    Однако, если вы хотите CAPTURE stdout AND stderr, то я думаю, что вам придется использовать временные файлы, потому что $() будет делать только один файл за раз, и он создает подоболочку, из которой вы не можете вернуть переменные.

    ответ дан SensorSmith, с репутацией 495, 22.05.2018
  • 1 рейтинг
    $ b=$( ( a=$( (echo stdout;echo stderr >&2) ) ) 2>&1 )
    $ echo "a=>$a b=>$b"
    a=>stdout b=>stderr
    
    ответ дан Mario Wolff, с репутацией 51, 25.10.2013
  • 1 рейтинг

    Этот пост помог мне придумать подобное решение для моих собственных целей:

    MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`
    

    Затем, пока наше СООБЩЕНИЕ не является пустой строкой, мы передаем его другим материалам. Это даст нам знать, если наши format_logs. py не удалось с каким-то исключением Python.

    ответ дан palmbardier, с репутацией 71, 19.08.2015
  • 0 рейтинг

    Если вы хотите обойти использование временного файла, вы можете использовать подстановку процесса. Я еще не совсем заставил его работать. Это была моя первая попытка:

    $ .useless.sh 2> >( ERROR=$(<) )
    -bash: command substitution: line 42: syntax error near unexpected token `)'
    -bash: command substitution: line 42: `<)'
    

    Тогда я попробовал

    $ ./useless.sh 2> >( ERROR=$( cat <() )  )
    This Is Output
    $ echo $ERROR   # $ERROR is empty
    

    Однако

    $ ./useless.sh 2> >( cat <() > asdf.txt )
    This Is Output
    $ cat asdf.txt
    This Is Error
    

    Таким образом, процесс подстановки делает , как правило, правильно , , к сожалению, всякий раз, когда я обертываю STDIN внутри >( ) чем-то в $(), пытаясь передать это переменной, я теряю содержимое $(). Я думаю, что это потому, что $() запускает подпроцесс, который больше не имеет доступа к дескриптору файла в / dev / fd, который принадлежит родительскому процессу.

    Подстановка процессов позволила мне работать с потоком данных, которого больше нет в STDERR, к сожалению, я не могу управлять им так, как мне хочется.

    ответ дан Barton Chittenden, с репутацией 3097, 31.05.2012
  • 0 рейтинг

    В зш:

    { . ./useless.sh > /dev/tty } 2>&1 | read ERROR
    $ echo $ERROR
    ( your message )
    
    ответ дан Ray Andrews, с репутацией 297, 14.11.2015
  • -1 рейтинг

    Для проверки ошибок ваши команды:

    execute [INVOKING-FUNCTION] [COMMAND]
    

    execute () {
        error=$($2 2>&1 >/dev/null)
    
        if [ $? -ne 0 ]; then
            echo "$1: $error"
            exit 1
        fi
    }
    

    Вдохновленный в Бережливом производстве:

    ответ дан Alberto Salvia Novella, с репутацией 1, 29.12.2017