Как вывести текст на экран и файл внутри сценария оболочки?

49

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

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

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

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

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

jurchiks
источник

Ответы:

71

Это работает:

command | tee -a "$log_file"

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

l0b0
источник
8

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

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

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

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

cat ./*.sh | . /dev/stdin

Это, по крайней мере, так же неразумно, как кажется. Конечно, вы можете сделать то же самое с sh, но цель. - выполнить в текущей среде оболочки, что, вероятно, вам и нужно, и, в зависимости от вашей оболочки, с большей вероятностью будет работать с heredoc, чем со стандартным анонимным каналом.

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

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

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

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

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

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

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

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

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

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

Веселиться!

mikeserv
источник
3

Я нашел этот ответ, пытаясь изменить скрипт установки для записи журнала установки.

Мой скрипт уже полон эхо-операторов, таких как:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

И я не хотел, чтобы оператор тройника запускал его (или другой сценарий для вызова существующего с тройником), поэтому я написал это:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Так что теперь в моем исходном скрипте я могу просто изменить «echo» на «echolog», где я хочу получить вывод в файле журнала.

AndruWitta
источник