User-Home kopieren, Einzeiler für Suchen&Ersetzen gesucht

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
dirk11
Beiträge: 2840
Registriert: 02.07.2013 11:47:01

User-Home kopieren, Einzeiler für Suchen&Ersetzen gesucht

Beitrag von dirk11 » 11.01.2014 10:32:32

Moin Leute,

um die Einrichtung der Xfce-Oberfläche für einen neuen User zu erleichtern, will ich einige der Files und Verzeichnisse unter /home/USERALT/ nach /home/USERNEU/ kopieren. Dort muss ich natürlich UID/GID anpassen. Das ist vergleichsweise leicht und sollte hiermit abgedeckt sein:
Erst von USERALT nach USERNEU kopieren, dann in /home/USERNEU folgendes machen:
find . -user 1100 -exec chown 1111 {} \;
find . -group 1100 -exec chgrp 1111 {} \;

Oder, noch einfacher:
chown -R USERNEU:USERNEU *

Mein Problem sind die kopierten Dateien: darin befindet sich oftmals ein Pfad, in welchem der User als Teil des Pfad steht. Das ist beispielsweise bei gkrellm so, wird aber bei vielen anderen config-files nicht anders sein, dass dort eine Zeile a la
home=/home/USERALT
drinsteht.

Wie kann ich nun mit einer ähnlichen Zeile wie oben genannt Suchen&Ersetzen machen?

Suche in allen Files ab&inklusive Standort (.) nach Zeichenkette USERALT und ersetze sie durch Zeichenkette USERNEU
lautet die Aufgabe.

Kann man sowas als kurzen Einzeiler mit sed oder egrep oder sowas lösen?

dirk11
Beiträge: 2840
Registriert: 02.07.2013 11:47:01

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von dirk11 » 11.01.2014 11:18:37

Ergänzung: wenn dann noch aufgelistet würde, welche files verändert wurden, wäre das ganze perfekt!

luka
Beiträge: 14
Registriert: 12.01.2014 19:26:56

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von luka » 13.01.2014 21:02:56

Deine Frage ist ja nun schon ein paar Tage alt und vermutlich hast Du Dein Problem schon irgendwie gelöst, aber vielleicht kannst Du es ja doch noch gebrauchen oder es hilft zumindest in Zukunft jmd. weiter: Ich würde eine Kombination von find, grep und sed einsetzen, um das Problem zu lösen:

Code: Alles auswählen

