Diese Website existiert nur weil wir Werbung mit AdSense ausliefern.
Bitte den AdBlocker daher auf dieser Website ausschalten! Danke.

[Final Version] Skript zum ändern in vielen Dateien

Alles rund um die verschiedenen Konsolen und shells sowie die Programmierung unter Linux

Moderator: Moderatoren

Antworten
stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

[Final Version] Skript zum ändern in vielen Dateien

Beitrag von stka » 4. Jul 2005, 16:05

Hallo an alle,

ich da mal ein kleines Skript geschrieben um Strings in vielen Textdateien mit Unterverzeichnissen zu ändern. Warum? Der Grund war ein Schreibfehler in einer Webseite. Dieser zog sich dank copy-and-past über mehrer Dateien in verschieden Unterverzeichnissen hin. Ein Änderung aller Dateien von Hand hätte etwas länger gedauert (ca. 100 Dateien). Aus diesem Grund ist das Skript entstanden.
Jetzt wird der eine oder andere sagen "das ist aber umständlich, das kann ich viel besser". Gerne dann zu verbessert es ;-). Diese Version ist in ca. 20 Minuten mit Test entstanden.

Code: Alles auswählen

#!/bin/bash

clear
mkdir /tmp/aendern_tmp
echo
while [ -z "$QUELLE" ]
do
        echo -n "Bitte Quelle eingeben (QUIT für Ende): "
        read QUELLE
        if [ $QUELLE = "QUIT" ]
        then
                exit 1
        fi
        if [ ! -d $QUELLE ]
        then
                echo
                echo
                echo "$QUELLE ist kein Verzeichnis. Die Quelle muss ein Verzeichnis sein."
                echo
                exit 2
        fi
done
echo
while [ -z "$ALT" ]
do
        echo -n "Bitte alten Wert eingeben : "
        read ALT
done
echo
while [ -z "$NEU" ]
do
        echo -n "Bitte neuen Wert eingeben : "
        read NEU
done
for  i in `grep -lr  $ALT $QUELLE`
do
        echo "DATEI $i wird geändert"
        DATEI=`basename $i`
        PFAD=`dirname $i`
        if [ -f $i ]
        then
                sed  s/$ALT/$NEU/g $i > /tmp/aendern_tmp/$DATEI
                mv /tmp/aendern_tmp/$DATEI $PFAD/$DATEI
        fi
done
rmdir /tmp/aendern_tmp
Aber vielleicht kann es ja doch jemand gebrauchen ;-)

Gruß

Stefan
Zuletzt geändert von stka am 8. Jul 2005, 15:15, insgesamt 1-mal geändert.
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Werbung:
Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 4. Jul 2005, 16:54

warum soll man daran groß was verbessern ;) vom Prinzip mache ich das auch so, 3 Kleinigkeiten sind mir aufgefallen:

1/
mkdir /tmp/aendern_tmp
dass kann schon existieren mit anderen Daten -> hier kann es zu Überschneidungen kommen schau Dir mal mktemp an, damit kannst Du eindeutige Dateinamen oder Verzeichnisnamen erzeugen.

2/
echo "DATEI $i wird geändert"
DATEI=`basename $i`
PFAD=`dirname $i`
if [ -f $i ]
then
sed s/$ALT/$NEU/g $i > /tmp/aendern_tmp/$DATEI
mv /tmp/aendern_tmp/$DATEI $PFAD/$DATEI
es kann nicht schaden, alles was mit Dateinamen in Bash-Scripten zu tun hat zu quoten.

3/

Aufräumen bei Problemen, schau Dir mal trap (man bash) an, und räum bei entsprechenden Signalen auf.

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Re: Skript zum ändern in vielen Dateien

Beitrag von regexer » 4. Jul 2005, 16:57

