linux-l: Find problem (was: text processing)

Jens Dreger dreger at physik.fu-berlin.de
Mo Dez 20 07:30:48 CET 1999


On Sun, 19 Dec -1, Mario Thaten wrote:

> Hallo liebe Liste,
> 
> danke für Eure Hilfe, mit sed klappts jetzt super.
> Nun soll sed diese Ersetzung in allen von find gefundenen Dateien
> vornehmen und die Datei soll direkt verändert werden. Ich möchte
> weiterhin sed statt perl verwenden. Also dachte ich an eine
> Umleitung in eine Dummydatei, und daran, diese nach erfolgter
> Operation wieder in die gefundene Datei umzubenennen. Mit der
> Syntax von find komme ich dabei allerdings nicht zurecht.
> 
> LOCATION='file:\/\/localhost\/home\/mario\/angelop\/'
> 
> find ./ -regex .*\.htm.* -exec sed -e 's/\(<base
> href="\).*\(">\)/\1'"$LOCATION"'\2/g' > tmp && mv tmp {} \;
>                                      ^^^^^^^^^^^^^^^^^^^^
> funktioniert so natürlich nicht. Aber was muß hier stehen, damit
> alles so klappt, wie ich es mir wünsche.

Ok, now this turns out to be rather complicated...

Ich habe mir obiges Konstrukt einige Minuten angeschaut, und recht schnell
beschlossen, dass das so zu nix fuehrt. Ich habe das Problem letztendlich
in Perl geloest, aber dazu spaeter...

0. Es muesste besser -regex '.*\.htm.*' heissen. Sonst interpretiert die
   shell den pattern und wuerde ihn ersetzen, wenn z.B. eine Datei namens
   .2.htm. exisiert. Zugegeben, recht unwahrscheinlich...

1. Der "^^^^^^"-Teil funktioniert nicht, weil sowohl '>' als auch '&&' von
   der shell interpretiert werde. Letzteres fuehrt zu der Fehlermeldung
   "find: missing argument to `-exec'", weil die shell die Zeile beim '&&'
   splittet. Als naechsten wuerde mal also versuchen '>' und '&&' zu
   quoten.

2. Jetzt erhaelt man sowas wie:
	sed: can't read >: No such file or directory
	sed: can't read &&: No such file or directory
	sed: can't read mv: No such file or directory
   Sed versucht '>' als Datei zu oeffnen. Dies bedeutet, dass find direkt
   sed startet ohne eine sh drumherum zu wrappen. Nur fuer sh wuerde aber
   '>' und '&&' Sinn machen. Also, sh muss her (langsam wird's
   unuebersichtlich ;-) Wir haben also:

	find ./ -regex '.*\.htm.*' \
	    -exec echo sed -e \'s/\\\(\<base \
	    href=\"\\\).*\\\(\"\>\\\)/\\\\1\'\"$LOCATION\"\'\\\\2/g\' {} \> tmp \
	    \&\& mv tmp {} \; | sh

   was sogar zu funktionieren scheint.

3. Wenn Du nicht sicher bist, dass in allen Dateien das Format des
   <base>-Tag so aussieht, wie Dein Pattern es erwartet, bekommst Du
   Probleme. Ich habe folgende Liste herangezogen:

	<base href="base1"><br>
	<base target=target href = "base>2" ><br>
	<base target="target" href=base><br>
	<base target='tar>get' href=base><br>
	<base href='base'><br>
	<base href = "base" ><br>
	<base href = base 1><br>
	<base href = "base 1" ><br>
	<base href ="base 1"><br>
	<base href ="base 1" ><br>
	<base href ="base 1" target="target"><br>
	<base href = "base>2" ><br>
	<base 
	  href = 'base'
	  target = 'target'
	>

Bisher kommt das Skript nur mit der ersten Zeile zurecht. Moeglicherweise
sind auch 'unerlaubte' Varianten darunter, was aber nichts daran aendert,
dass z.B. Netscape die alle schluckt. Spaetestens beim letzten Beispiel
(multiline) duerfte der sed-code richtig unuebersichtlich werden. Nicht,
dass es nicht gehen wuerde, aber...

Also: man nehme lieber gleich PERL. Habe ein paar skripts gebastelt, die
ich aber lieber nicht einfuege. Teilweise etwas laenglich, weil mit
Kommentaren und Debugging. Letzten Endes kann man es auch wieder
verkuerzen auf z.B.:

------8<------------------------------
#!/bin/sh
#
# basesub newbase <files ...>
LOCATION="$1"; shift;

perl -0777 -wpi -e \
's/(<base\s+.*?href\s*=\s*)(".*?"|\047.*?\047|\w+)([^>]*>)/$1'$LOCATION'$3/ioxg;'
"$@"
----------------8<--------------------

dann geht schon mal
	basesub 'file:\/\/localhost\/home\/mario\/angelop\/' <file>
und damit
	find ./ -regex '.*\.htm.*' \
	-exec basesub 'file:\/\/localhost\/home\/mario\/angelop\/'

Ich wuerde allerdings das sh-script gleich ganz ueberspringen:
------8<------------------------------
#!/usr/bin/perl -wi 
#
# basesub newbase <files ...>
undef $/;
my $location = shift || 
	do { print "usage: $0 newbase <files ...>\n"; exit(); };

while(<>) {
 s/(<base\s+.*?href\s*=\s*)(".*?"|\047.*?\047|\w+)([^>]*>)/$1$location$3/ioxg;
 print;
}
------8<------------------------------

jetzt geht:
	basesub 'file://localhost/home/mario/angelop/' <file>
und
        find ./ -regex '.*\.htm.*' \
        -exec basesub 'file://localhost/home/mario/angelop/'

Wenn Du backup-Files willst, ersetze -wi durch -wi~. Perl kommt mit allen
Faellen in der obigen Liste klar. Wenn jemand eine Base-Tag Variante
weiss, an der das Skript haengen bleibt, her damit ;)

Auch koennte man noch das find ins Perl-Skript holen (File::Find), oder
sowas wie HTML::Filter benutzen. Ich bezweifele aber, dass es dadurch
uebersichtlicher wird. Moeglicherweise muss man noch mehr Zeichen als \w+
zulassen. Netscape schluckt ja nahezu alles.

Die Perl-Skripts, die noch "dazwischenliegen" findest Du unter

	http://smart.physik.fu-berlin.de/~dreger/belug/basesub

Das erste hat Farbausgabe. Man nehme './basesub 1.htm | less -r'.
 
Gruss,

Jens.

________________________________________________________________________
Jens Dreger                     | Freie Universitaet Berlin
Cauerstrasse 35a                | Fachbereich Physik, WWW Administration
10587 Berlin                    | Arnimallee 14
Tel: (030)342-3616              | 14195 Berlin




Mehr Informationen über die Mailingliste linux-l