unix cat

pienipoika 24.07.08 17:03

järjestelmäfunktioilla toteutettu pienimuotoinen cat klooni. koodin tarkoitus on näyttää järjestelmäfunktioiden read, write, open ja close toimintaa.

 Tekstiversio  Arvo: 2 (4 ääntä)  Äänestä: +  -
/* main.c */

/* makro O_RDONLY */
#include <fcntl.h>

/* järjestelmäfunktiot */
#include <unistd.h>

int main (int argc, char *argv[]) {

        /* puskuri syötteen lukemista varten */
        char buf[1024];
        int n;
        int laskuri;
        char openerror[]="Tiedostoa ei voitu avata\n";
        char writeerror[]="Ongelma tulostettaessa näytölle\n";
        char closeerror[]="Virhe suljettaessa tiedostoa\n";
       
        /* tarkistaa annettiinko ohjelmalle parametrejä */
        if ( argc >= 2 ){

                /* suorittaa seuraavat toimenpiteet yhtä monta kertaa kuin ohjelmalla on parametrejä */
                for ( laskuri = 1; laskuri <= argc-1; laskuri++ ) {
                        int file;

                        /* avaa tiedoston */       
                        if ( (file = open(argv[laskuri], O_RDONLY, 0)) == -1 ) {
                            write(2, openerror, sizeof(openerror));
                            return 1;
                        }
       
                        /* lukee tiedoston stdout:iin */
                        while ( (n = read(file, buf, 1024) ) > 0)
                                if ( write(1, buf, n) != n ) {     
                                        write(2, writeerror, sizeof(writeerror));
                                        return 1;
                                }
       
                        /* sulkee tiedoston */
                        if ( close(file) == -1 ) {
                                write(2, closeerror, sizeof(closeerror));
                                return 1;
                        }
                }
        }
       
        /* jos parametrejä ei ole, ohjelma kaiuttaa stdin:iä stdout:iin */
        else {
                while ( (n=read(0, buf, 1024)) > 0 )
                        if ( write(1, buf, n) != n ) {
                            write(2, writeerror, sizeof(writeerror));
                            return 1;
                    }
        }
       
        return 0;
}

editoitu: 17:26 24.7.08
pienipoika 17:12 24.7.08 
koska ohjelma käyttää järjestelmäfunktioita se ei tarvitse yhtään #include-riviä. koodi on kuitenkin järjestelmäriippuvaista. toimii useimmissa unix järjestelmissä. itse käänsin ohjelman ubuntu gutsyllä. gcc versiolla 4.1.3
järjestelmäfunktio open avaa tiedoston argv[1]. parametrit 0 ja 0 tarkoittavat avausmuotoa sekä tiedoston oikeuksia. oikeudet jätetään lähes poikkeuksetta tyhjiksi. avausmuoto 0 tarkoittaa tiedoston lukemista, 1 kirjoittamista sekä 2 molempia. numerot on kuitenkin vakioitu tiedostossa fcntl.h. write kirjoittaa tiedostoon (1 eli stdout) buf taulukon sisällön.

suurin ero standardeihin funktioihin ja järjestelmäfunktioihin (tässä esimerkissä) on tiedoston tyyppi. stdio.h:ssa määritelty fopen funktio palauttaa FILE tyyppisen osoittimen kun taas järjestelmäfunktiot käyttävät tiedostojen tyyppinä int:iä. toivottavasti tästä on jollekkin hyötyä.
Torak 18:13 24.7.08 
Ininägeneraattori status: on

Kyllä ne otsikkotiedostot pitäisi pistää hyvän ohjelmointi tavan mukaisesti mukaan.
Muuttujien määrittelyt pitäisi olla funktion alussa eli buf ja n alkuun.
Ohjelma ei tarkasta onko argumentteja annettu -> ei annettu ohjelma kaatuu koska argv[1] on null.
close() uuppuu ja write() kirjoittamisen onnistumista ei tarkisteta.
1 sijasta voisi käyttää jotain määrittelyä stdout.
pienipoika 19:34 24.7.08 
et ymmärtänyt ideaa. otsikkotiedostoja ei laiteta kun funktiot on järjestelmäfunktioita. tämän koodivinkin tarkoitus on esitellä mitä nämä funktiot ylipäätään on. ja miksi writeen pitäisi laittaa stdout? se on stdio:ssa määritelty ja sen lisääminen aiheuttaa vain virheen. tietysti voisin itse laittaa ohjelman alkuun: #define stdout 1 mutta tarkoitus on näyttää miten nämä funktiot toimivat. harva ymmärtää että se on tosiaan int jos siinä lukee muuttujan nimi. (koska stdio:n tiedostotyyppi on FILE eikä int. muuttujien buf sekä n paikalla taas ei ole mitään väliä ja esittelen ne ennen käyttöä.

