Вывод Linux в файл и на экран одновременно

Обновлено: 06.07.2024

Большинство команд Linux считывают ввод, например файл или другой атрибут команды, и записывают вывод. По умолчанию ввод осуществляется с клавиатуры, а вывод отображается на экране. Клавиатура — это ваше стандартное устройство ввода (stdin), а экран или конкретное окно терминала — стандартное устройство вывода (stdout).

Однако, поскольку Linux является гибкой системой, применять эти настройки по умолчанию необязательно. Стандартным выводом, например, на сильно отслеживаемом сервере в большой среде может быть принтер.

Иногда вам может понадобиться поместить вывод команды в файл или вы можете захотеть ввести другую команду в выводе одной команды. Это известно как перенаправление вывода. Перенаправление осуществляется либо с помощью ">" (символ больше), либо с помощью "|" (конвейер) оператор, который отправляет стандартный вывод одной команды другой команде в качестве стандартного ввода.

Как мы уже видели, команда cat объединяет файлы и выводит их вместе на стандартный вывод. При перенаправлении этого вывода в файл это имя файла будет создано или перезаписано, если оно уже существует, так что будьте осторожны.

Нэнси:~> кошка тест1 некоторые слова Нэнси:~> кошка тест2 некоторые другие слова Нэнси:~> кошка тест1 тест2 > тест3 Нэнси:~> кошка тест3 некоторые слова некоторые другие слова

Не перезаписывать!

Будьте осторожны, чтобы не перезаписать существующие (важные) файлы при перенаправлении вывода. Многие оболочки, включая Bash, имеют встроенную функцию защиты от этого риска: noclobber. См. информационные страницы для получения дополнительной информации. В Bash вы можете добавить команду set -o noclobber в файл конфигурации .bashrc, чтобы предотвратить случайную перезапись файлов.

Перенаправление «ничего» в существующий файл равносильно очистке файла:

nancy:~> ls -l list -rw-rw-r-- 1 nancy nancy 117 2 апр 18:09 list nancy:~> > list nancy:~> ls -l list -rw-rw-r- - 1 Нэнси Нэнси 0 4 апр 12:01 список

Этот процесс называется усечением .

Такое же перенаправление на несуществующий файл создаст новый пустой файл с заданным именем:

nancy:~> ls -l newlist ls: newlist: Нет такого файла или каталога nancy:~> > newlist nancy:~> ls -l newlist -rw-rw-r-- 1 nancy nancy 0 4 апр. 12: 05 новый список

Некоторые примеры использования конвейера команд:

Чтобы найти слово в тексте, отобразите все строки, соответствующие "шаблону1", и исключите из отображения строки, также соответствующие "шаблону2":

файл шаблона grep1 | grep -v шаблон2

Чтобы отобразить вывод каталога по одной странице за раз:

Чтобы найти файл в каталоге:

ls -l | grep part_of_file_name

В другом случае вы можете захотеть, чтобы файл был входом для команды, которая обычно не принимает файл в качестве опции. Это перенаправление ввода выполняется с помощью оператора " (символ меньше).

Ниже приведен пример отправки файла кому-либо с использованием перенаправления ввода.

Если пользователь mike существует в системе, вам не нужно вводить полный адрес. Если вы хотите связаться с кем-то в Интернете, введите полный адрес в качестве аргумента для mail .

Это читается немного сложнее, чем файл cat для начинающих | напишите кому-нибудь , но это, конечно, гораздо более элегантный способ использования доступных инструментов.

В следующем примере сочетается перенаправление ввода и вывода. Файл text.txt сначала проверяется на наличие орфографических ошибок, а вывод перенаправляется в файл журнала ошибок:

написать text.txt > error.log

В следующей команде перечислены все команды, которые вы можете использовать для проверки другого файла при использовании less :

Майк:~> меньше --help | grep -i изучить :e [файл] Проверить новый файл. :n * Проверить (N-й) следующий файл из командной строки. :p * Проверить (N-й) предыдущий файл из командной строки. :x * Проверить первый (или N-й) файл из командной строки.

Опция -i используется для поиска без учета регистра — помните, что системы UNIX очень чувствительны к регистру.

Если вы хотите сохранить вывод этой команды для дальнейшего использования, перенаправьте вывод в файл:

Майк:~> меньше --help | grep -i изучить > проверить файлы без mike:~> cat проверить файлы без :e [файл] Проверить новый файл. :n * Проверить (N-й) следующий файл из командной строки. :p * Проверить (N-й) предыдущий файл из командной строки. :x * Проверить первый (или N-й) файл из командной строки.

