Bash betiği hala çalışırken çıktının bir dosyaya boşaltılmasını zorla


90

Aşağıdaki komutu kullanarak crontab tarafından günlük olarak adlandırılan küçük bir komut dosyam var:

/homedir/MyScript &> some_log.log

Bu yöntemle ilgili sorun, some_log.log dosyasının yalnızca MyScript bittikten sonra oluşturulmasıdır. Program çalışırken çıktısını dosyaya boşaltmak istiyorum, böylece

tail -f some_log.log

ve ilerlemeyi takip edin vb.


Küçük betiğinizin tam olarak ne yaptığına dair bir açıklamaya -ya da mümkünse- koduna ihtiyacımız olacak ...
ChristopheD

7
Python betiklerinin arabelleğini kaldırmak için "python -u" kullanabilirsiniz. Perl komut dosyalarının arabelleğini kaldırmak için, aşağıdaki Greg Hewgill yanıtı bölümüne bakın. Ve bunun gibi ...
Eloici

Betiği düzenleyebiliyorsanız, genellikle çıktı arabelleğini bir betik içinde, örneğin python ile açıkça temizleyebilirsiniz sys.stdout.flush().
drevicko

Yanıtlar:


28

bash, aslında günlük dosyanıza hiçbir çıktı yazmaz. Bunun yerine, betiğin bir parçası olarak çağırdığı komutların her biri çıktıyı ayrı ayrı yazacak ve istedikleri zaman temizleyecektir. Öyleyse sorunuz gerçekten bash betiğindeki komutları nasıl yıkamaya zorlayacağınızdır ve bu ne olduklarına bağlıdır.


25
Bu cevabı gerçekten anlamıyorum.
Alfonso Santiago

3
Standart çıktının neden böyle davrandığını daha iyi anlamak için stackoverflow.com/a/13933741/282728 adresine bakın . Kısa sürüm - varsayılan olarak, bir dosyaya yeniden yönlendirilirse, stdout tamamen arabelleğe alınır; bir dosyaya ancak yıkamadan sonra yazılır. Stderr değildir - her "\ n" den sonra yazılır. Çözümlerden biri, her satır sonundan sonra stdout'un temizlenmesi için user3258569 tarafından önerilen 'script' komutunu kullanmaktır.
Alex

2
Açık olanı ifade etmek ve on yıl sonra, ama bu bir yorum, bir cevap değil ve kabul edilen cevap olmamalı.
RealHandy

87

Burada buna bir çözüm buldum . OP'nin örneğini kullanarak temelde

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

ve ardından her çıktı satırından sonra tampon boşaltılır. nohupUzun işleri uzaktaki bir makinede çalıştırmak için sık sık bunu birleştiriyorum .

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

Bu şekilde, çıkış yaptığınızda işleminiz iptal edilmez.


1
İçin bazı belgelere bir bağlantı ekleyebilir misiniz stdbuf? Dayanarak Bu yoruma bazı dağıtımlarda geçerli değil gibi görünüyor. Açıklar mısın
Fon Monica'nın Davası

1
stdbuf -o stdout arabelleğe almayı ayarlar. Diğer seçenekler stdin ve stderr için -i ve -e'dir. L satır tamponlamasını ayarlar. Ayrıca arabellek boyutu veya arabelleğe alma olmadan 0 belirtilebilir.
Seppo Enarvi

5
bu bağlantı artık mevcut değil.
john-jones

2
@NicHartley: stdbufGNU coreutils'in bir parçasıdır, dokümantasyon gnu.org'da bulunabilir
Thor

Herkese yardımcı olması durumunda, kullanın export -f my_function ve sonra stdbuf -oL bash -c "my_function -args"komut dosyası yerine bir işlevi çalıştırmanız gerekirse
Anonim

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

Anahtar -f. Man komut dizisinden alıntı:

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

Arka planda çalıştır:

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

Vaov! İşe yarayan bir çözüm busybox! (kabuğum daha sonra donar, ama her neyse)
Victor Sergienko

9

teeTemizlemeye gerek kalmadan dosyaya yazmak için kullanabilirsiniz .

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

2
Bu, en azından Ubuntu 18.04 ortamımda çıktıyı hala tamponlar. İçerikler sonunda her iki şekilde de dosyaya yazılır, ancak OP'nin dosyanın yazılması bitmeden ilerlemeyi daha doğru bir şekilde izleyebilecekleri bir yöntem istediğini ve bu yöntem buna çıktı yeniden yönlendirmeden daha fazla izin vermediğini düşünüyorum. yapar.
mltsy

3

bashKabuğun yaptığı tek şey söz konusu dosyayı açmak ve ardından dosya tanımlayıcısını betiğin standart çıktısı olarak iletmek olduğu için bu bir işlevi değildir . Yapmanız gereken şey, çıktının betiğinizden temizlendiğinden emin olmaktır . şu anda olduğundan daha sık .

Örneğin Perl'de, bu şu ayarlarla gerçekleştirilebilir:

$| = 1;

Bununla ilgili daha fazla bilgi için perlvar'a bakın .


2

Çıktının tamponlanması, programınızın nasıl /homedir/MyScriptuygulandığına bağlıdır . Çıktının arabelleğe alındığını fark ederseniz, uygulamanızda onu zorlamanız gerekir. Örneğin, bir python programı ise sys.stdout.flush (), bir C programı ise fflush (stdout) kullanın.


2

Bu yardımcı olur mu?

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

Bu, stdbuf yardımcı programını kullanarak access.log'daki benzersiz girdileri hemen görüntüler .


