Потоки вывода
Сообщения скриптов выводятся во вполне определенные потоки - потоки вывода. Таким образом то, что мы выводим через
echo "Hello, world!"
не просто выводится на экран, а, с точки зрения системы, а конкретно - командных интерпретаторов sh и bash - выводится через определенный поток вывода. В случае echo - поток под номером 1 (stdout), с которым ассоциирован экран.
Некоторые программы и скрипты так-же используют другой поток вывода - под номером 2 (stderr). В него они выводят сообщения об ошибках. Благодаря этому можно раздельно выхватывать из потоков обычные информационные сообщения и сообщения об ошибках и направлять и обрабатывать их раздельно.
Например, Вы можете заблокировать вывод информационных сообщения, оставив только сообщения об ошибках. Или направить вывод сообщений об ошибках в отдельный файл для логирования.
Что такое ">somefile"
Такой записью в Unix (в интерпретаторах bash и sh) указывается перенаправление потоков вывода.
В следующем примере мы перенаправим все информационные (обычные) сообщения команды ls в файлmyfile.txt, получив таким образом в этом файле просто список ls:
$ ls > myfile.txt
При этом после нажатия на Enter Вы не увидите ничего на экране, зато в файле myfile.txt будет находится все то, что должно было отобразиться на экране.
Однако давайте сделаем заведомо ошибочную операцию:
$ ls /masdfasdf > myfile.txt
И что случится? Т.к. директории masdfasdf в корне файловой системы не существует (я так предполагаю - вдруг у Вас есть?), то команда ls сгенерирует ошибку. Однако вывалит эту ошибку она уже не через обычный потокstdout (1), а через поток ошибок stderr (2). А перенаправление задано лишь для stdout ("> myfile.txt").
Т.к. поток stderr (2) мы никуда не перенаправили - сообщение об ошибке появится на экране и НЕ появится в файле myfile.txt
А теперь давайте выполним команду ls так, чтобы информационные данные записались в файл myfile.txt, а сообщения об ошибках - в файл myfile.err, при этом на экране во время выполнения не появится ничего:
$ ls >myfile.txt 2>myfile.err
Здесь нам встречается впервые указание номера потока в качестве перенаправления. Запись "2>myfile.err" указывает, что поток с номером 2 (stderr) нужно перенаправить в файл myfile.err.
Конечно, мы можем оба потока направить в один файл или в одно и то же устройство.
2>&1
Нередко в скриптах можно встретить такую запись. Она означает: "Поток с номером 2 перенаправить в поток с номером 1", или "Поток stderr - направить через поток stdout". Т.е. все сообщения об ошибках мы направляем через поток, через который обычно печатаются обычные, не ошибочные сообщения.
$ ls /asfasdf 2>&1
А вот еще пример, в котором все сообщения перенаправляются в файл myfile.txt:
$ ls /asfasdf >myfile.txt 2>&1
При этом все сообщения, как об ошибках, так и обычные, будут записаны в myfile.txt, т.к. поток stdout мы сначала перенаправлили в файл, а потом указали, что ошибки нужно вываливать в stdout - соответственно, в файл myfile.txt
/dev/null
Однако иногда нам нужно просто скрыть все сообщения - не сохраняя их. Т.е. просто блокировать вывод. Для этого служит виртуальное устройство /dev/null. В следующем примере весь вывод обычных сообщений командыls мы направим в /dev/null:
$ ls > /dev/null
На экране не будет отображено ничего, кроме сообщений об ошибках. А в следующем примере - и сообщения об ошибках будут блокированы:
$ ls > /dev/null 2>&1
Причем эта запись эквивалентна записи вида:
$ ls >/dev/null 2>/dev/null
А в следующем примере мы заблокируем только сообщения об ошибках:
$ ls 2>/dev/null
Заметьте, что здесь уже нельзя указывать "2>&1", т.к. поток (1) не перенаправлен никуда и в таком случае сообщения об ошибках будут банально вывалены на экран.
Что первее - яйцо или курица?
Я приведу Вам здесь 2 примера.
Пример 1)
$ ls >/dev/null 2>&1
Пример 2)
$ ls 2>&1 >/dev/null
С виду - от перестановки мест слогаемых сумма не меняется. Но порядок указателей перенаправления играет роль!
Дело в том, что интерпретаторы читают и применяют перенаправления слева направо. И сейчас мы разберем оба примера.
Пример 1
1) ">/dev/null" - мы направляем поток 1 (stdout) в /dev/null. Все сообщения, попадающие в поток (1) - будут направлены в /dev/null.
2) "2>&1" - мы перенаправляем поток 2 (stderr) в поток 1 (stdout). Но, т.к. поток 1 уже ассоциирован с /dev/null - все сообщения все-равно попадут в /dev/null.
Результат: на экране - пусто.
Пример 2
1) "2>&1" - мы перенаправляем поток ошибок stderr (2) в поток stdout (1). При этом, т.к. поток 1 по-умолчанию ассоциирован с терминалом - сообщения об ошибках мы успешно увидим на экране.
2) ">/dev/null" - а уже здесь мы перенаправляем поток 1 в /dev/null. И обычные сообщения мы не увидим.
Результат: мы будем видеть сообщения об ошибках на экране, но не будет видеть обычные сообщения.
Вывод: сначала перенаправьте поток, а потом на него ссылайтесь.