stka hat geschrieben: sed s/$ALT/$NEU/g $i > /tmp/aendern_tmp/$DATEI
mv /tmp/aendern_tmp/$DATEI $PFAD/$DATEI
Ein paar Anmerkungen zu diesen Zeilen:
1. Den sed-Befehl würde ich in doppelte Hochkommas setzen. Was ist, wenn in $ALT oder $NEU ein space vorkommt?
2. Bitte beachten, dass es auch Metazeichen im sed gibt. Punkt und Stern sind einige davon. "la.er" findet z.B. auch "laber" und "laver".
3. Den move würde ich aus Sicherheitsgründen erst nach manuellem überprüfen der neuen Dateien durchführen. Die Gefahr, mir durch einen Tippfehler alle Dateien auf einmal zu versauen, wäre mir zu groß.

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 4. Jul 2005, 20:34

Jetzt sieht es so aus:

Code: Alles auswählen

#!/bin/bash

clear
TMPDIR=`mktemp -d`
echo
while [ -z "$QUELLE" ]
do
        echo -n "Bitte Quelle eingeben (QUIT für Ende): "
        read QUELLE
        if [ $QUELLE = "QUIT" ]
        then
                exit 1
        fi
        if [ ! -d $QUELLE ]
        then
                echo
                echo
                echo "$QUELLE ist kein Verzeichnis. Die Quelle muss ein Verzeichnis sein."
                echo
                exit 2
        fi
done
while [ -z "$ZIEL" ]
do
        echo -n " Bitte Ziel eingeben : "
        read ZIEL
        if [ ! -d $ZIEL ]
        then
                if [ -f $ZIEL ]
                then
                        echo
                        echo " Das Ziel $ZIEL ist eine Datei, das Ziel muss ein Verzeichnis sein! "
                        exit 3
                fi
                mkdir "$ZIEL"
        fi
done
echo
while [ -z "$ALT" ]
do
        echo -n "Bitte alten Wert eingeben : "
        read ALT
done
echo
while [ -z "$NEU" ]
do
        echo -n "Bitte neuen Wert eingeben : "
        read NEU
done
for  i in `grep -lr  $ALT $QUELLE`
do
        echo "DATEI "$i" wird geändert"
        DATEI=`basename "$i"`
        PFAD=`dirname "$i"`
        if [ -f "$i" ]
        then
                sed  "s/$ALT/$NEU/g" $i > "$TMPDIR/$DATEI"
                mv "$TMPDIR/$DATEI" "$ZIEL/$DATEI"
        fi
done
rmdir "$TMPDIR"

@TexPert Das mit dem Aufräumen musst du mir noch mal erklären, wie du das meinst :shock: An welcher Stelle und was?

@notoxp Das Skript sollte eigentlich nur für mich sein. Aber die Hinweise baue ich gerne ein. Das mit dem "." sprich Regulären Ausdrücken kenne ich. Aber man kann ja nicht alles abfangen :wink: .

Das ist das gute hier, so kann man mit einiger Hilfe und ein paar zusätzlichen Ideen was vernünftiges hin bekommen. Auf weitere Anregungen und Änderungen bin ich gespannt. Wer will kann auch selber was hinzu fügen.

Gruß

Stefan
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 4. Jul 2005, 20:56

stka hat geschrieben: @TexPert Das mit dem Aufräumen musst du mir noch mal erklären, wie du das meinst :shock: An welcher Stelle und was?
man bash -> trap

Du sollst bei gängigen Signalen (z.B. Exit) das temp. Verzeichnis löschen.

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer » 5. Jul 2005, 08:41

stka hat geschrieben: @TexPert Das mit dem Aufräumen musst du mir noch mal erklären, wie du das meinst :shock: An welcher Stelle und was?
TeXpert meint sicher, dass du Signale trappen solltest. Man kann alle Signale bis auf SIGKILL im Script abfangen.
Beispiel: Ganz am anfang eines Scripts steht folgender Befehl:

Code: Alles auswählen

trap "echo Signal zwei" 2
Du startest das script, drückst aber gleich danach STRG+C und brichst es somit ab. Im Hintergrund wird in diesem Fall SIGINT (Signal Nr. 2) an den Prozess geschickt. Doch bevor der Prozess beendet wird, wird noch der echo ausgeführt. Statt des echos kannst du natürlich auch mit "rm -f" deine bereits erstellten temporären Dateien aufräumen.