$ for i in $( find . -type f -print0 | xargs --null grep -l "USERALT" ) ; do 
> sed -i 's/USERALT/USERNEU/g' $i && echo $i
> done
Dabei geschieht folgendes:
  • die for-Schleife weist der Variable $i nacheinander die Ergebnisse der Befehlsersetzung $(...) zu und führt für jedes die Befehle zwischen do ... done aus
  • innerhalb der Befehlsersetzung $(...)
    • sucht find vom aktuellen Verzeichnis aus (.) nach allen regulären Dateien (-type f), wobei die Ergebnisse nicht mit einem Leerzeichen, sondern mit einem Null-Zeichen getrennt werden (-print0), so dass keine Gefahr besteht, dass im Folgenden ein Dateiname mit Leerzeichen als mehrere Dateien behandelt wird
    • die Ausgabe des vorigen Befehls wird per Pipe (|) als Eingabe an den nächsten Befehl weitergereicht
    • xargs fügt die Eingabe zu einer Argumentenliste zusammen, mit der es den Befehl ausführt, der auf seine eigenen Optionen folgt. Dabei berücksichtigt es, dass die Eingabe mit Null- statt Leerzeichen getrennt ist (--null)
    • grep durchsucht die Dateien nach dem Muster USERALT, wobei es statt der übereinstimmenden Zeilen nur die Namen derjenigen Dateien ausgibt, die eine oder mehrere Übereinstimmungen aufweisen (-l)
  • innerhalb der do ... done-Schleife
    • ersetzt der Streameditor sed alle Vorkommen von USERALT durch USERNEU ('s/USERALT/USERNEU/g', wobei er statt der Standardeingabe eine Datei als Datenstrom einliest (-i), den es Zeile für Zeile durchscannt. Bei der Datei handelt es sich um den jeweiligen Wert der Variable $i innerhalb der aktuellen Schleife.
      [*] Wenn der vorherige Befehle ohne Fehler ausgeführt wurde, wird der folgende Befehl ebenfalls ausgeführt (&&) (wobei sed auch dann einen fehlerfreien Durchlauf signalisiert, wenn es keine Ersetzungen vorgenommen hat - aber da grep ja bereits die Dateien herausgefischt hat, in denen das Muster USERALT vorkommt, können wir uns auf die Ausgabe dennoch verlassen)
Noch ein paar Anmerkungen:
  • um sicherzugehen, dass das Suchmuster nicht in manchen Dateien in ganz anderen Zusammenhängen erscheint, in denen keine Ersetzung erwünscht ist, würde ich zuerst mit folgendem Befehl überprüfen, was grep genau aufspürt:

    Code: Alles auswählen

    $ find . -type f -print0 | xargs --null grep --color=auto "USERALT"
    
  • sed und grep sind in dieser Form beide case sensitive, d.h. im Suchmuster ist auf Groß- und Kleinschreibung zu achten. Beide Befehle verfügen aber auch über Optionen, um die Zeichengröße(? :roll: ) zu ignorieren.
  • Warum xargs und nicht find ... -exec? Da du möglicherweise recht viele Ergebnisse haben wirst, ist xargs schneller, da es mehrere Ergebnisse zu einer Kette von Argumenten zusammenfasst, während exec für jedes Ergebnis einen eigenen Prozess aufruft (wobei der Geschwindigkeitsunterschied wahrscheinlich nicht ins Gewicht fällt). Praktischerweise achtet xargs dabei auch gleich auf die Längenbebegrenzung der Befehlszeile in Deiner aktuellen Umgebung und führt falls nötig mehrere Prozesse mit Argumenten aus (aber eben nur so viele, wie tatsächlich benötigt werden).
  • grep und sed sind recht mächtige Such- bzw. Prozessierungstools, mit denen Du über reguläre Ausdrücke auch nach sehr komplexen Mustern suchen kannst. Insbesondere sed kann noch sehr viel mehr als suchen und ersetzen. ich empfehle man sed zu überfliegen und info sed zum Vertiefen. Außerdem gibt es einige gute Online-Tutorials ...
Gruß,
Luka

Cae
Beiträge: 6349
Registriert: 17.07.2011 23:36:39
Wohnort: 2130706433

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von Cae » 13.01.2014 22:01:40

Schoener Post, du hast da echt Aufwand reingesteckt ;). Trotzdem wuerde ich gerne anmerken, dass die Schleife bei Treffern mit Dateinamen mit Whitespaces explodiert. Also wenn grep jetzt in einer Datei /foo\ bar/baz fuendig wird, bekommt das sed einmal /foo und dann bar/baz, was nicht im Sinne des Erfinders ist. Stattdessen wuerde ich gleich mit Nullbytes und xargs weitermachen:

Code: Alles auswählen

$ find -type f -print0 | xargs -0 grep -lZ 'USERALT' | xargs -0 sed -i 's/USERALT/USERNEU/g'
(kleine Aenderungen: ./ ist default, kann also weggelassen werden; -0 ist kuerzer als--null; -print0 von grep heisst -Z.) Bisschen bloed ist nur, dass man keine Liste der geaenderten Dateien bekommt, aber die kann man ja im Vorfeld betrachten. Uebrigens ist das potenziell 'ne groessere Menge an Daten, die auf's Terminal geschrieben werden, da wuerde ich less mit --RAW-CONTROL-CHARS nachschalten:

Code: Alles auswählen

$ find -type f -print0 | xargs -0 grep --col=always "USERALT" | less -R
(--color laesst sich abkuerzen und muss auf always stehen, weil's wegen der Pipe per default never ist.)

Wie sinnvoll das rohe Ersetzen bei SQLite-Datenbanken oder sonstigen BLOBs ist, kann man sich auch fragen... oder wenn da CRC oder sonstige Pruefsummen draufliegen. Aber da ist dann der TE schuld, denn er will's so haben ;).

Willkommen im Forum!

Gruss Cae
If universal surveillance were the answer, lots of us would have moved to the former East Germany. If surveillance cameras were the answer, camera-happy London, with something like 500,000 of them at a cost of $700 million, would be the safest city on the planet.

—Bruce Schneier

Benutzeravatar
ThorstenS
Beiträge: 2875
Registriert: 24.04.2004 15:33:31

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von ThorstenS » 13.01.2014 22:07:50

Wie wäre es mit einem bind-mount von /home/userneu auf /home/useralt?
Dann würdest du zwar weiterhin mit alten Pfaden arbeiten, aber der Drops ist nach wenigen Sekunden gelutscht…

dirk11
Beiträge: 2840
Registriert: 02.07.2013 11:47:01

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von dirk11 » 13.01.2014 22:54:03

Wow, es kommen ja doch noch Antworten :)

