dash: fajllistan vegigjaras

Kuzdok, es a netes peldak vhogy nem mukodnek, vagy en vagyok hulye.

Az alap problemam, hogy dash-ben kene fdupes segitsegevel adott eredmenylistan vegigrohanni egyesevel. Ez meg nem is lenne gond, de az fdupes lassan fut le, es az eredmenylistat nem csak egyszer kell hasznalnom.
A gondom akkor kezdodik, amikor az eredmenyemben vannak szokozos fajlnevek, mert nem csak sortoresnel, hanem szokeznel is uj eleme lesz a tombnek.

Ime a kod kivonata:


EREDMENY = "$(fdupes -nrq /konyvtar) | grep -v /nemkellkonyvtar"
for i in $EREDMENY; do
echo "$(stat $i)"
done
echo $EREDMENY

Amit probaltam, de csak kavalkad lett belole (ami ugye koszonheto a tudatlansagomnak is, ezert ilyen javaslatokat is szivesen latok):
* IFS allitasa
* sed segitsegevel a szokozok ele backslash
* ${EREDMENY[@]} a ciklusdefinicios sorba

Minden javaslatot, linket koszonok elore is

Hozzászólások

Rossz helyre írtad a záró zárójelet az első sorban. Gondolom, nem az fdupes kimenetét akartad parancsként végrehajtani, majd az így kapott kimenetet a grep-be pipe-olni. Az is fura, hogy a stat kimenetét miért kell az echo paramétereként helyettesíteni, hogy aztán az echo újra stdout-ra küldje. Nem egyszerűbb leírni, hogy stat $i ?

Anélkül, hogy halvány sejtelmem lenne az fdupes meg a stat dolgairól, valami ilyesmivel próbálkoznék bash-ben:

fdupes -nrq /konyvtar | grep -v /nemkellkonyvtar |\
    while read; do
        stat "$REPLY"
    done

Azt nem tudom, a dash-nek van-e REPLY változója.

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

Tök ugyanez volt az én ötletem is, de azért nem írtam, mert a felvetésben azt írta, hogy az fdupes eredményét később szeretné felhasználni. És ugyan megtehetném, hogy a cikluson belül meg gyűjtögetem, de azt csak nyakatekerten lehetne a cikluson kívül felhasználni.

Amúgy ha "while read i" formában írja, akkor a statnál hivatkozhat a "$i" -re, és meg van oldva a a REPLY probléma.

Én amúgy beletenném shell tömbváltozóba a dolgot, ellenben nem elég, hogy a ksh és a bash különböző módon teszi lehetővé egy tömb inicializálását (ksh-ban ráadásul csak 1023 elemű lehet), de ráadásul mint meglepetéssel tapasztaltam, a dash semmilyen formában nem kezeli azt :-(

Ez vajon házi feladat, vagy valós lehet? Ha valós, akkor lehet file-t is írni az újrahasznosításhoz:

TEMPFILE="$(mktemp)"
fdupes -nrq /konyvtar | grep -v /nemkellkonyvtar | tee "$TEMPFILE" |\
    while read; do
        stat "$REPLY"
    done

Aztán a TEMPFILE-ból előkotorhatja, ami kell.

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

:)
Ez valos feladat, pont hogy a hazi masolast szeretnem, meg persze minden mast megakadalyozni egy samba serveren. A felhasznalok buveszkednek, es rengeteg adatot masolnak le a sajat profiljukba a kozos meghajtorol. Azt szeretnem ellenorizni, hogy mit masoltak le.
Az fdupes a file duplikatumokat talalja meg, es az igy kapott eredmenyt szeretnem eloszor a pipe segitsegevel megszurni, h a halozati meghajtos dolgok ne legyenek benne (fdupes is kepes az elso talalat elrejtesere, de nem tudom garantalni, hogy az ne a diak fajlja legyen).
A stat csak egy egyszeru pelda volt, valojaban mast szeretnek csinalni vele, csak ez jutott egyszeru eszembe, ami nem teszi atlathatatlanna a mintakodot.

Mivel minimalis a bash ismeretem, ezert egybol meg is kerdezem, h a "$(parancs)" pontosan miben kulonbozik a `parancs` megoldastol.

Tempfilet valahogy eroltetettnek erzem itt, es nem latom, h miben lesz jobb, mintha ugyanaz az adat egy valtozoban van.

Ez a REPLY dolog nem tudom mi, de nem az a fo gondom, hogy nincsenek meg a tombom elemei a cikluson belul, hanem hogy rosszul tagolodnak a szokozok miatt.

A stat meg azert van echoba, mert a stat (pontosabban helyette valmai bonyolultabb parancs) kimenetet szeretnem kiiratni a scriptembol.

> Mivel minimalis a bash ismeretem, ezert egybol meg is kerdezem, h a "$(parancs)" pontosan miben kulonbozik a `parancs` megoldastol.

Semmiben. Régebbi sh (és pl. csh) csak a `forma` használatát támogatja.

A REPLY egy spéci változó, pár shel belső parancs használja (pl. a read is), és mint írtam, ha read VAR formában használod, akkor lehet később a "$VAR" formában hivatkozni a beolvasott adatra.

TMPFILE vs változó. Ciklus belsejében létrehozott (vagy ott módosított) változót a ciklus után csak nagyon macerás módon lehet előkapni, ezért nem javasolt azt erőltetni.

Ez az echo `stat` forma meg marhaság, lévén a stat simán kiírna dolgokat, tehát olvashatóbban kapod ugyanazt, ha csak simán annyit írsz: stat "$i", nem pedig echo `stat "$i"` .

Ja, és itt viszont már egyértelműen bash-t írtál (és nem dash-t). Melyik? Mert marhára nem mindegy :-)

Részben kaptál választ, azért kiegészíteném.

A $(parancs) és a `parancs` ugyanaz, viszont ha ezeket egymásba ágyazod, a $() forma jobb, hiszen nincs nyitó illetve záró `. Ebből viszont csúnya szintaktikai hiba lesz, hiszen az első ` jelet tekinti a shell nyitónak, a másodikat zárónak, nem pedig az elsőt, utolsót egy párnak.

A pipe-olás megoldja a szóköz gondjaidat, hiszen a kimenet egy sora függetlenül a szóközöktől egyben marad, ha idézőjeleket használsz. Ezért írtam "$REPLY"-t, s nem $REPLY-t. Nyilván igaz ez úgy is, ha "$i"-t írsz, s nem $i-t. A for ciklussal az a baj, hogy hiába írsz idézőjelet, a teljes stringet ugyan egyben helyettesíti a shell, ám második körben éppen a szóközök lesznek a termináló karakterek, s így valóban a szóközök mentén esik szét az egész, s ezen elemeket veszi majd sorra a for ciklus, de ez nem jó neked.

Zahy nem részletezte, de azért nehéz kihozni változót ciklusból - helyesebben olyan ciklusból, amely pipe-ban van -, mert a pipe-ban szereplő parancsok önálló process-ként futnak, így a ciklus subshell-ben, önálló process-ként fog futni. Ez azt is jelenti, hogy saját memóriaterületet allokál, s az ott definiált változó megszűnik, amikor a ciklus elvégezte a dolgát. A szülő shellnek nincs rálátása arra a memóriaterületre, ki is vágná a kernel a fenébe azt a process-t, amelyik egy másik memóriaterületén próbálna turkálni. Ezért javasoltam, hogy a tee írja file-ba a kimenetet, miközben pipe-olódik tovább. Így utólag újfent használható a kimeneted a file-ból anélkül, hogy újra előállítanád azt.

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

Hm, újraolvasva most tűnt fel, hogy kétszer is hivatkoztál tömbre (vagy tömbelemre). A nálam levő (legújabb) dash sem a doksi szerint, sem az általam ismert szintaxisokkal nem képes tömböt kezelni. Te hogyan csinálod? (Legalább a tömbelemek feltöltését add meg, lécci!)