Übrigens können auch noch andere Signale auftreten, je nach Situation.

Ich empfehle hierzu:

Code: Alles auswählen

man 7 signal

Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 5. Jul 2005, 09:14

genau, und zwar auf alle Signale, die das Skript terminieren, dann werden immer brav alle Temp.Files gleöscht

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 5. Jul 2005, 10:42

OK Ok :D, gebe mich geschlagen. Aber ihr habt recht, das macht Sinn. Ich werde das mal noch einarbeiten. Bin halt kein Programmierer sondern nun ein Netzwerker der ab und zu ein kleines Skript schreibt weil er zum Arbeiten zu faul ist ;-).
Aber ich bin ja lernfähig

Gruß

Stefan
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 5. Jul 2005, 12:15

Hallo TeXpert, hallo notoxp

so jetzt sieht es so aus:

Code: Alles auswählen

#!/bin/bash

clear
TMPDIR=`mktemp -d`
echo
while [ -z "$QUELLE" ]
do
        echo -n "Bitte Quelle eingeben (QUIT für Ende): "
        trap "rmdir $TMPDIR; exit 11" 2
        read QUELLE
        if [ $QUELLE = "QUIT" ]
        then
                rmdir $TMPDIR
                exit 1
        fi
        if [ ! -d $QUELLE ]
        then
                echo
                echo
                echo "$QUELLE ist kein Verzeichnis. Die Quelle muss ein Verzeichnis sein."
                echo
                rmdir $TMPDIR
                exit 2
        fi
done
while [ -z "$ZIEL" ]
do
        echo -n " Bitte Ziel eingeben : "
        trap "rmdir $TMPDIR; exit 12" 2
        read ZIEL
        if [ ! -d $ZIEL ]
        then
                if [ -f $ZIEL ]
                then
                        echo
                        echo " Das Ziel $ZIEL ist eine Datei, das Ziel muss ein Verzeichnis sein!"
                        rmdir $TMPDIR
                        exit 3
                fi
                mkdir "$ZIEL"
        fi
done
echo
while [ -z "$ALT" ]
do
        echo -n "Bitte alten Wert eingeben : "
        trap "rmdir $TMPDIR; exit 13" 2
        read ALT
done
echo
while [ -z "$NEU" ]
do
        echo -n "Bitte neuen Wert eingeben : "
        trap "rmdir $TMPDIR; exit 14" 2
        read NEU
done
for  i in `grep -lr  $ALT $QUELLE`
do
        echo "DATEI "$i" wird geändert"
        DATEI=`basename "$i"`
        PFAD=`dirname "$i"`
        if [ -f "$i" ]
        then
                sed  "s/$ALT/$NEU/g" $i > "$TMPDIR/$DATEI"
                mv "$TMPDIR/$DATEI" "$ZIEL/$DATEI"
                trap "rm -rf $TMPDIR; exit 15" 2
        fi
done
rmdir "$TMPDIR"
Ich habe es getestet jetzt wird auf jeden Fall das temp Verzeichnis gelöscht wenn zu einem Zeitpunkt strg+c gedrückt wird. Bei allen Fehlern wird das temp Verzeichnis auch gelöscht.
Und wieder was dazu gelernt :P

Gruß

Stefan
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 5. Jul 2005, 12:24

die Trap setzt Du nur einmal am Anfang ;) 1. zeile oder so...

und siehe man bash:
If a sigspec is EXIT (0) the command arg is executed on exit from the shell.
pack auch noch ein EXIT als Signal rein, und als Aktion ein aufräumen, dann wird _imme_ das komplette Verzeichnis gelöscht, bei normalem Exit oder nicht.

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 5. Jul 2005, 15:44

OK. Jetzt mal nur der Anfang:

Code: Alles auswählen

 #!/bin/bash
      2
      3 clear
      4 TMPDIR=`mktemp -d`
      5 trap "rmdir $TMPDIR; exit 9 "  0
      6 echo