@ThorstenS: Ich glaube, du hast das Grundproblem nicht verstanden, einfach nochmal im ersten post nachlesen ;)

Meine Lösung sieht zwischenzeitlich so aus:
erst

Code: Alles auswählen

chown -R USERNEU:USERNEU *
dann

Code: Alles auswählen

find . -type f | while read g; do echo "Bearbeite $g"; echo sed -i s/USERALT/USERNEU/g "$g"; done
Wenn es gut aussieht, einfach das zweite "echo" weglöschen.

Cae
Beiträge: 6349
Registriert: 17.07.2011 23:36:39
Wohnort: 2130706433

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von Cae » 14.01.2014 00:25:55

dirk11 hat geschrieben:@ThorstenS: Ich glaube, du hast das Grundproblem nicht verstanden, einfach nochmal im ersten post nachlesen ;)
Nein, er/sie hat sogar eine sehr geniale Loesung, um das Problem der unterschiedlichen Pfade erst gar nicht entstehen zu lassen: Das $HOME wird einfach an Ort und Stelle mit dem des "Testbenutzers" ersetzt. Der Pfad bleibt gleich, die Daten des "Produktivbenutzers" unangetastet. Wobei die "beiden Benutzer" in diesem Szenario sogar einfach derselbe Benutzer ist.

Gruss Cae
If universal surveillance were the answer, lots of us would have moved to the former East Germany. If surveillance cameras were the answer, camera-happy London, with something like 500,000 of them at a cost of $700 million, would be the safest city on the planet.

—Bruce Schneier

dirk11
Beiträge: 2840
Registriert: 02.07.2013 11:47:01

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von dirk11 » 14.01.2014 03:13:45

Ich hätt' ja lieber eine Kommentierung zu meinem Einzeiler.

Aber Cae, was wäre daran "genial"? Ehrlich gesagt verstehe ich das nicht. Es gibt keinen "Testbenutzer", ich kopiere nur bestimmte .*** (noch nicht einmal alle) eines bestimmten Benutzers, um z.B. die Einstellungen des Panel zu übernehmen. Der Benutzer soll die danach natürlich nach Belieben verändern können, ich will nur eine einheitliche Grundeinstellung erreichen.

newdeb
Beiträge: 134
Registriert: 03.02.2011 11:11:21
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: Frankfurt

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von newdeb » 14.01.2014 06:56:47

dirk11 hat geschrieben:

Code: Alles auswählen

 ... sed -i s/USERALT/USERNEU/g "$g"
Beachtung von Wortgrenzen im RegEx ist zu empfehlen, sonst wird schnell z.B. bei

Code: Alles auswählen

sed -i s/hans/bert/g
aus dem User "hansi" der User "berti" (der vielleicht nicht existiert).

luka
Beiträge: 14
Registriert: 12.01.2014 19:26:56

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von luka » 14.01.2014 15:59:14

Cae hat geschrieben:Schoener Post, du hast da echt Aufwand reingesteckt ;).
Naja, bei langen Befehlszeilen und unklarem Vorwissen finde ich es immer blöd, wenn nicht erklärt wird, was da passiert (wobei sich das mit dem Vorwissen inzwischen auch für mich aufgeklärt hat, hätte mir das zumindest für den OP also sparen können). Wenn es aber so (zu) ausführlich wird, wie hier, ist das immer ein klares Zeichen, dass ich entweder gerade jede Menge freie Zeit und Lust habe, sie in meinem "virtuellen Hobbykeller" zu verbringen, oder gar keine Zeit habe und vor einer wirklich dringenden Aufgabe, bei deren Lösung ich nicht weiterkomme, in den virtuellen Hobbykeller flüchte. :roll:

