[Bash]for loop met filenames met spaties

Pagina: 1
Acties:

  • icyx
  • Registratie: Januari 2007
  • Niet online

icyx

chown -R us ./base

Topicstarter
Ik heb een dir:
code:
1
2
3
met verschillende spaties.jpg
weerzonder.jpg
zonderspatie.jpg

Er zitten dus drie jpg's in. Nu wil ik die in een bash script gebruiken. Ok, dus ik zet het in de volgende loop:
code:
1
2
3
for i in `ls | grep -E "jpg|gif|png|jpeg"`
    do echo "Bestand: $i"
done

De output zou dus moeten zijn:
code:
1
2
3
Bestand: met verschillende spaties.jpg
Bestand: weerzonder.jpg
Bestand: zonderspatie.jpg

Ik krijgt echter:
code:
1
2
3
4
5
Bestand: met
Bestand: verschillende
Bestand: spaties.jpg
Bestand: weerzonder.jpg
Bestand: zonderspatie.jpg

Hoe krijg ik het voor elkaar om dit wel goed in de variabele te krijgen? Ik heb o.a. geprobeerd om het gedeelte tussen de backticks weer in de quotes te zetten, maar dat werkt natuurlijk ook niet, je krijgt dan een lange multiline quote, wat ik al verwachte. Het moet iets simpels zij was makkelijke te escapen valt, maar ik kom er niet uit..

When you think you’ve succeeded / but something’s missing / means you have been defeated / by greed, your weakness.


  • blaataaps
  • Registratie: Juli 2001
  • Niet online
"$i"

  • icyx
  • Registratie: Januari 2007
  • Niet online

icyx

chown -R us ./base

Topicstarter
Nee, dat is het dus niet.
code:
1
2
3
for i in `ls | grep -E "jpg|gif|png|jpeg"`
    do echo "Bestand: "$i""
done

geeft nog steeds:
code:
1
2
3
4
5
Bestand: met
Bestand: verschillende
Bestand: spaties.jpg
Bestand: weerzonder.jpg
Bestand: zonderspatie.jpg

Hij breekt het volgens mij bij de pipe af, alsof hij het gelijkt afkapt bij een spatie.

When you think you’ve succeeded / but something’s missing / means you have been defeated / by greed, your weakness.


  • Osiris
  • Registratie: Januari 2000
  • Niet online
a) Hoe wilde je dat in dat stukje code zien? echo "blabla "$i""?
b) $i bevat losse woorden.

Volgens mij splitst in bash de 'for' bij spaties. Maar ik zou niet weten hoe je het anders zou moeten implementeren zodat ie dat niet doet.

  • Spider.007
  • Registratie: December 2000
  • Niet online

Spider.007

* Tetragrammaton

Er weinig reden om ls te greppen; gebruik gewoon ls *.jpg *.png *.jpeg *.gif
Daarnaast is er weinig reden om ls te gebruiken; gebruik gewoon for i in *.jpg *.png *.jpeg *.gif

Op die manier werkt het zelfs weer gewoon bij mij:

code:
1
2
3
4
5
[sjon@sjon tmp]$ for f in `ls *.txt` ; do echo - $f - ; done
- a -
- b.txt -
[sjon@sjon tmp]$ for f in *.txt ; do echo - $f - ; done
- a b.txt -

[ Voor 39% gewijzigd door Spider.007 op 03-03-2009 16:48 ]

---
Prozium - The great nepenthe. Opiate of our masses. Glue of our great society. Salve and salvation, it has delivered us from pathos, from sorrow, the deepest chasms of melancholy and hate


  • icyx
  • Registratie: Januari 2007
  • Niet online

icyx

chown -R us ./base

Topicstarter
Spider.007 schreef op dinsdag 03 maart 2009 @ 16:46:
Er weinig reden om ls te greppen; gebruik gewoon ls *.jpg *.png *.jpeg *.gif
Daarnaast is er weinig reden om ls te gebruiken; gebruik gewoon for i in *.jpg *.png *.jpeg *.gif

Op die manier werkt het zelfs weer gewoon bij mij:

code:
1
2
3
4
5
[sjon@sjon tmp]$ for f in `ls *.txt` ; do echo - $f - ; done
- a -
- b.txt -
[sjon@sjon tmp]$ for f in *.txt ; do echo - $f - ; done
- a b.txt -
Dat is inderdaad wel een goede. Het enige 'foutje' is dat hij, wanneer *.png bijv. niet voorkomt, die ook nog print, maargoed, dat kan eruit gefilterd worden :).
Dit dus:
code:
1
2
3
4
met verschillende spaties.jpg
weerzonder.jpg
zonderspatie.jpg
*.png

When you think you’ve succeeded / but something’s missing / means you have been defeated / by greed, your weakness.


  • blaataaps
  • Registratie: Juli 2001
  • Niet online
Osiris schreef op dinsdag 03 maart 2009 @ 16:45:
[...]

a) Hoe wilde je dat in dat stukje code zien? echo "blabla "$i""?
Door het stuk dat nutteloos tussen quotes staat niet tussen quotes te zetten.

  • Osiris
  • Registratie: Januari 2000
  • Niet online
blaataaps schreef op dinsdag 03 maart 2009 @ 16:58:
[...]
Door het stuk dat nutteloos tussen quotes staat niet tussen quotes te zetten.
    do echo Bestand: "$i"


Levert bij mij alsnog 3 regels output op terwijl het er maar 1 moet zijn ;) En dat is nogal logisch als je nagaat dat de op deze manier geimplementeerde for-loop zelf de $i vult met de woordjes en dat er verder niets mis is met de echo.

[ Voor 20% gewijzigd door Osiris op 03-03-2009 17:02 ]


  • blaataaps
  • Registratie: Juli 2001
  • Niet online
