bash apróság

Van egy shell szkriptem, az a célja hogy egy könyvtárstruktúrát egy gyökértől fogva bejár, és ott beállítja a fájlok és könyvtárak jogosultságait. Időközben rájöttem, hogy van erre egy jobb módszer, csak a szkript készítése során belefutottam egy problémába. A for-in ciklus a fájlneveket is szétszedte, így már statolni sem tudtam.
Hogy tudnám megoldani, hogy a szóközös fájlneveim a cikluson belül együtt maradjanak?

A kis fájljaim a következőek:


janos@janos_mobile:~/b/gimnázium/biológia/Természet Búvár$ ls -Q
"A Gerje-sík.doc"                     "Finn nemzeti parkok.doc"
"A Kecsői-karr védett növényei.doc"   "FásultDiagram.xls"
"A Pireneusok tetején.doc"            "Hévízi_tó.doc"
"A Tétényi-fennsík.doc"               "Kis térségek mikroklímája.doc"
"A fóti Somlyó négy arca.doc"         "Körös-ér foltjai.doc"
"A halászsas.doc"                     "Magyar endemizmusok.doc"
"A naptár.doc"                        "Mérlegen a magyar erdők.doc"
"A növények fagytűrése.doc"           "Natúrpark.doc"
"A régi-új Nagy-Kapros.doc"           "Spirálok.doc"
"A védelemre váró Nyugat_Mecsek.doc"  "Szikrázó kvarckristályok.doc"
"Alaszka a mozgó jég világa.doc"      "Trofítás.doc"
"Az Etna.doc"                         "Világító ásványok.doc"
"Csak tiszta forrásból.doc"           "Új-Zéland.doc"
"Erdei szivárvány.doc"
janos@janos_mobile:~/b/gimnázium/biológia/Természet Búvár$  

Hozzászólások

A szkript meg a következő:


#!/bin/bash

USER=smb_client
GROUP=users
HDIR=`pwd`
DIR="$1"
CHMOD=`which chmod`
CHOWN=`which chown`
STAT=`which stat`

VERBOSE=0
ASK=0

set_verbose () {
	VERBOSE=1
}

set_ask () {
	ASK=1
}

for i in "$@"; do
	case "$i" in
		'-v')
			set_verbose
			;;
		'--verbose')
			set_verbose
			;;
		'-i')
			set_ask
			;;
		'--interactive')
			set_ask
			;;
		'--ask')
			set_ask
			;;
	esac
done

OPTS=""
if [ "$VERBOSE"="1" ]; then
	OPTS="$OPTS --verbose"
fi
if [ "$ASK"="1" ]; then
	OPTS="$OPTS --ask"
fi


cd "$DIR"
DIR=`pwd`
echo "entering $DIR"
cd "$HDIR"
#$CHMOD a+r,a+w,a+x "$DIR/*"
#$CHOWN $USER:$GROUP "$DIR/*"

for i in `ls -Q "$DIR"`; do
	OUT=`LANG=en_US stat --format=%F "$DIR/$i"`
	if [ "$OUT" = "regular file" ]; then
		#$CHMOD a-x "$DIR/$i"
		echo "$CHMOD a-x $DIR/$i"
	elif [ "$OUT" = "directory" ]; then
		$0 "$DIR/$i" $OPTS
	fi	
done

__________________________________________________________
Az életben csak egy dolog a szép, de az épp nem jut eszembe.

Én ezt szoktam használni...

CDPAR = jogosultságok könyvtárak számára
CFPAR = jogosultságok fájlok számára
DIR = adott könyvtárból rekurzívan


find $DIR -type d -exec chmod $CDPAR "{}" \;
find $DIR ! -type d -exec chmod $CFPAR "{}" \;

--
maszili

Még régebben csináltam egy szkriptet, ami a fájlneveket alakítja át, például az ékezetes betüket cseréli simára stb.
Talán segít. (Kivettem az átalakító részt, hogy átlátható legyen)


#!/bin/sh

ls >/tmp/noekez.tmp

while read i; do


echo $i

#
# Ékezetek keresése, lecserélése - új fájlnévre cserélés
#

done </tmp/noekez.tmp

Szerk.: semmi ördöngős nincs benne. Mindössze a jobb kezelhetőség és átláthatóság kedvéért egy fájlba irányítom az ls-t, majd soronként beolvasom. Mivel a read parancs csak egy változót tartalmaz, beolvassa az egész fájlnevet szpészestől, vesszőstől, szőröstől-bőröstől, mindenestől.

Nem nagy szám. Nem a szépség és optimális feladatmegoldás volt a cél, hanem, hogy minél gyorsabban kész legyen a fájlátnevezés. Kevés volt a hely a vinyón, az adatok meg jöttek gyorsan - mentenem kellett.
Tessék:


#!/bin/sh

ls >/tmp/noekez.tmp

while read i; do