Вывод одной команды может быть передан в другую команду практически столько раз, сколько вы хотите, при условии, что эти команды обычно считывают ввод из стандартного ввода и записывают вывод в стандартный вывод. Иногда это не так, но тогда могут быть специальные параметры, которые предписывают этим командам вести себя в соответствии со стандартными определениями; так что прочитайте документацию (man и информационные страницы) команд, которые вы используете, если вы сталкиваетесь с ошибками.

Еще раз убедитесь, что вы не используете имена существующих файлов, которые вам все еще нужны. Перенаправление вывода в существующие файлы заменит содержимое этих файлов.

Вместо перезаписи данных файла вы также можете добавить текст к существующему файлу, используя два следующих друг за другом знака «больше»:

mike:~> список желаний кота больше денег меньше работы mike:~> дата >> список желаний mike:~> список желаний кота больше денег меньше работы Чт, 28 февраля 20:23:07 CET 2002

Команда date обычно выводит на экран последнюю строку; теперь он добавлен в список желаний файла.

Echo

Недавно я писал о перенаправлении вывода команд (стандартный вывод и стандартная ошибка) в файл с помощью bash. Это часть фундаментального использования bash, но что, если вы хотите перенаправить вывод команды в файл, но при этом вывести этот вывод на экран. Что ж, в оболочке bash есть небольшая удобная команда, которая позволяет вам сделать именно это. Команда называется tee.

Вместо перенаправления вывода команды в файл с использованием стандартной техники, например:

Вместо этого вы можете направить вывод в tee и использовать эту команду, чтобы сохранить вывод в файл и в то же время вывести все на экран, например:

Просто, но очень полезно в определенных ситуациях.

Эхо и сохранение как Stdout, так и Stderr

Обычно вы можете сделать это только с помощью stdout. В конце концов, вы не можете направить и stdout, и stderr в tee. Но мы можем комбинировать перенаправление с командой tee, чтобы одновременно отображать и сохранять потоки вывода и ошибок. Сначала мы перенаправляем стандартную ошибку так, чтобы она указывала на то же место, что и stdout, а затем передаем весь этот беспорядок в tee, чтобы сохранять и отображать одновременно, например:

Эхо и сохранение только Stderr

Однако мы сталкиваемся с небольшими проблемами, если нас интересует только сохранение и вывод stderr, а не stdout. Проблема в том, что вы не можете передавать stderr, каналы работают только с stdout. Как вы уже догадались, мы можем использовать более хитрое перенаправление, чтобы обойти это препятствие :).

Описатели файлов, такие как 1 для stdout, 2 для stderr (и 0 для stdin), — не единственные файловые дескрипторы, которые мы можем использовать с bash. Другие числа также можно рассматривать как файловые дескрипторы, и большую часть времени они просто не используются. Что мы хотим сделать, так это использовать один из этих дополнительных файловых дескрипторов в качестве временной переменной, чтобы мы могли «поменять местами» stdout и stderr, например:

  • сначала мы указываем 3 туда, куда указывает stdout (экран), т. е. по существу делаем копию файлового дескриптора stdout и назначаем его 3
  • затем мы перенаправляем стандартный вывод в другое место, чтобы он не мешал тому, что нас действительно интересует
  • затем мы перенаправляем stderr на 3 (куда раньше указывал stdout), т. е., по сути, мы назначаем файловый дескриптор, на который раньше ссылались как на 1, а в настоящее время ссылаемся как на 3, на 2 :)
  • конвейер присоединен к самому файловому дескриптору, поэтому выполнение этой замены означает, что теперь мы будем передавать вывод ошибок

Хм, похоже, ничего не произошло, и файл _blah.tx_t пуст, поэтому мы ничего не выводим. Это связано с тем, что мы не получили никаких ошибок в предыдущей команде, давайте смоделируем ошибку, специально прервав нашу команду ls, например:

Теперь, когда мы получаем ошибки, все работает должным образом, наш файл blah.txt имеет тот же вывод, что и команда tee, выводящаяся на экран, что в свою очередь, это стандартный вывод ошибки команды ls. Кстати, обратите внимание на небольшой минус после 3, когда мы делаем последний редирект, это не просто косметика. Это позволяет нам закрыть файловый дескриптор 3 теперь, когда он нам больше не нужен.

Все это достаточно просто, но удивительно скользко, если вы попытаетесь полностью осознать это.

Есть много интересных и полезных советов по Bash, подобных приведенным выше, в Bash Cookbook, одной из книг, которые я просматриваю в данный момент. Посмотрите, если вам интересно. Хотя я уверен, что буду вести блог о многих других интересных трюках с bash, когда я сам узнаю о них (или мне напоминают об их существовании), так что вы можете просто подождать :).

