Bir borunun boş olup olmadığı nasıl kontrol edilir ve değilse veriler üzerinde bir komut çalıştırılır mı?


42

Bir bash betiğine bir çizgi çizdim ve bir programa beslemeden önce borunun veri olup olmadığını kontrol etmek istiyorum.

Arama yaptığım test -t 0ancak burada çalışmıyor. Her zaman yanlış döndürür. Borunun verileri olduğundan nasıl emin olunur?

Örnek:

echo "string" | [ -t 0 ] && echo "empty" || echo "fill"

Çıktı: fill

echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"

Çıktı: fill

Yukarıdaki boru hattının çıktı üretip üretmediğini test etmek için Standart / kanonik yöntemden farklı olarak ? programa geçmek için giriş korunmalıdır. Bu, genel olarak Çıktıyı bir işlemden diğerine nasıl boruya aktaracağınızı ancak yalnızca ilk çıktı çıktığında nasıl çalıştıracağını belirtir? Hangi e-posta göndermeye odaklanır.


Yanıtlar:


37

Yaygın olarak kullanılabilen kabuk araçlarını kullanarak bir borunun içeriğine göz atmanın bir yolu yoktur ve boruya bir karakter okumanın ve geri koymanın bir yolu yoktur. Bir borunun verileri olduğunu bilmenin tek yolu bir bayt okumaktır ve ardından bu baytı hedefine götürmeniz gerekir.

Yani sadece şunu yapın: bir bayt okuyun; dosyanın sonunu algılarsanız, giriş boşken ne yapmak istediğinizi yapın; Bir bayt okuyorsanız, giriş boş olmadığı zaman ne yapmak istediğinizi çatallayın, içine bayt olan boruyu yerleştirin ve geri kalan verileri yayınlayın.

first_byte=$(dd bs=1 count=1 2>/dev/null | od -t o1 -A n | tr -dc 0-9)
if [ -z "$first_byte" ]; then
  # stuff to do if the input is empty
else
  {
    printf "\\$first_byte"
    cat
  } | {
    # stuff to do if the input is not empty
  }      
fi

ifneDan yarar Joey Hess moreutils onun giriş boş değilse bir komut çalıştırır. Genellikle varsayılan olarak yüklenmez, ancak çoğu unix türevinde oluşturulabilir veya kolay olması gerekir. Giriş boşsa, ifnehiçbir şey yapmaz ve başarıyla çalışan komuttan ayırt edilemeyen 0 durumunu döndürür. Giriş boşsa bir şey yapmak istiyorsanız, komutun 0 döndürmemesini ayarlamanız gerekir; bu, başarı durumunun ayırt edici bir hata durumu döndürmesini sağlayarak yapılabilir:

ifne sh -c 'do_stuff_with_input && exit 255'
case $? in
  0) echo empty;;
  255) echo success;;
  *) echo failure;;
esac

test -t 0bununla hiçbir ilgisi yok; standart girişin bir terminal olup olmadığını test eder. Herhangi bir girişin mevcut olup olmadığı konusunda hiçbir şekilde bir şey söylemez.


STREAMS bazlı borulara sahip sistemlerde (Solaris HP / UX), I_PEEK ioctl cihazını bir borunun içindekileri tüketmeden bakmak için kullanabileceğinizi düşünüyorum.
Stéphane Chazelas

@ StéphaneChazelas ne yazık ki * BSD'de bir borudan / ikilikten verileri gözetlemenin bir yolu yoktur, bu yüzden gerçek veriyi bir borudan geri getirebilecek taşınabilir bir peek yardımcı programın uygulanması için bir perspektif yoktur . (4.4 BSD'de 386BSD vs borular soket çiftleri olarak uygulandı , ancak daha sonraları * BSD'nin daha sonraki sürümlerinde - çift yönlü tutuldular -.
mosvy

bash, 'a maruz kalan girişi kontrol etmek için bir rutine sahiptir read -t 0(bu durumda, merak ediyorsanız zaman aşımı anlamına gelir).
Isaac,

11

Basit bir çözüm ifnekomut kullanmaktır (giriş boş değilse). Bazı dağıtımlarda, varsayılan olarak yüklenmez. moreutilsÇoğu dağıtımda paketin bir parçası .