code:
1
for i in * ; do echo Bestand: "$i" ; done
Werkt hier prima anders, maar ik zie nu dat `ls` nog onhandiger is dan ik al dacht.

  • Demo
  • Registratie: Juni 2000
  • Laatst online: 26-01 09:09

Demo

Probleemschietende Tovenaar

Voor dit soort dingen gebruik ik meestal:
code:
1
2
3
find -name '*.jpg' -type f | while read $TXTFILE; do
  echo "$TXTFILE";
done

Bij for i in `ls` liep ik tegen hetzelfde probleem aan en for i in * is wat minder flexibel.

Unix doesn't prevent a user from doing stupid things, because that would necessarily prevent them from doing brilliant things.
while true ; do echo -n "bla" ; sleep 1 ; done


  • icyx
  • Registratie: Januari 2007
  • Niet online

icyx

chown -R us ./base

Topicstarter
Demoniac schreef op dinsdag 03 maart 2009 @ 17:10:
Voor dit soort dingen gebruik ik meestal:
code:
1
2
3
find -name '*.jpg' -type f | while read $TXTFILE; do
  echo "$TXTFILE";
done

Bij for i in `ls` liep ik tegen hetzelfde probleem aan en for i in * is wat minder flexibel.
Dat is ook erg netjes inderdaad. Ik heb het nu opgelost op de manier zoals al eerder gezegd was door Spider.007, deze dus:
code:
1
2
3
4
for i in *.jpg *.png
do
echo "Bestand: $i"
done

When you think you’ve succeeded / but something’s missing / means you have been defeated / by greed, your weakness.


  • deadinspace
  • Registratie: Juni 2001
  • Nu online

deadinspace

The what goes where now?

icyx schreef op dinsdag 03 maart 2009 @ 16:32:
code:
1
for i in `ls | grep -E "jpg|gif|png|jpeg"`
Kom ik weer met mijn kruistocht tegen backticks hoor :P

Ik raad aan om niet `command` te gebruiken, maar in plaats daarvan $(command), omdat dit leesbaarder is en wat andere prettige eigenschappen heeft, zie voor meer informatie deze post.
icyx schreef op dinsdag 03 maart 2009 @ 16:54:
Dat is inderdaad wel een goede. Het enige 'foutje' is dat hij, wanneer *.png bijv. niet voorkomt, die ook nog print, maargoed, dat kan eruit gefilterd worden :).
Als je bash gebruikt, dan kun je het volgende doen:
shopt -s extglob
for I in *.?(jpg|png|gif)

Merk op dat dit heel erg bash-specifiek is (zsh heeft wel vergelijkbare mogelijkheden, overigens), dus dit werkt niet op POSIX sh. Als je deze constructie gebruikt, zet dan ook #! /bin/bash bovenaan je script, en niet #! /bin/sh !

  • serkoon
  • Registratie: April 2000
  • Niet online

serkoon

mekker.

Hier stond iets wat al geroepen was..

Om toch nog iets toe te voegen:
Zo'n ls | while read fn ; do ... done loopje draait in een sub-shell, dus je moet wel ff uitkijken met scope van vars enzo.

[ Voor 47% gewijzigd door serkoon op 03-03-2009 19:17 . Reden: spuit-11 ]


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

wat is er nou mis met
code:
1
find -name '*.jpg' -o -name '*.png' -exec echo {} \;

ASSUME makes an ASS out of U and ME


Verwijderd

Dit is een bekend probleem en de oplossing is erg simpel: gebruik nooit ls in combinatie met een for loop omdat je dan problemen krijgt met spaties. Dit komt omdat je for loop dan over de verschillende door spaties gescheiden woorden heen gaat lopen. Je moet de shell altijd zelf zijn werk laten doen en wel op bijvoorbeeld deze wijze:

code:
1
2
3
4
5
#!/bin/bash
for i in *
do
echo "$i" | egrep '(jpg|jpeg|gif|png)$'
done

  • deadinspace
  • Registratie: Juni 2001
  • Nu online

deadinspace

The what goes where now?

H!GHGuY schreef op dinsdag 03 maart 2009 @ 19:18:
wat is er nou mis met
code:
1
find -name '*.jpg' -o -name '*.png' -exec echo {} \;
Best wel iets, kijk maar :P
% ls
meh.jpg  test.png
% find -name '*.jpg' -o -name '*.png' -exec echo {} \;
./test.png
%

Je bent de haakjes vergeten ;)
% find \( -name '*.jpg' -o -name '*.png' \) -exec echo {} \; 
./meh.jpg
./test.png
%

Bovendien kun je met find -exec maar één commando aanroepen, met een shell while-loop kun je makkelijk meerdere aanroepen. Je kunt wel weer een shellscript opgeven aan -exec natuurlijk, maar die moet je wel weer maken dan.

Ook zoekt find recursief alle files op, niet alleen die in de huidige directory, dat kan ongewenst zijn (al kun je dat natuurlijk wel weer beinvloeden met -depth oid).

Al met al hangt het een beetje van de situatie af welke van de twee aanpakken het prettigst werkt ;)

  • sam.vimes
  • Registratie: Januari 2007
  • Laatst online: 07-01 22:10
Je zou IFS op een newline kunnen zetten. IFS is een shellvariable die de tekens bevat waarop invoer (in dit geval $(ls) ) in stukjes wordt gehakt.
Bash:
1
2
3
4
5
6
7
oldIFS="$IFS"
IFS="
"
for f in $(ls)
do echo "[$f]"
done
IFS="$oldIFS"

Nadeel is dat dit niet meer werkt als er filenamen zijn met newlines erin. 8)7
Pagina: 1