shell script - agyonkommentelt szkript kezdoknek

Kesz a vegleges verzio, nagyon szepen koszonom mindenkinek a segitseget. Aki meg talal valami javitani valot, az kerem szoljon!

VEGLEGES VERZIO

Hozzászólások

Nekem egy kérdésem lenne:

cat "$fajl" | sed ... > "$fajl"

működik, míg a

iconv -f latin2 -t utf8 "$fajl" > "$fajl"

nem (azt hiszem, hogy először törli a $fajl-t, majd utána kezdi el olvasni). Ez miért van?

Más: a sed y paraméteréről eddig nem is hallottam, de nem én vagyok az etalon. Én tr-t használtam volna.

tr
Nallam nem vette figyelembe az ekezetes betuket, ezert hasznaltam 'sed'-et - az 'y' eleg egyertelmuen mukodik szerintem, amugy:

man sed -> Commands which accept address ranges -> y/source/dest/

Transliterate the characters in the pattern space which appear in source to the corresponding character in dest.

Egyebkent az iconv jo otlet, ezt meg nem ismertem, viszont akkor elore tudnom kene, hogy valoban rossz kodolasu-e a szovegfajl... Egyebkent epp az UTF-8 a bajos, ott - ha jol vettem eszre - az ekezetes karaktereket ket karakter megadasaval hozza ki.
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

...hopi, felreertettelek, bar az 'iconv' attol meg jo otletnek tunik. Szerintem azert torli eloszor a fajlt, mert a header-t (mar ha van ilyen egy szoveges fajlnnal) fogja atallitani, mivel ott tarolja a kodolasi informaciokat is. De ez nem biztos, csak egy tipp. Bar ahogy nezem, ez sehogy sem akar mukodni - se a rendes modon, se 'cat'-tal... o_O Google kene, de var a macim, hogy mikor fekszek mar le...
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Nem tudja. Pont ez a valódi probléma az ISO-8859-x kódolással, hogy sehonnan nem deríthető ki, hogy x= 1 (Latin1, kb Ny-Eu), vagy 2 (Latin2, K-Eu), vagy ...., és mivel van benne görög, cirill, így kicsit nehéz megállapítani ha te nem ismered az összes nyelvet. Ellenben UTF-8 esetén minden karakter önmaga mondja azt, hogy ez egy magyar nyelvben használatos Ő, vagy egy cirill betűs nyelvekben használatos keményjel, egy héber abc-beli alef, egy egyiptomi hieroglifa, és í. t.

A megoldas rettento egyszeru.

Azert nem mukodik az a megoldas, hogy parancs <file >file, mert ezt nem a parancs maga ertelmezi, hanem a shell.

A shell azt latja, hogy stdin helyett file-t kell megnyitni, hat megnyitja olvasasra. Aztan latja, hogy kimenetnek szinten azt kell megnyitni, igy megnyitja megegyszer, irasra, elolrol kezdve, felulirva.

Mikor ez megvan, elinditja a parancsot, stdinjet es stdoutjat megfeleloen beallitva. Csakhogy eddigre mar felulirta a filet.

A variansok, miszerint cat file | parancs >file hasonlo problemaba utkozik, a shell eloszor megnyitja & felulirja a filet, es csak utana inditja el magat a parancsot. Mivel piperol van szo, a ket oldalt kulon inditja, igy elofordulhat, hogy mielott a masodik fele elindulna, az elso mar beolvasta a filet, igy az memoriaban van, es nem jon elo a hiba.

Probald ki ezt:


cp /bin/bash /tmp/foo
cat /tmp/foo | sed -e 's,a,b,g' >/tmp/foo

Nagy valoszinuseggel /tmp/foo 0 byte hosszu lesz, mig ha csak mondjuk 2-3 sor lenne, akkor mukodne.

Ezt hivjak race conditionnek ;)

Amit te elvarsz, az az lenne, hogy parancs <file >file ugy mukodjon, hogy eloszor beolvassa, aztan kiirja. De ez nem igy megy. Eloszor megnyitja mindkettot (kimenetet felulirva), es utana indul csak a parancs maga.

