Команда sh: exec 2>&1
Что будет делать эта команда?
exec 2>&1
7 ответов
Технически говоря, он дублирует или копирует stderr на стандартный вывод.
Обычно вам не нужен exec для выполнения этого. Более типичное использование exec с файловыми дескрипторами состоит в том, чтобы указать, что вы хотите назначить файл неиспользуемому файловому дескриптору, например
Exec 35Кстати, не забывайте, что последовательность объявления при передаче в файл важна, поэтому
ls > mydirlist 2>&1
будет работать, потому что он направляет как stdout, так и stderr в файл mydirlist, тогда как команда
ls 2>&1 > mydirlist
направляет только stdout, а не stderr, на файл mydirlist, поскольку stderr был скопирован с stdout до того, как stdout был перенаправлен на mydirlist.
Изменить: это способ, которым оболочка работает сканирование слева направо. Поэтому прочитайте второй как "скопировать stderr на stdout" перед тем, как сказать "отправить stdout mydirlist". Затем прочитайте первый, который говорит "отправить стандартный вывод в файл mydirlist", прежде чем он скажет "дублировать стандартный вывод на этот стандартный вывод, который я настроил". Я знаю. Это совершенно не интуитивно понятно!
Одна из лучших статей, которые я видел о том, что делает "2>&1", это Bash One-Liners Explained, Часть III: Все о перенаправлениях.
Но то, что текущие ответы на этот вопрос не дают, это то, почему вы захотите сделать это после простого "exec". Как объясняется на странице руководства bash для команды exec: "Если команда не указана, любые перенаправления вступают в силу в текущей оболочке".
Я написал простой скрипт out-and-err.py
который записывает строку вывода в stdout, а другую строку в stderr:
#!/usr/bin/python
import sys
sys.stdout.write('this is stdout.\n')
sys.stderr.write('this is stderr.\n')
И затем я обернул это в сценарий оболочки с именем out-and-err.sh с помощью "exec 2>&1":
#!/bin/bash
exec 2>&1
./out-and-err.py
Если я запускаю только скрипт Python, stdout и stderr разделены:
$ ./out-and-err.py 1> out 2> err
$ cat out
this is stdout.
$ cat err
the is stderr.
Но если я запускаю скрипт оболочки, вы можете видеть, что exec позаботится о stderr после всего:
$ ./out-and-err.sh 1> out 2> err
$ cat out
this is stdout.
this is stderr.
$ cat err
$
Если ваш сценарий оболочки-оболочки делает намного больше, чем просто одна команда python, и вам нужно объединить весь вывод в stdout, выполнение "exec 2> & 1" сделает это легко для вас.
Он связывает стандартную ошибку со стандартным
2
это stderr и 1
это стандартный вывод Когда вы запускаете программу, вы получите обычный вывод в stdout, но любые ошибки или предупреждения обычно отправляются в stderr. Если вы хотите, например, направить весь вывод в файл, полезно сначала объединить stderr с stdout с помощью 2>&1
,
В эти дни я тоже был поглощен этой проблемой, но теперь я выпрыгиваю из нее. Итак, позвольте мне объяснить, что происходит после ввода exec 1>&2
в CLI.
Я хочу уничтожить проблему по частям, поэтому, если вы знаете, что знания уже прочитаны, просто просмотрите их, чтобы сэкономить ваше время.
- Что такое
exec
обозначает:
exec
это встроенная команда в Linux В отличие от традиционной команды, которая просто разветвляет процесс под-оболочки, exec
может изменить текущий процесс оболочки.
Что такое перенаправление ввода / вывода: перенаправление является функцией в Linux. При этом вы можете изменить стандартные устройства ввода / вывода. В Linux по умолчанию есть три файловых дескриптора.
Handle Name Description 0 stdin Standard input 1 stdout Standard output 2 stderr Standard error
Позвольте мне увидеть пример:
- открыть новый терминал
- Получить идентификатор процесса termianl
$ pstree -p | grep 'term' | tr -d ' '
- Проверьте дескриптор файла процесса.
$ sudo ls -l /proc/{pid}/fd
bash $ pstree -p | grep -i 'terminal' | tr -d ' ' ||||-gnome-terminal-(6008)-+-bash(7641)-+-grep(8355) $ sudo ls -l /proc/7641/fd total 0 lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
Как видите,ls
перечислите файл процесса PID(6860). Во-первых, все они именуются по номеру (0, 1, 2), во-вторых, все они связывают ссылку на файл с /dev/pts/3, это означает, что любой стандартный ввод / вывод / ошибка все будет отображаться в pts3. - Измените стандартный вывод на / tmp / stdout
bash $ ls /tmp/stdout ls: cannot access '/tmp/stdout': No such file or directory $ exec 1>/tmp/stdout $ ls $ pwd $ echo "Are you ok" $
Выходные данные команды исчезли в это время. - Откройте новый терминал, чтобы проверить файл proc
bash $ sudo ls -l /proc/7641/fd total 0 lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /tmp/stdout lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
Очевидно, что мы можем заметить, что 1(дескриптор файла) уже меняет ссылку на / tmp / stdout. Как и у нас, кроме стандартного вывода передачи в / tmp / stdout - Восстановите перенаправление.
bash $ exec 1>&2 $ cat /tmp/stdout a.sh /home/alopex Are you ok $ sudo ls -l /proc/7641/fd total 0 lrwx------ 1 alopex alopex 64 Oct 27 19:39 0 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 1 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 2 -> /dev/pts/3 lrwx------ 1 alopex alopex 64 Oct 27 19:39 255 -> /dev/pts/3
Опять же, ссылка 1(дескриптор файла) на /dev/pts/3, мы снова можем увидеть результат.
Резюме:
exec 1>&2
сделать стандартный вывод ---> стандартная ошибка
Как сказал @cma, он помещает stderr в стандартный вывод. Причина, по которой вам может потребоваться это поведение, заключается в использовании grep или любой другой утилиты для захвата выходных данных, которые появляются только в stderr. Или вы можете просто сохранить все выходные данные, включая stderr, в файл для последующей обработки.
Одно довольно полезное приложение exec 2>&1
что я наткнулся, это когда ты хочешь слить stderr
а также stdout
для нескольких команд, разделенных точкой с запятой. Мой конкретный пример произошел, когда я отправлял более одной команды popen
в PHP, и я хотел видеть чередование ошибок, как если бы вы вводили команды в командной строке:
$ echo hi ; yikes ; echo there
hi
-bash: yikes: command not found
there
$
Следующее не сливается stderr
а также stdout
кроме последнего echo
(что бессмысленно, потому что yikes
вызывает ошибку):
echo hi ; yikes ; echo there 2>&1
Я могу получить объединенный вывод "трудным путем" следующим образом:
echo hi 2>&1; yikes 2>&1; echo there 2>&1
Это выглядит намного чище и менее подвержен ошибкам, если вы используете exec
:
exec 2>&1 ; echo hi; echo yikes; echo there
Вы получаете stdout
а также stderr
выходные данные хорошо чередуются точно так, как вы увидите на терминале, если вы выполнили три команды, разделенные точкой с запятой.
Практический пример, когда использовать: Следующий пример представляет собой тестовый пример, выполняющий 1000 HTTP-запросов и измеряющий время. Вывод тестового примера должен быть отправлен в файл журнала, но измеренное время должно быть отправлено в стандартный вывод.
Для достижения этого стандартного вывода дублируется:
exec 3>&1
. Затем стандартный вывод перенаправляется в файл:
exec 1>log
. И, наконец, стандартная ошибка перенаправляется на стандартный вывод:
exec 2>&1
. Это означает, что стандартная ошибка также будет отправлена в файл.
log
, так как стандартный вывод уже был перенаправлен. После этого файловый дескриптор 3 по-прежнему можно использовать для отправки сообщений на стандартный вывод скрипта, хотя все остальное идет в лог-файл:
printf ... >&3
.
#! /bin/bash
export LC_ALL=C
exec 3>&1
exec 1>log
exec 2>&1
set -eux
timestamp () { date +%s.%N; }
loops=${1:-1000}
t0=$(timestamp)
for n in $(seq "$loops")
do
curl --silent --show-error --noproxy \* http://localhost:8000 &
done
wait
t1=$(timestamp)
printf '%d loops: %0.4f seconds\n' "$loops" "$(bc <<< "$t1 - $t0")" >&3