Bash betiğini komut satırından pipetlemeden tee kullanmaya zorla


20

Ben çıktı bir sürü bash betiği var ve ben tee ile boru olsaydı olur gibi bir dosyaya tüm çıktı kopyalamak için bu komut dosyası (yeni bir tane oluşturmak değil) değiştirmek istiyorum.

Bir dosya script.sh var :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

ve her seferinde yazmak zorunda kalmadan STDOUT dosyasının bir script.log dosyasına kopyalanmasını istiyorum./script.sh | tee script.log .

Teşekkürler, Tom


Bunu daha önce söylemediğim için üzgünüm, ama senaryo oldukça uzun, bu yüzden basit bir çözüm arıyorum. Ekleniyor | Her hatta tee biraz fazla.
Tom

Yanıtlar:


15

Dennis'in çok basit bir astarını çalıştıramadım, işte çok daha kıvrımlı bir yöntem. İlkini denerdim.

Belirtildiği gibi, tüm komut dosyası için standart hatayı ve standart çıkışı yeniden yönlendirmek için exec kullanabilirsiniz.
exec > $LOGFILE 2>&1 Bu şekilde: Bu, tüm stderr ve stdout'u $ LOGFILE dizinine çıkarır.

Şimdi, bunun bir günlük dosyasının yanı sıra konsola görüntülenmesini istediğiniz için, exec'nin yazmak ve okumak için tee için adlandırılmış bir kanal kullanmanız gerekecektir.
(Dennis'in tek astarı teknik olarak bunu da farklı bir şekilde olsa da yapıyor) Borunun kendisi ile yaratıldı mkfifo $PIPEFILE. Ardından aşağıdakileri yapın.

# Bir günlük dosyasına tee yazmaya başlayın, ancak girdisini adlandırılmış kanalımızdan çekin.
tee $ LOGFILE <$ PIPEFILE ve

# wait komutunun tee komutunun işlem kimliği.
TEEPID = $!

# stderr ve stdout'un geri kalanını adlandırılmış kanalımıza yönlendirin.
exec> $ PIPEFILE 2> & 1

echo "Komutlarınızı buraya yazın"
echo "Bütün standartları teed alacak."
echo "Standart hataları da öyle"> & 2

# stderr ve stdout dosya tanımlayıcılarını kapatın.
exec 1> & - 2> & -

# Borunun diğer ucu kapandığı için tişörtün bitmesini bekleyin.
$ TEEPID bekleyin

Kapsamlı olmak istiyorsanız, komut dosyanızın başında ve sonunda adlandırılmış kanal dosyasını oluşturabilir ve yok edebilirsiniz.

Kayıt için, bunun çoğunu rastgele bir erkeğin çok bilgilendirici blog gönderisinden topladım : ( Arşivlenmiş sürüm )


Cygwin'de işe yaramıyor gibi görünüyor. :-(
docwhat

Bu yazı eğitmek için iyi bir iş çıkarıyor. Çok teşekkür ederim.
Felipe Alvarez

27

Bunu betiğinizin başına ekleyin:

exec > >(tee -ia script.log)

Bu, stdout'a gönderilen tüm çıktıları dosyaya ekleyerek script.logönceki içerikleri yerinde bırakır. Eğer komut çalıştırılır taze her zaman başlatmak istiyorsanız hemen eklemek rm script.logbundan önce sağ execkomuta veya kaldırmak -agelen teekomuta.

-iSeçenek neden teekesme sinyalleri görmezden ve izin verebilir teedaha eksiksiz çıktı setini yakalamak için.

Tüm hataları (ayrı bir dosyada) da yakalamak için bu satırı ekleyin:

exec 2> >(tee -ia scripterr.out)

Çoklu >semboller arasındaki boşluklar önemlidir.


Çok ilginç bir çözüm.
inkaphink

2
Bu, yayınladığım tüm saçmalıklara kıyasla bunu yapmanın gerçekten zarif bir yoludur. Ama içinde bu iki satır bulunan bir senaryo oluşturduğumda askıda kalıyor ve asla düzgün çıkmıyor.
Christopher Karel

Bash'in hangi sürümü?
sonraki duyuruya kadar duraklatıldı.

GNU bash, RHEL5.3 kurulumunun bir parçası olarak sürüm 3.2.25 (1). Görünüşe göre bu resmi depolardan en son sürüm.
Christopher Karel

Bash 3.2.49 (23) sürümünde cygwin altında denedim ve dosya tanımlayıcı hataları aldım. Ben Ubuntu 9.10 Bash 4.0.33 (1) -release çalışır, bu yüzden bir Bash 4 şey olabilir.
sonraki duyuruya kadar duraklatıldı.

6

Bu, Dennis Williamson tarafından daha önce yayınlanan cevabın birleştirilmiş versiyonudur. Err & std komutunu script.log dosyasına doğru sırayla ekler.

Bu satırı komut dosyanızın başına ekleyin:

exec > >(tee -a script.log) 2>&1

>İşaretler arasındaki boşluğa dikkat edin. GNU bash, sürüm 3.2.25 (1) -çalışma (x86_64-redhat-linux-gnu) üzerinde çalışıyor


5

Sanırım başka bir yaklaşım şöyle:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 

3

Çıktının bir kopyasını istiyorsanız, teeşu şekilde kullanabilirsiniz :

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Ancak bu yalnızca stdout'u script.log dosyasına kaydeder. Hem stderr hem de stdout'un yönlendirildiğinden emin olmak için şunu kullanın:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Hatta küçük bir işlevle biraz daha güzel hale getirebilirsiniz:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini

İyi cevap. Gerçekten sadece stdout'u önemsiyorsa, 2>&1dışarı çıkıp sadece tişörtü çekerdim.
DaveParillo

Bu doğru, sadece stderr'i dahil ettim çünkü Igor'un önceki cevabı onu yönlendirmeye özen gösterdi ve iyi bir fikir olduğunu düşündüm.
inkaphink

1

Sadece şunu kullanırsınız:

script logfile.txt
your script or command here

Bu, her şeyi o günlüğe, her şeye çıktı verir ve ekranda görüntüler. TAM çıktı istiyorsanız, bu nasıl yapılır.

Ve sonra tembelseniz betiği tekrar oynatabilirsiniz.


Bunun scriptbir paket olduğunu not etmek iyi olabilir , örn. sudo apt-get install scriptDebian aromalı dağıtımlara kurmak için ek script -ac 'command opts' ~/Documents/some_log.scriptolarak bir terminal içinde aşağıdaki gibi bir şeyle incelenebilir strings ~/Documents/some_log.script | more; meraklı yeniden oynatma / görüntüleme yöntemlerine izin vermek için enjekte eden stringsbazı şeyleri önceden dezenfekte etmenin basit bir yolu olabilir . Aksi takdirde @Phishy sağlam bir cevap, çünkü interaktif şeyleri de korur. script script
S0AndS0

0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Nasıl çalıştığı hakkında daha fazla bilgiyi burada bulabilirsiniz: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/


1
İlk satırda yazılan günlüğü silmemesi için Igor'un cevabındaki ikinci satıra bir ikinci> ekledim.
Doug Harris

1
Yine de bir kopyasını istiyor, bu yüzden kullanması gerekiyor tee.
inkaphink

@Raphink: Bu doğru :)
Tom
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.