shell script - hibacsatorna figyelese

Irtam egy fajl atnevezo szkriptet, amit szeretnek finomhangolni ugy, hogy a csak a "-a utvonal" parametert fogadja el, vagy csak siman az "utvonal"-at vegye figyelembe; ha valamilyen masik opcioval akarnak inditani, irja ki, hogy ez nem fog menni, probald ezt-meg-ezt. A "-a utvonal" meg van oldva, viszont a sima "utvonal"-lal bajban vagyok, ugyanis ezt ugye nem lehet osszehasonlitani egy elore definialt sztringgel. Viszont - mivel ls-t is hasznalok a szkriptben - arra gondoltam, hogy akkor ls hibacsatornajat figyelve megtudhatom, hogy most utvonalat irtak-e be, vagy valami mast. Viszont nem tudom, hogy egy ideiglenes fajlba iras hasznalata nelkul hogy lehetne megoldani a dolgot? Vagy irjam ki fajlba a hibacsatorna uzenetet, aztan ha kiirta, hogy ez igy nem lesz jo, toroljem a fajlt?

Hozzászólások

Az ls figyelése nem a jó irány. man sh, keress rá a getopts -ra, és tanuld meg hogyan kell opciókat lekezelni.

Inkább a visszatérési értékekre figyelj. Az ls helyett egyszerűbb és szebb a test parancs használata (-d, -e, -f, -w, -x stb.). Az utóbbi helyesen felparaméterezve több kérdésre tud választ adni. Részletesen: man bash (man sh).

Mindkét változatot használhatod, az első a javasolt a megfelelően paraméterezett formában:

if [ -e /tmp/fileneve ]; then echo Letezik ; fi

vagy

if ls /tmp/fileneve >/dev/null 2>&1; then echo Letezik ; fi

És csak hogy legyen egy általad kért lehetséges megoldás is:

if ! (ls /tmp/fileneve 2>&1 >/dev/null | grep "cannot access" >/dev/null); then echo Letezik ; fi

Ralestep man getopts-ra, meg nezelodtem neten is peldak utan, es nagyjabol tudom is kezelni, viszont arra nem talaltam utalast, hogy ha argumentum nelkul, vagy epp argumentummal, de az azt koveto parameter nelkul inditom a szkriptet, akkor hogy lehet arra feltetelt szabni? Ugyanis ilyenkor egyszeruen hibauzenetet ir ki. Valami olyasmit szeretnek, mint az ls: ha argumentum nelkul inditom, a helyi konyvtarat vegye alapul, ha pedig argumentummal, parameter nelkul (azaz az eleresi ut megadasa nelkul), akkor szintugy a helyi mappara ertelmezze a parancsot.
--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

while getopts :ab: XX ; do
case "$XX" in
a) echo a opcio lekezelese ;;
b) echo b opcio lekezeles; b_arg="$OPTARG" ;;
:) echo hianyzik az "$OPTARG" opcio parametere;;
'?') echo nem letezo opcio
*) echo ILYEN NEM LEHET!!!
esac
shift $(( $OPTIND - 1 ))
estebe

Azaz, ha a getopts opcióit leíró első paraméter : -tal *kezdődik*, akkor a getopts nem lök ki neked hibaüzenetet, hanem a második paraméterként átadott változóban jelzi a hibát, és a hiba okát.

(Jelzem nálam man bash és ott a getopts belső parancs leírása ezt az infót tartalmazza:

getopts can report errors in two ways. If the first character
of optstring is a colon, silent error reporting is used. In
normal operation diagnostic messages are printed when invalid
options or missing option arguments are encountered. If the
variable OPTERR is set to 0, no error messages will be dis-
played, even if the first character of optstring is not a colon.

Én meg pont ezt írtam. Személy szerint az OPTERR változó használatát kompatibilitási okokból nem javaslom.)

...es ebbol melyiket szantad valasz gyanant...? o_O
Lehet, nem fogalmaztam tisztan:

script # ascript a helyi konyvtarra legyen ervenyes
script -a # szintugy, de az '-a' kapcsolot is vegye figyelembe
script ez/meg/az # a kapcsolok hasznalata nelkul vegye figyelembe az utvonalat

Na, ezt hogy lehet megvalositani?
--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

dir="." # alapbol az aktualis konyvtarral fogunk jatszani
[ "X-a" = "X$1" ] && # van egy "-a" opcio
{
amit a "-a" opcio miatt csinalni kell
shift # eldobjuk azt a "-a" opciot
} # "-a" vege

