Named pipe használata awk-ban

Azt szeretném, hogy amit egy programból named pipe-ba írok, azt awk-ban getline-nal el tudjam olvasni. A probléma viszont az, hogy ez blokkolós. Van egy ciklusom, amelynek futnia kell, s ha a named pipe-ba bejött egy sor, akkor azt átvenném getline-nal.

if ((getline row <pipe)>0) {
  printf("%s\n", row);
}

Ez a próbálkozásom. Ami szörnyű, hogy blokkolós, bent marad a getline-ban, amíg egy másik programból nem írok a pipe-ba. Utána már megy, csak az elején teszi ezt velem.

Hozzászólások

Nem azert blokkol mert az awk kozben olvasna be a kovetkezo sort? Probald ki igy:

awk 'BEGIN { while ( 1 ) { if ((getline row < "/tmp/fifo")>0 ) printf("[%s]\n",row); } }'

Nehezites hogy ugy latom /tmp/fifo-t mintha csak 1x nyitna meg a munkamenet soran. Igy ha a masik program ilyen echo whatever > /tmp/fifo jellegu parancsokkal eteti akkor jo kerdes hogy mi tortenik. Ez is bekavarhat, nem? 

A másik oldal értelemszerűen így eteti, különben nem lenne nagyon értelme. Amit írtam, az a BEGIN szekcióból hívott függvényben van. Az egész awk programom a BEGIN szekcióban van lényegében. A feladat nem kifejezetten awk-ra való, én lényegében egy C-szerű scriptnek használom az awk-t. :) Valójában egy hardware-nek küldök parancsokat, és azt akarom megoldani, hogy ha már egy példányban fut ez az awk program, mert oszcillogrammot rajzol valós időben, akkor ne okozzon kommunikációs zavart, hogy aszinkron módon küldök a hardware-nek parancsot. Sokkal inkább létrehoz az első awk - nyilván shell visszahívásával - egy named pipe-ot, a másik awk program ilyenkor beírja ebbe a mondanivalót, az első felszedi, s merge-eli az amúgy is küldött parancsok láncolatába.

Majdnem működik, csak az a bajom, hogy megáll a getline-ban. Próbáltam úgy kirúgni onnét, hogy

system("echo >" pipe);

de akkor ez is elakad. Gondolom, rájön, hogy ez vagy ugyanaz a process, vagy annak gyermeke. Még csak tapogatózom, de nem értem a jelenséget egyelőre.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Igy viszont szepen megy:

x=0; while true; do echo $x; x=$((x+1)); sleep 1; done > /tmp/fifo

A masik terminalban, ezelott inditva:

apal@laptop:~$ awk 'BEGIN { while ( 1 ) { if ((getline row < "/tmp/fifo")>0 ) printf("[%s]\n",row); } }'
[0]
[1]
[2]
[3]
[4]
[5]
[6]

Szepen, masodpercenkent, ahogy jon. 

El tudom játszani ebből az awk scriptből, hogy nem én vagyok az, aki a fifo-ba ír? Tehát magamnak szeretnék írni. Ugye, ha a fifo-ba írok, az a másik irány. Azt szeretném, hogy a távoli végébe írjak egy newline-t, de ebből a scriptből, aztán majd örül neki a getline, hogy jött valami.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Igen, de rájöttem, ez nem megoldás, mert csak azért nem blokkolt, mert nem zártam le a pipe-ot. Szóval nem ttudom átverni.

Akkor az kellene, hogy valamivel meg tudjam vizsgálni, üres-e a pipe. Ha igen, kihagynám a getline-t, ha nem, akkor olvastatnék vele. Vagy akkor azzal, amivel az ürességet vizsgálom. Most erre tényleg C-ben kell programot írni? Vagy erre ott lesz nekem a socat parancs?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Van egy zárt ciklusom. Amikor nincs semmi a fifoban, akkor socat-tal USB-re küldözget egy hardware-nek parancsokat, amelynek a válaszait feldolgozom, pontosabban gnuplottal oszcillogrammot rajzoltatok. Amikor beesik valami a fifo-ba, akkor a küldött parancsok közé kell merge-elni a fifoba érkezett parancsot. Ugye simán másik processből nem írhatok aszinkron az USB-re, mert az jó eséllyel épp foglalt, így szétesik a kommunikáció. Ezért szeretném ugyanazon processben intézni ezt akkor is, ha ezt az awk scriptet második példányban indítják el.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Most probaltam ilyesmiket:

mkfifo /tmp/fifo

exec 4<>/tmp/fifo

./nonblock 4 

Ahol a ./nonblock egy faek egyszeru program ami az adott fd-t O_NONBLOCK-ra alltja. Majd:

awk 'BEGIN { while ( 1 ) { if ( (getline row < "/dev/fd/4")>0 ) { printf("[%s]\n",row); } else { print "no data"; system("sleep 1"); } } }'

De az strace alapjan vsz ez sem fog igy osszejonni. Ugyanis az awk szereti meghivogatni a close()-t, foleg azert mert igy akkor ugyan nem blokkol a getline, ellenben a mogotte levo read() EAGAIN-nel ter vissza.  Szoval ja, az awk-t nem vszinu hogy erre talaltak ki :)

Az awk-ban van close. Ha nem használod, akkor ezt általában az END (stdin és EOF esetén) vagy a kilépés intézi.

Az strace alapjan nekem ugy tunt hogy a read() = -EAGAIN valtotta ki... persze lehet hogy beneztem, de ott volt egy hatarozott close(4) utana.

Érdemes lenne setlinebuf() kérdést is körüljárni, mivel a getline sort olvas.

Ez mi?

De barhogyis, ez szerintem ebben a formaban tulmutat az awk hataskoren. Mert a nonblock-ra bezarja az fd-t, a multipelxing pedig nem resze a nyelvnek. Olyasmit lehetne meg hogy van egy getline ciklus, es azt ket helyrol eteti a kollega: egyszer periodikusan a "routine operations" reszekent, egy masik process pedig kuldi az egyedi parancsokat. Csak ugye kerdes hogy mennyire keverednek ossze FIFO-ban a kulonbozo forrasok, van-e valami ami garantalja hogy sorbufferelt esetben azok nem mosodnak ossze. 

Vagy igy:

awk 'BEGIN { while ( 1 ) { while ( (getline row < "/tmp/fifo")>0 ) { printf("[%s]\n",row); } close("/tmp/fifo"); } }'

Vs:

x=0; while true; do echo $x > /tmp/fifo; x=$((x+1)); sleep 1; done

Ha bezarod a file-t amint elfogy, akkor mar a kovetkezo getline uj file-kent nyitja meg. Es akkor tudsz tobb echo ... > /tmp/fifo-val is operalni.

A fifoval én is szívtam. Végül megírtam 'C'-ben.

> Sol omnibus lucet.

Amíg a kollégám ebédelt, kitaláltam egy workaround-ot. Nem pipe, hanem file lesz a megoldás. Úgyis tmpfs-en RAM-ban van, szóval gyors meg pici is, itt néhány tíz byte-okról beszélünk.

A ciklusomban:

if ((getline row <tempfile) > 0) {
  printf("Ez jött: %s\n", row);
  close(tempfile);
  system("rm -f '" tempfile "'");
}

A külső program például:

echo 'Üzenet' >"$tempfile"

A tempfile értelemszerűen ugyanaz. És ez működik. :)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE