Wymuś opróżnianie danych wyjściowych do pliku, gdy skrypt bash nadal działa


89

Mam mały skrypt, który jest wywoływany codziennie przez crontab za pomocą następującego polecenia:

/homedir/MyScript &> some_log.log

Problem z tą metodą polega na tym, że plik some_log.log jest tworzony dopiero po zakończeniu działania MyScript. Chciałbym wrzucić dane wyjściowe programu do pliku podczas jego działania, aby móc na przykład robić rzeczy

tail -f some_log.log

i śledź postępy itp.


Musimy mieć opis - lub jeśli to możliwe kod - tego, co dokładnie robi twój mały skrypt ...
ChristopheD

7
Aby cofnąć buforowanie skryptów Pythona, możesz użyć "python -u". Aby odblokować skrypty perla, zobacz odpowiedź Grega Hewgilla poniżej. I tak dalej ...
Eloici

Jeśli możesz edytować skrypt, zwykle możesz wyczyścić bufor wyjściowy jawnie w skrypcie, na przykład w pythonie za pomocą sys.stdout.flush().
drevicko

Odpowiedzi:


28

Sam bash nigdy nie zapisze żadnych danych wyjściowych do pliku dziennika. Zamiast tego polecenia, które wywołuje jako część skryptu, będą indywidualnie zapisywać dane wyjściowe i opróżniać je, gdy tylko zechcą. Więc tak naprawdę twoje pytanie brzmi, jak zmusić polecenia w skrypcie bash do opróżnienia, a to zależy od tego, czym one są.


23
Naprawdę nie rozumiem tej odpowiedzi.
Alfonso Santiago

2
Aby lepiej zrozumieć, dlaczego standardowe wyjście zachowuje się w ten sposób, odwiedź stackoverflow.com/a/13933741/282728 . Krótka wersja - domyślnie po przekierowaniu do pliku standardowe wyjście jest w pełni buforowane; jest zapisywany do pliku dopiero po opróżnieniu. Stderr nie jest - jest zapisywany po każdym „\ n”. Jednym z rozwiązań jest użycie polecenia „script” zalecanego przez użytkownika3258569 poniżej, aby opróżniać standardowe wyjście po każdym końcu linii.
Alex

Stwierdzenie tego, co oczywiste, i dziesięć lat później, ale to jest komentarz, a nie odpowiedź i nie powinna to być akceptowana odpowiedź.
RealHandy

84

Tutaj znalazłem rozwiązanie tego problemu . Korzystając z przykładu OP, w zasadzie uruchamiasz

stdbuf -oL /homedir/MyScript &> some_log.log

a następnie bufor jest opróżniany po każdym wierszu wyjścia. Często łączę to z nohupwykonywaniem długich zadań na zdalnym komputerze.

stdbuf -oL nohup /homedir/MyScript &> some_log.log

W ten sposób Twój proces nie zostanie anulowany po wylogowaniu.


1
Czy możesz dodać link do jakiejś dokumentacji stdbuf? Na podstawie tego komentarza wydaje się, że nie jest on dostępny w niektórych dystrybucjach. Czy mógłbyś wyjaśnić?
Sfinansuj pozew Moniki

1
stdbuf -o dostosowuje buforowanie stdout. Inne opcje to -i i -e dla stdin i stderr. L ustawia buforowanie linii. Można również określić rozmiar bufora lub 0, aby wyłączyć buforowanie.
Seppo Enarvi

5
ten link nie jest już dostępny.
Fzs2

2
@NicHartley: stdbufjest częścią GNU coreutils, dokumentację można znaleźć na stronie gnu.org
Thor

Na wypadek, gdyby to komukolwiek pomogło, użyj, export -f my_function a następnie stdbuf -oL bash -c "my_function -args"jeśli potrzebujesz uruchomić funkcję zamiast skryptu
Anonimowy

28
script -c <PROGRAM> -f OUTPUT.txt

Klucz to -f. Cytat ze skryptu man:

-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'.

Uruchom w tle:

nohup script -c <PROGRAM> -f OUTPUT.txt

Łał! Rozwiązanie, które działa busybox! (moja muszla później się zawiesza, ale nieważne)
Victor Sergienko,

9

Możesz użyć teedo zapisu do pliku bez potrzeby opróżniania.

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null

2
To nadal buforuje dane wyjściowe, przynajmniej w moim środowisku Ubuntu 18.04. Treść ostatecznie zostanie zapisana w pliku w obie strony, ale myślę, że OP prosi o metodę, w której mogą dokładniej monitorować postęp przed zakończeniem zapisu pliku, a ta metoda nie pozwala na to bardziej niż przekierowanie wyjścia robi.
mltsy

3

Nie jest to funkcja programu bash, ponieważ wszystko, co robi powłoka, to otwiera dany plik, a następnie przekazuje deskryptor pliku jako standardowe wyjście skryptu. To, co musisz zrobić, to upewnić się, że dane wyjściowe skryptu są opróżniane częściej niż obecnie.

Na przykład w Perlu można to osiągnąć ustawiając:

$| = 1;

Więcej informacji na ten temat znajdziesz w perlvar .


2

Buforowanie danych wyjściowych zależy od sposobu /homedir/MyScriptimplementacji programu . Jeśli zauważysz, że dane wyjściowe są buforowane, musisz wymusić to w swojej implementacji. Na przykład użyj sys.stdout.flush (), jeśli jest to program w języku Python, lub fflush (stdout), jeśli jest to program w C.


2

Czy to pomogłoby?

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

Spowoduje to natychmiastowe wyświetlenie unikatowych wpisów z access.log za pomocą narzędzia stdbuf .


Jedynym problemem jest to, że stdbuf wydaje się być starym narzędziem, które nie jest dostępne w nowych dystrybucjach.
Ondra Žižka