ifne ve yalnızca standart giriş boş değilse, verilen bir komutu çalıştırır

Standart giriş boş değilse ifne, verilen komuta iletildiğini unutmayın.


2
2017'den itibaren Mac veya Ubuntu'da varsayılan olarak yoktur.
Sridhar Sarnobat

7

Eski soru, ama biri benim yaptığım gibi karşılaşırsa: Benim çözümüm zaman aşımı ile okumaktır.

while read -t 5 line; do
    echo "$line"
done

Eğer stdinboştur, bu 5 saniye sonra geri dönecek. Aksi takdirde, tüm girdileri okuyacak ve gerektiğinde işleyebilirsiniz.


Bu fikri sevdim ederken, -tne yazık ki POSIX parçası değildir: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
jepz

6

stdin (0) dosya tanıtıcısının açık mı yoksa kapalı mı olduğunu kontrol edin:

[ ! -t 0 ] && echo "stdin has data" || echo "stdin is empty"

Bazı verileri geçtiğinizde ve bazıları olup olmadığını kontrol etmek istediğinizde, yine de FD'yi geçersiniz, bu da iyi bir test değildir.
Jakuje

1
[ -t 0 ]fd 0 'ın tty' ye açılıp açılmadığını , kapalı veya açık olup olmadığını kontrol eder.
mosvy

@ mosvy, bu çözümü bir komut dosyasında kullanmayı nasıl etkileyeceğini açıklayabilir misiniz? İşe yaramadığı durumlar var mı?
JepZ

@JepZ ha? ./that_script </dev/null=> "stdin veriye sahip". Veya ./that_script <&-stdin'in gerçekten kapanmasını sağlamak .
mosvy

5

Sen kullanabilir test -s /dev/stdinsıra (açık alt kabukta).

# test if a pipe is empty or not
echo "string" | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