j=${i// /_}
j=${j//_-_/_}

j=`echo $j | tr Á A`
j=`echo $j | tr á a`
j=`echo $j | tr É E`
j=`echo $j | tr é e`
j=`echo $j | tr í i`
j=`echo $j | tr ó o`
j=`echo $j | tr ö o`
j=`echo $j | tr õ o`
j=`echo $j | tr ú u`
j=`echo $j | tr ü u`
j=`echo $j | tr û u`
j=`echo $j | tr Ü U`
j=`echo $j | tr Ö O`
j=`echo $j | tr Ú U`

if [ "$i" != "$j" ] ; then
echo "$i --> $j"
mv "$i" "$j"
fi

done </tmp/noekez.tmp

Ez csak az ékezeteket szedi ki más szkriptek meg az egyéb "nem kívánatos" karaktereket(ez éppen a Csillagkapu fájlneveit):


#!/bin/sh

ls >/tmp/noekez.tmp

while read i; do

j=${i//"Stargate SG-1 - "/""}
j=${j//" - [HUN]-[ENG] DUB"/""}
j=${j//" - "/"_"}
j=${j//"["/""}
j=${j//"]"/""}
#j=${j//" of "/"of"}
j=${j//" "/"_"}
j=${j//"'"/""}
#j=${j//"_-_"/"_"}

#j=${j//"(DivX)"/""}
#j=${j//"(MP3)"/""}
#j=${j//"(NFo)"/""}
#j=${j//"_[_1"/""}
#j=${j//"(1of1)"/""}
#j=${j//"(1of2)"/""}
#j=${j//"(1of3)"/""}
#j=${j//"(1of4)"/""}
#j=${j//"(2of3)"/""}

j=${j//"_."/"."}

echo "$j"

# echo "$i --> $j"
mv "$i" "$j"

done </tmp/noekez.tmp

Értelemszerüen bővíthető,kurtítható :)

Nem foglalkoztam vele - eddig. Mint írtam, gyorsan kellett összebarkácsolni a szkriptet.
Látszik is - az elején echo meg tr-t használok, ami elég noob megoldás. Helyette a:

j=${j//Á/A}

vagy:

j=${j//"Á"/"A"}

a nyerő. (nincs külső parancs - gyorsabb átláthatóbb.)

Ez az iconv-s megoldás nem tudom jó-e, ha igen használd azt, de meg fogom nézni én is, csak most kevés időm van.

Már vannak itt jó hozzászólások, de azt is érdemes tudni, hogy a for szétszedi a szövegeket, a while pedig nem. Tehát pl.

for nev in $(parancs); (vagy for nev in `parancs`;), ill.
for nev in $(< file); (vagy for nev in $(cat file); )

helyett:

parancs | while read nev;
cat file | while read nev;

Nem. A for az mindig szóköz mentén vág. A read pedig akkor ad falsot, ha nincs mit olvasnia. Tehát míg for esetében nem addig futsz, amig egy feltétel fals nem lesz, hanem amig van sorozatod. While esetén viszont addig futsz, amig falsra nem megy a feltétel. A read-nak meg megvan az a qva nagy előnye, hogy nem whitespace, hanem EOL karaktereket keres.

Nem értelek. A for, while stb ciklusszervező, míg a read input művelet.

Ezen a szinten (topik téma) semmi közük egymáshoz!

Szerk.: Kipróbálni egyszerű. Csinálj for ciklust, amiben read-el olvasol be sorokat.
A beolvasott sorok szépen megtartják a szpészeket, nem csonkolódnak a sorok!

A for egy lista (alapértelmezett esetben szóközzel elválasztott) tagjain megy végig. (Annyiszor fut le a ciklusmag ahány elemű a lista)

for futo_index in elem_1 elem_2 elem_3 do
ciklusmag
done

A while pedig addíg hajtja végre a ciklusmagot amíg a feltétel igaz

while feltétel
do
ciklusmag
done

A while esetében viszont nem egy előre elkészített listából vesszük az elemeket hanem pl. a read-el olvassuk be és akkor a read visszatérési értéke adja a feltételt. A while ciklus addíg fut amíg a read tud olvasni.

pl.


while read; do
    echo $REPLY
done < <(ls -l)

A pipe használata nem szerencsés...


ls -l | while read; do

mert ekkor a ciklusmag új környezetben fut és így körülményes a ciklusban megváltoztatott értékű változók használata a cukluson kívül.

--
maszili


fules@chaos:/tmp/q$ for i in `ls`; do echo $i; done
hacienda
hoze
ize

fules@chaos:/tmp/q$ for i in $(date); do echo $i; done
Tue
Oct
2
09:49:34
CEST
2007

fules@chaos:/tmp/q$ for i in habla gabla micsoda; do echo $i; done
habla
gabla
micsoda

Mint már fentebb írva is volt, a 'for' egy listán iterál végig. Hogy azt a listát egy parancs (pl. "ls", "date") futásának eredményeként kapja, vagy te adod meg explicite, az már rajtad múlik.

"a for szétszedi a szövegeket, a while pedig nem"

Ez ellen az is véd, hogy:

for nev in *.*

vagy for nev in mappa/*.*

Ez esetben a bash adja a fajlneveket a for-nak, es a szokozoket nem tekinti szeparatornak. Igy is mukszik. nem fontos az `ls valami`

Lehet, hogy a bash már eleve escapelve nyomja a szokozoket a for-nak, es azert nem választja el a szavakat?