.. ani w moim busyboxie :(
Campa

W tej chwili mam stdbufdostępne w Ubuntu, nie jestem pewien, skąd je mam.
Ondra Žižka

Mam stdbuf w Centos 7.5
Max

1
W Ubuntu 18.04 stdbufjest częścią coreutilus(znajduje się z apt-file search /usr/bin/stdbuf).
Rmano

1

Jak tylko dostrzeżone tutaj problemem jest to, że trzeba czekać, że programy, które biegną od skryptu zakończyć pracę.
Jeśli w swoim skrypcie uruchamiasz program w tle , możesz spróbować czegoś więcej.

Generalnie wywołanie syncprzed wyjściem pozwala opróżnić bufory systemu plików i może trochę pomóc.

Jeśli w skrypcie uruchamiasz niektóre programy w tle ( &), możesz poczekać , aż zakończą się, zanim wyjdziesz ze skryptu. Aby dowiedzieć się, jak to działa, możesz zobaczyć poniżej

#!/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
# ...

Ponieważ waitdziała zarówno z zadaniami, jak iz PIDliczbami, leniwym rozwiązaniem powinno być umieszczenie na końcu skryptu

for job in `jobs -p`
do
   wait $job 
done

Trudniejsza jest sytuacja, jeśli uruchamiasz coś, co uruchamia coś innego w tle, ponieważ musisz wyszukać i poczekać (jeśli tak jest) na koniec całego procesu potomnego : na przykład, jeśli uruchamiasz demona, prawdopodobnie tak nie jest czekać, kończy się :-).

Uwaga:

  • czekaj $ {!} oznacza „czekaj, aż zakończy się ostatni proces w tle”, gdzie $!jest PID ostatniego procesu w tle. Tak więc wstawienie wait ${!}tuż po program_2 &jest równoważne wykonaniu bezpośrednio program_2bez wysyłania go w tle za pomocą&

  • Z pomocy wait:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    

1

Dzięki @user3258569, skrypt to być może jedyna rzecz, która działa busybox!

Jednak po tym muszla zamarzała. Szukając przyczyny, znalazłem te duże czerwone ostrzeżenia „nie używaj w powłokach nieinteraktywnych” na stronie podręcznika skryptów :

scriptjest przeznaczony głównie do interaktywnych sesji terminalowych. Gdy stdin nie jest terminalem (na przykład echo foo | script:), sesja może się zawiesić, ponieważ powłoka interaktywna w sesji skryptu omija EOF i scriptnie ma pojęcia, kiedy zamknąć sesję. Więcej informacji można znaleźć w sekcji UWAGI .

Prawdziwe. script -c "make_hay" -f /dev/null | grep "needle"zamrażał dla mnie muszlę.

Wbrew ostrzeżeniu myślałem, że echo "make_hay" | scriptprzejdzie EOF, więc spróbowałem

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

i zadziałało!

Zwróć uwagę na ostrzeżenia na stronie podręcznika. To może nie działać dla Ciebie.


0

Alternatywą dla stdbuf jest to, awk '{print} END {fflush()}' że chciałbym mieć wbudowany bash, który to robi. Zwykle nie powinno to być konieczne, ale w starszych wersjach mogą występować błędy synchronizacji bash w deskryptorach plików.


-2

Nie wiem, czy to zadziała, ale co z dzwonieniem sync?


1
syncjest niskopoziomową operacją systemu plików i nie ma związku z buforowanym wyjściem na poziomie aplikacji.
Greg Hewgill,

2
syncw razie potrzeby zapisuje brudne bufory systemu plików do pamięci fizycznej. Jest to wewnętrzne dla systemu operacyjnego; aplikacje działające w systemie operacyjnym zawsze widzą spójny obraz systemu plików niezależnie od tego, czy bloki dysku zostały zapisane w fizycznej pamięci masowej. W pierwotnym pytaniu aplikacja (skrypt) prawdopodobnie buforuje dane wyjściowe w buforze wewnętrznym aplikacji, a system operacyjny nie będzie nawet wiedział (jeszcze), że dane wyjściowe są faktycznie przeznaczone do zapisania na standardowe wyjście. Zatem hipotetyczna operacja typu „synchronizacja” nie byłaby w stanie „sięgnąć” do skryptu i wyciągnąć danych.
Greg Hewgill,

-2

Miałem ten problem z procesem w tle w systemie Mac OS X przy użyciu rozszerzenia StartupItems. Oto jak to rozwiązuję:

Jeśli sprawię sudo ps aux, zobaczę, że mytooljest uruchomiony.

Zauważyłem, że (z powodu buforowania), gdy Mac OS X wyłącza się, mytoolnigdy nie przesyła danych wyjściowych do sedpolecenia. Jeśli jednak wykonam sudo killall mytool, mytoolprzekazuje dane wyjściowe do sedpolecenia. Dlatego dodałem do stopsprawy, StartupItemsktóra jest wykonywana, gdy Mac OS X się wyłącza:

start)
    if [ -x /sw/sbin/mytool ]; then
      # run the daemon
      ConsoleMessage "Starting mytool"
      (mytool | sed .... >> myfile.txt) & 
    fi
    ;;
stop)
    ConsoleMessage "Killing mytool"
    killall mytool
    ;;

To naprawdę nie jest dobra odpowiedź Freeman, ponieważ jest bardzo specyficzna dla twojego środowiska. OP chce monitorować wyjście, a nie go zabijać.
Gray

-3

czy ci się to podoba, czy nie, tak działa przekierowanie.

W twoim przypadku wyjście (co oznacza, że ​​skrypt się zakończył) twojego skryptu zostało przekierowane do tego pliku.

To, co chcesz zrobić, to dodać te przekierowania w swoim skrypcie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.