Tek sorun, stdbuf'un yeni dağıtımlarda bulunmayan eski bir yardımcı program gibi görünmesidir.
Ondra Žižka

..nor meşgul kutumda :(
Campa

Aslında stdbufşu anda Ubuntu'da mevcut, nereden aldığımdan emin değilim.
Ondra Žižka

Ben CentOS 7.5 de stdbuf var
Max

1
Ubuntu 18.04'te, (ile bulunur ) bir stdbufparçasıdır . coreutilusapt-file search /usr/bin/stdbuf
Rmano

1

Nasıl sadece benekli burada sorunu da komut çalıştırmak olduğunu programlar işlerini bitirmek beklemek zorunda olmasıdır.
Betiğinizde programı arka planda çalıştırırsanız , daha fazlasını deneyebilirsiniz.

Genelde syncçıkmadan önce yapılacak bir çağrı, dosya sistemi arabelleklerini temizlemeye izin verir ve biraz yardımcı olabilir.

Komut dosyasında arka planda ( &) bazı programları başlatırsanız, komut dosyasından çıkmadan önce bitmelerini bekleyebilirsiniz . Nasıl işleyebileceği hakkında fikir sahibi olmak için aşağıya bakabilirsiniz.

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

Yana waitişlerle yanı sıra ile işler PIDsayılar tembel çözüm senaryonun sonunda koymak olmalıdır

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

Arka planda başka bir şey çalıştıran bir şey çalıştırırsanız durum daha zordur, çünkü tüm alt sürecin sonunu aramanız ve beklemeniz gerekir (eğer durum buysa) : örneğin bir arka plan programı çalıştırırsanız muhtemelen durum böyle değildir beklemek biter :-).

Not:

  • bekle $ {!}, son arka plan işleminin $!PID'sinin bulunduğu "son arka plan işlemi tamamlanana kadar bekleyin" anlamına gelir . Yani wait ${!}hemen sonrasına koymak , arka planda göndermeden program_2 &doğrudan yürütmeye eşdeğerdir.program_2&

  • Yardımından wait:

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

1

Teşekkürler @user3258569, senaryo belki de işe yarayan tek şeybusybox !

Yine de kabuk benim için dondu. Nedeni aradığımda, komut dosyası kılavuz sayfasında şu büyük kırmızı uyarıları "etkileşimli olmayan kabuklarda kullanmayın" buldum :

scriptöncelikle etkileşimli terminal oturumları için tasarlanmıştır. Stdin bir uçbirim olmadığında (örneğin :)echo foo | script , betik oturumundaki etkileşimli kabuk EOF'yi kaçırdığından ve oturumu scriptne zaman kapatacağına dair hiçbir ipucu olmadığından oturum askıda kalabilir . Daha fazla bilgi için NOTLAR bölümüne bakın .

Doğru. script -c "make_hay" -f /dev/null | grep "needle"benim için kabuğu donduruyordu.

Uyarının tersine, echo "make_hay" | scriptbir EOF geçeceğini düşündüm , bu yüzden denedim

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

ve işe yaradı!

Man sayfasındaki uyarılara dikkat edin. Bu sizin için çalışmayabilir.


0

stdbuf'a alternatif, keşke awk '{print} END {fflush()}' bunu yapmak için bir bash yerleşik olsaydı. Normalde gerekli olmamalıdır, ancak daha eski sürümlerde dosya tanımlayıcılarda bash senkronizasyon hataları olabilir.


-2

İşe yarayıp yaramayacağını bilmiyorum, peki ya aramak sync?


1
syncdüşük seviyeli bir dosya sistemi işlemidir ve uygulama seviyesinde arabelleğe alınmış çıktıyla ilgisi yoktur.
Greg Hewgill

2
synckirli dosya sistemi arabelleklerini gerekirse fiziksel depolamaya yazar. Bu, işletim sisteminin içindedir; İşletim sisteminin üstünde çalışan uygulamalar, disk blokları fiziksel depolamaya yazılsın ya da yazılmasın her zaman dosya sisteminin tutarlı bir görünümünü görür. Orijinal soru için, uygulama (betik) muhtemelen çıktıyı uygulamanın içindeki bir arabellekte arabelleğe alıyor ve işletim sistemi çıktının aslında stdout'a yazılacağını (henüz) bile bilmeyecektir. Dolayısıyla, varsayımsal bir "senkronizasyon" tipi işlem, komut dosyasına "ulaşamaz" ve verileri çekemez.
Greg Hewgill

-2

Bu sorunu Mac OS X'te StartupItems. Ben böyle çözüyorum:

Ben yaparsanız sudo ps auxgörebildiğim mytoolbaşlatılır.

Mac OS X kapandığında (arabelleğe alma nedeniyle) mytoolçıktıyı hiçbir zaman sedkomuta aktarmadığını buldum . Ancak, çalıştırırsam sudo killall mytool, mytoolçıktıyı sedkomuta aktarır . Bu nedenle, Mac OS X kapandığında yürütülen bir stopvaka ekledim StartupItems:

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

Çevrenize çok özel olduğu için bu gerçekten iyi bir cevap değil Freeman. OP, çıktıyı öldürmek değil izlemek istiyor.
Grey

-3

hoşunuza gitsin ya da gitmesin, yönlendirme bu şekilde çalışır.

Sizin durumunuzda, betiğinizin çıktısı (betiğinizin bittiği anlamına gelir) o dosyaya yeniden yönlendirilir.

Yapmak istediğiniz, bu yönlendirmeleri komut dosyanıza eklemektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.