Принудительная очистка выходных данных в файл, пока скрипт bash все еще запущен
Вопрос
У меня есть небольшой скрипт, который ежедневно вызывается crontab с помощью следующей команды:
/homedir/MyScript &> some_log.log
Проблема с этим методом заключается в том, что some_log.log создается только после завершения работы MyScript.Я хотел бы сбросить выходные данные программы в файл во время ее запуска, чтобы я мог делать такие вещи, как
tail -f some_log.log
и следить за прогрессом, и т.д.
Решение
Сам bash никогда не будет записывать какие-либо выходные данные в ваш файл журнала. Вместо этого команды, которые он вызывает как часть сценария, будут по отдельности записывать выходные данные и сбрасываться всякий раз, когда им это нравится. Таким образом, ваш вопрос действительно в том, как заставить команды в скрипте bash сбрасываться, и это зависит от того, что они собой представляют.
Другие советы
Я нашел решение этой проблемы здесь.Используя пример OP, вы в основном запускаете
stdbuf -oL /homedir/MyScript &> some_log.log
и затем буфер очищается после каждой строки вывода.Я часто сочетаю это с nohup
для выполнения длительных заданий на удаленном компьютере.
stdbuf -oL nohup /homedir/MyScript &> some_log.log
Таким образом, ваш процесс не будет отменен при выходе из системы.
script -c <PROGRAM> -f OUTPUT.txt
Ключ - это -f.Цитата из man script:
-f, --flush Flush output after each write. This is nice for telecooperation: one person does `mkfifo foo; script -f foo', and another can supervise real-time what is being done using `cat foo'.
Запуск в фоновом режиме:
nohup script -c <PROGRAM> -f OUTPUT.txt
Вы можете использовать tee
для записи в файл без необходимости сброса.
/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
Это не функция bash
, так как все, что делает оболочка, - это открывает рассматриваемый файл, а затем передает дескриптор файла в качестве стандартного вывода сценария. Что вам нужно сделать, так это убедиться, что вывод из вашего скрипта сбрасывается чаще, чем вы сейчас.
Например, в Perl это можно сделать, установив:
$| = 1;
Дополнительную информацию об этом см. в perlvar .
Это поможет?
tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq
Это немедленно отобразит уникальные записи из access.log
http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html р>
Буферизация вывода зависит от того, как реализована ваша программа /homedir/MyScript
. Если вы обнаружите, что вывод буферизуется, вы должны принудительно применить его в своей реализации. Например, используйте sys.stdout.flush (), если это программа на Python, или используйте fflush (stdout), если это программа на C.
Как только что заметили здесь проблема в том, что вам приходится ждать, пока программы, которые вы запускаете из вашего скрипта, завершат свою работу.
Если в вашем скрипте вы запускаете программу в предыстория ты можешь попробовать что-нибудь еще.
В общем, призыв к sync
перед выходом позволяет очистить буферы файловой системы и может немного помочь.
Если в скрипте вы запускаете некоторые программы в предыстория (&
), вы можете подожди чтобы они завершились до того, как вы выйдете из скрипта.Чтобы иметь представление о том, как это может функционировать, вы можете посмотреть ниже
#!/bin/bash
#... some stuffs ...
program_1 & # here you start a program 1 in background
PID_PROGRAM_1=${!} # here you remember its PID
#... some other stuffs ...
program_2 & # here you start a program 2 in background
wait ${!} # You wait it finish not really useful here
#... some other stuffs ...
daemon_1 & # We will not wait it will finish
program_3 & # here you start a program 1 in background
PID_PROGRAM_3=${!} # here you remember its PID
#... last other stuffs ...
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3 # program 2 is just ended
# ...
С тех пор как wait
работает как с заданиями, так и с PID
числа ленивым решением должно быть поместить в конец скрипта
for job in `jobs -p`
do
wait $job
done
Более сложной является ситуация, если вы запускаете что-то, что запускает что-то еще в фоновом режиме, потому что вам приходится искать и ждать (если это так) окончания всех ребенок процесс:например, если вы запустите демон вероятно, это не тот случай, когда нужно ждать, пока он завершится :-).
Примечание:
wait ${!} означает "дождаться завершения последнего фонового процесса", где
$!
является PID последнего фонового процесса.Итак, чтобы поставитьwait ${!}
сразу послеprogram_2 &
эквивалентно непосредственному выполнениюprogram_2
не отправляя его в фоновом режиме с&
С помощью
wait
:Syntax wait [n ...] Key n A process ID or a job specification
Спасибо @user3258569
, скрипт , возможно, единственное, что работает в busybox
!
Однако после этого скорлупа показалась мне ледяной.В поисках причины я обнаружил эти большие красные предупреждения "не использовать в неинтерактивных оболочках" в страница руководства по скрипту:
script
в первую очередь предназначен для интерактивных терминальных сеансов.Когда стандартный интерфейс не является терминалом (например:echo foo | script
), то сеанс может зависнуть, потому что интерактивная оболочка внутри скрипта пропускает сеанс EOF иscript
понятия не имеет, когда закрывать сеанс.Посмотрите на Примечания раздел для получения дополнительной информации.
Верно. script -c "make_hay" -f /dev/null | grep "needle"
замораживал скорлупу для меня.
Несмотря на предупреждение, я думал, что echo "make_hay" | script
ПРОЙДЕТ EOF, поэтому я попробовал
echo "make_hay; exit" | script -f /dev/null | grep 'needle'
и это сработало!
Обратите внимание на предупреждения на справочной странице.Возможно, у вас это не сработает.
альтернативой stdbuf является awk '{print} END {fflush()}'
Я бы хотел, чтобы для этого был встроенный bash.
Обычно это не требуется, но в старых версиях могут быть ошибки синхронизации bash в файловых дескрипторах.
Я не знаю, сработает ли это, но как насчет вызова sync
?
У меня была эта проблема с фоновым процессом в Mac OS X с использованием StartupItems
. Вот как я это решаю:
Если я сделаю sudo ps aux
, я пойму, что mytool
запущен.
Я обнаружил, что (из-за буферизации), когда Mac OS X выключается, sed
никогда не передает вывод в команду sudo killall mytool
. Однако если я выполню stop
, то <=> передаст вывод команде <=>. Поэтому я добавил <=> случай к <=>, который выполняется при завершении работы Mac OS X:
start)
if [ -x /sw/sbin/mytool ]; then
# run the daemon
ConsoleMessage "Starting mytool"
(mytool | sed .... >> myfile.txt) &
fi
;;
stop)
ConsoleMessage "Killing mytool"
killall mytool
;;
Хорошо, нравится вам это или нет, так работает перенаправление. Р>
В вашем случае вывод (т.е. ваш сценарий завершен) вашего сценария перенаправлен в этот файл.
Что вы хотите сделать, так это добавить эти перенаправления в ваш скрипт.