close tosiaan unohtui. muokkasin koodia ja laitoin closen sinne sekä tarkistuksen parametreistä. laitoin myös tarkastuksen writelle.
LN 01:36 25.7.08 
C:ssä ei ole pakko mainita otsikkotiedostoja ylipäätään, ei siihen vaikuta se käyttääkö "järjestelmäfunktioita" vai jotain muuta. Funktioille ei tarvitse useimmiten antaa edes prototyyppiä, sen kuin käyttää vain ja ne toimivat. Siitäkin huolimatta otsikkotiedostojen kirjoittaminen näkyviin on hyvä tapa.

Muuttujien esittelyiden paikalla on väliä, jos ohjelman on tarkoitus kääntyä myös vähän vanhemmilla C-kääntäjillä. ANSI C89:ssä muuttujat on pakko esitellä lohkon alussa, ei missä sattuu.
pienipoika 15:29 25.7.08 
tiedän ettei ole pakko määritellä funktioita. kuitenkin jos haluaa käyttää esim gcc:tä niin että se varoittaa pienimmästäkin asiasta on headerit laitettava ohjelmaan tai käyttämällä exportia ilmoitettava käytettävistä funktioista. joka tapauksessa koodivinkit, kuten tämäkin, on usein suunnattu aloitteleville koodareille jotka eivät halua/viitsi kysyä apua joka ikisestä asiasta. koodivinkit ovat siihen hyödyllisiä. tämä etten käyttänyt includea painottaa sitä että joku ymmärtää että sitä ei tarvita ja että funktiot eivät esim ole stdio:sta. ja mitä tuohon ansi C89 tulee niin toivon että moni ei enää sellaisen standardin kääntäjiä käytä. kiitos kuitenkin kritiikistä. olisi tietenkin mukavampi ollut kuulla esim että "kiitos vinkistä, tätä en vielä tiennytkään", mutta meneehän se näinkin. kommentoikaa ihmeessä.
kahvari 23:31 25.7.08 
Kommentoidaan sitten. Ensinnäkin, pienipoika, kieli, joka sinun olisi syytä opetella, on suomi.
pienipoika 00:19 26.7.08 
pahoittelen suuresti sitä, etten ole suomen alkuperäistä väestöä enkä sen vuoksi osaa kieltänne täydellisesti. yritän kuitenkin parhaani ja mielestäni puhun (ja kirjoitan) lähes suomalaisen veroista tekstiä. kiitos palautteesta vaikka tarkoitinkin sitä pyytäessäni lähinnä koodivinkkiä. (ylempänä)
ane 09:57 29.7.08 
Kannattaa muistaa, että mikäli catille ei anneta tiedostoa, se kaiuttaa stdin:iä. Jos haluat matkia catia tällä ohjelmalla (jonka syvällisempää tarkoitusta en itse ainkaan ymmärrä), kannattaisi virheilmoituksen sijasta lukea tietoa stdinistä. Jos openilla haluat lukea stdiniltä, sinun pitää pyytää järjestelmältä stdinin numerotunniste funktiolla fileno.

editoitu: 16:25 1.8.08
T.M. 16:24 1.8.08 
kahvari kirjoitti:
Kommentoidaan sitten. Ensinnäkin, pienipoika, kieli, joka sinun olisi syytä opetella, on suomi.

mitäs vikaa hänen suomenkielen taidoissaan muka on?
editoitu: 19:26 4.8.08
pienipoika 18:43 4.8.08 
olet oikeassa ane. nyt ohjelma tulostaa stdin:n takaisin stdout:iin jos ohjelmalle ei anneta parametrejä.

"Jos openilla haluat lukea stdiniltä, sinun pitää pyytää järjestelmältä stdinin numerotunniste funktiolla fileno." <-
tietääkseni unix ohjelmissa on aina vakiona auki stdin, stdout ja stderr. näitä tiedostoja ei siis tarvitse erikseen avata tai sulkea. senkun kirjoittaa tai lukee vaan.
Liquid_Vision 01:43 7.8.08 
Haluat varmaan kokeilla onko argc suurempi tai yhtäsuuri kuin 2. Nyt parametreja tarvitaan 2 ennen kuin tiedosto tulostetaan..
LN 03:34 7.8.08 
Lisää pointteja:
1. Virheilmoituksia ei ole kovinkaan järkevää varsinkaan tämän tyyppisessä ohjelmassa tulostaa stdoutiin.
2. close()-funktion paluuarvoa ei tarkisteta, vaikka sen manuaalisivu nimenomaan kehottaa sen tarkistamaan.
3. Jälkimmäisen while-silmukan suoritettuaan ohjelma poistuu aina paluuarvolla nolla, tuli sitten virheitä tai ei.
4. Samaisessa silmukassa ei myöskään tarkisteta write():n paluuarvoa, vaikka sekin hyvin todennäköisesti voi palauttaa virheen siitäkin huolimatta että kirjoitetaan stdoutiin.
Ztane 08:36 7.8.08 
Öh. Jep, tämä missaa kokonaan catin pointin. Cathan tulee englannin kielen sanasta concatenate (files and print on the standard output).