Jetzt habe ich an allen Stellen im Skript das "rmdir $TMPDIR" entfernt. Das trap Kommando steht nur noch einmal ganz oben. Jetzt ist es egal ob ich das Skript vorzeitig mit strg+c oder mit QUIT abbreche oder das Skript bis zum Ende durchläuft, das TMP Verzeichnis wird immer gelöscht. Was ich jetzt noch nicht verstehe, aber vielleicht kann mir das einer von euche erklären, ist warum bei einem Abbruch mit strg+c das Verzeichnis auch gelöscht wird. Denn eigentlich müsste das doch ein SIGINT sein oder?

Gruß

Stefan
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 5. Jul 2005, 16:36

man 7 signal

1. SIGINT | 2 | A | Interrupt-Signal von der Tastatur

2. A Normalerweise wird der Prozess abgebrochen.

d.h. wenn das Signal2 nicht behandelt wird, dann wird der Default-handler aufgerufen und der ist ein quit, Beispiel:

Code: Alles auswählen

#!/bin/bash

while read line
do
  :
done
Strg-C beendet die Schleife

Code: Alles auswählen

#!/bin/bash
trap "echo 2" 2

while read line
do
  :
done
Strg-C gibt 2 aus und die Schleife läuft weiter.

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 5. Jul 2005, 17:03

Hallo TeXpert
Das verstehe ich jetzt. Da ich das Skript aber in der Schleife mit strg+c abbrechen können möchte, reicht doch oben ein trap mit dem Signal 0 und das TMP Verzeichnis wird auf jeden Fall gelöscht. Oder?

Gruß

Stefan
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer » 5. Jul 2005, 17:20

stka hat geschrieben:

Code: Alles auswählen

      4 TMPDIR=`mktemp -d`
      5 trap "rmdir $TMPDIR; exit 9 "  0
1. Ein paar Anmerkungen: rmdir kann nur Verzeichnisse löschen, die keinen Inhalt haben.
2. wenn du trap "xxx" 0 machst, wird der Befehl bei einem normalen exit aus der Shell ausgeführt.

Benutzeravatar
TeXpert
Guru
Guru
Beiträge: 2166
Registriert: 17. Jan 2005, 11:22

Beitrag von TeXpert » 5. Jul 2005, 17:33

notoxp hat geschrieben: 2. wenn du trap "xxx" 0 machst, wird der Befehl bei einem normalen exit aus der Shell ausgeführt.
auch bei Strg-C denn wenn dieses Signal nicht behandelt wird, wird der normale Exit-Handler aufgerufen

Code: Alles auswählen

# to resolve all your problems, try this:
HOWTO='pack c5,41*2,sqrt 7056,unpack(c,H)-2,oct 115' && perl -le "print $HOWTO"
Ich beantworte keine Supportfragen per PM!

stka
Moderator
Moderator
Beiträge: 3296
Registriert: 1. Jun 2004, 13:56
Wohnort: 51°58'34.91"N 7°38'37.47"E
Kontaktdaten:

Beitrag von stka » 8. Jul 2005, 15:14

Final Version ;-)

Code: Alles auswählen

#!/bin/bash

clear
# Erzeugt ein Temp-Verzeichnis in /tmp
TMPDIR=`mktemp -d`
# Loescht das Temp-Verzeichnis bei einem Programmabbruch und am Ende
trap "rmdir $TMPDIR; exit 9 "  0
echo
# Hier wird das Starverzeichnis festgelegt
while [ -z "$QUELLE" ]
do
        echo -n "Bitte Quelle eingeben (QUIT für Ende): "
        read QUELLE
# Wenn die Eingabe leer war, dann frage noch mal
        if [ -z "$QUELLE" ]
        then
                continue
        fi
# Wenn die Eingabe = QUIT Programmende
        if [ $QUELLE = "QUIT" ]
        then
                exit 1
        fi
