» »

Noob se loti programiranje v BASH

Noob se loti programiranje v BASH

Smradac ::

Pozdravljeni,

Napisati morm skripto v BASH, ki prejme kot argument mapo v kateri poišče datoteko z največ vrsticami in kot rezultat izpiše to datoteko in pa število njenih vrstic. Vem, sliši se preprosto, a mi ta skriptica dela težave že od jutra.

#!/bin/bash

kazalo=$1

if [ "$#" -ne 1 ]; then
echo Niste podali argumentov.Vnesti morate kazalo za iskanje!
else

STEVEC=0

for i in `ls $kazalo`
do
temp=`file -b $kazalo/$i`
if [ "$temp" != 'directory' ]; then
if [ "$STEVEC" -eq 0 ]; then
POMOZNA=`cat $kazalo/$i | wc -l`
let STEVILO=$POMOZNA
ZBIRKA=`cat $kazalo/$i`
let STEVEC=STEVEC+1
fi
POMOZNA=`cat $kazalo/$i | wc -l`
if [ "$POMOZNA" -gt $STEVILO ]; then
let STEVILO=$POMOZNA
ZBIRKA=`cat $kazalo/$i`
fi
fi
done
fi

echo Zbirka z najvec vrsticami je $ZBIRKA
echo Stevilo vrstic te zbirke je $STEVILO


Torej, glavno kar me muči je izpis in pa to, da mi neuspešno preverja za kakšen tip datoteke gre.Tale ukaz naj bi preveril če gre za datoteko ali ne, ampak kot zgleda ne deluje: temp=`file -b $kazalo/$i` , ravno tako mi namesto imena zbirke izpiše neko dolgo klobaso, ponavadi večkrat :D Število vrstic pa mislim da izpisuje pravo.

Kot testne podatke sem uporabljal ./ ter /usr.

Ma kdo kak nasvet kako usposobit zadevo?
I see myself on the knees in the night saying prairs in the street light.
  • spremenilo: Smradac ()

zee ::

Nasvet: zaradi preglednosti bi ti svetoval, da vsak IF stavek zamaknes v desno, s cimer je lazje slediti kodi + lazje vidis morebitne napake.
zee
Linux: Be Root, Windows: Re Boot
Giant Amazon and Google Compute Cloud in the Sky.

Zgodovina sprememb…

  • spremenilo: zee ()

Smradac ::

Kodo imam narejeno z zamiki, a mi jih je forum povozil :) Sem probal z [code] tagi, pa niso prijeli :)

Če pa uporabim gumb, da bi dodal tage, mi pa javi da sem uporabil neveljavno HTML značko.
I see myself on the knees in the night saying prairs in the street light.

Zgodovina sprememb…

  • spremenilo: Smradac ()

Smradac ::

Če želiš ti lahko pošljem na mail zadevo, če bo potem kaj lažje razumljivo :)
I see myself on the knees in the night saying prairs in the street light.

Zgodovina sprememb…

  • spremenilo: Smradac ()

c3p0 ::

Za direktorij v bashu preverjaš z if [ -d $var ]; ...

Verjetno ima skripta probleme, če so v imenih datotek ali dirov presledki, nekam na začetek zato daj: IFS=$'\t\n'

redo ::

Najlažja varianta za tvojo nalogo se mi zdi nekaj v stilu

find "$1" -type f -print0 | wc -l --files0-from=- | sort -g -k1,1 | tail -n2 | head -n1


ali variacije na to temo. Sicer pa ne vem, kaj ti je dovoljeno uporabit (namesto sort,tail in head bi raje uporabil awk, če se npr. hočeš izogniti sortiranju).

redo ::

Da se izogneš težavam s posebnimi znaki in presledki v imenih, daš spremenljivke v dvojne narekovaje. Jaz si pri bashu vedno pomagam s tem virom (poleg manuala seveda). Veliko lahko zveš tudi ob prebiranju comp.unix.shell.

redo ::

Če vzamem tvojo rešitev in jo malo predelam (upam da ne zameriš) bi spisal tole