[ $# -ne 0 ] && # van meg parameter
{
dir="$1"
shift # ezt a parametert is eldobjuk
} # vege a plusz parameter figyelesenek

es itt
jon a
tobbi
termeszetesen "$dir" -t hasznalva

====

Persze ha nem csak egy nyomorult "-a" -t akarsz kezelni, akkor az elso test ( "[" ) parancs helyett ertelmesen eloallitott getopts -ot erdemes rakni (es persze lehetne "if" segitsegevel irni, en jobb szeretem ezt a format.

Valami hasonlon gondolkodtam en is, viszont az lenne az igazi, ha nem kene tobbszor ellenoroznom ugyanazt a kapcsolot (egyszer a getopts, majd a $1 miatt), hanem valami olyan kene, hogy ha getopts erzekelte az -a kapcsolot, es talalt mellette parametert, akkor hajtsa vegre a parancsot a parameterben megadott konyvtarra, ha viszont parameter nelkul volt a kapcsolo, akkor a helyi konyvtarra vonatkozolag intezkedjen. Csak hat ennel az a baj, hogy ha a script -a utan nem irok parametert, akkor a getopts hibat jelez es a case resznek nem ad tovabb argumentumot. (azaz sehogy se jelzi, hogy -a kapcsoloval inditottuk a scriptet)
Hogy ne csak a levegobe beszeljek, alljon itt egy kezdetleges atnevezo program, amely a nagybetukbol kisbetut csinal, az ekezetes betuket 'angolositja', a szokozoket alsovonas-karakterre csereli. A megoldas eleg maszek, hisz majdnemhogy ketszer csinalom ugyanazt, ed epp erre keresek megolodast: hogy vagy a getopts segitsegevel oldok meg mindent, vagy az argumentumokat nezegetem egymas utan...


#!/bin/sh

# ez a karakteratalakito es a fajl-atnevezo resz, azaz a skript lenyegi resze
function f_move
{
# a "for" ciklus miatt kell elobb atalakitanom a szokozoket, mivel az "i" minden
# whitespace-szel elvalasztott sztringet, vagy karaktert uj erteknek tekint,
# marpedig itt szokozoket is tartalmazo mappaneveket vesz fel
for i in `$list --color=never $path | sed -e "y/ /_/"`; do
# * a masodik "echo" helyere "mv" fog kerulni, de a kiiratas szemleletesebb
# * az alsovonast visszaalakitjuk szokozre, hogy az "mv" megtalalja az eredeti fajlt
echo `echo $i | sed -e "y/_/ /"` `echo $i | sed -e "y/áéíóöőúüűÁÉÍÓÖŐÚÜŰ/aeiooouuuAEIOOOUUU/" | tr A-Z a-z`
done
}

getopts ": a: n:" arg

# "ls" beallitasa az argumentumnak megfeleloen
case $arg in
# normal fajlokra es mappakra
n ) list="ls -Q" ; path=$OPTARG; f_move; exit 0;;
# minden fajlra es mappara
a ) list="ls -QA"; path=$OPTARG; f_move; exit 0;;
esac &&

case $1 in
# normal fajlokra es mappakra a helyi konyvtarban
"" ) list="ls -Q" ; path="."; f_move; exit 0;;
"-n" ) list="ls -Q" ; path="."; f_move; exit 0;;
# minden fajlra es mappara a helyi konyvtarban
"-a" ) list="ls -QA"; path="."; f_move; exit 0;;
esac

# ha egyik lehetoseg sem mukodott, akkor rossz argumentumokkal inditottak a szkriptet,
# ezert kiirjuk a helyes hasznalatot
echo -e " -n [UTVONAL]\tnormal fajlok es konvtarak atnevezse\n -a [UTVONAL]\tminden fajl es konyvtar atnevezese (rejtett is)"

exit 1

--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

Bocs, erre most nem érek rá átrágni, majd tán más megteszi, ellenben nem olvastad el figyelmesen az általam beküldött getopts-os kódot.

> valami olyan kene, hogy ha getopts erzekelte az -a kapcsolot, es talalt mellette parametert, akkor hajtsa vegre a parancsot a parameterben megadott konyvtarra, ha viszont parameter nelkul volt a kapcsolo, akkor a helyi konyvtarra vonatkozolag intezkedjen. Csak hat ennel az a baj, hogy ha a script -a utan nem irok parametert, akkor a getopts hibat jelez es a case resznek nem ad tovabb argumentumot. (azaz sehogy se jelzi, hogy -a kapcsoloval inditottuk a scriptet)

Ott van fenn feketén fehéren: ha a getopts -nak olyan *első* paramétert adunk, ami ":" -tal kezdődik, akkor nem ír ki semmilyen hibát, hanem ezt rádbízza. A *második* paraméterként átadott változóba a ":" értéket teszi, és a "$OPTARG" -ban van a problémás opció. Azaz igenis lehet tudni, hogy -a opció volt paraméter nélkül. Azaz le kell írnod egyszr a "-a" opció helyes, és egyszer a "-a" helytelen használatakor, hogy mit csináljon. Nyilván első esetben egy dir="$OPTARG", másodikban pedig egy dir="." jellegű a megfelelő művelet.

Ja, rápillantotam a kódodra: ne legyen az opcióleírásban szóköz, mert akkor azt is elfogadja jó opciónak, ami erősen ellenjavalt. Szóval nem

getopts ": a: n:" blabla

hanem getopts ":a:n:" blabla

És hogy ne a levegőbe beszéljek:

`` szkriptem "- " akarmi'' esetén átadtam egy "- " opciót, amit a tied elfogad (de nem kezel le), az enyém meg el se fogad.

