Fórumok
Sziasztok, a szabvány shell parancssorában viszek be egy többsoros szöveget:
user@host~: sh -c 'a=" asdf
> mnb"'; a=${a#*d}; echo "$a";'
ez jól működik, viszont egyelőre sehogy sem tudom megoldani, hogy a \newline-nál csapjon le ebből a többsoros szövegből.
Szerintetek hogy tudnám ezt kifejezni szabvány shell-ben?
Hozzászólások
Játszottam vele:
Erre vágytál?
Szerk.: Ez kimaradt: bash-5.0.11-1.fc31.x86_64
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
sajnos ez csak bash-hal működik, ott nem is lenne kérdés, de nekem dash vagy bármi alatt kéne, ezért is hangsúlyoztam és hangsúlyozom, hogy szabvány shell alatt kell, hogy működjön, és nem értem még, hogy hogy fejezzem ki a \newline-t, úgy, hogy működjön (ha változóba raktam, akkor is úgy tűnt, hogy csak sima karakterre ment)....
https://commons.wikimedia.org/wiki/Libre art
Muszáj ezt shellben? Esetleg sed vagy awk nem jöhet szóba? Vagy tr? Mondanám azt, hogy a shell nem erre való, ráadásul van shell, ami tudja is. De, hogy épp dash-sel hogyan lehet megcsinálni, azt nem tudom. Van Fedorára is dash, de most így szombaton inkább mást csinálok. :) Egyébként bash közvetlenül is megengedi:
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Most nem próbáltam ki, de read vagy cut nem játszik?
Igen, szabyánv shellben érdekel, hogy ne csak más karakterre, hanem \n -re is tudjak vágni, ez a kihívás, és nem értem még a logikáját, az ellenőriztem, hogy \012 -ként rátolja el az újsort, tényleg, nincs semmi trükk, de a kifejezésben erre vágni még nem tudok. Szóval ez a kihívás:
read: a szabvány shellben a read a leírások szerint és a kísérleteim szerint TÉNYLEG csak STDIN-ről olvas.
Nem szeretném temp fájlba kiíratni a cuccot, nem szeretnék subshelt nyitni, amiből megint csak mindig macera az eredmény visszakapása, de mindképpen alapvetően az a kérdés, hogy a szabvány shellben egy változóban lévő tetszőleges oktet-sorozatot csak az ASCII vagy bármelyik karakterkódú karakternél el tudom-e vágni :)
https://commons.wikimedia.org/wiki/Libre art
Még a newline-ra látok esélyt, de például a \0-ra már nem. Szóval, ha bináris feldolgozást szeretnél, írd meg C-ben! :)
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
egyelőre örülnék, hogy ha \newline-ra is el tudnám vágni szabvány shellben, ez a kihívás :)
https://commons.wikimedia.org/wiki/Libre art
hm, erre nem gondoltam, hogy közvetlenül vigyem be, ne \n formában, de műk, köszi! :)
https://commons.wikimedia.org/wiki/Libre art
Ugyan bash-sel, de kipróbáltam a newline bevitelét közvetlenül, amikor még nem volt hozzászólás a topic-odban, működött is, de nem említettem, mert annyira ronda megoldásnak éreztem, hogy mindenképp valami jól megfogalmazható megoldásra vágytam. Igaz, akkor sem tudtam volna meg, hogy dash-sel működik-e, vagy sem. De mindegy is, már okafogyott, lényeg, hogy megvan a megoldás.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
ez egyfajta gondolkozás, amire nekem is ritkábban áll rá az agyam, de abszolút nyitott vagyok blikkfangosabb megoldásra, de én még nem találtam!
https://commons.wikimedia.org/wiki/Libre art
Na most, ha mégis
\n
-es formában akarod, akkor a Caro által javasoltread
is megoldás lehet:Használat:
Ez persze nem a legszebb (POSIX shellben nincsenek tömbök, így nem lehet egyszer tömbbé alakítani), de működik. Aki akar finomíthat rajta.
Oldschool Computer - http://oscomp.hu
á, igen, ez nem ugrott be, hogy miért nem pipe-ból read-elek, ez kellett nekem, asszem:
user@host:~/d$ sh -c 'echo " asdf
mnb" | while read -r line; do echo "$line ."; done'
asdf .
mnb .
pseudo indexelt tömböket szoktam használni eval segítségével létrehozva, olvasva.
https://commons.wikimedia.org/wiki/Libre art
> ez kellett nekem, asszem
Akkor megérte leírni, bár tagadhatatlan, hogy az sz által leírt megoldás jóval egyszerűbb.
Oldschool Computer - http://oscomp.hu
a programnak egyébként ezen a szinten azt kéne tudnia, hogy megmondja, melyik sor hány karakter hosszú esetleges bevezető szóközöket is beleértve
https://commons.wikimedia.org/wiki/Libre art
wc -m
5
4
Oldschool Computer - http://oscomp.hu
Én is while read-del szoktam, ráadásul, ha 1 sorban több változó van (pl. mysql oszlopok), akkor felsorolom, hogy read A B C
és a do után echo "$A - $B - $C" már rögtön változóban felhasználható.
Jogos, bár itt csak egy változó volt, az egész sor.
Oldschool Computer - http://oscomp.hu
Csak az a baj, ugye a |-os megoldásokkal, hogy alhéjat nyitnak, és az eredményt nem tudom visszakapni a főfüggvényekben...
https://commons.wikimedia.org/wiki/Libre art
Miért ne tudnád? Illusztrálnád egy konkrét példával, hogy mire gondolsz?
Oldschool Computer - http://oscomp.hu
Szerintem erre:
a=alma; echo korte | read a; echo "$a"
alma
Ugye azért, mert a pipe miatt önálló shellben az az 'a' változó egészen más memóriaterületen lett allokálva.
tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE
Dehát pont erre adtam egy példát, ahol függvényből kérjük le az egyes elemeket. Ezt minden további nélkül át lehet írni hosszra is.
Bármi, amit egy subshellben (UNIX shellben a "függvény" is csak egy subshell) kiírunk a
stdout
-ra, azt el lehet kapni a subshell operátorokkal és fel lehet használni. Tényleg nem értem a problémát.Oldschool Computer - http://oscomp.hu
ez igaz, ha bent hagyom függyvényben, akkor kezelhetőbb ebből a szempontból, a wc nem játszik, mert ugye shellben maradok, több irányban is gondolkozom, hogy mi legyen a kód, amiben végül is egy ASCII art karaktereit elemzem és kezelem, amihez először is meg akarom állapítani a dimenzióit. Most egyelőre azon az úton vagyok, hogy a fő ciklusomban karakterről karakterre olvasom, asszem végül is így fogom megkapni, hogy hány oszlopos, hány soros, ami nekem kell
https://commons.wikimedia.org/wiki/Libre art
De miért ne játszana a
wc
? Az is astdout
-ra írkál, tehát el lehet kapni a kimenetét. Nem kell függvénybe se rakni:Tudnál mutatni példát, hogy mi a probléma?
Oldschool Computer - http://oscomp.hu
Ennél azért ez egy cseppet olcsóbb:
strlen="${#line}"
tl;dr
Egy-két mondatban leírnátok, hogy lehet ellopni egy bitcoin-t?
Az mondjuk igaz.
Oldschool Computer - http://oscomp.hu
én is ebben gondolkodtam, csak sajnos tapasztaltam olyat, hogy a sor eleji szóközt nem számolja...
https://commons.wikimedia.org/wiki/Libre art
önmagában nekem is jól csinálja, de aztán valahol meg azt írta, hogy 3 5 3, bár most van egy valamennyire működő megoldásom, de nyilván én is az egész feladatra a legjobb, legrövidebb és legalapabb-legbiztosabb megoldást szeretném, hogy a kapott értékekkel a legjobban dolgozhassak, igazából az lesz a program, hogy egy ascii-artot oszlopokra hasogassak és paramétertől függően csal egy részét mutassam rajzoljam ki a szabvány terminál bal oldalán...
https://commons.wikimedia.org/wiki/Libre art
azért, mert ahogy írtam, szabvány shellben kell az eredmény, itt tart a projekt:
https://commons.wikimedia.org/wiki/Libre art
A
wc
egy külső parancs, amit szabvány shellből is meg lehet hívni. Ami pedig a subshell-t illeti, ha subshellbe rakod a ciklust is, akkor ki tudod echozni belőle a változódat:Így a
RMAXW
változóban megkaptad a legszélesebb sor hosszát, azaz az ASCII-art valódi szélességét. És ez POSIX shellből is megy.Oldschool Computer - http://oscomp.hu
Ez soronként hány processzt pazarol el?
"Pazarol el?" Lássuk csak...
Soronként három processzt indít: egy
read
-et, egyecho
-t és egywc
-t. Ha van egy "teljes képernyős" (80x25)-ös ASCII-artunk, akkor ez összesen 75 processzt fog indítani.A másik karakterenként indít egy
printf
-et. Az összesen 2025 processz egy 80x25-ös ASCII-art esetén, mivel ott a newline is számít.Azaz 75-2025=-1950 processzt "pazarol el".
Oldschool Computer - http://oscomp.hu
"Soronként három processzt indít: egy
read
-et, egyecho
-t"Hogymi?!
A read mindenképpen shell builtin, különben nem tudná a shellben látható környezeti változó értékét változtatni.
Az echo lehet külön processz, lásd /bin/echo, de a legtöbb shellben builtin, éppen az extra processzek overheadjének elkerülése miatt. A printf szintén.
Cserébe viszont ott vannak a subshellek a pipe-oknak és a command substitution-öknek...
> A read mindenképpen shell builtin, különben nem tudná a shellben látható környezeti változó értékét változtatni.
The read utility historically has been a shell built-in. It was separated off into its own utility to take advantage of the richer description of functionality introduced by this volume of POSIX.1-2017.
Azaz lehet külön és builtin is, de akkor legyen builtin. Az mínusz 25 processz.
> Az echo lehet külön processz, lásd /bin/echo, de a legtöbb shellben builtin, éppen az extra processzek overheadjének elkerülése miatt. A printf szintén.
A
echo
és aprintf
a POSIX shellben nem builtin.> Cserébe viszont ott vannak a subshellek a pipe-oknak és a command substitution-öknek...
A subshellek? Két subshell van és mind a kettő a cikluson kívül. Az összesen kettő subshell. A pipe és a substitution ugyan ott van, de közben Zahy fentebb mutatott jobbat. Azaz:
És ezzel a rössel megspóroltuk a substitution-t, az
echo
-t a pipe-t és awc
-t is. Akkor soronként 0 processz. Szemben a 2025printf
-fel és 2025 subsitutionnel.Oldschool Computer - http://oscomp.hu
Az, hogy egy parancs a "Utilities" fejezetben kapott helyet, nem jelenti azt, hogy azt nem lehet builtinként implementálni.
Lásd pl. alias, cd, getopts, read, ulimit, amik mind abban a fejezetben vannak, de nem lehet őket külső parancsként implementálni.
A pipe és a command substitution is subshelleket használ.
A ciklus belsejében jelen pillanatban nulla darab parancs, subshell és substitution található. De lentebb benchmarkoltam is egyet. Az eredmény magáért beszél. A karakterenkénti feldolgozás kb. ~130x pazarlóbb, mint a soronkénti.
Oldschool Computer - http://oscomp.hu
Igen, mostmár, mert Zahy lelőtte a poént.
Mondjuk a ciklus körüli subshell még mindig felesleges...
Tényleg? Nézzük meg Zahy megoldása nélkül az eredeti javaslatomat:
Ez még így is ~11x gyorsabb, mint a karakterenkénti feldolgozás (~36 sec vs. ~3.2 sec).
> Mondjuk a ciklus körüli subshell még mindig felesleges...
Nem az. Ha leszedem, a végeredmény nulla lesz. Próbáld ki.
Oldschool Computer - http://oscomp.hu
"Tényleg? Nézzük meg Zahy megoldása nélkül az eredeti javaslatomat:
~3.2 sec"
Hát bizony, a pipe és wc sokkal lassabb, mint a shell-től megkérdezni a string hosszát.
Höhö.
"> Mondjuk a ciklus körüli subshell még mindig felesleges...
Nem az. Ha leszedem, a végeredmény nulla lesz. Próbáld ki."
De felesleges, csak nem jól szedted le:
> Hát bizony, a pipe és wc sokkal lassabb, mint a shell-től megkérdezni a string hosszát.
Nem mondod... Lényeg megvolt, hogy a karakteres feldolgozáshoz képest még így is 11x gyorsabb volt?
> De felesleges, csak nem jól szedted le:
A megfordítás tényleg nem jutott eszembe. Viszont az érintett subshell egyszer futott az egész script alatt, úgyhogy nem túl sok vizet zavart.
Oldschool Computer - http://oscomp.hu
"Nem mondod... Lényeg megvolt, hogy a karakteres feldolgozáshoz képest még így is 11x gyorsabb volt?"
Nem, a lényeg az, hogy a subshellek és a külsö processzek drágák.
A karakterenkénti feldolgozás önmagában nem baj, a karakterenkénti subshell volt ott az igazi gond. A subshellek és a külső processz még soronként is gond: egy tisztességesen implementált karakterenkénti feldolgozás több mint ötször gyorsabb tud lenni, mint a soronként $(echo "$line" | wc -m)-et futtató változat.
"Viszont az érintett subshell egyszer futott az egész script alatt, úgyhogy nem túl sok vizet zavart."
Az érintett subshellel együtt a script elején lévő $(cat "$1") is mehetett a levesbe, és ezek együtt kb 30% gyorsulást jelentenek. Jobban megnézve, a ciklus körüli command substitution is teljesen felesleges, anélkül már 40%-nál járunk.
> Nem, a lényeg az, hogy a subshellek és a külsö processzek drágák.
> A karakterenkénti feldolgozás önmagában nem baj, a karakterenkénti subshell volt ott az igazi gond.
Meg is válaszoltad magadnak. A másik karakterenként csinált subshellt és
printf
call-t. Na, az az igazi pazarlás.> A subshellek és a külső processz még soronként is gond: egy tisztességesen implementált karakterenkénti feldolgozás több mint ötször gyorsabb tud lenni, mint a soronként $(echo "$line" | wc -m)-et futtató változat.
Viszont, ha nincs soronként külső processz, meg subshell, akkor a soronkénti feldolgozás mindenképpen sokkal gyorsabb lesz, mint a karakterenkénti feldolgozás, mert kevesebbet kell interpreterből iterálni. (Egyébként mit értesz "tisztességesen implementált" karakterenkénti feldolgozáson?)
> Az érintett subshellel együtt a script elején lévő $(cat "$1") is mehetett a levesbe, és ezek együtt kb 30% gyorsulást jelentenek. Jobban megnézve, a ciklus körüli command substitution is teljesen felesleges, anélkül már 40%-nál járunk.
Ez mondjuk jogos, a
cat
tényleg sokat nyomott.Oldschool Computer - http://oscomp.hu
Tudom, hogy meg lehet, végül is a progi ezen fázisának ez lett kb a kódja:
https://commons.wikimedia.org/wiki/Libre art
És ez így jobb?
Oldschool Computer - http://oscomp.hu
jobbnak tűnik, a printf builtin :)
https://commons.wikimedia.org/wiki/Libre art
A
printf
mióta builtin a POSIX shellben? Az csak bash-ban builtin. POSIX-ban az külön utility. Direkt POSIX compliant megoldást kértél, miért a bash-specifikus megoldás most?Oldschool Computer - http://oscomp.hu
A printf (és az echo) abban a shellben builtin, amelyik úgy implentálja. A Bash-en kívül ilyen a dash, ksh, ksh93, mksh/lksh, yash, FreeBSD és NetBSD /bin/sh, ...
De ő szabvány shell-es megoldást kért. A szabvány a POSIX. Abban nem builtin egyik sem.
Oldschool Computer - http://oscomp.hu
hm, na most ezen elgondolkoztam, mert én bash-t nem használok, de dash-ban is van builtin printf, és olyan megfogalmazásban is olvastam, hogy ez POSIX builtin utility, most akkor, látva, hogy printf-em van a különféle shellekben, még dash-ban is, szóval használom, de végül is érdekel, hogy akkor most mi az abszolút igazság??
https://commons.wikimedia.org/wiki/Libre art
A
dash
egy konkrét shell, ami nem 100% POSIX compliant. (http://man7.org/linux/man-pages/man1/dash.1.html)Például pont a builtin
printf
-nek van is nem javított bugja, ami töri a POSIX-kompatibilitást (mondjuk téged pont nem érint).Te egész konkrétan szabvány shell megoldást kértél, én azt adtam, egy szóval nem mondtad, hogy dash-specifikus legyen.
Oldschool Computer - http://oscomp.hu
viszonylag sokat olvastam most arról, hogy akkor mi a fene van az echo-val és a printf-fel, ha jól értem, azt mondja a POSIX, hogy valahol legyen:
https://www.unix.com/man-page/posix/1posix/printf/
a ${#var} is helyenként szarul működni látszott, úgy, hogy nem vette figyelembe a sor eleji szóközöket, ami viszont biztos nem opció nekem, szóval, most ez van, a tuti külső processzindításokat kerülném:
https://commons.wikimedia.org/wiki/Libre art
Benchmarkoltam.
A tiéd:
Az enyém:
A tesztfájl (RAW itt: http://oscomp.hu/depot/asciiart.txt):
A teszt:
Azaz a tiéd 50 elemzést kb. 36 másodperc alatt csinált meg, az enyém pedig kb. 1/4 másodperc alatt. A különbség 127.351x-es. Ekkora overheaddel jár, ha karakterenként dolgozol fel egy interpretált nyelvből a soronkénti feldolgozáshoz képest.
Oldschool Computer - http://oscomp.hu
izgalmas, köszi, kezd kifejezetten érdekes lenni számomra, hogy ez a fajta soronként feldolgozás megbízható-e, mert elég kompatibilisnek tűnik, és nekem tetszik, ha működik, akkor mégis ezen az úton mennék tovább, tesztelem én is, kösz
https://commons.wikimedia.org/wiki/Libre art
Nincs mit, de ha azzal kezded, hogy az a feladat, hogy "igazából az lesz a program, hogy egy ascii-artot oszlopokra hasogassak és paramétertől függően csal egy részét mutassam rajzoljam ki a szabvány terminál bal oldalán", akkor már rég mondtam volna, hogy ne szenvedj shell-el. Ezt jobb megírni mondjuk C-ben:
Ez pontosan azt csinálja, amit szeretnél: N db oszlopot kicopy-z a fájlból és kiírja a terminálra. Leteszteltem, működik, persze bug lehet benne.
Sz*rk: Frissítettem a programot: beleraktam, hogy magától mozgassa a képernyő bal felére a tartalmat, escape szekvenciákkal.
Oldschool Computer - http://oscomp.hu
impozáns és meg fogom nézni! De nekem most megvan az okon, hogy miért shellel kell kiúsztatni :)
https://commons.wikimedia.org/wiki/Libre art
És titok, hogy mi az ok? :)
Oldschool Computer - http://oscomp.hu
Kíváncsi lettem. ;)
real 0m2.624s
user 0m1.417s
sys 0m1.194s
real 0m0.015s
user 0m0.007s
sys 0m0.008s
real 0m0.005s
user 0m0.002s
sys 0m0.003s
Az utóbbi megoldás:
Bár nem felel meg a specifikációnak. :(
Nem rossz. :)
Oldschool Computer - http://oscomp.hu
Iderakom a C programodat is:
real 0m0.002s
user 0m0.001s
sys 0m0.001s
Bár ekkora és ilyen bonyolult C programot még nem láttam. ;)
A lényeg: Vagy 30 éve junixozom, dolgoztam fel már 30M sort is, ami ennél sokkal bonyolultabbnak tűnt ;), de szöveget soha nem shellben, mert nem arra való. (Mert akkor sqlben is lehetne.:)) Úgy 1-1,5M rekordig awk - ha nincs különös követelmény, felette C.
Az nem baj, hogy a benchmarkolt shell kódok és a C kód teljesen mást csinál? :)
Biztos jó amúgy erre az oszlopcopyzásra az
awk
is, de én meg azt nem ismerem annyira. (Fogadnék egy teszkógazdaságos gumikecskepörköltbe, hogy tovább tartott volna megkeresnem, hogyawk
-ból hogy oldom meg, mint ameddig megírtam a C-s verziót.) Amúgy is, célfeladatra céleszköz; ez pontosan azt csinálja, amit sas kért, nem kell bűvészkedni mindenféle paraméterekkel, ráadásul ez 10 kB, azawk
meg ~650 kB. :POldschool Computer - http://oscomp.hu
:-D
Némi túlzással, ha odalépsz egy programozó mögé, akkor első pillantásra el sem tudod dönteni, hogy C-ben, awk-ban vagy shellben írja a kódot. Szóval a mondásodból csak annyi igaz, hogy nemigen dolgoztál még awk-ban.
A használt programnyelv kiválasztásánál egyik szempot csak annyi, hogy a C:awk kb. 1:6 futásidőt igényel. De tesztelésre is jó az awk, mert utána berakod a kódot C-be, vagy PIC assemblerbe :-D és pillanatok alatt át lehet írni. Legalábbis egyszerűbb feladatoknál, ahol nincs sok regexp. Ha meg van, akkor az awk győz az asszociatív tömbökkel. (Magyarázat: Bonyolultabb szöveges feldolgozásoknál általában szükség van szótárakra, sőt javító szótárakra is. Bár lehet ilyet írni, illetve biztosan meg is írták már mindenféle nyelvre, de az awk sokkal gyorsabb feladatmegoldást tesz lehetővé.)
Ehhez a feladathoz nagyságrendileg legfeljebb a rejtélyes substr() függvényre lehet szükség. ;)
A C programod hatalmas, biztosan jól meg van írva, de fix vagy maximált hosszú sorok feldolgozáskor
- nincs buffer - csak egy (max két) sornyi (nincs fopen, seek, meg ilyen marhaságok)
- nincs malloc()
- a fentiek alapján nyugodtan lehet használni a marhák szerint obsolete gets() függvényt
Ezzel 2/3 program ki is van dobva.
Az awk tényleg marhanagy. Én meg POWER-en programoztam, ahol jóval kisebbek a binárisok, mint egy pécén. :P
Ha már marháskodunk, ahol a tesztelést végeztem (linux) az awk mérete csak 418k, a hátam mögött (freebsd) csak 167k. :)
> Némi túlzással, ha odalépsz egy programozó mögé, akkor első pillantásra el sem tudod dönteni, hogy C-ben, awk-ban vagy shellben írja a kódot. Szóval a mondásodból csak annyi igaz, hogy nemigen dolgoztál még awk-ban.
Nem igazán világos, hogy mi az összefüggés a két mondat között, pláne aközött, amit én mondtam...
> A használt programnyelv kiválasztásánál egyik szempot csak annyi, hogy a C:awk kb. 1:6 futásidőt igényel. De tesztelésre is jó az awk, mert utána berakod a kódot C-be, vagy PIC assemblerbe :-D és pillanatok alatt át lehet írni. Legalábbis egyszerűbb feladatoknál, ahol nincs sok regexp. Ha meg van, akkor az awk győz az asszociatív tömbökkel. (Magyarázat: Bonyolultabb szöveges feldolgozásoknál általában szükség van szótárakra, sőt javító szótárakra is. Bár lehet ilyet írni, illetve biztosan meg is írták már mindenféle nyelvre, de az awk sokkal gyorsabb feladatmegoldást tesz lehetővé.)
Asszem elkerülte a figyelmedet a "célfeladatra céleszköz" kitétel. Ha regexp hegyekre lett volna szükség, nem veszem elő a C-t. De itt pár oszlopot kellett kicopyzni.
> Ehhez a feladathoz nagyságrendileg legfeljebb a rejtélyes substr() függvényre lehet szükség. ;)
> A C programod hatalmas, biztosan jól meg van írva, de fix vagy maximált hosszú sorok feldolgozáskor
> - nincs buffer - csak egy (max két) sornyi (nincs fopen, seek, meg ilyen marhaságok)
> - nincs malloc()
> - a fentiek alapján nyugodtan lehet használni a marhák szerint obsolete gets() függvényt
> Ezzel 2/3 program ki is van dobva.
Csak előrántottam a már ötvenezer éve megírt "rántsunk be egy fájlt memóriába" függvényemet és kész; lévén ASCII-artról beszélünk, tuti befér a memóriába, onnantól kezdve meg édes mindegy, hogy most hogy is van bufferelve. (De ha akarod, kimérheted, szerintem a soronkénti beolvasás lassabb lesz, mint az egybeni, mert az annyiszor annyi blokkművelet, még akkor is, ha az FS a cache-ből rántja elő a szektort.)
> Az awk tényleg marhanagy. Én meg POWER-en programoztam, ahol jóval kisebbek a binárisok, mint egy pécén. :P
> Ha már marháskodunk, ahol a tesztelést végeztem (linux) az awk mérete csak 418k, a hátam mögött (freebsd) csak 167k. :)
Most szét vannak szedve a PowerMac-ek, így nem tudom lemérni mennyi lenne PPC-n a bináris, de valószínűleg ha x86-on 10 kB volt, akkor POWER-en se lesz nagyobb, mint 418k vagy 167k. :P
Oldschool Computer - http://oscomp.hu
Első idézet magyarázata: Valószínűleg nem sokat kellene tanulnod az awk használatához. Van egy-két specifikum, de azt felszedi az ember menet közben.
Második idézet magyarázata: Csak úgy l'art pour l'art írogattam az awk szépségéről és hasznosságáról. Semmi bajom a C-vel.
Harmadik idézet magyarázata: Az indoklás elfogadható szintű. ;)
A soronkénti beolvasás tényleg lassabb. Ha tudjuk, hogy ASCII-artról van szó, akkor kell mondani egy gets()-t és máris ott csücsül a default 5k bufferben a szöveg. Ezt az oprendszer és a shell biztosítja. Az awk esetén is.
A "célfeladatra céleszköz", ok nézd meg a kódomat. Gyorstalpaló és egyszerűsített awk tanfolyam:
- Egy program 3 opcionális részből állhat: BEGIN{} {(body)} END{}
- A body minden beolvasott sorra végrehajtódik, amíg a sorok el nem fogynak.
- A length() függvény paraméter nelkül az $0 változó hosszát adja vissza, ami nem más, mint a beolvasott sor. Ezért is nem féltem 2x meghívni, mert ez a hossz már a beolvasáskor előáll.
- A numerikus változók 0 értékre inicializáltak. (Ide ennyi elég.)
Itt a vége. Ha eddig nem tudtad volna elolvasni a kódomat (amit azért nem hiszek), akkor máris menni fog.
Nem feledvén, hogy a programod jóval többet elvégez...
Ez kb. az awk programnak megfelelő valami. Ebből vagy az awk-ból előállitva a programodat sokkal szebb lesz, és kb. ugyanannyi sorral kell kiegészíteni.. ;)
Az látszik, hogy a feladatot megoldva mindkét nyelven rövid a fejlesztési idő és hasonló az eredmény.
Bár nem ismerem a PowerMac-et, de mondjuk győztél. ;(
Kösz az
awk
gyorstalpalót. Majd lehet, hogy egyszer jobban belemélyedek.A mellékelt C programmal csak egy baj van, hogy a
gets()
astdin
-ről olvas be, azaz ahhoz, hogy ez a program bármit csináljon be kell pipe-olni neki az ASCII-artot, tehát nem standalone; ahhoz akkorfgets()
kell, tehát a fájlkezelést nem lehet megúszni. Az előre a veremben allokált buffer persze amalloc()
-ot feleslegessé teszi, de hát csak gyorsan elővettem a saját fájlberántómat...Szóval, ha kiegészítjük a C példád, hogy a paramétereket is lekezelje, meg ne csak a hosszát nézze a sornak, hanem copyzza ki, amit kell (BTW copy, írtad a
substr()
függvényt; na az C-ben nincs, csak C++-ban: astring
ősosztály egyik metódusa, meg amúgy is, azzal nem sokra megyünk, mert a terminál bal oldalán kell "lebegtetni" a kicopyzott oszlopokat), akkor ezt kapjuk:Ez 60 sor a másik felállás 100 sora helyett, szóval az "Ezzel 2/3 program ki is van dobva." az inkább jó 1/3-nak (pontosabban: 40%-nak). :P A bináris mérete is 10k-ról 6k-ra ment össze. Amitől megszabadultunk: a berántós függvény, a
malloc()
és lekezelése, valamint a sortörések külön kezelése (fgets()
itt intézi helyettünk); szóval annyira azért nem volt bloated az a C program, amit adtam. :PSebességben kb. semmi különbség nincs a kettő között; pár tízezreléknyi különbséget lehet csak mérni az egyben beolvasós javára:
De gondolom ez az fs-cache miatt van így. Valami retroplatformon biztos nagyobb lenne a különbség. :P
Oldschool Computer - http://oscomp.hu
Aha. Ilyenkor ezt szoktam ajánlani. ;)
Az a "visszakacsacsőr" olyat tesz, mintha a program ezt csinálta volna: openat(STDIN_FILENO,"asciiart.txt",O_RDONLY)
Tehát itt egy darab pipeline sincs. Ezt átirányításnak hívják.
Mivel csak a "program" és a shell vesz részt a Nagy Műveletben, ezért "standalone". (WTF standalone?)
Valójában a következő történik:
Persze a keyboard és a file elemhez is tarozik buffer, pontosan akkora, amekkora őket megilleti. Hacsak mást nem mondasz nekik. ;)
A shell és a program is ugyanazokat a fd-ket használja, de ez még viszonylag egyszerű eset. Ha sok program fut, akkor már érdemes úrrá lenni a kavarodáson.
Ki hitte volna? ;)
De ilyen van:
Ami kb. megfelel a "print substr(buffer,i+1,j-i+1)" awk kifejezésnek.
Hááát, ezt 10M alatt nem fogod megtudni. :-D
Az ilyen "írok-olvasok" feldolgozások általában egyenletes sebességgel futnak. Ugyanez több szálon sem problematikus. A feladat akkor kezdődik, ha sok adattal, több szálon, bonyolult feldolgozást és indexelést is kell végezni. Ilyenkor a legfontosabb a feladathoz hangolt diszk alrendszer és az io buffer (per stream) mérete. Jelentős diszk terhelésnél érdemes úgy hangolni a rendszert, hogy a több szál kicsivel hosszabb ideig fusson, mint az egy. ;)
A retroplatform alatt mit értsek? (C64 nem ér!)
> Az a "visszakacsacsőr" olyat tesz, mintha a program ezt csinálta volna: openat(STDIN_FILENO,"asciiart.txt",O_RDONLY)
Jogos, a redirectről megint megfelejtkeztem, de ettől függetlenül ez még mindig egy külső függőség (a shelltől), míg amit én írtam az UNIX-okon kívül is működik, ott is ahol ez az operátor nincs.
> Ki hitte volna? ;)
> De ilyen van:
>
buffer[j+1]=0;
>
puts(&buffer[i]);
> Ami kb. megfelel a "print substr(buffer,i+1,j-i+1)" awk kifejezésnek.
Volt a mondatnak egy második fele is: "meg amúgy is, azzal nem sokra megyünk, mert a terminál bal oldalán kell "lebegtetni" a kicopyzott oszlopokat".
> Hááát, ezt 10M alatt nem fogod megtudni. :-D
> Az ilyen "írok-olvasok" feldolgozások általában egyenletes sebességgel futnak. Ugyanez több szálon sem problematikus. A feladat akkor kezdődik, ha sok adattal, több szálon, bonyolult feldolgozást és indexelést is kell végezni. Ilyenkor a legfontosabb a feladathoz hangolt diszk alrendszer és az io buffer (per stream) mérete. Jelentős diszk terhelésnél érdemes úgy hangolni a rendszert, hogy a több szál kicsivel hosszabb ideig fusson, mint az egy. ;)
Tekintve, hogy itt az egész file valószínűleg belefér egy szektorba, így kb. maradhatunk annál, hogy elméletileg gyorsabb az egyszerre beolvasós, gyakorlatilag meg kb. nem az. Illetve elhanyagolhatóan pici mértékben.
> A retroplatform alatt mit értsek? (C64 nem ér!)
Dehogy, nem arra gondoltam, hanem annál azért erősebb gépre, pl. Amiga, vagy 286-os PC.
Oldschool Computer - http://oscomp.hu
Mottó: Ha valaki a szilikonmellével dicsekszik, az olyan, mintha azzal dicsekedne, hogy hibátlanul elfingotta a Für Elise-t. :-)
- Ha ez megnyugtat, PIC18 platformon (8 bit) 64MHz-es órajellel is megcsinálom gyorsabban.
- Nem *nix-ra néhány abszolút üdítő kivétellel nem írtam programot már 25 éve. Windowsra is cygwin alatt írok. ;)
Ha ez egy "versenyprogram" vagy benchmark akar lenni...annak elég picinke.
Viszont a teljes rendszer ismerete nélkül még egy ilyen kis feladatról sem lehet semmit biztosan állítani. Példa:
Az asciiart.txt 2626 bájt hosszú. Lehet olyan fs alattad, ahol - ennél a méretnél még - az inode tárolja a dir+file elemet. De olyan is, ahol nem, ráadásul 1k logikai blokkmérettel (itt legyen 2 szektor) dolgozik. Az első 1, a második 4 fs műveletet igényel. Közben meg ott csücsül a világ leglassabb entitása a printf(). A második esetben már jobban jársz, ha nem várod ki a teljes beolvasást! Ennek ellenére majdnem mindegy, mert a cache tényleg mindent visz.
Fokozzuk egy kicsit a feszültséget! Van egy rendszerem, amely >80M processzt futtat naponta. A CPU upgrade előtt olyan nagy volt a terhelés, hogy az 5 perc periódusidőt meg kellett növelni, hogy a kiosztott feladatok le tudjanak futni. Utána annyira fürge lett, hogy az 5 perc lejárta előtt végezhet a feladatokkal, aztán pihen egy kicsit. Ok, tehát gyorsabb. És mi van akkor, ha ez a pici program pont az 5 perces periódus elején fut, amikor csúcsterhelés van? (A rendszer diszket is használ! ;)) Na, ilyenkor meg akár a cache is kiürülhet két olvasás között.
A "semmi" futásidejű programokat nem lehet shellben for ciklussal megmérni, mert a process indítás, a script futásidő, stb. akármennyi is lehet. Majdnem azt írtam, hogy szór, de inkább laza a korreláció. ;)
Fogtam a parancsot és csináltan egy N sor hosszú scriptet, azaz lineáris végrehajtást. Ugyanazon a rendszeren a futtató shell függvényében (csh, bash, ksh) 1:2 arányban változhat a végrehajtási idő.
Másik rendszeren a két program futásideje 1:1,1 arányú volt, lineárisan meg 1,1:1.
Még a user és system idők változását és arányait magyarázni sem nagyon lehet, nemhogy megérteni!
Nem is csoda, mert a "real 0m0.002s" értéket a méréstechnikában úgy hívják: zaj. :-D
Ha sok zajt összeadsz, akkor is zajt kapsz.
Szóval ilyet beépített hardver timerrel lehet mérni, ami <1us felbontást tesz lehetővé. A POWER architektúra rendelkezik ilyennel, de mintha 15 éve a linux/Intel viszonylatban olvastam volna hasonlót, bár azt nem használtam. Ezzel az eszközzel olyat is meg tudtam mérni, hogy a memóriába töltött adatbázis három tábájának átlagos lekérdezési ideje 120, 160 és 220ns.
Ennél a kis C programnál a mérési pontokat a main() elején és végén kell elhelyezni. A kapott eredményhez már hozzá lehet adni a programindítás idejét. Hasonlóan mérhető hardverrel is, ha van egy bit gpio. A mérés elején bebillented a bitet, a végén vissza. A jelenséget 1500 forintos logikai analizátorral pontosan meg tudood mérni, esetleg szkóppal.
Retro gépeim: Atlas 604 (100MHz PowerPc 604, 1996), Alix 1D (AMD Geode LX800), bookpc (VIA C3 1GHz), p615 (POWER 4+ 1GHz, 2004)
Ebből most mértem egyet az Alix/FreeBSD 10.3 konfiguráción: 10m40s, tehát a mérésednél 5x lassabb.
Ja, és folyamatosan értem mi a feladat, de asszem már régen túlnőttünk rajta. ;)
> Mottó: Ha valaki a szilikonmellével dicsekszik, az olyan, mintha azzal dicsekedne, hogy hibátlanul elfingotta a Für Elise-t. :-)
Másik mottó: Jobb későn, mint sárgadinnye.
> - Ha ez megnyugtat, PIC18 platformon (8 bit) 64MHz-es órajellel is megcsinálom gyorsabban.
Minél gyorsabban? Az asztali gépemen futó programnál? Vagy ha átírom C64-re, annál?
> - Nem *nix-ra néhány abszolút üdítő kivétellel nem írtam programot már 25 éve. Windowsra is cygwin alatt írok. ;)
És? Én meg próbálom úgy megírni mindenemet, hogy minél hordozhatóbb legyen.
> Ha ez egy "versenyprogram" vagy benchmark akar lenni...annak elég picinke.
Mármint hogy mi? A topicnyitót kérdezd, hogy mi ez, én csak egy lehetséges megoldást adtam neki.
> Viszont a teljes rendszer ismerete nélkül még egy ilyen kis feladatról sem lehet semmit biztosan állítani. ... Ennek ellenére majdnem mindegy, mert a cache tényleg mindent visz.
...
> A "semmi" futásidejű programokat nem lehet shellben for ciklussal megmérni, mert a process indítás, a script futásidő, stb. akármennyi is lehet. Majdnem azt írtam, hogy szór, de inkább laza a korreláció. ;)
Ezért kell baromi sokszor megmérni: egy bizonyos mérésszám felett kijön a különbség a két program között, ugyanis a többi, ami azonos, ha adott pillanatban külső behatások (clock, load, stb.) miatt más időt is ad, sok mérés után az összesítés konvergálni fog a valódihoz.
> Nem is csoda, mert a "real 0m0.002s" értéket a méréstechnikában úgy hívják: zaj. :-D
> Ha sok zajt összeadsz, akkor is zajt kapsz.
És a különbség lesz az, ami nem zaj.
> Szóval ilyet beépített hardver timerrel lehet mérni, ami <1us felbontást tesz lehetővé. A POWER architektúra rendelkezik ilyennel, de mintha 15 éve a linux/Intel viszonylatban olvastam volna hasonlót, bár azt nem használtam. Ezzel az eszközzel olyat is meg tudtam mérni, hogy a memóriába töltött adatbázis három tábájának átlagos lekérdezési ideje 120, 160 és 220ns.
> Ennél a kis C programnál a mérési pontokat a main() elején és végén kell elhelyezni. A kapott eredményhez már hozzá lehet adni a programindítás idejét. Hasonlóan mérhető hardverrel is, ha van egy bit gpio. A mérés elején bebillented a bitet, a végén vissza. A jelenséget 1500 forintos logikai analizátorral pontosan meg tudood mérni, esetleg szkóppal.
Egy mai rendszeren, SMP, multitaszking és változó órajel van, még a programon belül mérve is kaphatsz eltérő eredményeket. Nem a hardveres timer miatt, hanem mert nem biztos, hogy mindig ugyanannyi idő lesz, amíg lefut a program. Épp nem kapott időt, épp lejjebb volt az órajel, whatever.
> Ebből most mértem egyet az Alix/FreeBSD 10.3 konfiguráción: 10m40s, tehát a mérésednél 5x lassabb.
És melyiket mérted meg? Az egybe behúzóst, vagy a soronkénti olvasóst?
> Ja, és folyamatosan értem mi a feladat, de asszem már régen túlnőttünk rajta. ;)
Hát nem gyengén.
Oldschool Computer - http://oscomp.hu