Ich habe nicht das Herz, alles noch einmal zu machen, aber ich habe dies als Antwort auf Commandline Find Sed Exec geschrieben . Dort wollte der Fragesteller wissen, wie ein ganzer Baum, möglicherweise mit Ausnahme eines oder zweier Verzeichnisse, verschoben und alle Dateien und Verzeichnisse mit der Zeichenfolge "OLD" in "NEW" umbenannt werden .
Neben der Beschreibung des wie mit mühsamer Ausführlichkeit unten kann dieses Verfahren auch eindeutig sein, dass es enthält eingebaute in debuggen. Grundsätzlich werden nur die Befehle kompiliert und in einer Variablen gespeichert, von denen es glaubt, dass sie ausgeführt werden sollen, um die angeforderte Arbeit auszuführen.
Außerdem werden Schleifen explizit so weit wie möglich vermieden . Abgesehen von der sedrekursiven Suche nach mehr als einer Übereinstimmung des Musters gibt es meines Wissens keine andere Rekursion.
Und zuletzt ist dies vollständig nullabgegrenzt - es wird kein Zeichen in einem Dateinamen außer dem ausgelöst null. Ich denke nicht, dass du das haben solltest.
Das ist übrigens WIRKLICH schnell. Aussehen:
% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" | tail -n 2 )
<actual process time used:>
0.06s user 0.03s system 106% cpu 0.090 total
<output from wc:>
Lines Words Bytes
115 362 20691 -
<output from tail:>
mv .config/replacement_word-chrome-beta/Default/.../googlestars \
.config/replacement_word-chrome-beta/Default/.../replacement_wordstars
HINWEIS: Für die oben genannten functionSchritte sind wahrscheinlich GNUVersionen von sedund erforderlich find, um die Aufrufe find printfund sed -z -eund ordnungsgemäß zu verarbeiten :;recursive regex test;t. Wenn Ihnen diese nicht zur Verfügung stehen, kann die Funktionalität wahrscheinlich mit ein paar geringfügigen Anpassungen dupliziert werden.
Dies sollte alles tun, was Sie von Anfang bis Ende mit sehr wenig Aufwand wollten. Ich habe forkmit sed, aber ich war auch einige praktizierende sedrekursive Verzweigung Techniken ist also, warum ich hier bin. Es ist so, als würde man in einer Friseurschule einen Rabatt-Haarschnitt bekommen, denke ich. Hier ist der Workflow:
rm -rf ${UNNECESSARY}
- Ich habe absichtlich jeden Funktionsaufruf ausgelassen, der Daten jeglicher Art löschen oder zerstören könnte. Sie erwähnen, dass
./appdies unerwünscht sein könnte. Löschen Sie es oder verschieben Sie es vorher an eine andere Stelle, oder Sie können alternativ eine \( -path PATTERN -exec rm -rf \{\} \)Routine einbauen, findum dies programmgesteuert zu tun, aber das gehört Ihnen.
_mvnfind "${@}"
- Deklarieren Sie die Argumente und rufen Sie die Worker-Funktion auf.
${sh_io}ist besonders wichtig, da es die Rückgabe von der Funktion speichert. ${sed_sep}kommt in einer knappen Sekunde; Dies ist eine beliebige Zeichenfolge, die verwendet wird, um auf seddie Rekursion in der Funktion zu verweisen . Wenn ${sed_sep}auf einen Wert gesetzt ist, der möglicherweise in einem Ihrer Pfad- oder Dateinamen gefunden werden kann, auf die reagiert wird ... nun, lassen Sie es einfach nicht zu.
mv -n $1 $2
- Der ganze Baum wird von Anfang an bewegt. Es wird viel Kopfschmerzen sparen; glaube mir. Der Rest von dem, was Sie tun möchten - das Umbenennen - ist einfach eine Frage der Metadaten des Dateisystems. Wenn Sie dies beispielsweise von einem Laufwerk auf ein anderes oder über Dateisystemgrenzen hinweg verschieben, ist es besser, dies sofort mit einem Befehl zu tun. Es ist auch sicherer. Beachten Sie die
-noclobberOption für mv; Wie geschrieben, wird diese Funktion nicht dort platzieren, ${SRC_DIR}wo ${TGT_DIR}bereits eine vorhanden ist.
read -R SED <<HEREDOC
- Ich habe alle Befehle von sed hier gefunden, um Ärger zu vermeiden, und sie in eine Variable eingelesen, um sie unten an sed weiterzuleiten. Erklärung unten.
find . -name ${OLD} -printf
- Wir beginnen den
findProzess. Mit finduns nur für etwas suchen , die Umbenennung braucht , weil wir bereits alle der tat Ort-zu-Ort - mvOperationen mit der ersten Befehl der Funktion. Anstatt eine direkte Aktion mit beispielsweise findeinem execAufruf auszuführen, verwenden wir sie stattdessen, um die Befehlszeile dynamisch mit aufzubauen -printf.
%dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
- Nachdem
findwir die benötigten Dateien gefunden haben, wird sie direkt erstellt und (der größte Teil ) des Befehls ausgedruckt, den wir zum Verarbeiten Ihrer Umbenennung benötigen. Das %dir-deptham Anfang jeder Zeile angeheftete Element stellt sicher, dass nicht versucht wird, eine Datei oder ein Verzeichnis im Baum mit einem übergeordneten Objekt umzubenennen, das noch umbenannt werden muss. findverwendet alle Arten von Optimierungstechniken, um Ihren Dateisystembaum zu durchsuchen, und es ist nicht sicher, ob die benötigten Daten in einer betriebssicheren Reihenfolge zurückgegeben werden. Deshalb werden wir als nächstes ...
sort -general-numerical -zero-delimited
- Wir sortieren die gesamte
findAusgabe danach, %directory-depthsodass die Pfade, die in Bezug auf $ {SRC} am nächsten liegen, zuerst bearbeitet werden. Dies vermeidet mögliche Fehler beim mvEinfügen von Dateien in nicht vorhandene Speicherorte und minimiert die Notwendigkeit einer rekursiven Schleife. ( Tatsächlich kann es schwierig sein, überhaupt eine Schleife zu finden. )
sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
- Ich denke, dies ist die einzige Schleife im gesamten Skript, und sie durchläuft nur die zweite Schleife, die
%Pathfür jede Zeichenfolge gedruckt wird, falls sie mehr als einen $ {OLD} -Wert enthält, der möglicherweise ersetzt werden muss. Alle anderen Lösungen, die ich mir vorgestellt habe, beinhalteten einen zweiten sedProzess, und obwohl eine kurze Schleife möglicherweise nicht wünschenswert ist, schlägt sie sicherlich das Laichen und Verzweigen eines gesamten Prozesses.
- Im Grunde genommen
sedwird hier nach $ {sed_sep} gesucht und dann, nachdem es gefunden wurde, es und alle Zeichen, auf die es trifft, gespeichert, bis es $ {OLD} findet, das es dann durch $ {NEW} ersetzt. Es kehrt dann zu $ {sed_sep} zurück und sucht erneut nach $ {OLD}, falls es mehr als einmal in der Zeichenfolge vorkommt. Wenn es nicht gefunden wird, druckt es die geänderte Zeichenfolge stdout(an die es dann als nächstes wieder abfängt) und beendet die Schleife.
- Dadurch wird vermieden, dass die gesamte Zeichenfolge analysiert werden muss, und es wird sichergestellt, dass die erste Hälfte der
mvBefehlszeichenfolge, die natürlich $ {OLD} enthalten muss, diese enthält und die zweite Hälfte so oft geändert wird, wie zum Löschen der Zeichenfolge erforderlich ist $ {OLD} Name aus mvdem Zielpfad.
sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
- Die beiden
-execAnrufe hier erfolgen ohne Sekunde fork. Wie wir gesehen haben, ändern wir im ersten Schritt den mvBefehl, der vom Funktionsbefehl find's bereitgestellt wird , -printfnach Bedarf, um alle Verweise von $ {OLD} auf $ {NEW} ordnungsgemäß zu ändern. Dazu mussten wir jedoch einige verwenden beliebige Referenzpunkte, die nicht in die endgültige Ausgabe aufgenommen werden sollten. Sobald sedalles erledigt ist, weisen wir es an, seine Referenzpunkte aus dem Haltepuffer zu löschen, bevor es weitergegeben wird.
Und jetzt sind wir wieder da
read erhält einen Befehl, der folgendermaßen aussieht:
% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000
Es wird reades ${msg}als ${sh_io}was nach Belieben außerhalb der Funktion untersucht werden kann.
Cool.
-Mike