Is there a standard Bash tool that acts like echo but outputs to stderr rather than stdout?
I know I can do echo foo 1>&2
but it’s kinda ugly and, I suspect, error prone (e.g. more likely to get edited wrong when things change).
asked Jun 7, 2010 at 14:36
2
You could do this, which facilitates reading:
>&2 echo "error"
>&2
copies file descriptor #2 to file descriptor #1. Therefore, after this redirection is performed, both file descriptors will refer to the same file: the one file descriptor #2 was originally referring to. For more information see the Bash Hackers Illustrated Redirection Tutorial.
answered May 8, 2014 at 18:59
Marco AurelioMarco Aurelio
20.5k1 gold badge17 silver badges16 bronze badges
8
You could define a function:
echoerr() { echo "$@" 1>&2; }
echoerr hello world
This would be faster than a script and have no dependencies.
Camilo Martin’s bash specific suggestion uses a «here string» and will print anything you pass to it, including arguments (-n) that echo would normally swallow:
echoerr() { cat <<< "$@" 1>&2; }
Glenn Jackman’s solution also avoids the argument swallowing problem:
echoerr() { printf "%sn" "$*" >&2; }
answered Jun 7, 2010 at 14:52
JamesJames
6,9582 gold badges17 silver badges18 bronze badges
12
Since 1
is the standard output, you do not have to explicitly name it in front of an output redirection like >
. Instead, you can simply type:
echo This message goes to stderr >&2
Since you seem to be worried that 1>&2
will be difficult for you to reliably type, the elimination of the redundant 1
might be a slight encouragement to you!
Pang
9,491146 gold badges81 silver badges122 bronze badges
answered Jul 10, 2012 at 21:24
Brandon RhodesBrandon Rhodes
82.8k16 gold badges106 silver badges147 bronze badges
2
Another option
echo foo >>/dev/stderr
answered Jan 16, 2013 at 3:57
5
No, that’s the standard way to do it. It shouldn’t cause errors.
answered Jun 7, 2010 at 14:37
Matthew FlaschenMatthew Flaschen
277k50 gold badges514 silver badges538 bronze badges
6
If you don’t mind logging the message also to syslog, the not_so_ugly way is:
logger -s $msg
The -s option means: «Output the message to standard error as well as to the system log.»
answered Aug 4, 2014 at 19:15
Grzegorz LuczywoGrzegorz Luczywo
9,8721 gold badge33 silver badges22 bronze badges
2
Another option that I recently stumbled on is this:
{
echo "First error line"
echo "Second error line"
echo "Third error line"
} >&2
This uses only Bash built-ins while making multi-line error output less error prone (since you don’t have to remember to add &>2
to every line).
answered Feb 18, 2019 at 0:17
GuyPaddockGuyPaddock
2,1152 gold badges21 silver badges24 bronze badges
3
Note: I’m answering the post- not the misleading/vague «echo that outputs to stderr» question (already answered by OP).
Use a function to show the intention and source the implementation you want. E.g.
#!/bin/bash
[ -x error_handling ] && . error_handling
filename="foobar.txt"
config_error $filename "invalid value!"
output_xml_error "No such account"
debug_output "Skipping cache"
log_error "Timeout downloading archive"
notify_admin "Out of disk space!"
fatal "failed to open logger!"
And error_handling
being:
ADMIN_EMAIL=root@localhost
config_error() { filename="$1"; shift; echo "Config error in $filename: $*" 2>&1; }
output_xml_error() { echo "<error>$*</error>" 2>&1; }
debug_output() { [ "$DEBUG"=="1" ] && echo "DEBUG: $*"; }
log_error() { logger -s "$*"; }
fatal() { which logger >/dev/null && logger -s "FATAL: $*" || echo "FATAL: $*"; exit 100; }
notify_admin() { echo "$*" | mail -s "Error from script" "$ADMIN_EMAIL"; }
Reasons that handle concerns in OP:
- nicest syntax possible (meaningful words instead of ugly symbols)
- harder to make an error (especially if you reuse the script)
- it’s not a standard Bash tool, but it can be a standard shell library for you or your company/organization
Other reasons:
- clarity — shows intention to other maintainers
- speed — functions are faster than shell scripts
- reusability — a function can call another function
- configurability — no need to edit original script
- debugging — easier to find the line responsible for an error (especially if you’re deadling with a ton of redirecting/filtering output)
- robustness — if a function is missing and you can’t edit the script, you can fall back to using external tool with the same name (e.g. log_error can be aliased to logger on Linux)
- switching implementations — you can switch to external tools by removing the «x» attribute of the library
- output agnostic — you no longer have to care if it goes to STDERR or elsewhere
- personalizing — you can configure behavior with environment variables
answered Mar 18, 2016 at 18:07
My suggestion:
echo "my errz" >> /proc/self/fd/2
or
echo "my errz" >> /dev/stderr
echo "my errz" > /proc/self/fd/2
will effectively output to stderr
because /proc/self
is a link to the current process, and /proc/self/fd
holds the process opened file descriptors, and then, 0
, 1
, and 2
stand for stdin
, stdout
and stderr
respectively.
The /proc/self
link doesn’t work on MacOS, however, /proc/self/fd/*
is available on Termux on Android, but not /dev/stderr
. How to detect the OS from a Bash script? can help if you need to make your script more portable by determining which variant to use.
Sled
18.4k27 gold badges119 silver badges167 bronze badges
answered Sep 3, 2017 at 1:48
SebastianSebastian
4,4533 gold badges41 silver badges42 bronze badges
3
Don’t use cat
as some have mentioned here. cat
is a program
while echo
and printf
are bash (shell) builtins. Launching a program or another script (also mentioned above) means to create a new process with all its costs. Using builtins, writing functions is quite cheap, because there is no need to create (execute) a process (-environment).
The opener asks «is there any standard tool to output (pipe) to stderr», the short answer is : NO … why? … redirecting pipes is an elementary concept in systems like unix (Linux…) and bash (sh) builds up on these concepts.
I agree with the opener that redirecting with notations like this: &2>1
is not very pleasant for modern programmers, but that’s bash. Bash was not intended to write huge and robust programs, it is intended to help the admins to get there work with less keypresses
And at least, you can place the redirection anywhere in the line:
$ echo This message >&2 goes to stderr
This message goes to stderr
answered Oct 9, 2014 at 13:18
return42return42
5433 silver badges10 bronze badges
4
This is a simple STDERR function, which redirect the pipe input to STDERR.
#!/bin/bash
# *************************************************************
# This function redirect the pipe input to STDERR.
#
# @param stream
# @return string
#
function STDERR () {
cat - 1>&2
}
# remove the directory /bubu
if rm /bubu 2>/dev/null; then
echo "Bubu is gone."
else
echo "Has anyone seen Bubu?" | STDERR
fi
# run the bubu.sh and redirect you output
tux@earth:~$ ./bubu.sh >/tmp/bubu.log 2>/tmp/bubu.err
answered Feb 3, 2012 at 8:40
erselbsterselbst
1631 silver badge6 bronze badges
2
Combining solution suggested by James Roth and Glenn Jackman
- add ANSI color code to display the error message in red:
echoerr() { printf "e[31;1m%se[0mn" "$*" >&2; }
# if somehow e is not working on your terminal, use u001b instead
# echoerr() { printf "u001b[31;1m%su001b[0mn" "$*" >&2; }
echoerr "This error message should be RED"
answered Jun 6, 2020 at 17:20
PolymerasePolymerase
6,23111 gold badges45 silver badges64 bronze badges
read
is a shell builtin command that prints to stderr, and can be used like echo without performing redirection tricks:
read -t 0.1 -p "This will be sent to stderr"
The -t 0.1
is a timeout that disables read’s main functionality, storing one line of stdin into a variable.
answered Jan 24, 2013 at 0:16
Douglas MayleDouglas Mayle
20.9k9 gold badges42 silver badges57 bronze badges
1
Make a script
#!/bin/sh
echo $* 1>&2
that would be your tool.
Or make a function if you don’t want to have a script in separate file.
BCS
75.1k68 gold badges185 silver badges293 bronze badges
answered Jun 7, 2010 at 14:48
n0rdn0rd
11.8k5 gold badges35 silver badges55 bronze badges
3
Here is a function for checking the exit status of the last command, showing error and terminate the script.
or_exit() {
local exit_status=$?
local message=$*
if [ "$exit_status" -gt 0 ]
then
echo "$(date '+%F %T') [$(basename "$0" .sh)] [ERROR] $message" >&2
exit "$exit_status"
fi
}
Usage:
gzip "$data_dir"
or_exit "Cannot gzip $data_dir"
rm -rf "$junk"
or_exit Cannot remove $junk folder
The function prints out the script name and the date in order to be useful when the script is called from crontab
and logs the errors.
59 23 * * * /my/backup.sh 2>> /my/error.log
answered Oct 13, 2020 at 8:39
Miroslav PopovMiroslav Popov
3,2754 gold badges31 silver badges55 bronze badges
Часто возникает необходимость, чтобы скрипт командного интерпретатора Bash выводил результат своей работы. По умолчанию он отображает стандартный поток данных — окно терминала. Это удобно для обработки результатов небольшого объёма или, чтобы сразу увидеть необходимые данные.
В интерпретаторе можно делать вывод в файл Bash. Применяется это для отложенного анализа или сохранения массивного результата работы сценария. Чтобы сделать это, используется перенаправление потока вывода с помощью дескрипторов.
Стандартные дескрипторы вывода
В системе GNU/Linux каждый объект является файлом. Это правило работает также для процессов ввода/вывода. Каждый файловый объект в системе обозначается дескриптором файла — неотрицательным числом, однозначно определяющим открытые в сеансе файлы. Один процесс может открыть до девяти дескрипторов.
В командном интерпретаторе Bash первые три дескриптора зарезервированы для специального назначения:
Дескриптор | Сокращение | Название |
---|---|---|
0 | STDIN | Стандартный ввод |
1 | STDOUT | Стандартный вывод |
2 | STDERR | Стандартный вывод ошибок |
Их предназначение — обработка ввода/вывода в сценариях. По умолчанию стандартным потоком ввода является клавиатура, а вывода — терминал. Рассмотрим подробно последний.
1. Перенаправление стандартного потока вывода
Для того, чтобы перенаправить поток вывода с терминала в файл, используется знак «больше» (>).
#!/bin/bash
echo "Строка 1"
echo "Промежуточная строка" > file
echo "Строка 2" > file
Как результат, «Строка 1» выводится в терминале, а в файл file записывается только «Строка 2»:
Связано это с тем, что > перезаписывает файл новыми данными. Для того, чтобы дописать информацию в конец файла, используется два знака «больше» (>>).
#!/bin/bash
echo "Строка 1"
echo "Промежуточная строка" > file
echo "Строка 2" >> file
Здесь «Промежуточная строка» перезаписала предыдущее содержание file, а «Строка 2» дописалась в его конец.
Если во время использования перенаправления вывода интерпретатор обнаружит ошибку, то он не запишет сообщение о ней в файл.
#!/bin/bash
ls badfile > file2
echo "Строка 2" >> file2
В данном случае ошибка была в том, что команда ls не смогла найти файл badfile, о чём Bash и сообщил. Но вывелось сообщение в терминал, а не записалось в файл. Всё потому, что использование перенаправления потоков указывает интерпретатору отделять мух от котлет ошибки от основной информации.
Это особенно полезно при выполнении сценариев в фоновом режиме, где приходится предусматривать вывод сообщений в журнал. Но так как ошибки в него писаться не будут, нужно отдельно перенаправлять поток ошибок для того, чтобы выполнить их вывод в файл Linux.
2. Перенаправление потока ошибок
В командном интерпретаторе для обработки сообщений об ошибках предназначен дескриптор STDERR, который работает с ошибками, сформированными как от работы интерпретатора, так и самим скриптом.
По умолчанию STDERR указывает в то же место, что и STDOUT, хотя для них и предназначены разные дескрипторы. Но, как было показано в примере, использование перенаправления заставляет Bash разделить эти потоки.
Чтобы выполнить перенаправление вывода в файл Linux для ошибок, следует перед знаком«больше» указать дескриптор 2.
#!/bin/bash
ls badfile 2> errors
echo "Строка 1" > file3
echo "Строка 2" >> file3
В результате работы скрипта создан файл errors, в который записана ошибка выполнения команды ls, а в file3 записаны предназначенные строки. Таким образом, выполнение сценария не сопровождается выводом информации в терминал.
Пример того, как одна команда возвращает и положительный результат, и ошибку:
ls -lh test badtest 2> errors
Команда ls попыталась показать наличие файлов test и badtest. Первый присутствовал в текущем каталоге, а второй — нет. Но сообщение об ошибке было записано в отдельный файл.
Если возникает необходимость выполнить вывод команды в файл Linux, включая её стандартный поток вывода и ошибки, стоит использовать два символа перенаправления, перед которыми стоит указывать необходимый дескриптор.
ls -lh test test2 badtest 2> errors 1> output
Результат успешного выполнения записан в файл output, а сообщение об ошибке — в errors.
По желанию можно выводить и ошибки, и обычные данные в один файл, используя &>.
ls -lh test badtest &> output
Обратите внимание, что Bash присваивает сообщениям об ошибке более высокий приоритет по сравнению с данными, поэтому в случае общего перенаправления ошибки всегда будут располагаться в начале.
Временные перенаправления в скриптах
Если есть необходимость в преднамеренном формировании ошибок в сценарии, можно каждую отдельную строку вывода перенаправлять в STDERR. Для этого достаточно воспользоваться символом перенаправления вывода, после которого нужно использовать & и номер дескриптора, чтобы перенаправить вывод в STDERR.
#!/bin/bash
echo "Это сообщение об ошибке" >&2
echo "Это нормальное сообщение"
При выполнении программы обычно нельзя будет обнаружить отличия:
Вспомним, что GNU/Linux по умолчанию направляет вывод STDERR в STDOUT. Но если при выполнении скрипта будет перенаправлен поток ошибок, то Bash, как и полагается, разделит вывод.
Этот метод хорошо подходит для создания собственных сообщений об ошибках в сценариях.
Постоянные перенаправления в скриптах
Если в сценарии необходимо перенаправить вывод в файл Linux для большого объёма данных, то указание способа вывода в каждой инструкции echo будет неудобным и трудоёмким занятием. Вместо этого можно указать, что в ходе выполнения данного скрипта должно осуществляться перенаправление конкретного дескриптора с помощью команды exec:
#!/bin/bash
exec 1> testout
echo "Это тест перенаправления всего вывода"
echo "из скрипта в другой файл"
echo "без использования временного перенаправления"
Вызов команды exec запускает новый командный интерпретатор и перенаправляет стандартный вывод в файл testout.
Также существует возможность перенаправлять вывод (в том числе и ошибок) в произвольном участке сценария:
#!/bin/bash
exec 2> testerror
echo "Это начально скрипта"
echo "И это первые две строки"
exec 1> testout
echo "Вывод сценария перенаправлен"
echo "из с терминала в другой файл"
echo "но эта строка записана в файл ошибок" >&2
Такой метод часто применяется при необходимости перенаправить лишь часть вывода скрипта в другое место, например в журнал ошибок.
Выводы
Перенаправление в скриптах Bash, чтобы выполнить вывод в файл Bash, является хорошим средством ведения различных журналов, особенно в фоновом режиме.
Использование временного и постоянного перенаправлений в сценариях позволяет создавать собственные сообщения об ошибках для записи в отличное от STDOUT место.
Обнаружили ошибку в тексте? Сообщите мне об этом. Выделите текст с ошибкой и нажмите Ctrl+Enter.
Статья распространяется под лицензией Creative Commons ShareAlike 4.0 при копировании материала ссылка на источник обязательна .
A default error messaging variable built-in Bash script is known as stderr
. It is also known as Standard Error, a default output device for errors.
Sometimes we must redirect the errors into the output channel. The Linux environment identifies every file object with its description, known as FD
.
It is a positive integer value that identifies the open file session. For stderr
, the value of the file descriptor is 2
.
This article will learn about the stderr
and its functionality. Also, we will look at some examples that will make the topic easier.
Echo to stderr
in Bash
The command stderr
is mainly used to keep a recode of error during the execution of any command. The general syntax of this command is:
Your command here 2>error.log
In the sample syntax shared above, you can find a symbol 2>
and a file named error.log
. Now the 2>
indicates the value of FILE DESCRIPTOR
, which represents the identity of stderr
.
In our case, the value of the FILE DESCRIPTOR
is 2
. Now the file name we provided is for the log file generated during the command execution.
You can also echo
the stderr
message by following the below syntax format.
Your command here >&2 echo "error"
Let’s see an example to make the topic easier. Suppose we have created an error command and want to echo
the output error message.
Now our example code will look like this:
echo $( 3 + 2 )) >&2 echo "error"
In the code shared above, we intentionally made an error to understand how it works. We removed one of the brackets here, so this can be an error.
After that, we show that error with the echo
. An output like the one below is shown after running the code above.
./example.sh: line 1: syntax error near unexpected token `)'
./example.sh: line 1: `echo $( 3 + 2 )) >&2 echo "error" '
Now we correct the code like below and run the command again.
echo $(( 3 + 2 )) >&2 "No error has been found."
After running the code, you will get an output like the below.
5 No error has been found.
All the codes used in this article are written in Bash. It will only work in the Linux Shell environment.
Время на прочтение
9 мин
Количество просмотров 335K
Bash-скрипты: начало
Bash-скрипты, часть 2: циклы
Bash-скрипты, часть 3: параметры и ключи командной строки
Bash-скрипты, часть 4: ввод и вывод
Bash-скрипты, часть 5: сигналы, фоновые задачи, управление сценариями
Bash-скрипты, часть 6: функции и разработка библиотек
Bash-скрипты, часть 7: sed и обработка текстов
Bash-скрипты, часть 8: язык обработки данных awk
Bash-скрипты, часть 9: регулярные выражения
Bash-скрипты, часть 10: практические примеры
Bash-скрипты, часть 11: expect и автоматизация интерактивных утилит
В прошлый раз, в третьей части этой серии материалов по bash-скриптам, мы говорили о параметрах командной строки и ключах. Наша сегодняшняя тема — ввод, вывод, и всё, что с этим связано.
Вы уже знакомы с двумя методами работы с тем, что выводят сценарии командной строки:
- Отображение выводимых данных на экране.
- Перенаправление вывода в файл.
Иногда что-то надо показать на экране, а что-то — записать в файл, поэтому нужно разобраться с тем, как в Linux обрабатывается ввод и вывод, а значит — научиться отправлять результаты работы сценариев туда, куда нужно. Начнём с разговора о стандартных дескрипторах файлов.
Стандартные дескрипторы файлов
Всё в Linux — это файлы, в том числе — ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов.
Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.
0
,STDIN —
стандартный поток ввода.1
,STDOUT —
стандартный поток вывода.2
,STDERR —
стандартный поток ошибок.
Эти три специальных дескриптора обрабатывают ввод и вывод данных в сценарии.
Вам нужно как следует разобраться в стандартных потоках. Их можно сравнить с фундаментом, на котором строится взаимодействие скриптов с внешним миром. Рассмотрим подробности о них.
STDIN
STDIN —
это стандартный поток ввода оболочки. Для терминала стандартный ввод — это клавиатура. Когда в сценариях используют символ перенаправления ввода — <
, Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.
Многие команды bash принимают ввод из STDIN
, если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat
.
Когда вы вводите команду cat
в командной строке, не задавая параметров, она принимает ввод из STDIN
. После того, как вы вводите очередную строку, cat
просто выводит её на экран.
STDOUT
STDOUT —
стандартный поток вывода оболочки. По умолчанию это — экран. Большинство bash-команд выводят данные в STDOUT
, что приводит к их появлению в консоли. Данные можно перенаправить в файл, присоединяя их к его содержимому, для этого служит команда >>
.
Итак, у нас есть некий файл с данными, к которому мы можем добавить другие данные с помощью этой команды:
pwd >> myfile
То, что выведет pwd
, будет добавлено к файлу myfile
, при этом уже имеющиеся в нём данные никуда не денутся.
Перенаправление вывода команды в файл
Пока всё хорошо, но что если попытаться выполнить что-то вроде показанного ниже, обратившись к несуществующему файлу xfile
, задумывая всё это для того, чтобы в файл myfile
попало сообщение об ошибке.
ls –l xfile > myfile
После выполнения этой команды мы увидим сообщения об ошибках на экране.
Попытка обращения к несуществующему файлу
При попытке обращения к несуществующему файлу генерируется ошибка, но оболочка не перенаправила сообщения об ошибках в файл, выведя их на экран. Но мы-то хотели, чтобы сообщения об ошибках попали в файл. Что делать? Ответ прост — воспользоваться третьим стандартным дескриптором.
STDERR
STDERR
представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT
, именно поэтому при возникновении ошибки мы видим сообщение на экране.
Итак, предположим, что надо перенаправить сообщения об ошибках, скажем, в лог-файл, или куда-нибудь ещё, вместо того, чтобы выводить их на экран.
▍Перенаправление потока ошибок
Как вы уже знаете, дескриптор файла STDERR —
2. Мы можем перенаправить ошибки, разместив этот дескриптор перед командой перенаправления:
ls -l xfile 2>myfile
cat ./myfile
Сообщение об ошибке теперь попадёт в файл myfile
.
Перенаправление сообщения об ошибке в файл
▍Перенаправление потоков ошибок и вывода
При написании сценариев командной строки может возникнуть ситуация, когда нужно организовать и перенаправление сообщений об ошибках, и перенаправление стандартного вывода. Для того, чтобы этого добиться, нужно использовать команды перенаправления для соответствующих дескрипторов с указанием файлов, куда должны попадать ошибки и стандартный вывод:
ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent
Перенаправление ошибок и стандартного вывода
Оболочка перенаправит то, что команда ls
обычно отправляет в STDOUT
, в файл correctcontent
благодаря конструкции 1>
. Сообщения об ошибках, которые попали бы в STDERR
, оказываются в файле errorcontent
из-за команды перенаправления 2>
.
Если надо, и STDERR
, и STDOUT
можно перенаправить в один и тот же файл, воспользовавшись командой &>
:
Перенаправление STDERR и STDOUT в один и тот же файл
После выполнения команды то, что предназначено для STDERR
и STDOUT
, оказывается в файле content
.
Перенаправление вывода в скриптах
Существует два метода перенаправления вывода в сценариях командной строки:
- Временное перенаправление, или перенаправление вывода одной строки.
- Постоянное перенаправление, или перенаправление всего вывода в скрипте либо в какой-то его части.
▍Временное перенаправление вывода
В скрипте можно перенаправить вывод отдельной строки в STDERR
. Для того, чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR
, при этом перед номером дескриптора надо поставить символ амперсанда (&
):
#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"
Если запустить скрипт, обе строки попадут на экран, так как, как вы уже знаете, по умолчанию ошибки выводятся туда же, куда и обычные данные.
Временное перенаправление
Запустим скрипт так, чтобы вывод STDERR
попадал в файл.
./myscript 2> myfile
Как видно, теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл.
Сообщения об ошибках записываются в файл
▍Постоянное перенаправление вывода
Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять соответствующую команду к каждому вызову echo
неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec
:
#!/bin/bash
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"
Запустим скрипт.
Перенаправление всего вывода в файл
Если просмотреть файл, указанный в команде перенаправления вывода, окажется, что всё, что выводилось командами echo
, попало в этот файл.
Команду exec
можно использовать не только в начале скрипта, но и в других местах:
#!/bin/bash
exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2
Вот что получится после запуска скрипта и просмотра файлов, в которые мы перенаправляли вывод.
Перенаправление вывода в разные файлы
Сначала команда exec
задаёт перенаправление вывода из STDERR
в файл myerror
. Затем вывод нескольких команд echo
отправляется в STDOUT
и выводится на экран. После этого команда exec
задаёт отправку того, что попадает в STDOUT
, в файл myfile
, и, наконец, мы пользуемся командой перенаправления в STDERR
в команде echo
, что приводит к записи соответствующей строки в файл myerror.
Освоив это, вы сможете перенаправлять вывод туда, куда нужно. Теперь поговорим о перенаправлении ввода.
Перенаправление ввода в скриптах
Для перенаправления ввода можно воспользоваться той же методикой, которую мы применяли для перенаправления вывода. Например, команда exec
позволяет сделать источником данных для STDIN
какой-нибудь файл:
exec 0< myfile
Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл myfile
, а не обычный STDIN
. Посмотрим на перенаправление ввода в действии:
#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
Вот что появится на экране после запуска скрипта.
Перенаправление ввода
В одном из предыдущих материалов вы узнали о том, как использовать команду read
для чтения данных, вводимых пользователем с клавиатуры. Если перенаправить ввод, сделав источником данных файл, то команда read
, при попытке прочитать данные из STDIN
, будет читать их из файла, а не с клавиатуры.
Некоторые администраторы Linux используют этот подход для чтения и последующей обработки лог-файлов.
Создание собственного перенаправления вывода
Перенаправляя ввод и вывод в сценариях, вы не ограничены тремя стандартными дескрипторами файлов. Как уже говорилось, можно иметь до девяти открытых дескрипторов. Остальные шесть, с номерами от 3 до 8, можно использовать для перенаправления ввода или вывода. Любой из них можно назначить файлу и использовать в коде скрипта.
Назначить дескриптор для вывода данных можно, используя команду exec
:
#!/bin/bash
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"
После запуска скрипта часть вывода попадёт на экран, часть — в файл с дескриптором 3
.
Перенаправление вывода, используя собственный дескриптор
Создание дескрипторов файлов для ввода данных
Перенаправить ввод в скрипте можно точно так же, как и вывод. Сохраните STDIN
в другом дескрипторе, прежде чем перенаправлять ввод данных.
После окончания чтения файла можно восстановить STDIN
и пользоваться им как обычно:
#!/bin/bash
exec 6<&0
exec 0< myfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
y) echo "Goodbye";;
n) echo "Sorry, this is the end.";;
esac
Испытаем сценарий.
Перенаправление ввода
В этом примере дескриптор файла 6 использовался для хранения ссылки на STDIN
. Затем было сделано перенаправление ввода, источником данных для STDIN
стал файл. После этого входные данные для команды read
поступали из перенаправленного STDIN
, то есть из файла.
После чтения файла мы возвращаем STDIN
в исходное состояние, перенаправляя его в дескриптор 6
. Теперь, для того, чтобы проверить, что всё работает правильно, скрипт задаёт пользователю вопрос, ожидает ввода с клавиатуры и обрабатывает то, что введено.
Закрытие дескрипторов файлов
Оболочка автоматически закрывает дескрипторы файлов после завершения работы скрипта. Однако, в некоторых случаях нужно закрывать дескрипторы вручную, до того, как скрипт закончит работу. Для того, чтобы закрыть дескриптор, его нужно перенаправить в &-
. Выглядит это так:
#!/bin/bash
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3
После исполнения скрипта мы получим сообщение об ошибке.
Попытка обращения к закрытому дескриптору файла
Всё дело в том, что мы попытались обратиться к несуществующему дескриптору.
Будьте внимательны, закрывая дескрипторы файлов в сценариях. Если вы отправляли данные в файл, потом закрыли дескриптор, потом — открыли снова, оболочка заменит существующий файл новым. То есть всё то, что было записано в этот файл ранее, будет утеряно.
Получение сведений об открытых дескрипторах
Для того, чтобы получить список всех открытых в Linux дескрипторов, можно воспользоваться командой lsof
. Во многих дистрибутивах, вроде Fedora, утилита lsof
находится в /usr/sbin
. Эта команда весьма полезна, так как она выводит сведения о каждом дескрипторе, открытом в системе. Сюда входит и то, что открыли процессы, выполняемые в фоне, и то, что открыто пользователями, вошедшими в систему.
У этой команды есть множество ключей, рассмотрим самые важные.
-p
Позволяет указатьID
процесса.-d
Позволяет указать номер дескриптора, о котором надо получить сведения.
Для того, чтобы узнать PID
текущего процесса, можно использовать специальную переменную окружения $$
, в которую оболочка записывает текущий PID
.
Ключ -a
используется для выполнения операции логического И
над результатами, возвращёнными благодаря использованию двух других ключей:
lsof -a -p $$ -d 0,1,2
Вывод сведений об открытых дескрипторах
Тип файлов, связанных с STDIN
, STDOUT
и STDERR —
CHR (character mode, символьный режим). Так как все они указывают на терминал, имя файла соответствует имени устройства, назначенного терминалу. Все три стандартных файла доступны и для чтения, и для записи.
Посмотрим на вызов команды lsof
из скрипта, в котором открыты, в дополнение к стандартным, другие дескрипторы:
#!/bin/bash
exec 3> myfile1
exec 6> myfile2
exec 7< myfile3
lsof -a -p $$ -d 0,1,2,3,6,7
Вот что получится, если этот скрипт запустить.
Просмотр дескрипторов файлов, открытых скриптом
Скрипт открыл два дескриптора для вывода (3
и 6
) и один — для ввода (7
). Тут же показаны и пути к файлам, использованных для настройки дескрипторов.
Подавление вывода
Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null
. Это — что-то вроде «чёрной дыры».
Вот, например, как подавить вывод сообщений об ошибках:
ls -al badfile anotherfile 2> /dev/null
Тот же подход используется, если, например, надо очистить файл, не удаляя его:
cat /dev/null > myfile
Итоги
Сегодня вы узнали о том, как в сценариях командной строки работают ввод и вывод. Теперь вы умеете обращаться с дескрипторами файлов, создавать, просматривать и закрывать их, знаете о перенаправлении потоков ввода, вывода и ошибок. Всё это очень важно в деле разработки bash-скриптов.
В следующий раз поговорим о сигналах Linux, о том, как обрабатывать их в сценариях, о запуске заданий по расписанию и о фоновых задачах.
Уважаемые читатели! В этом материале даны основы работы с потоками ввода, вывода и ошибок. Уверены, среди вас есть профессионалы, которые могут рассказать обо всём этом то, что приходит лишь с опытом. Если так — передаём слово вам.
Синтаксис
- команда </ path / to / file # Перенаправить стандартный ввод в файл
- command> / path / to / file # Перенаправить стандартный вывод на flie
- command file_descriptor> / path / to / file # Перенаправить вывод файла file_descriptor в файл
- command> & file_descriptor # Перенаправить вывод в file_descriptor
- command file_descriptor> & another_file_descriptor # Перенаправить file_descriptor в another_file_descriptor
- команда <& file_descriptor # Перенаправить file_descriptor в стандартный ввод
- command &> / path / to / file # Перенаправить стандартный вывод и стандартную ошибку в файл
параметры
параметр | подробности |
---|---|
внутренний файловый дескриптор | Целое число. |
направление | Один из > , < или <> |
внешний файловый дескриптор или путь | & за ним следует целое число для файлового дескриптора или пути. |
замечания
Консольные программы UNIX имеют входной файл и два выходных файла (входные и выходные потоки, а также устройства) рассматриваются как файлы операционной системы.) Обычно это клавиатура и экран, но любой или все из них могут быть перенаправлены из — или перейти к файлу или другой программе.
STDIN
является стандартным входом, и как программа получает интерактивный вход. STDIN
обычно назначается файловым дескриптором 0.
STDOUT
является стандартным выходом. Все, что испускается в STDOUT
, считается «результатом» программы. STDOUT
обычно назначается файловым дескриптором 1.
STDERR
— это то, где отображаются сообщения об ошибках. Как правило, при запуске программы с консоли STDERR
выводится на экран и неотличим от STDOUT
. Обычно для STDERR
назначается файловый дескриптор 2.
Порядок перенаправления важен
command > file 2>&1
Перенаправляет ( STDOUT
и STDERR
) в файл.
command 2>&1 > file
Перенаправляет только STDOUT
, поскольку дескриптор файла 2 перенаправляется в файл, на который указывает файловый дескриптор 1 (который еще не является файловым file
при оценке оператора).
Каждая команда в конвейере имеет свой собственный STDERR
(и STDOUT
), потому что каждый из них является новым процессом. Это может создать неожиданные результаты, если вы ожидаете, что перенаправление повлияет на весь конвейер. Например, эта команда (завернутая для удобочитаемости):
$ python -c 'import sys;print >> sys.stderr, "Python error!"'
| cut -f1 2>> error.log
будет печатать «Python error!» на консоль, а не на файл журнала. Вместо этого прикрепите ошибку к команде, которую вы хотите захватить:
$ python -c 'import sys;print >> sys.stderr, "Python error!"' 2>> error.log
| cut -f1
Перенаправление стандартного вывода
>
перенаправить стандартный вывод (иначе STDOUT
) текущей команды в файл или другой дескриптор.
Эти примеры записывают вывод команды ls
в файл file.txt
ls >file.txt
> file.txt ls
Целевой файл создается, если он не существует, иначе этот файл усекается.
Дескриптор перенаправления по умолчанию — стандартный вывод или 1
если ни один не указан. Эта команда эквивалентна предыдущим примерам со стандартным выходом, явно указанным:
ls 1>file.txt
Примечание: перенаправление инициализируется исполняемой оболочкой, а не выполненной командой, поэтому она выполняется перед выполнением команды.
Перенаправление STDIN
<
читает его правый аргумент и записывает его левый аргумент.
Чтобы записать файл в STDIN
мы должны прочитать /tmp/a_file
и записать в STDIN
т. STDIN
0</tmp/a_file
Примечание. Внутренний дескриптор файла по умолчанию равен 0
( STDIN
) для <
$ echo "b" > /tmp/list.txt
$ echo "a" >> /tmp/list.txt
$ echo "c" >> /tmp/list.txt
$ sort < /tmp/list.txt
a
b
c
Перенаправление STDOUT и STDERR
Файловые дескрипторы, такие как 0
и 1
являются указателями. Мы изменим, на что указывают дескрипторы файлов с перенаправлением. >/dev/null
означает, что 1
указывает на /dev/null
.
Сначала мы укажем 1
( STDOUT
) на /dev/null
затем на точку 2
( STDERR
) на 1
пункт.
# STDERR is redirect to STDOUT: redirected to /dev/null,
# effectually redirecting both STDERR and STDOUT to /dev/null
echo 'hello' > /dev/null 2>&1
4,0
Это может быть дополнительно сокращено до следующего:
echo 'hello' &> /dev/null
Однако эта форма может быть нежелательной в производстве, если совместимость с оболочкой является проблемой, поскольку она конфликтует с POSIX, вводит разбор синтаксического разбора и оболочки без этой функции, будет неверно истолковывать ее:
# Actual code
echo 'hello' &> /dev/null
echo 'hello' &> /dev/null 'goodbye'
# Desired behavior
echo 'hello' > /dev/null 2>&1
echo 'hello' 'goodbye' > /dev/null 2>&1
# Actual behavior
echo 'hello' &
echo 'hello' & goodbye > /dev/null
ПРИМЕЧАНИЕ. &>
, Как известно, работает как в Bash, так и Zsh.
Перенаправление STDERR
2
— STDERR
.
$ echo_to_stderr 2>/dev/null # echos nothing
Определения:
echo_to_stderr
— это команда, которая записывает "stderr"
в STDERR
echo_to_stderr () {
echo stderr >&2
}
$ echo_to_stderr
stderr
Добавить vs Truncate
Усекать >
- Создайте указанный файл, если он не существует.
- Truncate (удалить содержимое файла)
- Написать в файл
$ echo "first line" > /tmp/lines
$ echo "second line" > /tmp/lines
$ cat /tmp/lines
second line
Добавить >>
- Создайте указанный файл, если он не существует.
- Добавить файл (запись в конце файла).
# Overwrite existing file
$ echo "first line" > /tmp/lines
# Append a second line
$ echo "second line" >> /tmp/lines
$ cat /tmp/lines
first line
second line
STDIN, STDOUT и STDERR объяснили
Команды имеют один вход (STDIN) и два вида выходов, стандартный вывод (STDOUT) и стандартную ошибку (STDERR).
Например:
STDIN
[email protected]~# read
Type some text here
Стандартный ввод используется для ввода ввода в программу. (Здесь мы используем для read
предопределённого для чтения строки из STDIN.)
STDOUT
[email protected]~# ls file
file
Стандартный вывод обычно используется для «нормального» вывода из команды. Например, ls
перечисляет файлы, поэтому файлы отправляются в STDOUT.
STDERR
[email protected]~# ls anotherfile
ls: cannot access 'anotherfile': No such file or directory
Стандартная ошибка (как следует из названия) используется для сообщений об ошибках. Поскольку это сообщение не является списком файлов, оно отправляется в STDERR.
STDIN, STDOUT и STDERR являются тремя стандартными потоками. Они идентифицируются с оболочкой числом, а не именем:
0 = Стандарт в
1 = стандартный
2 = стандартная ошибка
По умолчанию STDIN подключается к клавиатуре, и на терминале появляются STDOUT и STDERR. Однако мы можем перенаправить STDOUT или STDERR на все, что нам нужно. Например, допустим, что вам нужен только стандарт, и все сообщения об ошибках, напечатанные на стандартной ошибке, должны быть подавлены. Именно тогда мы используем дескрипторы 1
и 2
.
Перенаправление STDERR в / dev / null
Взяв предыдущий пример,
[email protected]~# ls anotherfile 2>/dev/null
[email protected]~#
В этом случае, если есть какой-либо STDERR, он будет перенаправлен на / dev / null (специальный файл, который игнорирует что-либо в нем), поэтому вы не получите никаких ошибок в оболочке.
Перенаправление нескольких команд в один и тот же файл
{
echo "contents of home directory"
ls ~
} > output.txt
Использование именованных каналов
Иногда вы можете что-то выводить по одной программе и вводить ее в другую программу, но не можете использовать стандартный канал.
ls -l | grep ".log"
Вы можете просто записать во временный файл:
touch tempFile.txt
ls -l > tempFile.txt
grep ".log" < tempFile.txt
Это отлично tempFile
для большинства приложений, однако никто не будет знать, что делает tempFile
, и кто-то может удалить его, если он содержит вывод ls -l
в этом каталоге. Здесь вызывается именованная труба:
mkfifo myPipe
ls -l > myPipe
grep ".log" < myPipe
myPipe
— это технически файл (все в Linux), поэтому давайте сделаем ls -l
в пустой директории, в которой мы только что создали канал:
mkdir pipeFolder
cd pipeFolder
mkfifo myPipe
ls -l
Выход:
prw-r--r-- 1 root root 0 Jul 25 11:20 myPipe
Обратите внимание на первый символ в разрешениях, он указан как канал, а не файл.
Теперь давайте сделаем что-нибудь классное.
Откройте один терминал и обратите внимание на каталог (или создайте его так, чтобы очистка была простой), и создайте канал.
mkfifo myPipe
Теперь давайте поместим что-то в трубку.
echo "Hello from the other side" > myPipe
Вы заметите, что это зависает, другая сторона трубы все еще закрыта. Давайте откроем другую сторону трубы и пропустим это.
Откройте другой терминал и перейдите в каталог, в котором находится труба (или, если вы знаете, добавьте его в трубу):
cat < myPipe
Вы заметите, что после получения hello from the other side
программа в первом терминале заканчивается, как и во втором терминале.
Теперь запустите команды в обратном порядке. Начните с cat < myPipe
а затем повторите что-нибудь в нем. Он по-прежнему работает, потому что программа будет ждать, пока что-то не будет помещено в трубку до завершения, потому что он знает, что он должен что-то получить.
Именованные каналы могут быть полезны для перемещения информации между терминалами или между программами.
Трубы маленькие. После полной записи автор блокирует, пока какой-либо читатель не прочитает содержимое, поэтому вам нужно либо запустить считыватель и запись на разных терминалах, либо запустить один или другой в фоновом режиме:
ls -l /tmp > myPipe &
cat < myPipe
Дополнительные примеры с использованием именованных каналов:
-
Пример 1 — все команды на одном и том же терминале / той же оболочке
$ { ls -l && cat file3; } >mypipe & $ cat <mypipe # Output: Prints ls -l data and then prints file3 contents on screen
-
Пример 2 — все команды на одном и том же терминале / той же оболочке
$ ls -l >mypipe & $ cat file3 >mypipe & $ cat <mypipe #Output: This prints on screen the contents of mypipe.
Имейте в виду, что первое содержимое
file3
отображается, а затем отображаютсяls -l
данные (конфигурация LIFO). -
Пример 3 — все команды на одном и том же терминале / той же оболочке
$ { pipedata=$(<mypipe) && echo "$pipedata"; } & $ ls >mypipe # Output: Prints the output of ls directly on screen
Имейте в виду, что переменная
$pipedata
недоступна для использования в главном терминале / основной оболочке, так как использование&
вызывает подоболочку, а$pipedata
доступно только в этой подоболочке. -
Пример 4 — все команды на одном и том же терминале / той же оболочке
$ export pipedata $ pipedata=$(<mypipe) & $ ls -l *.sh >mypipe $ echo "$pipedata" #Output : Prints correctly the contents of mypipe
Это правильно печатает значение переменной
$pipedata
в основной оболочке из-за объявления экспорта переменной. Основной терминал / основная оболочка не висит из-за вызова фоновой оболочки (&
).
Печатать сообщения об ошибках в stderr
Сообщения об ошибках обычно включаются в сценарий для целей отладки или для обеспечения богатого пользовательского опыта. Просто напишите сообщение об ошибке следующим образом:
cmd || echo 'cmd failed'
может работать для простых случаев, но это не обычный способ. В этом примере сообщение об ошибке будет загрязнять фактический вывод сценария путем смешивания как ошибок, так и успешного вывода в stdout
.
Короче говоря, сообщение об ошибке должно идти в stderr
не stdout
. Это довольно просто:
cmd || echo 'cmd failed' >/dev/stderr
Другой пример:
if cmd; then
echo 'success'
else
echo 'cmd failed' >/dev/stderr
fi
В приведенном выше примере сообщение об успешном завершении будет напечатано на stdout
пока сообщение об ошибке будет напечатано на stderr
.
Лучшим способом печати сообщения об ошибке является определение функции:
err(){
echo "E: $*" >>/dev/stderr
}
Теперь, когда вам нужно напечатать сообщение об ошибке:
err "My error message"
Перенаправление на сетевые адреса
2,04
Bash рассматривает некоторые пути как специальные и может выполнять некоторую сетевую связь, записывая в /dev/{udp|tcp}/host/port
. Bash не может настроить сервер прослушивания, но может инициировать соединение, а TCP может читать результаты как минимум.
Например, чтобы отправить простой веб-запрос, который можно было бы сделать:
exec 3</dev/tcp/www.google.com/80
printf 'GET / HTTP/1.0rnrn' >&3
cat <&3
и результаты веб-страницы www.google.com
умолчанию будут напечатаны на стандартный stdout
.
так же
printf 'HIn' >/dev/udp/192.168.1.1/6666
отправит сообщение UDP, содержащее HIn
слушателю на 192.168.1.1:6666