Vagy pedig nem is dash, hanem bash?

Ok, akkor pontositok.
A rendszer debian 6, ugye alap a dash, de van fent bash is, azt is hasznalhatok igazabol nincs megkotes, gondoltam dash lenne az okosab valasztas, ha az az alap.

Tombot mondtam, de valojaban nem is teljesen tomb, inkabb a kimenet stringjenek a feldarabolasa, es a darabokon valo vegigmenest szeretnem elerni, de ugy, hogy csak az uj sor legyen a darabolasi hatar, es a szokoz ne.

IFS='
'
ezzel egy db ENTER-re változtatod az elválasztókaraktert, így aztán a dolog már működik:

EREDMENY=$(fdupes -nrq /konyvtar | grep -v /nemkellkonyvtar)
IFS='
'
for i in $EREDMENY; do
stat "$i"
done
echo "$EREDMENY"

Ennek dash és bash alatt is kell működnie.

Valamit elronthattál, mert én eleve dash-sal próbáltam mielőtt megírtam :-)

$ dash
$ i="1 2
> 3"
$ echo ":$i:"
:1 2
3:
$ IFS='
> '
$ for x in $i ; do printf ':%s:\n' "$x" ; done
:1 2:
:3:
$

A kettőspontok csak azért vannak, hogy látszódjon, tényleg 2 db printf-fel történik a kiíratás :-) (Ja, és nem véletlenül vannak illetve hiányoznak idézőjelek bizonyos helyeken - ezt sajnos elég sokan nem hajlandók (nem tudják???) megtanulni, hogy mikor kell/lehet/tilos.

most nincs elottem a gep,de majd kiprobalom.
amugy nekem ugy tunt hogy dashben az ifs atallitasa sikerult,csak a visszaallitas nem,pedig az kell,kulonben a belso fajlelero parancsom hulyul meg.

ha mar itt tartunk,mik a lenyeges kulonbsegek a bash,dash,sh hasznalata kozt?elonyok,hatranyok,praktikussag,elegancia temakorben
gondolom kismillio oldal van neten,csak telefonrol vagyok,igy a rakereses kesobbre marad...

Különben a for ciklusos megoldásod nem tetszik, mert a teljes eredmény listát helyettesíteni kell. Amennyiben van erre felső memória limit, úgy belefuthatsz abba, hogy egyszer csak nem működik. Az lehet, hogy aki a bash-t írta, volt olyan jó fej, hogy nem szabott limitet, amíg a kernel ad RAM-ot, addig jó, de akárhogy nézzük, mindenképpen csak véges hosszúságú lista dolgozható így fel. Ráadásul kétszer foglalod le a memóriát ugyanazzal a listával: egyszer az EREDMENY változó által, másodjára a for-ba történő helyettesítéskor.

Ezzel szemben a pipe bármilyen hosszú listát feldolgoz, hiszen a lista előállítása és feldolgozása röptében történik, a pipe buffer mérete talán 4 kiB - aki jobban nyomonköveti a kernel fejlődését nálam, legfeljebb kijavít, ha már nem ennyi.

Ennek tehát az a következménye, hogy a feldolgozandó adatmennyiségtől függetlenül kevés memóriát allokálsz, így nem kell attól tartanod, hogy az Out of Memory Killer szépen kivágja a programodat, mert elfogyott a RAM. Persze előtte vergődik egy kicsit, mert a kernel jóságos, igyekszik menteni a helyzetet swap-eléssel, ettől legalább az egész gép nagyon lassú lesz.

Amikor a programot szervezed, erre is légy tekintettel. Egy eleve meghatározhatatlan adatmennyiséget, mint amilyen az fdupes kimenete, nem szerencsés változóban tárolni, aztán még egyszer helyettesíteni.

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