Aber danke für Deine Reaktion - grep -Z war mir nicht bewusst und die --RAW-CONTROL-CHARS für less hatte ich mir immer schon gewünscht und bisher übersehen.
Also wenn grep jetzt in einer Datei /foo\ bar/baz fuendig wird, bekommt das sed einmal /foo und dann bar/baz, was nicht im Sinne des Erfinders ist.
Stimmt! Oh mann! Jetzt springt es geradezu ins Auge :facepalm: Das hätt ich wohl besser mal testen sollen ... (Wieso gibt es denn hier kein :werdrot: ?)
Stattdessen wuerde ich gleich mit Nullbytes und xargs weitermachen: [...] Bisschen bloed ist nur, dass man keine Liste der geaenderten Dateien bekommt, aber die kann man ja im Vorfeld betrachten.
Jep. Trotzdem dachte ich mir als ich das gestern Abend las, dass das doch auch in einem Befehl und auf dem Weg, den ich oben eingeschlagen hatte, zu machen sein müsste: "da gab es doch noch die Systemkonstante $IFS (Internal Field Seperator) und damit kann ich die Zeile oben doch bestimmt mal eben ---" - und schon waren wieder 2 Stunden in meinem "Hobbykeller" mit Stöbern in manpages und Ausprobieren rum. Denn sobald ich versuchte, den verfluchten IFS dazu zu kriegen, einen Null-Character oder nicht-SPACE-Whitespace zu akzeptieren, wurde es einfach ignoriert (ok, zwischendurch habe ich auch versucht, andere Wege einzuschlagen). Schließlich habe ich also doch im Netz nachgeschaut, wie man den $IFS rumkriegt, was zu meinem zweiten :facepalm:-Moment führte (denn zumindest auf IFS=$(echo -en "\n") hätte ich eigentlich selbst kommen können). Danach war es dann einfach:

Code: Alles auswählen

$ IFS=$'\n\t' ; for i in $(find . -type f -print0 | xargs --null grep -l "USERALT") ; do
> sed -i 's/USERALT/USERNEU/g' $i && echo $i ;
> IFS=$' \t\n'   # aus Default zurücksetzen
> done
Mit IFS=Null geht das übrigens nicht, dann führt die Expansion zu einer Aneinanderreihung der Felder ganz ohne Trennzeichen (was in man bash soweit ich sehen konnte nur indirekt dokumentiert ist, nämlich bei dem speziellen Parameter $*).
Aber das eh nur um noch einen möglichen "Lösungsweg" darzustellen. Für die praktische Verwendung ist Deine Vorgehensweise nicht nur übersichtlicher sondern wohl auch wesentlich schneller (insb. bei großen Datenmengen). Im übrigen könnte man auch da noch mit tee die grep-Ausgabe in eine Datei umleiten um sie dann am Ende auszugeben:

Code: Alles auswählen

$ find -type f -print0 | xargs -0 grep -lZ 'USERALT' | tee temp.log | xargs -0 sed -i 's/USERALT/USERNEU/g' ; cat temp.log | tr "\0" "\n" | less

Soweit dazu ...
Willkommen im Forum!
Merci ;-)
Luca

luka
Beiträge: 14
Registriert: 12.01.2014 19:26:56

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von luka » 14.01.2014 16:17:20

newdeb hat geschrieben:
dirk11 hat geschrieben: Beachtung von Wortgrenzen im RegEx ist zu empfehlen
Oh ja, stimmt! Aber

Code: Alles auswählen

 ... sed -i s/USERALT/USERNEU/g "$g"
:kopfkratz:? mein gnu sed kennt auf jeden Fall

Code: Alles auswählen

sed -i 's/\bUSERALT\b/USERNEU/g' file

dirk11
Beiträge: 2840
Registriert: 02.07.2013 11:47:01

Re: User-Home kopieren, Einzeiler für Suchen&Ersetzen gesuch

Beitrag von dirk11 » 15.01.2014 01:34:11

luka hat geschrieben:Naja, bei langen Befehlszeilen und unklarem Vorwissen finde ich es immer blöd, wenn nicht erklärt wird, was da passiert (wobei sich das mit dem Vorwissen inzwischen auch für mich aufgeklärt hat, hätte mir das zumindest für den OP also sparen können).
Nein, hättest du nicht! Vielen Dank für die ausführlichen Erläuterungen, damit ist auch mir einiges klarer geworden! :THX:

Antworten