Я знаю, як використовувати tee
для запису вихід ( STDOUT
) aaa.sh
до bbb.out
, поки все ще відображає його в терміналі:
./aaa.sh | tee bbb.out
Як би я тепер також писав STDERR
у файл з назвою ccc.out
, але все ще мав його відображати?
Я знаю, як використовувати tee
для запису вихід ( STDOUT
) aaa.sh
до bbb.out
, поки все ще відображає його в терміналі:
./aaa.sh | tee bbb.out
Як би я тепер також писав STDERR
у файл з назвою ccc.out
, але все ще мав його відображати?
Відповіді:
Я припускаю, що ви хочете все ще бачити STDERR і STDOUT на терміналі. Ви можете попросити відповіді Джоша Келлі, але я знаходжу на tail
заднім плані, що виводить ваш файл журналу дуже хакерським і незграбним. Зверніть увагу, як вам потрібно тримати FD exra та робити очищення після цього, вбиваючи його, а технічно слід робити це в a trap '...' EXIT
.
Існує кращий спосіб зробити це, і ви вже відкрили для себе це: tee
.
Тільки замість того, щоб просто використовувати його для вашого stdout, мати трійник для stdout та один для stderr. Як ви це зробите? Заміна процесу та перенаправлення файлів:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Давайте розділимо його та пояснимо:
> >(..)
>(...)
(заміна процесу) створює FIFO і дозволяє tee
слухати його. Потім він використовує >
(перенаправлення файлів) для перенаправлення STDOUT command
на FIFO, який tee
слухає ваш перший .
Те саме на секунду:
2> >(tee -a stderr.log >&2)
Ми знову використовуємо підстановку процесу, щоб зробити tee
процес, який зчитується з STDIN і скидає його в stderr.log
. tee
виводить свій вхід назад на STDOUT, але оскільки його вхід є нашим STDERR, ми хочемо знову перенаправити tee
STDOUT на наш STDERR. Тоді ми використовуємо перенаправлення файлів, щоб перенаправити command
STDERR на вхід FIFO ( tee
's STDIN).
Дивіться сторінку http://mywiki.wooledge.org/BashGuide/InputAndOutput
Заміна процесу - одна з тих справді прекрасних речей, які ви отримуєте як бонус за вибір bash
оболонки на відміну від sh
(POSIX або Bourne).
У цьому sh
вам потрібно буде робити вручну:
out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
що працює, але чекає на введення. Чи є проста причина, чому це відбувається?
/bin/bash 2> err
і/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
чому б не просто:
./aaa.sh 2>&1 | tee -a log
Це просто переспрямовується stderr
на stdout
, тому трійник перегукується як на вхід, так і на екран. Можливо, мені чогось не вистачає, тому що деякі інші рішення здаються справді складними.
Примітка: з версії 4 bash ви можете використовувати |&
як абревіатуру для 2>&1 |
:
./aaa.sh |& tee -a log
./aaa.sh |& tee aaa.log
працює (в баш).
set -o pipefail
після якого слід ;
або &&
якщо я не помиляюся.
Це може бути корисно для людей, які виявили це через google. Просто коментуйте той приклад, який ви хочете спробувати. Звичайно, сміливо перейменуйте вихідні файли.
#!/bin/bash
STATUSFILE=x.out
LOGFILE=x.log
### All output to screen
### Do nothing, this is the default
### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1
### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1
### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)
### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)
### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}
### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)
### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}
### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
exec >
означає перемістити ціль дескриптора файлу до певного пункту призначення. За замовчуванням дорівнює 1, отже, exec > /dev/null
переміщує висновок stdout в / dev / null з цього моменту в цьому сеансі. Поточні дескриптори файлів для цього сеансу можна побачити ls -l /dev/fd/
. Спробуй це! Потім подивіться, що відбувається, коли ви видаєте exec 2>/tmp/stderr.log.
Додатково, exec 3>&1
означає, створити новий дескриптор файлу з номером 3 та перенаправити його на цільовий дескриптор файлу 1. У прикладі цільовим був екран, коли команда була видана.
Щоб перенаправити stderr у файл, виведіть stdout на екран, а також збережіть stdout у файл:
./aaa.sh 2> ccc.out | трій ./bbb.out
EDIT : Щоб відобразити на екрані і stderr, і stdout, а також зберегти їх у файл, ви можете скористатися перенаправленням вводу / виводу bash :
#!/bin/bash
# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out
# Also print the contents of this file to screen.
tail -f ccc.out &
# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out
# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
Іншими словами, ви хочете передати stdout в один фільтр ( tee bbb.out
), а stderr в інший фільтр ( tee ccc.out
). Немає стандартного способу передавати щось інше, ніж stdout, в іншу команду, але ви можете обійти це, використовуючи дескриптори файлів.
{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
Див. Також Як зібрати стандартний потік помилок (stderr)? і Коли ви використовуєте додатковий дескриптор файлу?
У bash (і ksh і zsh), але не в інших оболонках POSIX, таких як тире, ви можете використовувати заміну процесу :
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
Остерігайтеся, що в bash ця команда повертається, як тільки ./aaa.sh
закінчується, навіть якщо tee
команди все ще виконуються (ksh і zsh дійсно чекають підпроцесів). Це може бути проблемою, якщо ви робите щось подібне ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. У цьому випадку використовуйте дескриптор файлів жонглювання або ksh / zsh.
sh
, корисний для завдань Cron, де заміна процесів недоступна.
Якщо ви використовуєте bash:
# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect
# Redirect standard error and out together
% cmd >stdout-redirect 2>&1
# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2
Кредит (не відповідаючи вгорі голови) йде тут: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
У моєму випадку сценарій виконував команду під час перенаправлення як stdout, так і stderr у файл, наприклад:
cmd > log 2>&1
Мені потрібно було оновити його так, щоб, коли стався збій, вжити деяких дій, заснованих на повідомленнях про помилки. Я, звичайно, міг би видалити копію 2>&1
та захопити stderr зі сценарію, але тоді повідомлення про помилки не потраплять у файл журналу для довідки. Хоча прийнята відповідь від @lhunath повинна робити те ж саме, вона переспрямовує stdout
і stderr
на різні файли, що не те, що я хочу, але це допомогло мені знайти точне рішення, яке мені потрібно:
(cmd 2> >(tee /dev/stderr)) > log
З урахуванням зазначених вище, журнал буде мати копію як stdout
і stderr
я можу захопити stderr
з мого сценарію без необхідності турбуватися про stdout
.
Наступне буде працювати для KornShell (кш), де заміна процесу недоступна,
# create a combined(stdin and stdout) collector
exec 3 <> combined.log
# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3
# cleanup collector
exec 3>&-
Справжній трюк тут - це послідовність, 2>&1 1>&3
яка в нашому випадку перенаправляє stderr
на stdout
та перенаправляє stdout
дескриптор 3
. На даний момент stderr
та stdout
ще не поєднані.
Насправді, stderr
(as stdin
) передається туди, tee
куди він входить, stderr.log
а також перенаправляє до дескриптора 3.
І дескриптор 3
постійно реєструє його combined.log
. Отже, combined.log
містить і те, stdout
і stderr
.
Якщо ви використовуєте zsh , ви можете використовувати кілька переадресацій, тому вам навіть не потрібно tee
:
./cmd 1>&1 2>&2 1>out_file 2>err_file
Тут ви просто переспрямовуєте кожен потік до себе та цільового файлу.
Повний приклад
% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err
Зверніть увагу, що для цього потрібно встановити MULTIOS
опцію (яка є типовою).
MULTIOS
Виконайте неявні
tee
s абоcat
s під час спроби декількох переадресацій (див. Перенаправлення ).