#!/bin/bash

if [ $# -ne 1 ]; then
NAME=$( basename "$0" )
echo "Uporaba: $NAME ime_direktorija"
exit 1
fi

KAZALO="$1"

if [ ! -d "$KAZALO" ]; then
echo "Argument ni direktorij"
exit 1
fi

MAX=0

for i in "$KAZALO"/*; do
# Preveri če je navadna datoteka
if [ -f "$i" ]; then
VRSTIC=$( cat "$i" | wc -l )
if [ $VRSTIC -gt $MAX ]; then
MAX=$VRSTIC
DAT="$i"
fi
fi
done

# Izpiši, če smo našli kakšno datoteko
if [ -n "$DAT" ]; then
echo "$DAT ima $MAX vrstic"
fi

Kako je pa to odporno na vse obskurne primere pa ne vem. Ni pa to čisto enako tisti rešitvi s find (se pa da tista naredit enaka tej, kot tudi ta enaka oni). (Se opravičujem, ker ni zamikov, ampak st.koda bash ne razume narekovajev in šumnikov.)

c3p0 ::

Pri for zanki te dvojni narekovaj ne reši, ker ti že sam for servira vsako besedo posebaj. Če ime datoteke vsebuje presledek, seveda.

http://mindspill.net/computing/linux-no...

Smradac ::

Najlepša hvala vsem za pomoč, sem usposobil zadevo. Sedaj pa grem googlat in man-at, da vidim kje točno sem ga pobiksal pri moji skripti. :) Hvala še enkrat.
I see myself on the knees in the night saying prairs in the street light.

redo ::

Pri for zanki te dvojni narekovaj ne reši, ker ti že sam for servira vsako besedo posebaj.

No, če sem malo zloben, potem to ne drži čisto.

$ for i in "/mnt" "/var/lib/vmware/Virtual Machines" "/dev"; do echo $i; done
/mnt
/var/lib/vmware/Virtual Machines
/dev
$ for i in /mnt /var/lib/vmware/"Virtual Machines" /dev; do echo $i; done
/mnt
/var/lib/vmware/Virtual Machines
/dev
$ for i in /mnt /var/lib/vmware/Virtual" "Machines /dev; do echo $i; done
/mnt
/var/lib/vmware/Virtual Machines
/dev

Niso težave zaradi for zanke same po sebi. Ampak v primeru

vals='/mnt /var/lib/vmware/Virtual Machines /dev /proc /sys /tmp /usr/portage /var/tmp'

te ne reši niti igranje z IFS. Prav tako
for i in "$vals"

pomeni nekaj drugega, kot
for i in $vals


Sedaj pa grem googlat in man-at, da vidim kje točno sem ga pobiksal pri moji skripti.

No, največja napaka je tale vrstica
ZBIRKA=`cat $kazalo/$i`

misek ::

Pa namesto
cat "$i" | wc -l
uporabi
wc -l "$i"

Včasih je hitrost pomembna...

c3p0 ::

@redo,

pozabljaš, da smo $IFS modificirali, da lomi na \n in \t, odstranili smo le ' '. Tvoj primer pa ne vsebuje niti \n, niti \t, le ' '.

Da te vidimo kaj boš naredil v for i in `find ....` ali v for i in `ls`, ko se pojavijo presledki?

redo ::

pozabljaš, da smo $IFS modificirali, da lomi na \n in \t, odstranili smo le ' '. Tvoj primer pa ne vsebuje niti \n, niti \t, le ' '.

Ti si prilepil ta primer, ne jaz. Tvoj primer je, ne moj.

Da te vidimo kaj boš naredil v for i in `find ....` ali v for i in `ls`, ko se pojavijo presledki?

Eh. Če pišeš take stvari, si si sam kriv (a res kdo dejansko piše 'for i in `ls`'? piši preprosto 'for i in *'). In kaj narediš ti, ko ime kakšne datoteke vsebuje '\n' ali '\t'? Spet spremeniš IFS? Ali bog-ne-daj vsebuje ali celo je '*' (da ne omenjamo bolj obskurnih primerov)? Saj ne rečem, da ni kdaj smiselno spreminjati IFS-ja, ampak za ta primera pač ne.

c3p0 ::

Tak primer si dal ti in tega sem komentiral:

Niso težave zaradi for zanke same po sebi. Ampak v primeru vals='/mnt /var/lib/vmware/Virtual Machines /dev /proc /sys /tmp /usr/portage /var/tmp' te ne reši niti igranje z IFS. Prav tako


...Ker ls, find ipd. nikoli ne bodo vrnili takega formata, je primer povsem zgrešen.

(a res kdo dejansko piše 'for i in `ls`'? piši preprosto 'for i in *')


Ponavadi podam bolj splošne rešitve, vso srečo ko bo treba upoštevat še subdire ali ogromno št. datotek. A boš pisal v rekurzivne funkcije, ali boš uporabil find? Saj nismo mazohisti?

In kaj narediš ti, ko ime kakšne datoteke vsebuje '\n' ali '\t'? Spet spremeniš IFS?


\n v imenu datoteke je pa zelo uporabljan ja, vsakdanji problem. ;=)
Se pa da enostavno, findu dodaš kak -print0, nato v kombinaciji z xargs -0.

Q: In kaj narediš, ko imaš direktorij z nekaj 1000 datotekami in ti bash ob tvoji metodi vrže ven Argument list too long?

redo ::

Tak primer si dal ti in tega sem komentiral:

Niso težave zaradi for zanke same po sebi. Ampak v primeru vals='/mnt /var/lib/vmware/Virtual Machines /dev /proc /sys /tmp /usr/portage /var/tmp' te ne reši niti igranje z IFS. Prav tako


...Ker ls, find ipd. nikoli ne bodo vrnili takega formata, je primer povsem zgrešen.

Morda bi se odločil in preveril, preden limaš linke. Sledi citat tvojega komentarja v celoti.
Pri for zanki te dvojni narekovaj ne reši, ker ti že sam for servira vsako besedo posebaj. Če ime datoteke vsebuje presledek, seveda.

http://mindspill.net/computing/linux-no...

Omenjeni primer je na zgornjem linku pod podnaslovom 'The problem'.

(a res kdo dejansko piše 'for i in `ls`'? piši preprosto 'for i in *')


Ponavadi podam bolj splošne rešitve, vso srečo ko bo treba upoštevat še subdire ali ogromno št. datotek. A boš pisal v rekurzivne funkcije, ali boš uporabil find? Saj nismo mazohisti?

Če že res rabiš zanko in hočeš subdire, uporabi (ne razumem, kaj misliš z ogromno št. datotek, glej spodaj)

find ... -print0 | while read -d $'\0' i; do ...; done

Je to mazohistično? Zato ker poskrbi za vse primere?

In kaj narediš ti, ko ime kakšne datoteke vsebuje '\n' ali '\t'? Spet spremeniš IFS?


\n v imenu datoteke je pa zelo uporabljan ja, vsakdanji problem. ;=)

Toliko huje, ker je nevsakdanji problem. ;)

Q: In kaj narediš, ko imaš direktorij z nekaj 1000 datotekami in ti bash ob tvoji metodi vrže ven Argument list too long?

S 'for i in *' si uspel pridelati 'Argument list too long'? To bi pa rad videl.

$ mkdir large; cd large
$ seq -f'zelozelozelozelozelozelozelozelozelodolgoime%05.0f' 1 50000 | xargs touch
$ ls *
bash: /bin/ls: Argument list too long
$ for i in *; do echo "$i"; done
zelozelozelozelozelozelozelozelozelodolgoime00001
...
zelozelozelozelozelozelozelozelozelodolgoime50000
$ find . -type f -delete
$ cd ..; rmdir large
$

Tole me pa zdaj res zanima. Lahko prilimaš kak link, ki razloži povezavo med globbingom in omenjeno napako. Ker v primeru 'ls *' ni globbing, ki dela težave, ampak prevelika velikost argumentov, ki jih sproducira bash ko razvije *. To je omejitev, ki je v kernelu ne v bash-u.

BigWhale ::

S 'for i in *' si uspel pridelati 'Argument list too long'? To bi pa rad videl.


To se je meni ze precejkrat zgodilo.... Pri kakem 'dumpu podatkov' je to kar pogosta zadeva.

Nasploh napaka 'Argument list too long' ni nekaj redkega... Precej bolj pogosta kot \n v imenu datoteke. :)

redo ::

Ja ja. Anekdotični dokazi. Kar pokaži kaj si delal z zanko 'for i in *', da si dobil omenjeno napako in razloži zakaj misliš, da si jo dobil. In še bolje, razloži zakaj pa meni pa deluje? In zakaj naj bi pa 'for i in `ls`' pa delovala v primeru, ko bi ona druga odpovedala zaradi te napake?

Zgodovina sprememb…

  • spremenilo: redo ()

BigWhale ::

Hm, moj post je nekam zginil... :/

V glavnem, ja. I stand corrected. for bla in * ne bo sproduciral Argument List Too Long.

Brane2 ::

Kar se tiče for i in .... , po moje ni pretežko priti čez meje. Stvar najprej ekspandira vse argumente v nek buffer, potem pa jih iz njega črpa. Če imaš šitload stvari v "...", zna priti do težav.

Sploh pa bi jaz uporabil tu prej "find", ki ti lahko najde željene datoteke.

Če ti pa to ne diši, je mogoče namesto x= `file bla` boljša varianta x=$( stat -c%F bla )
On the journey of life, I chose the psycho path.

redo ::

Kar se tiče for i in .... , po moje ni pretežko priti čez meje. Stvar najprej ekspandira vse argumente v nek buffer, potem pa jih iz njega črpa. Če imaš šitload stvari v "...", zna priti do težav.


Že, ampak to ni napaka 'Argument list too long'. Slednja je, ker je buffer za podajo argumentov med parent in child procesi v kernelu velik 128KB. Drugače povedano, ti lahko v bashu napišeš vrstico, ki je daljša od tega (dodajo se še spremenljivke okolja), ampak kernel je tisti, ki vrže napako. Bom kar citiral:
there's a lot more to it than that, but the point is that there is a
128K buffer which is the only thing "held" from the parent process to
the child. the "Argument list too long" error message is actually the
kernel's E2BIG error code, returned when the execve() is not able to
fit the supplied argument list and environment into the 128K buffer.


for pa ni program, ampak ukaz v bashu, zato ga moraš dati med narekovaje, če nočeš for zanke in hočeš klicati program for. V slednjem primeru (če na tvojem sistemu obstaja program for) pa boš dobil 'Argument list too long'.

Omejitev o kateri govoriš ti, je pa IMHO veliko bližje omejitvi prostega pomnilnika sistema (čeprav vem pa ne, pa se mi tudi ne da preverjati). Če jo dosežeš, po mojem mnenju nisi varen niti s kakšno drugo rešitvijo tega tipa in bi se vsaj jaz raje odločil za začasno datoteko.

c3p0 ::

I stand corrected on that, for i in * res ne povzroči te napake, kot si bi mislil. Ti pa vseeno ne da rekurzije, kar bo v praksi pri takšni skripti nuja.

find ... -print0 ... reši \n in ostale probleme pri namingu datotek, kot sem napisal, a v praksi se s tem ne ukvarjam, ker do takšnega primera še nisem prišel. Sem bolj pragmatik, akademske primere puščam akademikom ;).


Vredno ogleda ...

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

python pomoč

Oddelek: Programiranje
393282 (2203) Mavrik
»

[bash]Problem s pomnjenjem pri štetju vrstic

Oddelek: Programiranje
121069 (878) Keki
»

[ Linux ] Skripta po meri

Oddelek: Operacijski sistemi
5993 (913) 64202
»

[UNIX] arg list too long

Oddelek: Programiranje
331846 (1101) bijonda
»

Linux skript

Oddelek: Programiranje
92030 (1844) MRB0rYS

Več podobnih tem