Ha precizek akarunk lenni, mar az is csoda, hogy a "cat fajl | sed > fajl" mukodik, azaz szabaly: nem csinalunk olyat, hogy egy (esetleg csovekkel osszekotott, ciklusokkal tarkitott, stb) parancson belul ugyanazt a kimenetet irjuk, amit bemenetkent olvasunk. Eletveszelyes, eszement hibahelyzeteket tud produkalni. Mindig masik fajl legyen a kimenet, aztan legrosszabb esetben mv -val visszarakjut.

Es az iconv -os pelda azert nem megy, mert elobb tortenik meg a > miatt a fajl csonkolasos megnyitasa, mint hogy az iconv egyaltalan elindul, es megprobalja megnyitni.

Mondjuk ez a 'forras > forras' szitu gyanus volt nekem, kicsit vegtelen ciklusra hasonlit, de hat masoktol lattam, gondoltam csak mukodik. (Mukodik is! :P De azert korrigalom...) Ami azt illeti azert volt meg fifikansabb megoldas is, de az nem is mukodott (hal' Istennek):

parancs < forras > forras

Na, ez engem kiutott, nana, hogy az olvashatatlansaga miatt. (nekem ez kinai, ha nagyon akarnam, ertenem en, csak hat oda-vissza nyilak... :/ )
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Erdemes vetni egy pillantast a Debian moreutils csomagjanak sponge nevu programjara:

man sponge:

NAME
sponge - soak up standard input and write to a file

SYNOPSIS
sed ’...’ file | grep ’...’ | sponge file

DESCRIPTION
sponge reads standard input and writes it out to the specified file. Unlike a shell redirect, sponge soaks up all
its input before opening the output file. This allows for constructing pipelines that read from and write to the
same file.

If no output file is specified, sponge outputs to stdout.

szerk.: typo

Azert az nem tunik olyan jo otlettnek hogy memoriaban folhalmozd a teljes inputot mielott elkezded kitolni az outputra. Ennel a sokak altal emlitett
[source]
p0 < srcfile| p1 | ... > tmpfile ; mv tmpfile srcfile
[/source]
joval hatekonyabbnak tunik. Plane, hogy remelhetoleg az egesz pipe (az osszes tagja) kozel egyszerre vegez, aztan marad egy file rename, mig sponge-al ahogy nezem, eloszor vegez a pipe eleje aztan a pipe osszes tobbi tagjanak meg kell varnia amig a sponge kitolja amit olvasott.
==
`Have some wine,' the March Hare said in an encouraging tone.
Alice looked all round the table, but there was nothing on it but tea.

Igazatok van, ez nem nyert. Valamiert az elt bennem, hogy az ex nem olvassa be az egesz file-t, de inkabb az lehet a helyzet, hogy minden ex hivaskor pont ez tortenik, szoval meg rosszabb. :-)

Masfelol arra azert jo, hogy helyben, teljes ujrairas nelkul modositson egy file-t.

986 kerdesere valaszolva:

Igen, a 100 egy file-leiro ("csatorna"), az exec-es sorban gyak. megnyitom a data nevu file-t olvasasra, majd olvasom sorrol sorra. Az ex-szel kezdodo blokk pedig elinditja az ex editort a data file-on ugy, hogy a ket EOF kozotti sorok az ex sztenderd bemenetere kerulnek (ezt hivjak bash-ban here-document-nek), igy ott ex parancsok szerepelnek.

$i c # az i. sor felulirasa
$line # az adott sor bevitele
. # a 'c'-vel kezdett beviteli mod vege
x # kilepes az ex-bol mentessel

Bakkfitty, en meg kezdo vagyok, nemi komment nem artott volna... o_O
Ha jol ertem, a '100' itt egy csatornat jelol? (mint ahogy az 'stderr' a 2-es csatorna) Masfelol mi ez a vegen?

$i c
$line
.
x
EOF

--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

HEREDOC-nak hivjak egyes helyeken.

Ugy mukodik, hogy:

parancs <<STRING
tartalom
STRING

Ez leforditva kb annyit tesz, hogy: a parancs stdinjere kuld el azt a szoveget, ami a kovetkezo sortol kezodik, es "STRING" -ig tart.

A peldaban a string az EOF volt, a parancs meg az echo. az "echo <<EOF" es az "EOF" kozotti resz kerul a program stdinjere.

Én csak annyit mondanék, hogy próbáld meg a kapcsolók szerinti esetszétválasztást, tehát nem ilyen file='.', hanem több file-nál find, egy file-nál test -e.

$ find . -maxdepth 1 -el listázod egy könyvtár tartalmát, az ls-t jobb elfelejteni szkriptekben.

Ha soronként akarsz bejárni valamit (file-t, vagy jelen esetben a find kimenetét), akkor a
$ cat xyz | while read line ; do ... echo "$line"; ... done
szerkezetet használd a for helyett. (a read után akármi lehet, nézd meg a man sh-t)

A 'cat file > file' működött neked valaha is?

Pozitívum:), hogy sed-et használtál tr helyett, a tr nem kezeli az utf8-at.

> $ cat xyz | while read line ; do ... echo "$line"; ... done

helyett

$ while read line ; do ... echo "$line"; ... done < xyz

sporolsz egy processzt (cat), egy pipe -ot, es azt a nagy szivast, hogy van olyan shell, amelyik ilyen cat | while ciklus eseten a done utan *elfelejti* pl. a ciklusmagban levo valtozoertekadast, es hasonlokat. Az eroforras nem azert van, hogy pazaroljuk, hanem azert, hogy sporoljunk vele.

No, en kezdek belegabajodni:
Igen, sporolunk egy processzt, de a fajllista lemezre irasa is igenybe veszi az eroforrasokat. :P Sot, igy mar 2 ideiglenes fajlunk lesz: egy a fajllistanak, egy pedig maganak a fajlnak, hogy ne onmagaba irjunk vissza, mint azt korabban emlitetted. Vagy felreertettem valamit...?
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Sem a beidezett, sem az altalam irt verzio nem irja a fajlt a diszkre, hanem olvassa onnan, és kuldi az STDOUT-ra.
Ettol eltekintve, az, hogy a program egy masik pontjan mennyi eroforrast hasznalunk el, nem valtoztat azon a tenyen, hogy ha egy egyszeru modszerrel sporolhatunk, akkor tegyuk azt.

A peldakban az 'xyz' nem a 'find' altal kikopott fajllista? Mert ha ez nem az, hanem maga a file, amit manipulalni akarunk, akkor en most tenyleg nem ertem, hogy minek kellene soronkent olvasni... o_O
Na jo, inkabb leirom az eddig szimpatikusnak velt megoldasokat:

__ FAJLNEVEK BEOLVASASA A '-A' KAPCSOLO ESETEN
A 'find' megoldast kicsit atvarialva egesz hasznalhato lehetne a dolog:

for i in `find ./ -maxdepth 1 | sed "s/\.\///g"`; do
# szoveg konvertalasa a "$i" fajlban
done

A 'sed' azert kellett, hogy a '.' konyvtarat ne akarja megnyitni. Viszont ezzel meg mindig az a baj, hogy a szokozoket nem kezeli le. Ha viszont az egesz 'find ...' reszt idezojelek koze teszem, akkor 'i' az osszes fajl nevet egyetlen hosszu sztringnek tekinti, ami ugye nem mukodokepes. Ha van olyan listazo program, aminel be lehetne allitani, hogy a mappakat ne irja ki, akkor a 'sed'-es reszt elhagyhatnam, de a szokozok meg mindig bajt okoznak...

__ FAJL FELULIRASA
Zahy megoldasa tetszik, bar nem ertem, hogy itt miert nincs atmeneti fajl...? o_O (a 'masik' nem szamit annak?)

mv eredeti masik
parancs < masik > eredeti
rm masik

__KARAKTER KODOLAS
hud 'iconv' otlete megtetszett, csak itt meg meg kell tudni, hogy hogy lehet ellenorizni, hogy valoban rossz-e a szovegfajl kodolasa...

====

Az utolso kerdesre megvan a valasz: ha 'iconv' hibat ir ki, ugy nem 'latin2' kodolasu a fajl.

Egy furcsasag:
(bar mar elore sejtem a valaszt) ha a szkriptet 'utf8'-ban mentem el, akkor hibat ir ki a sed: nem egyenlo a ket sztring hossza. Beleneztem 'mcview'-val, ott valoban az ekezetes karakterek helyen ket speci karakter all - ez ugye a rendszer beallitasai miatt van? (marmint nallam a default asszem a 'latin2') Ha igen, akkor a kodolasi mizeria megoldasa, hogy... *uff* Bakkfitty, ez a progi (ha igy egy-az-egyben leszedik netrol, nem pedig bemasoljak egy szovegszerkesztobe) nem fog rendesen mukodni minden gepen... :( Na, mindenesetre az jutott eszembe, hogy akkor a szovegfajl kodolasat ugyanolyanna kell tenni, mint a szkriptfajle, es akkor nem lesz vele gond. (elvileg)
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Ha van olyan listazo program, aminel be lehetne allitani, hogy a mappakat ne irja ki

Ha jól értem, mire gondolsz, akkor:

find . -type f

hud 'iconv' otlete megtetszett, csak itt meg meg kell tudni, hogy hogy lehet ellenorizni, hogy valoban rossz-e a szovegfajl kodolasa...
Nem értem, mi az, hogy rossz a szövegfájl kódolása...

de a file parancs megmondja pl. hogy utf8-e vagy iso8859 (azt persze nem tudja, hogy latin-1 vagy latin-2)

Probald ki, hogy egy szovegszerkesztoben elmented az allabi sztringet 'utf8' es 'latin2' kodolassal is ket kulon fajlba:

"éáíóöőúüű"

$ cat "latin2" "utf8"

Ha az elmeletem helyes, akkor a kimeneten meg fog jelenni egyreszt az eredeti sztring, masreszt ugyanez a sztring, csak kiolvashatatlan formaban, mivel a shell (vagy a rendszer - ez az amit nem tudok) is bizonyos karikodolasra van beallva. Ez bezavarhat az ekezetes betukkel manipulalo szkriptekbe is, mivel ha mondjuk a shell alapbol 'utf8' kodolast hasznal es te futtatni akarsz rajta egy 'latin2' kodolasu szkriptet, ami tortenetesen a 'sed' parameterekent tartalmazza az ekezetes betuket, akkor ebbe a hibauzenetbe utkozhetsz:

strings for `y' command are different lengths

Ez az, ami miatt bajlodok a karikodolassal; ha egy 'latin2' szoveget akarsz konvertalni egy 'utf8' shellben, akkor a 'sed' nem fogja latni az ekezetes betuket...
De valami hasonlo szerintem megoldas lenne:

iconv -f "utf8" -t "latin2" "fajl" > "/dev/null"
if [ "$?" == "0" ]; then
# utf8 fajl, nem kell konvertalni
else
# nem utf8, konvertald
fi

Kozben ahogy tesztelgettem, 'latin2'-rol 'utf8'-ra at lehet kodolni, meg akkor is, ha a fajl valojaban 'utf8' kodolasu, tehat tenyleg erdemesebb ezt hasznalni, viszont nallam polo a shell csak fura karaktereket kop ki az 'utf8' kodolasu fajloknal, ami mukodeskeptelen szkriptekhez vezethet.
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Értem.

A locale beállításaid határozzák meg, hogy a programok mit vesznek egy karakternek.

pl. LC_CTYPE

Ha beállítod utf8-ra, akkor azt jól fogják kezelni a programok. (nekem pl. LANG=en_US.UTF-8 után minden jó (de persze pl. a sorbarendezés nem a magyar abc sorrend szerint működik))

Ha olyan fájlod van, ami más kódolású, azt először iconv-val konvertáld át, és utána már a szövegfeldolgozó parancsok megfelelően fel tudják dolgozni.

iconv-val nézni meg, hogy milyen kódolású a fájl szerintem pazarlás.

A file parancs kiváló erre:
gee@karvaly:~$ file valami*
valami: UTF-8 Unicode text
valami2.lat: ISO-8859 text

G

Nallam legtobbszor inkabb a fajl milyenseget irja ki (C kod, ASCII text, ... ), nem pedig a kodolas fajtajat, es sajna nem is talaltam olyan kapcsolot, ami csak ezt figyelne.
Amugy nem pazarlas, ha kicsit atrendezzuk:

iconv -f "utf8" -t "latin2" "fajl" > "ideiglenes_fajl"
if [ "$?" == "0" ]; then
# utf8 fajl, nem kell konvertalni
rm "ideiglenes_fajl"
else
# nem utf8, a hibatlanul lefutott 'iconv' kimenetevel felul irjuk a fajlt
mv "ideiglenes_fajl" "fajl"
fi

--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Ugye nem baj, ha tovabb morgolodok?

a) "f" opcio lekezeles: az if utan nem kell ( zarojelek koze ) tenni a parancsot, sot! Mivel a kerek zarojelpar azt jelenti, hogy a parancs(ok) egy al-shellben hajtodik/nak vegre, ez folosleges eroforraspazarlas. Siman "if test ..." a megfelelo forma. Ha zavarja a szemedet/szeperzekedet, akkor irjal kapcsos zarojelet - "if { test ... ; } ; then ..." formaban - igaz, ekkor a zaro kapcsos zarojel ele is *kell* a pontosvesszo - az ugyanis nem indit meg egy plusz processzt. (Ez a rossz szokas mas if-jeidnel is szerepel).

