Bir kabuk betiğinde hem ekrana hem de dosyaya metin çıktısı nasıl verilir?


49

Şu anda mesajları şöyle bir günlük dosyasına kaydeden bir kabuk betiğim var:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

Bu betiği yürütürken ekrana çıktı yok ve sunucuya macunla bağlandığım için başka bir bağlantı açmam ve "tail -f log_file_path.log" yapmam gerekiyor çünkü çalışmayı sonlandıramıyorum komut dosyası ve ben çıktıyı gerçek zamanlı olarak görmek istiyorum.

Açıkçası, istediğim metin mesajlarının ekrana ve dosyaya basılmış olması, ancak birini dosyaya yönlendirmeyen iki satır değil tek satırda yapmak istiyorum.

Bu nasıl başarılır?

Yanıtlar:


71

Bu çalışıyor:

command | tee -a "$log_file"

teegirişi bir dosyaya kaydeder ( -aüzerine yazmak yerine kullanmak için kullanın ) ve girişi standart çıktıya da kopyalar.


8

Burada-doc ve kullanabilirsiniz. Verimli, POSIX dostu genel bir toplayıcı model için kaynak.

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

Heredoc'u açtığınızda, kabuğu bir IOHERE giriş belirteci ile işaretler, girişini sınırlayıcı belirtecinizin diğer ucuyla karşılaşıncaya kadar, belirttiğiniz dosya tanımlayıcısına yönlendirmesi gerekir. Etrafa baktım ancak kullanımı her ne kadar POSED temel kabuk komut yönergelerinde açıkça belirtilmiş olsa da, heredoc operatörüyle birlikte yukarıda gösterildiği gibi yeniden yönlendirme fd numarasının kullanımına ilişkin pek çok örnek görmedim. Çoğu kişi sadece onu stdin'e yöneltip ateş eder, ancak kaynak bültenleri bu şekilde stdin'den uzak tutabilir ve kurucu uygulamaların engellenen G / Ç yolları hakkında şikayet etmesini önleyebilirim.

Heredoc içeriği, daha sonra kabuk kodu olarak yorumlanır ve tarafından yürütülen, belirttiğiniz dosya tanımlayıcısına aktarılır. yerleşik, ancak için belirli bir yol belirtmeden. . Eğer / proc / self path size sorun çıkarırsa / dev / fd / n veya / proc / $$ komutunu deneyin. Bu aynı yöntem, bu arada, borular üzerinde çalışır:

cat ./*.sh | . /dev/stdin

Muhtemelen en azından göründüğü kadar beceriksizdir. Tabii ki sh ile aynı şeyi yapabilirsiniz, ancak. Amaç mevcut kabuk ortamında, muhtemelen istediğiniz şeydir ve kabuğuna bağlı olarak, heredoc ile çalışmaktan çok daha muhtemeldir. standart anonim bir boru ile.

Her neyse, muhtemelen fark ettiğiniz gibi, sorunuzu hala cevaplamadım. Ancak, bunu düşünürseniz, heredoc aynı kodunuzu tüm kaynak kodlarına aktarırken, aynı zamanda size basit ve basit bir bakış açısı sunar:

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

Bu yüzden, heredoc'unuzda yürütülen herhangi bir koddaki terminal stdout'larının tamamı çıkarıldı. Tabii ki tek bir borudan kolayca ayrılabilir. Arabelleksiz kedi çağrısını dahil ettim çünkü şu anki stdout yönü konusunda belirsizim, ama muhtemelen fazla olması (neredeyse kesinlikle yine de yazıldığı gibi) ve boru hattı da tam olarak sona erebilir.

İkinci örnekte eksik olan ters eğik çizgi teklifini de sorgulayabilirsiniz. Bu bölüme geçmeden önce anlamanız önemlidir ve nasıl kullanılabileceği hakkında size birkaç fikir verebilir. Bir alıntı heredoc sınırlayıcı (şimdiye kadar IOHERE ve EOIN kullandık ve ilk önce ters eğik çizgi ile alıntı yaptım, ancak "tek" veya "çift" tırnaklar aynı amaca hizmet etse de), kabuk üzerinde herhangi bir parametre genişlemesi gerçekleştirmesini engelleyecektir. içeriği, ancak bildirilmemiş bir sınırlayıcı, içeriğini genişlemeye açık bırakacaktır. Bunun özgeçmişiniz olduğunda bunun sonuçları. kaynaklı dramatik:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

Çünkü heredoc sınırlayıcısına alıntı yapmadım, kabuk içeriğini okur ve sonuçta ortaya çıkan dosya tanımlayıcısını sunmadan önce içeriğini genişletti. yürütmek için. Bu aslında komutların iki kez ayrıştırılmasına neden oldu - yine de genişletilebilir olanlar. Ben ters eğik çizgi $ vars parametresi genişlemesini alıntı yaptığım için kabuk ilk geçişteki bildirimini görmezden geldi ve sadece ters eğik çizgiyi çıkardı, böylece tüm printf genişletilmiş içeriği boş olduğunda değerlendirilebilirdi. İkinci geçişte senaryoyu yazdı.

Bu işlevsellik temel olarak tam olarak tehlikeli eval shell'in sağladığı şeydir, alıntı yapmak heredoc'ta değerlendirmekle değerlendirmekten çok daha kolay ve eşit derecede tehlikeli olabilir. Dikkatlice planlamazsanız, "EOF" sınırlayıcısını alışkanlık olarak kullanmak en iyisidir. Sadece söylüyorum.

EDIT: Eh, buna tekrar bakıyorum ve biraz gergin olduğunu düşünüyorum. Eğer TÜM yapmanız gereken, birkaç çıkışı tek bir boruya birleştiriyorsa, en basit yöntem sadece şunu kullanmaktır:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

Eğriler, mevcut kabuktaki içeriği çalıştırmaya çalışır, oysa parenler otomatik olarak alttan çıkar. Yine de, herkes bunu söyleyebilir ve en azından benim düşünceme göre. heredoc çözümü, özellikle kabuğun gerçekte nasıl çalıştığını anlamak istiyorsanız, çok daha değerli bir bilgidir.

İyi eğlenceler!


3

Bir yükleme betiğini bir yükleme günlüğü yazmak üzere değiştirmeyi ararken bu cevabı buldum.

Komut dosyam zaten aşağıdaki gibi yankı ifadeleriyle dolu:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

Ve bir tee ifadesinin çalışmasını istemedim (veya varolanı tee ile çağırmak için başka bir komut dosyası), bu yüzden şunu yazdım:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Şimdi, orijinal senaryomda, çıktıyı bir günlük dosyasında istediğim 'echo'yu' echolog 'olarak değiştirebilirim.

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.