Valamint úgy javasolt, hogy a getoptsban csak beállítják a megfelelő változókat, és az egész getopts után jöhet a végrehajtás.

(És valamelyik példában "script" -nek hívod, remélem nem gondoltad komolyan, ez létező *X parancs. Szóval mielőtt nevet választasz, mindig add ki a "type szerintemezjonevlesz" parancsot (meg esetleg a "man ..." -t is) - és csak akkor folytasd ezzel a névvel, ha nincs találat.)

Ertem, es koszonom az ertekes tanacsokat. :) Viszont bárhogy is probalkozom, ha mv-t akarom hasznalni, kiirja, hogy vagy nem talalja a fajlt, vagy hogy a parameter nem mappat jelol. Pedig ha manualisan irom be terminalba, hogy `mv "erről a szövegről" "erre_a_szovegre", akkor mukodik, csak igy a szkriptem keretein belul nem. Pedig (ha kiiratom a parametereket) total ugyanezek a parameterek; ahol szokoznek kell lennie, ott szokoz van, az idezojelek megjelennek, elviekben minden klappol, a gyakorlatban meg megse, probaltam mar mv -T opcioval is, azzal se megy.... Ez miert lehet?
--
Azt akarom, hogy az emberek ne kényszerből tanuljanak, hanem azért, mert tudni akarnak.

No latom, nem uszom meg. Tehat:

Nem 1 db. getopts -ot kell meghivni, hanem ciklusban addig, amig hibat nem ad vissza, ugyanis akkor van vege az opcioknak. Mivel ketfele opciot lehet megadni, ezert tobbszor kell hivogatni. Az, hogy az opcioid ellentmondanak egymasnak, az nem baj, jelenlegi allapotaban egyszeruen az utolso felulbiralja az elobb levoket.

Nem veletlenul irtam (remelem irtam) a valtozohivatkozasok kore "-et, pont azert kell, hogy ne legyenek problemak a szokozoket/tabulatorokat/sorvegjeleket tartalmazo parameterekkel. Azaz en olyan helyen is kirakom, ahol szigoruan veve nem kell, de jobb ezt megszokni.

Ami a te igenyedet illeti, kifejezetten ellenjavalt olyan parameterezest kitalalni, ahol egy opcio vagy kap parametert, vagy nem. Nem lehet igazan korrekten lekezelni (legalabbis shell-scriptben, es a getopts-sal. Ez itt lenn is latszik).

Nem bantanam az f_move fuggvenyedet, hanem a tobbi reszet csiszolnam:

=====
# ezek azert kellenek, hogy opcio nelkul is ertelmes legyen
# nyilvan valami ertelmes alapra allitsd be
list="ls -Q"
path="."
while getopts ":a:n:" arg ; do
# "list" es "path" beallitasa az argumentumnak megfeleloen
case "$arg" in
# normal fajlokra es mappakra
n ) list="ls -Q" ; path="$OPTARG";;
# minden fajlra es mappara
a ) list="ls -QA"; path="$OPTARG";;
# hianyzott a parameter az opcio utan
: ) case "$OPTARG" in
# normal fajlokra es mappakra a helyi konyvtarban
"n" ) list="ls -Q" ; path=".";;
# minden fajlra es mappara a helyi konyvtarban
"a" ) list="ls -QA"; path=".";;
esac # ez a "$OPTARG" -os resz vege
;;
# ha egyik lehetoseg sem mukodott, akkor rossz argumentumokkal inditottak a szkriptet,
# ezert kiirjuk a helyes hasznalatot
'?' )
echo -e " -n [UTVONAL]\tnormal fajlok es konvtarak atnevezse\n -a [UTVONAL]\tminden fajl es konyvtar atnevezese (rejtett is)" ;;
esac # vege a getopts-nak

shift $(( $OPTIND - 1 ))

# es miutan elokeszitettuk a terepet, lehet meghivni a kivant fv-t.

f_move
exit $? # nem 0, hanem az utolso parancs (jelenleg f_move utolso parancsa) statusza.
=====

(Ja ertelemszeruen tordelve kicsit olvashatobb lenne)

Egy másik lehetséges megoldás, ha nem az ls hibacsatornáját figyeled, hanem az ls lefutása utáni errorlevel-t.

Gondolom a hibacsatorna figyelést valahogy így érted:
(Tegyük fel, hogy nem létezik a hello.txt.)

ls hello.txt 2> hibafile.txt

Az errorlevel figyelés így működik:
(Tegyük fel, hogy nem létezik a hello.txt.)

ls hello.txt
echo $?
2

A $? mindig az utolsó errorlevel-t tartalmazza, ami 0, ha minden rendben lefutott, és 0-tól különböző, ha hiba volt. A fenti esetben, nem létező hello.txt esetén 2.

Ezt script-ben egyszerűen átadhatod egy másik változónak, vagy elágazhatsz rajta stbi.

---Sorx

Használható még a feltételes végrehajtás ahol a feltétel egy előzőleg végrehajtott parancs (program) befejezésének sikeressége.

PROGRAM && echo igaz || echo hamis

--
maszili