b) ugyanitt az "else" agban rossz az exit parametere. A "$?" az utoljara vegrehajtott parancs statuszkodja - ami a hibauzenetet kiiro echo parancs jelen esetben. No egy echo 99,9999...%-ban 0 (azaz hibatlan) statusszal ter vissza. (Keresztkerdes: mikor nem?) - igy a programod, miutan kiir egy hibauzenetet, visszaad egy "hibatlan" statuszkodot - ami rossz. Azaz hibahelyzetben igen is konkret hibakod javasolt. (Mondjuk exit 1, jelezve a doksiban - tudom, nincs -, hogy ez a hibakod azt jelenti, hogy.... Tovabbiakban tobbszor is ugyanez a statuszkod hiba.)

c) hianyzik a nemletezo opcio - azaz a megjegyzesben leirt "?" - kezelese. Amig csak te hasznalod, nem baj, de ha publikalod, akkor azert erdemes bele is kodolni. Plane, hogy kb az echo es exit parancsokat kell odairni.

No ennyi, a fajlatnevezesi resszel most se foglalkoztam, ha lesz egy kis turelmem, beleragom majd magam esetleg abba is. Jo latni, hogy haladsz :-)

Szerk: most latom, hogy a "cat file | tr > file" a te talalmanyod. HIBA! lasd fenti megjegyzesem.

