» »

linux - pipe - čudno obnašanje

linux - pipe - čudno obnašanje

slovencl ::

Hočem naredit eno aplikacijo, kjer mi string iz rs232 porta preoblikuje v ustrezno obliko. To sem naredil s pipe-i. Če prek com porta sprejmem nek string, (v tem primeru "ff1234") mi ga:
1: - v prvem primeru preoblikuje v "6666313233340"
2: - potem to nadalje z gawk preoblikujem v "#102 b00110001001100100011001100110100 !"
3: - ta rezultat pa bi nadalje rad posla naprej v nov pipe, vendar zadeva ne deluje. Če ga pošljem npr v "cat" mi ne izpiše ničesar več.
4: - če pa ta string "6666313233340a" pošljem direktno v gawk, pa zadeva deluje, in mi izpiše "#102 b00110001001100100011001100110100 !"

Tega ne razumem. Kako je možno da mi v četrtem primeru zadevo izpiše, v tretjem pa ne. Saj je vse standardni input/output?

1: laptop:~/vcd$ cat /dev/ttyUSB0 | while read juhu; do xxd -ps <<<"$juhu"; done | cat 
6666313233340a
^C
2: laptop:~/vcd$ cat /dev/ttyUSB0 | while read juhu; do xxd -ps <<<"$juhu"; done | gawk -f hextovcd.awk 
#102 b00110001001100100011001100110100 !
^C
3: laptop:~/vcd$ cat /dev/ttyUSB0 | while read juhu; do xxd -ps <<<"$juhu"; done | gawk -f hextovcd.awk | cat
^C
4: laptop:~/vcd$ echo "6666313233340a" | gawk -f hextovcd.awk | cat
#102 b00110001001100100011001100110100 !
laptop:~/vcd$ 
  • spremenil: slovencl ()

slovencl ::

Naj samo še povem takaj imam
"while read juhu; do xxd -ps <<<"$juhu"; done"
. Zato, ker če uporabim samo "xxd -ps", mi spet nič ne izpiše. Tud to ne štekam zakaj...

ziga7 ::

Zato, ker imaš spredaj komentar? '#'

slovencl ::

Mislim da ni to. Problem v tem znaku #. Mislim da je problem v xxd (oz. od ima enak efekt). Če imam com port linije sklenjene (se pravi kar pošljem dobim tudi nazaj na vhod), in pošiljam na izhod:
echo "123456" > /dev/ttyUSB0


dobim v različnih primerih različne rezultate. Enostavno ne razumem zakaj mi v četrtem primeru nič ne izpiše. Kot da ta xxd (od) ne bi imel za izhod "standard output" (je to možno?)

1: laptop:~$ cat /dev/ttyUSB0
123456
123456
123456
^C
2: laptop:~$ cat /dev/ttyUSB0 | cat
123456
123456
123456
^C
3: laptop:~$ xxd -p /dev/ttyUSB0
3132333435360a3132333435360a3132333435360a^C
4: klemen@klemen-laptop:~$ xxd -p /dev/ttyUSB0 | cat
^C


Tako enostaven primer - kar dobim na vhod bi rad pretvoril v hex in preusmeril naprej - pa se že dva dni zaje..a.a... to je kr nekej...
A se da na "nižjem nivoju" pogledat, kaj vrže ven v enem in drugem primeru, oz kaj je problem da ne dela... kako naj to debugiram?

Aja, pa spet, če uporabim echo, pa dela vse kot je treba, tudi z xxd.
laptop:~$ echo "123456" | xxd -p | cat
3132333435360a


A je lahko to komu jasno :|

Zgodovina sprememb…

  • spremenil: slovencl ()

jype ::

Da se pogledat, kaj kdo bere in kaj kdo piše.

V drugi lupini poženeš strace -p <pid od procesa, ki bi ga rad opazoval.>

Jean-Paul ::

A se da na "nižjem nivoju" pogledat, kaj vrže ven v enem in drugem primeru, oz kaj je problem da ne dela... kako naj to debugiram?
UTSL

Zgodovina sprememb…

Jean-Paul ::

IMHO je stvar povezana z bufferiranjem. Cat gotovo uporablja bufferiran output stream, kar poenostavljeno pomeni, da se do prvega newline-a na izhodu ne bo nič prikazalo, ker se buffer pač ne bo flushnil.

Vsekakor pa bi za ta tvoj enostaven primer veliko lažje kot z dolgo kačo pip shajal s C-jevskim programčkom, v katerem bi lahko vse stvari (buffering, ...) nastavljal in nadziral po mili volji.

slovencl ::

Sej biffer flushne, poglej zadnji primer echo "123456" | xxd -p | cat

Sej program v c-ju sem imel na 3/4 napisan, ampak se je potem ustavilo pri pipah, ker še nikoli delal z njimi, zato sem se potem odločil uporabit obstoječe stvari.

slovencl ::

Če se ti da, (prosim) poskusi če imaš linux, najbrž bi šlo tudi, če na tty nastaviš echo - najbrž bi delovalo isto kot brez hardverske povezave na com portu (nisem probal, ker trenutno nimam možnosti).

Jean-Paul ::

Naj ti bo. V resnici že xxd bufferira svoj output.

Sej biffer flushne, poglej zadnji primer echo "123456" | xxd -p | cat
Tukaj stvar seveda dela, saj xxd flushne output takoj, ko najde na svojem vhodu EOF. Takrat seveda izvede tudi exit().

V primeru, ko pa se na vhodu ne pojavi EOF (in to je seveda tudi v primeru tvojega branja s tty device-a), xxd flushne šele takrat, ko je njegov izhodni buffer poln.