В bash при вызове foo любые выходные данные этой команды будут отображаться в стандартном выводе.

Вызов foo > output перенаправит любой вывод этой команды в указанный файл (в данном случае 'output').

Есть ли способ перенаправить вывод в файл и вывести его на стандартный вывод?


Замечание по терминологии: при выполнении foo > output данные записываются в стандартный вывод, а стандартный вывод является файлом с именем output . То есть запись в файл является записью в стандартный вывод.Вы спрашиваете, можно ли писать как в стандартный вывод, так и в терминал.

@WilliamPursell Я не уверен, что ваше разъяснение улучшит ситуацию :-) Как насчет этого: OP спрашивает, возможно ли направить стандартный вывод вызываемой программы как в файл, так и в вызывающий стандартный вывод программы (последний является стандартным выводом, который унаследовала бы вызванная программа, если бы не было сделано ничего особенного; т. е. терминал, если вызывающая программа является интерактивным сеансом bash). И, возможно, они также захотят аналогичным образом направить stderr вызываемой программы («любой вывод этой команды» можно разумно интерпретировать как включение stderr).

11 ответов 11

Требуемая команда называется tee :

Например, если вас интересует только стандартный вывод:

Если вы хотите включить stderr, сделайте следующее:

2>&1 перенаправляет канал 2 (stderr/стандартная ошибка) в канал 1 (stdout/стандартный вывод), так что оба записываются как stdout. Он также направляется в указанный выходной файл командой tee.

Кроме того, если вы хотите дополнить файл журнала, используйте tee -a as:


Если OP хочет, чтобы "все выходные данные" были перенаправлены, вам также необходимо получить stderr: "ls -lR / 2>&1 | tee output.file"

@evanmcdonnal Ответ не является неправильным, он просто может быть недостаточно конкретным или полным в зависимости от ваших требований. Конечно, существуют условия, при которых вы можете не захотеть сохранять stderr как часть вывода в файл. Когда я ответил на это 5 лет назад, я предположил, что OP нужен только стандартный вывод, поскольку он упомянул стандартный вывод в теме сообщения.

Ах, извините, я, возможно, немного запутался. Когда я попробовал, у меня просто не было вывода, возможно, все это было в stderr.

Используйте аргумент -a для tee, чтобы добавить содержимое в output.file вместо его перезаписи: ls -lR / | tee -a output.file

Если вы используете $? после этого он вернет код состояния tee , что, вероятно, не то, что вам нужно. Вместо этого вы можете использовать $ .

2>&1 выводит потоки stderr и stdout. tee outfile берет полученный поток и записывает его на экран и в файл "outfile".

Вероятно, это то, что ищет большинство людей. Вероятная ситуация: какая-то программа или скрипт долго работает и выдает много вывода. Пользователь хочет периодически проверять ход выполнения, но также хочет, чтобы вывод записывался в файл.

Проблема (особенно при смешивании потоков stdout и stderr) заключается в том, что существует зависимость от потоков, очищаемых программой. Если, например, все записи в stdout не сбрасываются, но все записи в stderr сбрасываются, то в конечном итоге они будут не в хронологическом порядке в выводе. файл и на экране.

Плохо также, если программа выводит только 1 или 2 строки каждые несколько минут, чтобы сообщить о прогрессе. В таком случае, если выходные данные не были сброшены программой, пользователь даже не увидел бы никаких выходных данных на экране в течение нескольких часов, потому что ни один из них не проталкивался бы через конвейер в течение нескольких часов.

В настоящее время у меня есть сценарий оболочки, который записывает сообщения в файл журнала следующим образом:

При выполнении этого скрипта нет вывода на экран, и, поскольку я подключаюсь к серверу через шпатлевку, мне приходится открывать другое соединение и делать "tail -f log_file_path.log", потому что я не могу завершить работающий скрипт, и я хочу увидеть результат в реальном времени.

Очевидно, что я хочу, чтобы текстовые сообщения печатались на экране и в файл, но я хотел бы сделать это в одну строку, а не в две строки, одна из которых не имеет перенаправления в файл.

Как этого добиться?

4 ответа 4

tee сохраняет ввод в файл (используйте -a для добавления, а не для перезаписи), а также копирует ввод в стандартный вывод.