# Hier wir ueberprueft ob die Quelle ein Verzeichnis ist
        if [ ! -d $QUELLE ]
        then
                echo
                echo
                echo "$QUELLE ist kein Verzeichnis. Die Quelle muss ein Verzeichnis sein."
                echo
                exit 2
        fi
done
# Jetzt wir das Zielverzeichnis eingelesen.
while [ -z "$ZIEL" ]
do
        echo -n " Bitte Ziel eingeben : "
        read ZIEL
        if [ ! -d $ZIEL ]
        then
# Wenn das Ziel eine Datei ist erfolgt ein Abbruch
                if [ -f $ZIEL ]
                then
                        echo
                        echo " Das Ziel $ZIEL ist eine Datei, das Ziel muss ein Verzeichnis sein!"
                        exit 3
                fi
# Wenn das Ziel nicht existiert wird es angelegt
                mkdir "$ZIEL"
        fi
done
echo
# Hier wird der zu ersetzende Wert eingelesen ACHTUNG bei
# Leerzeichen "text immer" quoten
while [ -z "$ALT" ]
do
        echo -n "Bitte alten Wert eingeben : "
        read ALT
done
echo
# Das gleiche für den neuen Wert.
while [ -z "$NEU" ]
do
        echo -n "Bitte neuen Wert eingeben : "
        read NEU
done
# In einer Schleife werden rekursiev alle Dateien
# nach dem alten Wert durchsucht und geändert
for  i in `grep -lr  $ALT $QUELLE`
do
        echo "DATEI "$i" wird geändert"
# Hier wir der Pfad zur aktuellen Datei ermittelt
        DATEI=`basename "$i"`
# Hier wir der Dateiname der aktuellen Datei ermittelt
        PFAD=`dirname "$i"`
        if [ -f "$i" ]
        then
# Hier wird nun wirklich ersetzt
                sed  "s/$ALT/$NEU/g" $i > "$TMPDIR/$DATEI"
# Und an neue Ziel verschoben
                mv "$TMPDIR/$DATEI" "$ZIEL/$DATEI"
        fi
done
Du hörst nicht auf zu laufen weil du alt wirst. Du wirst alt weil du aufhörst zu laufen.
Das neue Buch http://www.kania-online.de/fachbuecher

Benutzeravatar
harley
Hacker
Hacker
Beiträge: 397
Registriert: 4. Sep 2005, 20:11
Wohnort: Leipzig
Kontaktdaten:

Beitrag von harley » 21. Jan 2007, 03:27

Tolles Skript. Zwei Sachen fehlen mir: 1) die Ordnerstruktur wird nicht übernommen 2) Links können nicht geändert werden, da das Slash-Zeichen als Trenner für sed verwendet wird.

Code: Alles auswählen

# Hier wird nun wirklich ersetzt 
              sed  "s|$ALT|$NEU|g" $i > "$TMPDIR/$DATEI" 
 
# An dieser Stelle wird der Verzeichnisbaum erstellt
				  PFADNEU=${PFAD#$QUELLE}
				  if [ ! -d $ZIEL/$PFADNEU ]
				  then			  	
				  			mkdir -p "$ZIEL/$PFADNEU"
				  fi
					              
# Datei wird ans neue Ziel verschoben 
              mv "$TMPDIR/$DATEI" "$ZIEL/$PFADNEU/$DATEI" 
Micha
++ aus der anleitung für die bedienung von electronicgehirnen + 12 c 3 merke: dein computer ist nicht allwissend + n. +++ (Prokop, G. "Wer stiehlt schon Unterschenkel",Berlin (1983), S. 231)

»Denken ist wie Googeln, nur eben viel krasser.«

Yehudi
Guru
Guru
Beiträge: 2152
Registriert: 21. Jan 2004, 13:51
Wohnort: Hamburg
Kontaktdaten:

Beitrag von Yehudi » 3. Apr 2007, 09:59

Ich würde das Script gerne ins Wiki übernehmen, aber der Titel klingt etwas zu oberflächlich. Der Titel sollte möglichst kurz und prägnant sein.

Antworten