Za demonstracijo sem ti spisal programček myecho.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
        int ntimes = -1;
        int i;

        if (argc == 2) {
                ntimes = atoi(argv[1]);
        }


        if (ntimes == -1) {
                while (1) {
                        printf("123456\n");
                        sleep(1);
                }
        } else {
                for (i = 0; i < ntimes; ++i) {
                        printf("123456\n");
                        sleep(1);
                }
        }

        return 0;
}
V konzoli poženi sledeče in opiši, kaj vidiš:
a) ./myecho 5 | xxd -p
b) ./myecho | xxd -p

Zgodovina sprememb…

slovencl ::

Nimam dostopa do linux mašine, ampak se mi zdi da vem kaj bo prišlo ven. Po 30 znakov naenkrat, ker ima -p po defaultu nastavljen -c na 30. To sem se že igral, ampak tudi če dam xxd -p -c6, mi zadeva ne deluje. Brez cat na koncu dela, z cat pa ne.

Ok, če je problem EOF, in ga echo na koncu prilepi, lahko potem z stty nastavim da mi bo com port na koncu prejetega stringa pošiljal še EOF, oz nekako drugače zraven prilepim EOF in je? cat pa poženem z nohup opcijo (oz neko opcijo ki ne zapre cat programa)?

Zgodovina sprememb…

  • spremenil: slovencl ()

Jean-Paul ::

Ne, ne, ne in ne.

xxd bi moral nekako prisiliti, da po vsaki vrstici izvede flush. Catch je v tem, da se io streami v primeru, ko je standardni izhod ali vhod povezan na interaktivno napravo (tipkovnica, konzola, ...), obnašajo drugače, kot pa če je povezava preusmerjena v drug proces. Tako npr. C-jevska printf() funkcija po znaku za newline izvede flush le v primeru, ko je stdout usmerjen na konzolo. Če pa je stdout preusmerjen v datoteko ali na stdin drugega procesa, potem se ta flush ne izvede implicitno.

Sem malenkost spremenil programček:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>

int main(int argc, char *argv[]) {
        bool doflush = false;

        if (argc == 2 && strcmp(argv[1],"-f") == 0) {
                doflush = true;
        }

        while (1) {
                printf("123456\n");
                if (doflush) {
                        fflush(stdout);
                }
                sleep(1);
        }

        return 0;
}
Poženi:
a) ./myecho | xxd -p
b) ./myecho -f | xxd -p
in poročaj, kaj opaziš.

Namesto xxd programa lahko uporabiš tudi spodnjo kodo, ki jo lahko prilagodiš svojim potrebam.

myxxd.c:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main() {
        char *line = NULL;
        size_t len = 0;
        ssize_t read;
        int i;

        while ((read = getline(&line, &len, stdin)) != -1) {
                for (i = 0; i < read; ++i) {
                        printf("%02x", line[i]);
                }
                printf("\n");
        }

        free(line);
        exit(EXIT_SUCCESS);
}
P.S. Za bolše razumevanje si preberi odgovore na stackoverflow

Zgodovina sprememb…

slovencl ::

Aha, hvala :) sem preizkusil in dela. Brez flusha ne izpisuje. Tole zadnje sem pa tudi preizkusil, ampak to ne morem uporabiti direktno, ali pac. Ne morem tega izhoda peljat v pipo/drug vhod?

slovencl ::

Evo zadnji rezultati:

laptop:~/vcd$ echo "123456" | ./myxxd | gawk '{printf"izhod: %s\n",$1}'
izhod: 3132333435360a
vse kul...

preko com porta:
laptop:~/vcd$ cat /dev/ttyUSB0  | ./myxxd 
3132333435360a
tud vse kul...

laptop:~/vcd$ cat /dev/ttyUSB0 | ./myxxd | gawk '{printf"izhod: %s\n",$1}'
^C
nič ne pride ven...

Zgodovina sprememb…

  • spremenil: slovencl ()

slovencl ::

A da uporabljaš pipe | ni potrebno uporabljati popen, write, read? Kdaj pa se potem to uporablja?

Jean-Paul ::

laptop:~/vcd$ cat /dev/ttyUSB0 | ./myxxd | gawk '{printf"izhod: %s\n",$1}'
Seveda ne dela, saj myxxd spet bufferira svoj output (ker njegov izhod ne konča v terminalu pač pa je preusmerjen na gawk-ov standardni vhod). Sedaj bi moral to že vedeti. Če želiš, da boš dobil izpis po vsakem newline, moraš stream flushniti (v myxxd.c dodaj klic fflush(stdout) na pravo mesto). Prav tako uporaba gawk-a sploh ni potrebna, saj lahko izpis formatiraš že v C kodi myxxd.c, ki jo lahko seveda drugače oz. ustrezneje poimenuješ.

A da uporabljaš pipe | ni potrebno uporabljati popen, write, read? Kdaj pa se potem to uporablja?
Ne, ker za to poskrbi lupina (bash, csh, zsh, ...). Popen() in kolegice uporabljaš takrat, ko se želiš iti medprocesne komunikacije v lastnoročno napisani C-jevski kodi. Za več uporabi man in google.

Zgodovina sprememb…

slovencl ::

Super, Jean-Paul, res hvala za pomoč :)


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

Izšel LibreOffice 3.5 (strani: 1 2 )

Oddelek: Novice / Pisarniški paketi
6217466 (13275) Icematxyz
»

[php] Knjiga gostov

Oddelek: Izdelava spletišč
293155 (2358) darix
»

C++ šahovske pozicije

Oddelek: Programiranje
71927 (1927) Thomas
»

Supercomputing '06 konferenca

Oddelek: Novice / Omrežja / internet
172454 (1794) noraguta
»

Primerjanje dveh datotek - vrstico po vrstico

Oddelek: Programiranje
9963 (779) |Luka|

Več podobnih tem