Bash betiğinde stdout satır satır nasıl yakalanır


26

Bir bash betiğinde, uzun bir komut satırının standart çıktısını satır satır yakalamak istiyorum, böylece ilk komut hala çalışırken analiz edilebilir ve raporlanabilirler. Bu, yapmayı hayal edebildiğim karmaşık yoldur:

# Start long command in a separated process and redirect stdout to temp file
longcommand > /tmp/tmp$$.out &

#loop until process completes
ps cax | grep longcommand > /dev/null
while [ $? -eq 0 ]
do
    #capture the last lines in temp file and determine if there is new content to analyse
    tail /tmp/tmp$$.out

    # ...

    sleep 1 s  # sleep in order not to clog cpu

    ps cax | grep longcommand > /dev/null
done

Bunu yapmanın daha basit bir yolu olup olmadığını bilmek istiyorum.

DÜZENLE:

Sorumu netleştirmek için bunu ekleyeceğim. longcommandSaniyede bir kez çizgiyle görüntüler durumunu hattı. longcommandTamamlanmadan önce çıktıyı yakalamak istiyorum .

Bu şekilde, longcommandbeklediğim sonuçları sağlamıyorsa , potansiyel olarak öldürebilirim .

Denedim:

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

Ancak whatever(örneğin echo) yalnızca longcommandtamamlandıktan sonra yürütülür.


Yanıtlar:


32

Sadece komutu bir whiledöngüye yönlendirin. Bunun için bir takım nüanslar var, fakat temelde ( bashPOSIX kabuğundaki veya içindeki ):

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

Bununla ilgili diğer ana sonuç ise ( IFSaşağıdakiler dışında), bitiminden sonra döngü içerisindeki değişkenleri kullanmaya çalıştığınız zamandır . Bunun nedeni, döngünün değişkenlere erişemediğiniz bir alt kabukta (sadece başka bir kabuk işleminde) yürütülmesidir (ayrıca döngünün sonunda biter, bu noktada değişkenler tamamen gider.). yapabilirsin:

longcommand | {
  while IFS= read -r line
  do
    whatever "$line"
    lastline="$line"
  done

  # This won't work without the braces.
  echo "The last line was: $lastline"
}

Ayarlamanın HAUKE örneğinin lastpipede bashbaşka bir çözümdür.

Güncelleştirme

'Olduğu gibi' komutunun çıktısını stdbufişlediğinizden emin olmak için , işlemi ' stdoutsatır arabelleğe alınacak şekilde ayarlamak için kullanabilirsiniz .

stdbuf -oL longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

Bu, işlemi, çıkışını bloklar halinde dahili olarak tamponlamak yerine, her seferinde boruya bir satır yazmak için yapılandırır. Programın bu ayarı kendi içinde değiştirebileceğine dikkat edin. Benzer bir etki unbuffer(parçası expect) veya ile elde edilebilir script.

stdbufGNU ve FreeBSD sistemlerinde mevcuttur, sadece stdiotamponlamayı etkiler ve sadece dinamik olarak bağlı olan setuid olmayan, setgid olmayan uygulamalar için çalışır (LD_PRELOAD hile kullandığı için).


@Stephane Bu IFS=gerekli değil bash, bunu son kez kontrol ettim.
Graeme

2
Evet öyle. İhmal ederseniz gerekli olmaz line(bu durumda sonuç $REPLYöndeki ve sondaki boşluklar kırpılmadan verilir). Deneyin: veya echo ' x ' | bash -c 'read line; echo "[$line]"'ile karşılaştırınecho ' x ' | bash -c 'IFS= read line; echo "[$line]"'echo ' x ' | bash -c 'read; echo "[$REPLY]"'
Stéphane Chazelas 16

@Stephane, tamam, bununla adlandırılmış bir değişken arasında bir fark olmadığını asla anlamadım. Teşekkürler.
Graeme

@Graeme Sorumu açıklığa kavuşturmamış olabilirim ama uzun komut tamamlanmadan çıkış hattını satır satır işlemek istiyorum (uzun komutlar bir hata mesajı görüntülerse hızlı tepki vermek için).
Sorumumu

@gfrigon, güncellendi.
Graeme

2
#! /bin/bash
set +m # disable job control in order to allow lastpipe
shopt -s lastpipe
longcommand |
  while IFS= read -r line; do lines[i]="$line"; ((i++)); done
echo "${lines[1]}" # second line
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.