Принудительная очистка выходных данных в файл, пока скрипт bash все еще запущен

StackOverflow https://stackoverflow.com/questions/1429951

  •  07-07-2019
  •  | 
  •  

Вопрос

У меня есть небольшой скрипт, который ежедневно вызывается 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
    ;;

Хорошо, нравится вам это или нет, так работает перенаправление.

В вашем случае вывод (т.е. ваш сценарий завершен) вашего сценария перенаправлен в этот файл.

Что вы хотите сделать, так это добавить эти перенаправления в ваш скрипт.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top