Pár random vélemény, talán némelyiket már mások is írták

exit "$?" -- semmi értelme, ez az exit defaultja, tehát sima exit is megteszi, ráadásul a fájl végére teljesen fölösleges.

--color=never -- shell szkriptbe teljesen fölösleges

A szóköz tartalmú fájlok kezelése nem triviális, a sztori egyik részét már érted (mindent idézőjelek közé, néha talán kissé túlzásba is viszed), aztán lesznek még egyéb trükkök. Az aláhúzással nem értem, mit akarsz. Nem oldod meg a szóközös fájlok problémáját, viszont az aláhúzást is elrontod, tehát rosszabb, mintha nem tennél semmit ez ügyben.

cat parancsnak nagyon ritkán van értelme, inkább a < nyíllal rakd be a fájlt a progi std inputjára.

Hibaüzenetet illik stderr-re írni: echo >&2

-f kapcsoló: Ha egy fájlt alakítasz át, biztos hogy be akarsz gépelni egy -f kapcsolót, nem csak simán a fájlnevet? Sőt, esetleg többet is?

nekem ez rettentő hasznos,

köszi hogy megosztottad.

off: grat a site desing-hoz, ez a terminal feeling begyere, lehet lenyúlom :D

Kesz a vegleges verzio: link
--
Azt akarom, hogy az emberek ne kenyszerbol tanuljanak, hanem azert, mert tudni akarnak.

Erdemes lenne egy ilyen iskola peldaban az mktemp-et hasznalni. Tfh, rootkent szoktad futtatni a scriptet es egy user tudja ezt, es ismeri a scriptet. Nincs mas dolga mint mielott futtatnad:

$ ln -s /etc/passwd /var/tmp/ekezet.lst

==
`Have some wine,' the March Hare said in an encouraging tone.
Alice looked all round the table, but there was nothing on it but tea.