Як перенаправити stdout у файл, а stdout + stderr на інший?


32

Як я можу досягти

cmd >> file1 2>&1 1>>file2

Тобто, stdout та stderr повинні перенаправлятись до одного файлу (file1), а лише stdout (file2) повинен перенаправлятись на інший (обидва в режимі додавання)?

Відповіді:


41

Проблема полягає в тому, що коли ви перенаправляєте свій вихід, він вже не доступний для наступного переадресації. Ви можете підключити до teeдодаткової оболонки, щоб зберегти вихід для другого перенаправлення:

( cmd | tee -a file2 ) >> file1 2>&1

або якщо ви хочете бачити вихід у терміналі:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

Щоб не додавати stderr першого teeдо file1, слід перенаправити stderr вашої команди до дескриптора файлу (наприклад, 3), а пізніше додати його до stdout знову:

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(спасибі @ fra-san)


16

З zsh:

cmd >& out+err.log > out.log

У режимі додавання:

cmd >>& out+err.log >> out.log

В zshі за умови, що mult_iosопція не була вимкнена, коли дескриптор файлу (тут 1) перенаправляється кілька разів для запису, тоді оболонка реалізує вбудований модуль teeдля дублювання виводу на всі цілі.


Я не можу зрозуміти, що out+errі outщо тут мається на увазі. Імена файлів? Потоки для переадресації?
gronostaj

@gronostaj Подумайте, що команда читаєcmd >& file1 > file2
Ісаак

Це рішення дозволить зберегти порядок, в якому був створений результат. Щоб насправді зберігати stdout та stderr (у такому порядку), вам потрібен інший підхід.
Ісаак

1
@Isaac, порядок не обов'язково буде збережено, оскільки вихід stdout буде проходити через трубу (до процесу, який пересилає його до кожного файлу), тоді як висновок stderr перейде безпосередньо до файлу. У будь-якому випадку, це не схоже на те, що ОП попросило, щоб висновок stderr прийшов після stdout.
Стефан Шазелас

3

Ви можете: тег stdout (використовуючи UNBUFFERED sed, тобто:), sed -u ...також stderr також перейти до stdout (без тегів, оскільки він не пройшов цей тег sed) і, таким чином, мати можливість диференціювати 2 в отриманому лог-файлі.

Наступне: повільно (Це може бути серйозно оптимізовано, використовуючи для primeple скрипт perl замість часу ...; do ...; done, for exampleple, який буде нерестувати підрозділи та команди в кожному рядку!), Дивно (здається, мені потрібні 2 {} етапи, щоб в одному перейменувати stdout, а потім в іншому додати до нього "провалився" stderr) і т. д. Але це: " доказ концепції ", який намагатимуться зберегти замовлення виводу максимально stdout & stderr наскільки це можливо:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file

Це насправді важко зрозуміти. (1) Чому ви використовуєте такий складний регістр використання ( ls unknown), щоб надрукувати щось на stderr? >&2 echo "error"було б добре. (2) teeможе додавати одночасно до декількох файлів. (3) Чому б не просто catзамість grep "^"? (4) ваш сценарій вийде з ладу, коли починається більш жорсткий вихід _stdout_. (5) Чому?
pLumo

@RoVo: перші 2 коментованих рядка показують алгоритм, простіший, ніж доказ поняття прикладу. 1): це ls loopбуде виводитись як на stdout, так і на stderr, змішану (альтернативно), у контрольованому порядку; так що ми можемо перевірити, чи зберігали ми впорядкування stderr / stdout, незважаючи на теги 2 stdout): gnu tail, можливо, але звичайний хвіст (напр., aix.). 3): grep "^" також показує обидві назви файлів. 4): це може бути змінено змінною. 5): зведений приклад працює на старих осах (колишній, старий акс), де я перевіряв це (відсутність перл).
Олів'є Дулак

(1) багатократне відлуння до більш жорсткого і товстого було б добре, але добре, не важливо, просто було б легше читати. (3) згоден, (4) впевнений, але це не вдасться, якщо він почнеться з того, що містить змінна. (5) Бачу.
pLumo

@RoVo Я згоден з вашим 1). для 4), змінна може бути настільки складною, як це потрібно, щоб зникнути pb (наприклад: uniquetag="banaNa11F453355B28E1158D4E516A2D3EDF96B3450406...)
Олів'є Дулак

1
Звичайно, це не дуже ймовірно, але все одно це може ввести проблему безпеки пізніше. І тоді ви, можливо, захочете видалити цей рядок перед друком у файл ;-)
pLumo

2

Якщо порядок виведення повинен бути: stdout then stderr ; немає рішення тільки з перенаправленням.
Stderr повинен бути збережений у тимчасовому файлі

cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err

Опис:

Єдиний спосіб перенаправити один вихід (fd як stdout або stderr) на два файли - це відтворити його. Команда teeє правильним інструментом для відтворення вмісту дескриптора файлу. Отже, початковою ідеєю мати один вихід на два файли було б використовувати:

... |  tee file1 file2

Це відтворює stdin tee в обох файлах (1 і 2), залишаючи вихід tee ще не використаним. Але нам потрібно додати (використовувати -a) і потрібен лише один примірник. Це вирішує обидва питання:

... | tee -a file1 >>file2

Щоб забезпечити teestdout (той, який потрібно повторити), нам потрібно споживати stderr безпосередньо з команди. Один із способів, якщо порядок не важливий (порядок виводу буде (найімовірніше) зберігатися як генерований, залежно від того, що буде виведено, буде збережено першим). Або:

  1. cmd 2>>file1 | tee -a file2 >>file1
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. ( cmd | tee -a file2 ) >> file1 2>&1

Варіант 2 працює лише в деяких оболонках. Варіант 3 використовує додатковий підшалл (повільніше), але використовуйте імена файлів лише один раз.

Але якщо stdout повинен бути першим (залежно від того, який згенерований результат замовлення), нам потрібно зберегти stderr, щоб додати його до файлу наприкінці (перше розміщене рішення).


1
Або зберігати в пам’яті, як spongeце робить:(cmd | tee -a out >> out+err) 2>&1 | sponge >> out+err
Стефан Шазелас

1

В інтересах різноманітності:

Якщо ваша система підтримує /dev/stderr, то

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

буду працювати. Стандартний вихід вихідного сигналу cmd направляється як на stdout, так і на stderr трубопроводу. Стандартна похибка cmdобходу tee і виходить жорсткіше трубопроводу.

Так

  • прокладка трубопроводу - це лише відтінок cmd, і
  • stderr трубопроводу - це stdout і stderr cmd, змішані.

Тоді це проста справа надсилання цих потоків у правильні файли.

Як і при майже будь-якому такому підході (включаючи відповідь Стефана ), file1лінії можуть вийти з ладу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.