Поскольку команда может определить, что она сейчас выполняется в неинтерактивном режиме, ее поведение может измениться. Наиболее распространенным побочным эффектом является отключение цветного вывода. Если это произойдет (и вам нужен вывод с цветовой кодировкой ANSI), вы должны проверить документацию команды, чтобы узнать, есть ли способ заставить ее вернуться к интерактивному поведению, например, grep --color=always . Имейте в виду, что это означает, что файл журнала будет также включать эти escape-коды, и вам нужно будет использовать меньше --RAW-CONTROL-CHARS "$log_file", чтобы прочитать его, не отвлекая литералы escape-кода. Также имейте в виду, что нет никакого способа сделать содержимое файла журнала отличным от того, что выводится на экран при выполнении вышеуказанной команды, поэтому вы не можете иметь цветной вывод на экран и нецветный вывод в файл журнала.

Вы можете использовать здесь-doc и . используйте эффективную, совместимую с POSIX общую модель коллектора.

Когда вы открываете heredoc, вы сигнализируете оболочке входным токеном IOHERE, что она должна перенаправлять свои входные данные на указанный вами дескриптор файла, пока не встретит другой конец вашего ограничительного токена.Я посмотрел вокруг, но я не видел много примеров использования номера fd перенаправления, как я показал выше, в сочетании с оператором heredoc, хотя его использование четко указано в основных правилах команд оболочки POSIX. Большинство людей просто наводят его на стандартный ввод и стреляют, но я считаю, что использование скриптлетов таким образом может защитить стандартный ввод и приложения, входящие в его состав, от жалоб на заблокированные пути ввода-вывода.

Содержимое heredoc передается в указанный вами файловый дескриптор, который, в свою очередь, затем интерпретируется как шелл-код и выполняется . встроенный, но не без указания конкретного пути для . . Если путь /proc/self вызывает у вас затруднения, попробуйте /dev/fd/n или /proc/$$. Кстати, этот же метод работает и с каналами:

Вероятно, это неразумно, как кажется. Вы можете сделать то же самое с sh, конечно, но цель . состоит в том, чтобы выполняться в текущей среде оболочки, что, вероятно, является тем, что вы хотите, и, в зависимости от вашей оболочки, гораздо более вероятно, что он будет работать с heredoc, чем со стандартным анонимным каналом.

В любом случае, как вы, наверное, заметили, я так и не ответил на ваш вопрос. Но если подумать, так же, как heredoc передает весь ваш код в . in, он также дает вам один простой недостаток:

Таким образом, весь стандартный вывод терминала из любого кода, выполняемого в вашем heredoc, передается из . как само собой разумеющееся, и его можно легко отсоединить от одной трубы. Я включил небуферизованный вызов cat, потому что мне неясно текущее направление stdout, но это, вероятно, избыточно (почти наверняка так, как написано), и конвейер, вероятно, может закончиться прямо на tee.

Вы также можете поставить под сомнение отсутствующую обратную косую черту во втором примере. Эту часть важно понять перед тем, как приступить к работе, и она может дать вам мало идей о том, как ее можно использовать. Ограничитель heredoc с кавычками (до сих пор мы использовали IOHERE и EOIN, и первый я цитировал с обратной косой чертой, хотя «одинарные» или «двойные» кавычки служат той же цели) запретит оболочке выполнять любое расширение параметра в содержимого, но ограничитель без кавычек оставит его содержимое открытым для расширения. Последствия этого, когда ваш heredoc . источники драматичны:

Поскольку я не указал ограничитель heredoc, оболочка расширила содержимое по мере его чтения и перед передачей результирующего файлового дескриптора в . выполнить. По сути, это привело к тому, что команды анализировались дважды — в любом случае расширяемые. Поскольку я цитировал расширение параметра $vars с обратной косой чертой, оболочка проигнорировала его объявление при первом проходе и удалила только обратную косую черту, чтобы все расширенное содержимое printf могло быть оценено как null при . получил сценарий на втором проходе.

Эта функциональность в основном именно то, что может предоставить опасная встроенная оболочка eval, даже если с цитированием гораздо проще обращаться в heredoc, чем с eval, и может быть столь же опасным. Если вы не планируете это тщательно, вероятно, лучше всего по привычке указать ограничитель «EOF». Просто говорю.

РЕДАКТИРОВАТЬ: Эх, я оглядываюсь на это и думаю, что это слишком много. Если ВСЕ, что вам нужно сделать, это объединить несколько выходных данных в один канал, то самый простой способ — просто использовать a:

Кривые будут пытаться запустить содержимое в текущей оболочке, тогда как скобки автоматически отключатся. Тем не менее, любой может сказать вам это, и, по крайней мере, на мой взгляд, . решение heredoc представляет собой гораздо более ценную информацию, особенно если вы хотите понять, как на самом деле работает оболочка.

Читайте также: