Я хотел бы сказать, что это удивительно полезный вопрос. Многие люди не знают, как это сделать, поскольку им не приходится делать это часто, и это не лучшее документированное поведение Bash.
Роберт Вм Рудисуэли,
2
Иногда полезно просмотреть выходные данные (как обычно) и перенаправить их в файл. Смотрите ответ Марко ниже. (Я говорю это здесь, потому что легко посмотреть на первый принятый ответ, если этого достаточно для решения проблемы, но другие ответы часто предоставляют полезную информацию.)
Согласно wiki.bash-hackers.org/scripting/obsolete , он выглядит устаревшим в том смысле, что он не является частью POSIX, но страница руководства по bash не упоминает об удалении его из bash в ближайшем будущем. Страница man указывает предпочтение '&>' перед '> &', что в противном случае эквивалентно.
Чепнер
13
Я предполагаю, что мы не должны использовать &>, поскольку это не в POSIX, и обычные оболочки, такие как "dash", не поддерживают это.
Сэм Уоткинс
27
Дополнительный совет: если вы используете это в скрипте, убедитесь, что он начинается с, #!/bin/bashа не с #!/bin/sh, так как в требует bash.
Тор Клингберг
8
Или & >> добавить вместо перезаписи.
Александр Гончий
449
do_something 2>&1| tee -a some_file
Это позволит перенаправить stderr в stdout и stdout some_fileи распечатать его в stdout.
В AIX (ksh) ваше решение работает. Принятый ответ do_something &>filenameне. +1.
Удержан
11
@Daniel, но этот вопрос конкретно о bash
Джон Ла Рой
3
Я понимаю, Ambiguous output redirect.почему?
Александр Холден Дейли
1
У меня есть скрипт ruby (который я не хочу изменять), который печатает сообщения об ошибках жирным красным цветом. Этот скрипт ruby вызывается из моего скрипта bash (который я могу изменить). Когда я использую вышеупомянутое, он печатает сообщения об ошибках в виде простого текста, минус форматирование. Есть ли способ сохранить форматирование на экране и получить вывод (и stdout, и stderr) в файл?
Атлантида
8
Обратите внимание, что (по умолчанию) это имеет побочный эффект, который $?больше не относится к состоянию выхода do_something, а к состоянию выхода tee.
Flimm
255
Вы можете перенаправить stderr в stdout, а stdout в файл:
Этот формат предпочтительнее, чем самый популярный &> формат, который работает только в bash. В оболочке Bourne это можно интерпретировать как запуск команды в фоновом режиме. Кроме того, формат более читабелен 2 (STDERR) перенаправлен на 1 (STDOUT).
РЕДАКТИРОВАТЬ: изменил порядок, как указано в комментариях
Это перенаправляет stderr в исходный stdout, а не в файл, куда идет stdout. Поставьте '2> & 1' после '> file.log' и все заработает.
1
В чем преимущество этого подхода перед some_command &> file.log?
Ubermonkey
6
Если вы хотите добавить файл, вы должны сделать это следующим образом: echo "foo" 2> & 1 1 >> bar.txt AFAIK Нет способа добавить, используя &>
SlappyTheFish
9
Ага, извините, эхо "foo" 1 >> bar.txt 2> & 1
SlappyTheFish
11
Я думаю, что интерпретация, что 2> & 1 перенаправляет stderr в stdout, неверна; Я полагаю, что точнее будет сказать, что он отправляет stderr в то же самое место, куда идет stdout в данный момент времени. Таким образом, место 2> & 1 после первого перенаправления является существенным.
JDG
201
# Close STDOUT file descriptor
exec 1<&-# Close STDERR FD
exec 2<&-# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
Теперь простое эхо запишет в $ LOG_FILE. Полезно для демонизации.
Автору оригинального поста,
Это зависит от того, что вам нужно достичь. Если вам просто нужно перенаправить вход / выход из команды, которую вы вызываете из скрипта, ответы уже даны. Мой рассказ о перенаправлении в текущем скрипте, который влияет на все команды / встроенные модули (включая вилки) после упомянутого фрагмента кода.
Другое крутое решение - это перенаправление одновременно на std-err / out и на logger или log-файл, что подразумевает разделение «потока» на две части. Эта функциональность обеспечивается командой 'tee', которая может записывать / добавлять сразу несколько файловых дескрипторов (файлов, сокетов, каналов и т. Д.): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
exec 3>&14>&21>>(tee >(logger -i -t 'my_script_tag')>&3)2>>(tee >(logger -i -t 'my_script_tag')>&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid(){local ppid="$1"
RETVAL=''local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"}# Needed to kill processes running in background
cleanup(){local current_pid element
local pids=("$$")
running_pids=("${pids[@]}")while:;do
current_pid="${running_pids[0]}"[-z "$current_pid"]&&break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"[-z "$new_pids"]&&continuefor element in $new_pids;do
running_pids+=("$element")
pids=("$element""${pids[@]}")donedone
kill ${pids[@]}2>/dev/null
}
Итак, с самого начала. Предположим, у нас есть терминал, подключенный к / dev / stdout (FD # 1) и / dev / stderr (FD # 2). На практике это может быть труба, розетка или что-то еще.
Создайте FD # 3 и # 4 и укажите на то же «местоположение», что и # 1 и # 2 соответственно. Смена FD # 1 с сегодняшнего дня не влияет на FD # 3. Теперь FDs # 3 и # 4 указывают на STDOUT и STDERR соответственно. Они будут использованы как реальные терминала STDOUT и STDERR.
1>> (...) перенаправляет STDOUT для команды в паренсе
parens (sub-shell) выполняет чтение 'tee' из STDOUT (pipe) exec и перенаправляет на команду 'logger' через другой канал в sub-shell в parens. В то же время он копирует тот же вход в FD # 3 (терминал)
вторая часть, очень похожая, посвящена тому же трюку для STDERR и FD № 2 и № 4.
Результат выполнения скрипта с указанной выше строкой и дополнительно такой:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...как следует:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep2315:54:03 wks056 my_script_tag[11644]:Will end up in STDOUT(terminal) and /var/log/messages
Если вы хотите увидеть более четкую картинку, добавьте эти 2 строки в скрипт:
только одно исключение. в первом примере вы написали: exec 1 <> $ LOG_FILE. это потому, что оригинальный лог-файл всегда написан на более высоком уровне. для реального входа в журнал лучше: exec 1 >> $ LOG_FILE, потому что журнал всегда добавляется.
Зник
4
Это правда, хотя это зависит от намерений. Мой подход заключается в том, чтобы всегда создавать уникальный файл журнала с метками времени. Другой должен добавить. Оба способа являются «логотатируемыми». Я предпочитаю отдельные файлы, которые требуют меньше разбора, но, как я уже сказал, все, что делает вашу лодку плавающей :)
викторина
1
Ваше второе решение информативно, но что со всем кодом очистки? Это не актуально, и если да, то только запутывает другой хороший пример. Я также хотел бы видеть, что он немного переработан, так что FD 1 и 2 не перенаправляются на регистратор, а 3 и 4, так что все, что вызывает этот сценарий, может манипулировать 1 и 2 в соответствии с общим предположением stdout == 1 и stderr == 2, но мои краткие эксперименты показывают, что это более сложно.
JFlo
1
Мне больше нравится с кодом очистки. Это может немного отвлечь от основного примера, но удаление его сделает пример неполным. Сеть уже полна примеров без обработки ошибок, или, по крайней мере, дружеское замечание, что ей все еще нужно около ста строк кода, чтобы сделать его безопасным для использования.
Золтан К.
1
Я хотел уточнить код очистки. Это часть скрипта, которая демонизирует эрго и становится невосприимчивой к сигналу HANG-UP. 'tee' и 'logger' - это процессы, порожденные одним и тем же PPID, и они наследуют ловушку HUP от основного сценария bash. Итак, когда основной процесс умирает, он наследуется init [1]. Они не станут зомби (defunc). Код очистки гарантирует, что все фоновые задачи будут убиты, если основной сценарий умирает. Это также относится к любому другому процессу, который мог быть создан и запущен в фоновом режиме.
викторина
41
bash your_script.sh 1>file.log 2>&1
1>file.logинструктирует оболочку отправлять STDOUT в файл file.logи 2>&1указывает перенаправить STDERR (дескриптор файла 2) в STDOUT (дескриптор файла 1).
Примечание: порядок имеет значение, как указано в liw.fi, 2>&1 1>file.logне работает.
Обратите внимание, что оператор '&' сообщает bash, что 2 - это дескриптор файла (который указывает на stderr), а не имя файла. Если мы пропустим '&', эта команда напечатает stdoutв stdout, создаст файл с именем "2" и напишетstderror туда.
Экспериментируя с приведенным выше кодом, вы можете сами убедиться, как работают операторы перенаправления. Например, изменяя, какой файл, какой из двух дескрипторов 1,2перенаправлен /dev/nullна следующие две строки кода, удаляйте все из stdout и все из stderror соответственно (печатая то, что осталось).
Чтобы по-настоящему понять это, я настоятельно рекомендую вам прочитать это веб-страницу в таблицах дескрипторов файлов . Предполагая, что вы сделали это чтение, мы можем продолжить. Обратите внимание, что Bash обрабатывает слева направо; таким образом, Bash видит >/dev/nullпервым (что совпадает с 1>/dev/null) и устанавливает файловый дескриптор 1 так, чтобы он указывал на / dev / null вместо stdout. Сделав это, Bash затем движется вправо и видит 2>&1. Это устанавливает файловый дескриптор 2 так, чтобы он указывал на тот же файл, что и файловый дескриптор 1 (а не на сам файловый дескриптор 1 !!!! (см. Этот ресурс по указателям).для получения дополнительной информации)) . Поскольку дескриптор файла 1 указывает на / dev / null, а дескриптор файла 2 указывает на тот же файл, что и дескриптор файла 1, дескриптор файла 2 теперь также указывает на / dev / null. Таким образом, оба файловых дескриптора указывают на / dev / null, и поэтому вывод не производится.
Чтобы проверить, действительно ли вы понимаете концепцию, попробуйте угадать вывод, когда мы переключаем порядок перенаправления:
(echo "stdout"; echo "stderror">&2)2>&1>/dev/null
stderror
Причиной здесь является то, что при оценке слева направо Bash видит 2> & 1 и, таким образом, устанавливает дескриптор файла 2 так, чтобы он указывал на то же место, что и дескриптор файла 1, то есть stdout. Затем он устанавливает файловый дескриптор 1 (помните, что> / dev / null = 1> / dev / null), чтобы он указывал на> / dev / null, удаляя, таким образом, все, что обычно отправляется стандарту. Таким образом, все, что нам осталось, это то, что не было отправлено в stdout в подоболочке (код в скобках) - то есть «stderror». Интересно отметить, что хотя 1 является просто указателем на стандартный вывод, перенаправление указателя 2 на 1 через 2>&1НЕ образует цепочку указателей 2 -> 1 -> стандартный вывод. Если это произошло, в результате перенаправления 1 в / dev / null код2>&1 >/dev/null выдаст цепочку указателей 2 -> 1 -> / dev / null, и, следовательно, код не сгенерирует ничего, в отличие от того, что мы видели выше.
Наконец, я хотел бы отметить, что есть более простой способ сделать это:
Из раздела 3.6.4 здесь мы видим , что мы можем использовать оператор &>перенаправления как стандартный вывод и стандартный поток ошибок. Таким образом, чтобы перенаправить вывод stderr и stdout любой команды \dev\null(которая удаляет вывод), мы просто набираем
$ command &> /dev/null
или в моем примере:
$ (echo "stdout"; echo "stderror">&2)&>/dev/null
Ключевые выносы:
Файловые дескрипторы ведут себя как указатели (хотя файловые дескрипторы не совпадают с файловыми указателями)
Перенаправление файлового дескриптора «a» на файловый дескриптор «b», который указывает на файл «f», заставляет файловый дескриптор «a» указывать на то же место, что и файловый дескриптор b - файл «f». НЕ образует цепочку указателей a -> b -> f
Из-за вышеизложенного порядок имеет значение, 2>&1 >/dev/nullэто! = >/dev/null 2>&1. Один генерирует вывод, а другой нет!
Файловые дескрипторы (0, 1, 2) просто смещаются в таблицу. Когда используются 2> & 1, эффект - это слот FD [2] = dup (1), поэтому, куда бы FD [1] указывал, FD [2] теперь указывает на. Когда вы изменяете FD [1] на / dev / null, то FD [1] изменяется, но это не меняет слот FD [2] (который указывает на стандартный вывод). Я использую термин dup (), потому что это системный вызов, который используется для дублирования дескриптора файла.
Я предполагаю, что это не работает из-за "/ dev / fd / 3 Отказано в доступе". Переключение на> & 3 может помочь.
викторина
6
Я хотел, чтобы решение получило вывод из stdout плюс stderr в файл журнала, а stderr все еще находился на консоли. Поэтому мне нужно было продублировать вывод stderr через тройник.
Это решение, которое я нашел:
command 3>&11>&22>&31>>logfile | tee -a logfile
Первый обмен stderr и stdout
затем добавьте стандартный вывод в файл журнала
труба stderr, чтобы тройник и добавить его также в файл журнала
Кстати, это не сработало для меня (лог-файл пуст). | тройник не имеет никакого эффекта. Вместо этого я получил его с помощью stackoverflow.com/questions/692000/…
Ярослав Булатов
4
Для ситуации, когда необходим «трубопровод», вы можете использовать:
| &
Например:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
или
TIMEFORMAT=%R;for i in`seq 1 20`;do time kubectl get pods |grep node >>js.log ;done|& sort -h
Эти решения на основе bash могут передавать STDOUT и STDERR по отдельности (из STDERR из «sort -c» или из STDERR в «sort -h»).
Следующие функции могут быть использованы для автоматизации процесса переключения выходов между stdout / stderr и файлом журнала.
#!/bin/bash#set -x# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()function save_standard_outputs {if["$OUTPUTS_REDIRECTED"=="true"];then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}# Params: $1 => logfile to write tofunction redirect_outputs_to_logfile {if["$OUTPUTS_REDIRECTED"=="true"];then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;fi
LOGFILE=$1
if[-z "$LOGFILE"];then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"fiif[!-f $LOGFILE ];then
touch $LOGFILE
fiif[!-f $LOGFILE ];then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"}# "private" function used by save_standard_outputs() function restore_standard_outputs {if["$OUTPUTS_REDIRECTED"=="false"];then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;fi
exec 1>&-#closes FD 1 (logfile)
exec 2>&-#closes FD 2 (logfile)
exec 2>&4#restore stderr
exec 1>&3#restore stdout
OUTPUTS_REDIRECTED="false"}
Пример использования внутри скрипта:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
В ситуациях, когда вы рассматриваете возможность использования таких вещей, как exec 2>&1я, легче читать, если это возможно, переписывать код, используя функции bash, например:
Ответы:
Посмотрите здесь . Должно быть:
(перенаправляет как
stdout
иstderr
на имя файла).источник
#!/bin/bash
а не с#!/bin/sh
, так как в требует bash.Это позволит перенаправить stderr в stdout и stdout
some_file
и распечатать его в stdout.источник
do_something &>filename
не. +1.Ambiguous output redirect.
почему?$?
больше не относится к состоянию выходаdo_something
, а к состоянию выходаtee
.Вы можете перенаправить stderr в stdout, а stdout в файл:
Видеть Http://tldp.org/LDP/abs/html/io-redirection.html.
Этот формат предпочтительнее, чем самый популярный &> формат, который работает только в bash. В оболочке Bourne это можно интерпретировать как запуск команды в фоновом режиме. Кроме того, формат более читабелен 2 (STDERR) перенаправлен на 1 (STDOUT).
РЕДАКТИРОВАТЬ: изменил порядок, как указано в комментариях
источник
Теперь простое эхо запишет в $ LOG_FILE. Полезно для демонизации.
Автору оригинального поста,
Это зависит от того, что вам нужно достичь. Если вам просто нужно перенаправить вход / выход из команды, которую вы вызываете из скрипта, ответы уже даны. Мой рассказ о перенаправлении в текущем скрипте, который влияет на все команды / встроенные модули (включая вилки) после упомянутого фрагмента кода.
Другое крутое решение - это перенаправление одновременно на std-err / out и на logger или log-файл, что подразумевает разделение «потока» на две части. Эта функциональность обеспечивается командой 'tee', которая может записывать / добавлять сразу несколько файловых дескрипторов (файлов, сокетов, каналов и т. Д.): Tee FILE1 FILE2 ...> (cmd1)> (cmd2) ...
Итак, с самого начала. Предположим, у нас есть терминал, подключенный к / dev / stdout (FD # 1) и / dev / stderr (FD # 2). На практике это может быть труба, розетка или что-то еще.
Результат выполнения скрипта с указанной выше строкой и дополнительно такой:
...как следует:
Если вы хотите увидеть более четкую картинку, добавьте эти 2 строки в скрипт:
источник
1>file.log
инструктирует оболочку отправлять STDOUT в файлfile.log
и2>&1
указывает перенаправить STDERR (дескриптор файла 2) в STDOUT (дескриптор файла 1).Примечание: порядок имеет значение, как указано в liw.fi,
2>&1 1>file.log
не работает.источник
Любопытно, что это работает:
Но это дает синтаксическую ошибку:
Вы должны использовать:
источник
&>>
Кажется, работает на BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
Краткий ответ:
Command >filename 2>&1
илиCommand &>filename
Объяснение:
Рассмотрим следующий код, который печатает слово «stdout» в stdout, а слово «stderror» - в stderror.
Обратите внимание, что оператор '&' сообщает bash, что 2 - это дескриптор файла (который указывает на stderr), а не имя файла. Если мы пропустим '&', эта команда напечатает
stdout
в stdout, создаст файл с именем "2" и напишетstderror
туда.Экспериментируя с приведенным выше кодом, вы можете сами убедиться, как работают операторы перенаправления. Например, изменяя, какой файл, какой из двух дескрипторов
1,2
перенаправлен/dev/null
на следующие две строки кода, удаляйте все из stdout и все из stderror соответственно (печатая то, что осталось).Теперь мы можем объяснить, почему решение, почему следующий код не производит вывод:
Чтобы по-настоящему понять это, я настоятельно рекомендую вам прочитать это веб-страницу в таблицах дескрипторов файлов . Предполагая, что вы сделали это чтение, мы можем продолжить. Обратите внимание, что Bash обрабатывает слева направо; таким образом, Bash видит
>/dev/null
первым (что совпадает с1>/dev/null
) и устанавливает файловый дескриптор 1 так, чтобы он указывал на / dev / null вместо stdout. Сделав это, Bash затем движется вправо и видит2>&1
. Это устанавливает файловый дескриптор 2 так, чтобы он указывал на тот же файл, что и файловый дескриптор 1 (а не на сам файловый дескриптор 1 !!!! (см. Этот ресурс по указателям).для получения дополнительной информации)) . Поскольку дескриптор файла 1 указывает на / dev / null, а дескриптор файла 2 указывает на тот же файл, что и дескриптор файла 1, дескриптор файла 2 теперь также указывает на / dev / null. Таким образом, оба файловых дескриптора указывают на / dev / null, и поэтому вывод не производится.Чтобы проверить, действительно ли вы понимаете концепцию, попробуйте угадать вывод, когда мы переключаем порядок перенаправления:
Причиной здесь является то, что при оценке слева направо Bash видит 2> & 1 и, таким образом, устанавливает дескриптор файла 2 так, чтобы он указывал на то же место, что и дескриптор файла 1, то есть stdout. Затем он устанавливает файловый дескриптор 1 (помните, что> / dev / null = 1> / dev / null), чтобы он указывал на> / dev / null, удаляя, таким образом, все, что обычно отправляется стандарту. Таким образом, все, что нам осталось, это то, что не было отправлено в stdout в подоболочке (код в скобках) - то есть «stderror». Интересно отметить, что хотя 1 является просто указателем на стандартный вывод, перенаправление указателя 2 на 1 через
2>&1
НЕ образует цепочку указателей 2 -> 1 -> стандартный вывод. Если это произошло, в результате перенаправления 1 в / dev / null код2>&1 >/dev/null
выдаст цепочку указателей 2 -> 1 -> / dev / null, и, следовательно, код не сгенерирует ничего, в отличие от того, что мы видели выше.Наконец, я хотел бы отметить, что есть более простой способ сделать это:
Из раздела 3.6.4 здесь мы видим , что мы можем использовать оператор
&>
перенаправления как стандартный вывод и стандартный поток ошибок. Таким образом, чтобы перенаправить вывод stderr и stdout любой команды\dev\null
(которая удаляет вывод), мы просто набираем$ command &> /dev/null
или в моем примере:Ключевые выносы:
2>&1 >/dev/null
это! =>/dev/null 2>&1
. Один генерирует вывод, а другой нет!Наконец, взгляните на эти замечательные ресурсы:
Документация Bash по перенаправлению , объяснение таблиц файловых дескрипторов , введение в указатели
источник
Это связано: Запись stdOut & stderr в системный журнал.
Это почти работа, но не из xinted; (
источник
Я хотел, чтобы решение получило вывод из stdout плюс stderr в файл журнала, а stderr все еще находился на консоли. Поэтому мне нужно было продублировать вывод stderr через тройник.
Это решение, которое я нашел:
источник
Для ситуации, когда необходим «трубопровод», вы можете использовать:
Например:
или
Эти решения на основе bash могут передавать STDOUT и STDERR по отдельности (из STDERR из «sort -c» или из STDERR в «sort -h»).
источник
Самый простой способ (только bash4)
ls * 2>&- 1>&-
.источник
Следующие функции могут быть использованы для автоматизации процесса переключения выходов между stdout / stderr и файлом журнала.
Пример использования внутри скрипта:
источник
@ Фернанду-fabreti
Добавив к тому, что вы сделали, я немного изменил функции и удалил закрытие & - и это сработало для меня.
источник
В ситуациях, когда вы рассматриваете возможность использования таких вещей, как
exec 2>&1
я, легче читать, если это возможно, переписывать код, используя функции bash, например:источник
Для tcsh я должен использовать следующую команду:
При использовании
command &> file
он выдаст ошибку «Invalid null command».источник