Eli cat-komennon, jotta siitä nyt olisi varsinaisesti mitään hyötyä, pitää pystyä lukemaan myös usea komentoriviltä annettu tiedosto, sekä ymmärtää mahdollinen '-' standardisyötteeksi, useassa kohdassa. Se, että catia voi käyttää sinällään tiedoston tulostamiseen on vain yksi käyttötapa...

Ja, kyllä ne otsikkotiedostot pitää laittaa; jos vaikka suoraan sanois, että opetelkaa ite koodaamaan ennen ku alatte muita väärin neuvomaan ;). Koodi ei nimittäin "automaattisesti" toimi ilman niitä. Jos esimerkiksi vahingossa kirjoitat
C
long size;
write(1, buf, size);
 
ja longin koko promootiosääntöjen vuoksi ei olekaan sama kuin intin, räjähtää koodi helposti silmille.
Ztane 08:42 7.8.08 
sitä paitsi, open(fname, 0..) on väärin, koska funktion dokumentaation mukaan siinä on käytettävä jotain makroa O_RDONLY, O_RDWR tai O_WRONLY. Se, että O_RDONLY sattuu taas olemaan 0, on hyvää tuuria. Eli openille


C
       #include <sys/stat.h>
       #include <fcntl.h>
 
editoitu: 02:28 9.8.08
pienipoika 17:38 8.8.08 
Liquid_Vision kirjoitti:
Haluat varmaan kokeilla onko argc suurempi tai yhtäsuuri kuin 2. Nyt parametreja tarvitaan 2 ennen kuin tiedosto tulostetaan..

kyllä. unohtui "="-merkki siitä perästä. asia korjattu.
LN kirjoitti:
1. Virheilmoituksia ei ole kovinkaan järkevää varsinkaan tämän tyyppisessä ohjelmassa tulostaa stdoutiin.

laitoin ne stderr:iin
LN kirjoitti:
2. close()-funktion paluuarvoa ei tarkisteta, vaikka sen manuaalisivu nimenomaan kehottaa sen tarkistamaan.

korjattu.
LN kirjoitti:
3. Jälkimmäisen while-silmukan suoritettuaan ohjelma poistuu aina paluuarvolla nolla, tuli sitten virheitä tai ei.

korjattu.
LN kirjoitti:
4. Samaisessa silmukassa ei myöskään tarkisteta write():n paluuarvoa, vaikka sekin hyvin todennäköisesti voi palauttaa virheen siitäkin huolimatta että kirjoitetaan stdoutiin.

sekin korjattu..
Ztane kirjoitti:
Öh. Jep, tämä missaa kokonaan catin pointin.

koodin on tarkoitus näyttää open, close, read ja write funktioiden toimintaa. päätin tehdä sen cat ohjelman tyyliin.
tarkoitus ei siis ollut kirjoittaa täydellistä cat kloonia.
Ztane kirjoitti:
Eli cat-komennon, jotta siitä nyt olisi varsinaisesti mitään hyötyä, pitää pystyä lukemaan myös usea komentoriviltä annettu tiedosto

korjattu.
Ztane kirjoitti:
sitä paitsi, open(fname, 0..) on väärin, koska funktion dokumentaation mukaan siinä on käytettävä jotain makroa O_RDONLY, O_RDWR tai O_WRONLY. Se, että O_RDONLY sattuu taas olemaan 0, on hyvää tuuria.

open(fname, 0..) on ihan oikein. yleensä se 0 on read only ja ja 1 write onlyä.
laitoin tuon nyt kuitenkin käyttämään tuota fcntl.h:n makroa.
Ztane kirjoitti:
Eli openille


C
       #include <sys/stat.h>
       #include <fcntl.h>
 

miksi #include <sys/stat.h>? ei open funktio määrittele tiedoston oikeuksia.

nyt on muutama includekin siinä koodin alussa. pistäkää vaan lisää palautetta / korjausehdotuksia.
tällä hetkellä kääntyy ainakin gcc optiolla -Wall ilman mitään palautetta.
Konaparta 21:41 16.8.08 
Kyllä nuo makrot, joiden nimi jo kertoo mitä tehdään, on paljon selvempiä kuin nollat tai ykköset... Ei tarvitse tarkistaa dokumentaatiosta mitä se 0 tai 1 tarkoitti, kun siellä lukeekin O_RDONLY.....
pienipoika 23:01 16.8.08 
nollat ykköset tai kakkoset. juu. ei tarvii, mutta sanoin sen tossa kommentissa kuitenkin.