echo "string" | tail -n+2 | 
    (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

: | (test -s /dev/stdin && echo 'pipe has data' && cat || echo 'pipe is empty')

8
Benim için çalışmıyor. Her zaman borunun boş olduğunu söyler.
amphetamachine

2
Mac'imde çalışıyor ama Linux kutumda değil.
zirve

3

Bash:

read -t 0 

Bir girişin veri olup olmadığını tespit eder (hiçbir şey okumadan). Daha sonra girişi okuyabilirsiniz (eğer okuma okunurken mevcutsa):

if     read -t 0
then   read -r input
       echo "got input: $input"
else   echo "No data to read"
fi

Not: Bunun zamanlamaya bağlı olduğunu anlayın. Bu, girişin yalnızca çalışma zamanında veriye sahip olup olmadığını tespit eder read -t.

Örneğin,

{ sleep 0.1; echo "abc"; } | read -t 0; echo "$?"

çıkış 1(okuma hatası, yani: boş giriş). Yankı bazı verileri yazar ancak ilk baytını başlatmak ve yazmak için çok hızlı değildir, bu nedenle read -t 0program henüz bir şey yazmadığından girişinin boş olduğunu bildirir.


github.com/bminor/bash/blob/… - Burada bash'ın bir şeylerin dosya tanıtıcısında olduğunu algılamasının kaynağı.
Pavel Patrin

Thanks @PavelPatrin
Isaac

1
@PavelPatrin Bu çalışmıyor . Bağlantınızdan açıkça görüldüğü gibi, çalışması için olması gerektiği gibi, bashbir select()veya bir ioctl(FIONREAD)veya her ikisini de yapmaz. read -t0kırılmış, bozulmuş. Kullanmayın
mosvy

Oooh, bugün iki saat boyunca neyin yanlış olduğunu anlamaya çalışıyorum! Teşekkürler, @mosvy!
Pavel Patrin

3

Unix'te okunabilecek verilerin olup olmadığını kontrol etmenin kolay bir yolu FIONREADioctl'dir.

Bunu yapan standart bir yardımcı program düşünemiyorum, bu yüzden burada bunu yapan küçük bir program var ( ifnemoreutils IMHO'dan daha iyi ) ;-).

fionread [ prog args ... ]

Stdin'de kullanılabilir veri yoksa, durum 1 ile çıkacaktır prog. Veri varsa, çalışacaktır . Eğer hayır progverilmezse, durum 0 ile çıkacaktır.

Sen kaldırabilirsiniz pollyalnızca veri ilgilenen eğer çağrıyı derhal kullanılabilir. Bu, yalnızca borularla değil, çoğu fds türüyle çalışmalıdır.

fionread.c

#include <unistd.h>
#include <poll.h>
#include <sys/ioctl.h>
#ifdef __sun
#include <sys/filio.h>
#endif
#include <err.h>

int main(int ac, char **av){
        int r; struct pollfd pd = { 0, POLLIN };
        if(poll(&pd, 1, -1) < 0) err(1, "poll");
        if(ioctl(0, FIONREAD, &r)) err(1, "ioctl(FIONREAD)");
        if(!r) return 1;
        if(++av, --ac < 1) return 0;
        execvp(*av, av);
        err(1, "execvp %s", *av);
}

Bu program gerçekten işe yarıyor mu? POLLHUPBoş vakayı ele almak için bir olayı beklemenize gerek yok mu? Borunun diğer ucunda birden fazla dosya açıklaması varsa bu işe yarar mı?
Gilles 'SO durdurma varlık kötülük'

Evet çalışıyor. POLLHUP sadece anket tarafından döndürülür, POLLHUP'ı beklemek için POLLIN kullanmanız gerekir. Borunun herhangi bir ucunda kaç tane açık tutma kolu olduğu önemli değildir.
mosvy

Bkz unix.stackexchange.com/search?q=FIONREAD+user%3A22565 Perl içerisinden FIONREAD nasıl çalıştırılacağı için (daha yaygın kullanılan derleyiciler yerine)
Stéphane Chazelas

FIONREAD (veya HAVE_SELECT) kullanan yordam burada bash olarak uygulanır .
Isaac,

2

Kısa ve şifreli tek gömlekleri seviyorsanız:

$ echo "string" | grep . && echo "fill" || echo "empty"
string
fill
$ echo "string" | tail -n+2 | grep . && echo "fill" || echo "empty"
empty

Orijinal sorudan örnekler kullandım. Borulu veri istemiyorsanız -qgrep ile seçeneği kullanın


0

Bu, ilk satırın tamamını okumaktan memnunsanız, bash'te makul bir uygulama olarak görülüyor.

ifne () {
        read line || return 1
        (echo "$line"; cat) | eval "$@"
}


echo hi | ifne xargs echo hi =
cat /dev/null | ifne xargs echo should not echo

5
readgiriş boş değilse ancak yeni satır içermiyorsa , false değerini döndürür , girişinde bir readmiktar işlem yapar ve siz çağırmazsanız birden fazla satır okuyabilir IFS= read -r line. echokeyfi veriler için kullanılamaz.
Stéphane Chazelas

0

Bu benim için çalışıyor read -rt 0

orijinal sorudan örnek, veri olmadan:

echo "string" | tail -n+2 | if read -rt 0 ; then echo has data ; else echo no data ; fi

hayır, bu işe yaramıyor. { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }(yanlış negatif) ve true | { sleep .1; read -rt0 && echo YES; }(yanlış pozitif) ile deneyin . Aslında, bash en readaçılan fds tarafından bile aptal edilecek salt modu: { read -rt0 && echo YES; cat; } 0>/tmp/foo. Yaptığı tek şey o select(2)fd üzerinde.
mosvy

... ve selecteğer bir read(2)bloke olmazsa, ne olursa olsun EOFveya bir hata olursa olsun bir fd'yi "hazır" olarak döndürür. Sonuç: read -t0bir kırık içinde bash. Kullanmayın.
mosvy

@mosvy Bunu bashbug ile bildirdin mi?
Isaac,

@ mosvy Bu { sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }yanlış bir negatif değil çünkü (okuma yapılırken ) girdi yok. Daha sonra (uyku .1) o girdi olduğunu (kedi için) mevcuttur.
Isaac,

@ mosvy r seçeneği neden algılamayı etkiler ?: echo "" | { read -t0 && echo YES; }YES yazdırır, ancak echo "" | { read -rt0 && echo YES; }yapmaz.
Isaac,
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.